๋๋ ํจ๊ป ์ผํ๊ธฐ ์์ํ๋ค ๊ตฌ๋ฆ ํ์ฑ 4 ๋ ์ . ๊ทธ ์ดํ๋ก ์ ๋ ์ด๋ฏธ ์์ฐ ์ค์ธ ์ธํ๋ผ๋ฅผ ํฌํจํ์ฌ ๋ง์ ์ธํ๋ผ๋ฅผ ๋ง๊ฐ๋จ๋ ธ์ต๋๋ค. ํ์ง๋ง ๋ญ๊ฐ๋ฅผ ๋ง์น ๋๋ง๋ค ์๋ก์ด ๊ฒ์ ๋ฐฐ์ ์ต๋๋ค. ์ด ๊ฒฝํ์ ํตํด ์ ๊ฐ ๋ฐฐ์ด ๊ฐ์ฅ ์ค์ํ ๊ตํ ์ค ์ผ๋ถ๋ฅผ ๊ณต์ ํ๊ฒ ์ต๋๋ค.
๋ ์จ 1: ๋ณ๊ฒฝ์ฌํญ์ ๋ฐฐํฌํ๊ธฐ ์ ์ ํ ์คํธ
๋๋ ์ด ๊ตํ์ ์ผ์ ์์ํ ์งํ์ ๋ฐฐ์ ๋ค. ๊ตฌ๋ฆ ํ์ฑ. ๊ทธ๋ ์ ํํ ๋ฌด์์ ๊นจ๋จ๋ ธ๋์ง๋ ๊ธฐ์ต๋์ง ์์ง๋ง, ๋ค์ ๋ช ๋ น์ ์ฌ์ฉํ ๊ฒ์ ํ์คํ ๊ธฐ์ตํฉ๋๋ค. AWS ํด๋ผ์ฐ๋ํฌ๋ฉ์ด์ ์ ๋ฐ์ดํธ. ์ด ๋ช ๋ น์ ๋ฐฐํฌ๋ ๋ณ๊ฒฝ ์ฌํญ์ ํ์ธํ์ง ์๊ณ ๋จ์ํ ํ ํ๋ฆฟ์ ๋กค์์ํฉ๋๋ค. ๋ชจ๋ ๋ณ๊ฒฝ ์ฌํญ์ ๋ฐฐํฌํ๊ธฐ ์ ์ ํ ์คํธํด์ผ ํ๋ ์ด์ ์ ๋ํด์๋ ์ค๋ช ์ด ํ์ํ์ง ์๋ค๊ณ ์๊ฐํฉ๋๋ค.
์ด ์คํจ ํ ๋๋ ์ฆ์ ๋ฐ๋์๋ค ๋ฐฐํฌ ํ์ดํ ๋ผ์ธ, ์ ๋ฐ์ดํธ ๋ช ๋ น์ ๋ค์ ๋ช ๋ น์ผ๋ก ๋ฐ๊ฟ๋๋ค. ์์ฑ-๋ณ๊ฒฝ-์ธํธ
# OPERATION is either "UPDATE" or "CREATE"
changeset_id=$(aws cloudformation create-change-set
--change-set-name "$CHANGE_SET_NAME"
--stack-name "$STACK_NAME"
--template-body "$TPL_PATH"
--change-set-type "$OPERATION"
--parameters "$PARAMETERS"
--output text
--query Id)
aws cloudformation wait
change-set-create-complete --change-set-name "$changeset_id"
๋ณ๊ฒฝ ์ธํธ๊ฐ ์์ฑ๋๋ฉด ๊ธฐ์กด ์คํ์๋ ์ํฅ์ ์ฃผ์ง ์์ต๋๋ค. update ๋ช
๋ น๊ณผ ๋ฌ๋ฆฌ ๋ณ๊ฒฝ ์งํฉ ์ ๊ทผ ๋ฐฉ์์ ์ค์ ๋ฐฐํฌ๋ฅผ ํธ๋ฆฌ๊ฑฐํ์ง ์์ต๋๋ค. ๋์ ๋ฐฐํฌ ์ ์ ๊ฒํ ํ ์ ์๋ ๋ณ๊ฒฝ ์ฌํญ ๋ชฉ๋ก์ ์์ฑํฉ๋๋ค. aws ์ฝ์ ์ธํฐํ์ด์ค์์ ๋ณ๊ฒฝ ์ฌํญ์ ๋ณผ ์ ์์ต๋๋ค. ํ์ง๋ง ๊ฐ๋ฅํ ๋ชจ๋ ๊ฒ์ ์๋ํํ๊ณ ์ถ๋ค๋ฉด CLI์์ ํ์ธํ์ธ์.
# this command is presented only for demonstrational purposes.
# the real command should take pagination into account
aws cloudformation describe-change-set
--change-set-name "$changeset_id"
--query 'Changes[*].ResourceChange.{Action:Action,Resource:ResourceType,ResourceId:LogicalResourceId,ReplacementNeeded:Replacement}'
--output table
์ด ๋ช ๋ น์ ๋ค์๊ณผ ์ ์ฌํ ์ถ๋ ฅ์ ์์ฑํด์ผ ํฉ๋๋ค.
--------------------------------------------------------------------
| DescribeChangeSet |
+---------+--------------------+----------------------+------------+
| Action | ReplacementNeeded | Resource | ResourceId |
+---------+--------------------+----------------------+------------+
| Modify | True | AWS::ECS::Cluster | MyCluster |
| Replace| True | AWS::RDS::DBInstance| MyDB |
| Add | None | AWS::SNS::Topic | MyTopic |
+---------+--------------------+----------------------+------------+
Action์ด ๋ณ๊ฒฝ๋ ๋ถ๋ถ์ ํน๋ณํ ์ฃผ์๋ฅผ ๊ธฐ์ธ์ด์ธ์. ๊ต์ฒด, . ์๋๋ฉด ์ด๋์ ๊ต์ฒด ํ์ - ์ฐธ. ์ด๋ ๊ฐ์ฅ ์ํํ ๋ณ๊ฒฝ์ด๋ฉฐ ์ผ๋ฐ์ ์ผ๋ก ์ ๋ณด ์์ค๋ก ์ด์ด์ง๋๋ค.
๋ณ๊ฒฝ ์ฌํญ์ด ๊ฒํ ๋๋ฉด ๋ฐฐํฌํ ์ ์์ต๋๋ค.
aws cloudformation execute-change-set --change-set-name "$changeset_id"
operation_lowercase=$(echo "$OPERATION" | tr '[:upper:]' '[:lower:]')
aws cloudformation wait "stack-${operation_lowercase}-complete"
--stack-name "$STACK_NAME"
๋ ์จ 2: ์คํ ์ ์ฑ ์ ์ฌ์ฉํ์ฌ ์ํ ์ ์ฅ ๋ฆฌ์์ค๊ฐ ๊ต์ฒด๋๊ฑฐ๋ ์ ๊ฑฐ๋๋ ๊ฒ์ ๋ฐฉ์ง
๋จ์ํ ๋ณ๊ฒฝ ์ฌํญ์ ๋ณด๋ ๊ฒ๋ง์ผ๋ก๋ ์ถฉ๋ถํ์ง ์์ ๊ฒฝ์ฐ๋ ์์ต๋๋ค. ์ฐ๋ฆฌ๋ ๋ชจ๋ ์ธ๊ฐ์ด๊ณ ๋ชจ๋ ์ค์๋ฅผ ํฉ๋๋ค. ๋ณ๊ฒฝ ์ธํธ๋ฅผ ์ฌ์ฉํ๊ธฐ ์์ํ ์งํ ํ์์ด ์์ ๋ ๋ชจ๋ฅด๊ฒ ๋ฐฐํฌ๋ฅผ ์ํํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์ ๋ฐ์ดํธ๋์์ต๋๋ค. ํ ์คํธ ํ๊ฒฝ์ด์๊ธฐ ๋๋ฌธ์ ๋์ ์ผ์ ์์์ต๋๋ค.
์คํฌ๋ฆฝํธ๊ฐ ๋ณ๊ฒฝ ์ฌํญ ๋ชฉ๋ก์ ํ์ํ๊ณ ํ์ธ์ ์์ฒญํ์ง๋ง ๋ณ๊ฒฝ ์ฌํญ ๋ชฉ๋ก์ด ๋๋ฌด ์ปค์ ํ๋ฉด์ ๋ง์ง ์์ ๋ฐ๊พธ๊ธฐ ๋ณ๊ฒฝ์ ๊ฑด๋๋ฐ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ด๋ ํ ์คํธ ํ๊ฒฝ์์๋ ์ผ๋ฐ์ ์ธ ์ ๋ฐ์ดํธ์๊ธฐ ๋๋ฌธ์ ๋ณ๊ฒฝ ์ฌํญ์ ํฐ ๊ด์ฌ์ ๋์ง ์์์ต๋๋ค.
๊ต์ฒดํ๊ฑฐ๋ ์ ๊ฑฐํ๊ณ ์ถ์ง ์์ ๋ฆฌ์์ค๊ฐ ์์ต๋๋ค. ์ด๋ RDS ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ธ์คํด์ค ๋๋ Elasticsearch ํด๋ฌ์คํฐ ๋ฑ๊ณผ ๊ฐ์ ์ํ ์ ์ฅ ์๋น์ค์
๋๋ค. ์ํ ์ค์ธ ์์
์์ ํด๋น ๋ฆฌ์์ค๋ฅผ ์ญ์ ํด์ผ ํ๋ ๊ฒฝ์ฐ aws๊ฐ ์๋์ผ๋ก ๋ฐฐํฌ๋ฅผ ๊ฑฐ๋ถํ๋ฉด ์ข์ ๊ฒ์
๋๋ค. ๋คํ์ค๋ฝ๊ฒ๋ cloudformation์๋ ์ด๋ฅผ ์ํํ๋ ๋ฐฉ๋ฒ์ด ๋ด์ฅ๋์ด ์์ต๋๋ค. ์ด๋ฅผ ์คํ ์ ์ฑ
์ด๋ผ๊ณ ํ๋ฉฐ ์ด์ ๋ํ ์์ธํ ๋ด์ฉ์ ๋ค์์์ ํ์ธํ ์ ์์ต๋๋ค.
STACK_NAME=$1
RESOURCE_ID=$2
POLICY_JSON=$(cat <<EOF
{
"Statement" : [{
"Effect" : "Deny",
"Action" : [
"Update:Replace",
"Update:Delete"
],
"Principal": "*",
"Resource" : "LogicalResourceId/$RESOURCE_ID"
}]
}
EOF
)
aws cloudformation set-stack-policy --stack-name "$STACK_NAME"
--stack-policy-body "$POLICY_JSON"
๋ ์จ 3: ๋น๋ฐ ๋งค๊ฐ๋ณ์๋ก ์คํ์ ์ ๋ฐ์ดํธํ ๋ UsePreviousValue ์ฌ์ฉ
RDS mysql ์ํฐํฐ๋ฅผ ์์ฑํ ๋ AWS์์๋ MasterUsername ๋ฐ MasterUserPassword ์ ๊ณต์ ์๊ตฌํฉ๋๋ค. ์์ค ์ฝ๋์ ๋น๋ฐ์ ์ ์งํ์ง ์๋ ๊ฒ์ด ๋ ๋ซ๊ณ ๋ชจ๋ ๊ฒ์ ์์ ํ ์๋ํํ๊ณ ์ถ์๊ธฐ ๋๋ฌธ์ ๋ฐฐํฌํ๊ธฐ ์ ์ ์๊ฒฉ ์ฆ๋ช ์ s3์์ ๊ฐ์ ธ์ค๊ณ ์๊ฒฉ ์ฆ๋ช ์ ์ฐพ์ ์ ์๋ ๊ฒฝ์ฐ ์ ์๊ฒฉ ์ฆ๋ช ์ ์์ฑํ๋ "์ค๋งํธ ๋ฉ์ปค๋์ฆ"์ ๊ตฌํํ์ต๋๋ค. s3 ์ ์ ์ฅ๋ฉ๋๋ค.
๊ทธ๋ฌ๋ฉด ์ด๋ฌํ ์๊ฒฉ ์ฆ๋ช ์ด cloudformation create-change-set ๋ช ๋ น์ ๋งค๊ฐ๋ณ์๋ก ์ ๋ฌ๋ฉ๋๋ค. ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ๋ ๋์ s3์ ๋ํ ์ฐ๊ฒฐ์ด ๋์ด์ก๊ณ ๋ด "์ค๋งํธ ๋ฉ์ปค๋์ฆ"์ด ์ด๋ฅผ ์ ์๊ฒฉ ์ฆ๋ช ์ ์์ฑํ๋ ์ ํธ๋ก ์ฒ๋ฆฌํ์ต๋๋ค.
ํ๋ก๋์ ์์ ์ด ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ๊ธฐ ์์ํ๋๋ฐ ์ฐ๊ฒฐ ๋ฌธ์ ๊ฐ ๋ค์ ๋ฐ์ํ๋ฉด ์ ์๊ฒฉ ์ฆ๋ช ์ผ๋ก ์คํ์ด ์ ๋ฐ์ดํธ๋ฉ๋๋ค. ์ด ํน๋ณํ ๊ฒฝ์ฐ์๋ ๋์ ์ผ์ด ์ผ์ด๋์ง ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ ๋ ์ด ์ ๊ทผ ๋ฐฉ์์ ํฌ๊ธฐํ๊ณ ์คํ์ ์์ฑํ ๋ ์๊ฒฉ ์ฆ๋ช ์ ํ ๋ฒ๋ง ์ ๊ณตํ๋ ๋ค๋ฅธ ์ ๊ทผ ๋ฐฉ์์ ์ฌ์ฉํ๊ธฐ ์์ํ์ต๋๋ค. ๋์ค์ ์คํ์ ์ ๋ฐ์ดํธํด์ผ ํ ๋ ๋งค๊ฐ๋ณ์์ ๋น๋ฐ ๊ฐ์ ์ง์ ํ๋ ๋์ ๊ฐ๋จํ ๋ค์์ ์ฌ์ฉํฉ๋๋ค. UsePreviousValue=true:
aws cloudformation create-change-set
--change-set-name "$CHANGE_SET_NAME"
--stack-name "$STACK_NAME"
--template-body "$TPL_PATH"
--change-set-type "UPDATE"
--parameters "ParameterKey=MasterUserPassword,UsePreviousValue=true"
๋ ์จ 4: ๋กค๋ฐฑ ๊ตฌ์ฑ ์ฌ์ฉ
์ ๊ฐ ํจ๊ป ์ผํ๋ ๋ค๋ฅธ ํ์ด ์ด ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ต๋๋ค. ๊ตฌ๋ฆ ํ์ฑ~๋ผ๊ณ ๋ถ๋ฆฌ๋ ๋กค๋ฐฑ ๊ตฌ์ฑ. ๋๋ ์ด์ ์ ๊ทธ๊ฒ์ ๋ณธ ์ ์ด ์์์ผ๋ฉฐ ๊ทธ๊ฒ์ด ๋ด ์คํ ๋ฐฐํฌ๋ฅผ ๋์ฑ ๋ฉ์ง๊ฒ ๋ง๋ค ๊ฒ์ด๋ผ๋ ๊ฒ์ ๋นจ๋ฆฌ ๊นจ๋ฌ์์ต๋๋ค. ์ด์ ๋ cloudformation์ ์ฌ์ฉํ์ฌ ๋๋ค ๋๋ ECS์ ์ฝ๋๋ฅผ ๋ฐฐํฌํ ๋๋ง๋ค ์ด๋ฅผ ์ฌ์ฉํฉ๋๋ค.
์๋ ๋ฐฉ์: ๊ทํ๊ฐ ์ง์ ํฉ๋๋ค. CloudWatch ๊ฒฝ๋ณด ๋งค๊ฐ๋ณ์์์ --๋กค๋ฐฑ ๊ตฌ์ฑ๋ณ๊ฒฝ ์ธํธ๋ฅผ ์์ฑํ ๋. ๋์ค์ ์ผ๋ จ์ ๋ณ๊ฒฝ ์ฌํญ์ ์คํํ๋ฉด aws๋ ์ต์ XNUMX๋ถ ๋์ ๊ฒฝ๋ณด๋ฅผ ๋ชจ๋ํฐ๋งํฉ๋๋ค. ์ด ์๊ฐ ๋์ ๊ฒฝ๋ณด ์ํ๊ฐ ALARM์ผ๋ก ๋ณ๊ฒฝ๋๋ฉด ๋ฐฐํฌ๋ฅผ ๋กค๋ฐฑํฉ๋๋ค.
๋ค์์ ํ ํ๋ฆฟ ๋ฐ์ท์ ์์ ๋๋ค. ๊ตฌ๋ฆ ํ์ฑ๋ด๊ฐ ๋ง๋๋ ๊ณณ ํด๋ผ์ฐ๋์์น ์๋, ํด๋ผ์ฐ๋ ๋ก๊ทธ์ ์ค๋ฅ ์๋ก ํด๋ผ์ฐ๋ ์ฌ์ฉ์ ์ธก์ ํญ๋ชฉ์ ์ถ์ ํฉ๋๋ค. ์ธก์ ํญ๋ชฉ์ ๋ค์์ ํตํด ์์ฑ๋ฉ๋๋ค. ๋ฉํธ๋ฆญ ํํฐ):
Resources:
# this metric tracks number of errors in the cloudwatch logs. In this
# particular case it's assumed logs are in json format and the error logs are
# identified by level "error". See FilterPattern
ErrorMetricFilter:
Type: AWS::Logs::MetricFilter
Properties:
LogGroupName: !Ref LogGroup
FilterPattern: !Sub '{$.level = "error"}'
MetricTransformations:
- MetricNamespace: !Sub "${AWS::StackName}-log-errors"
MetricName: Errors
MetricValue: 1
DefaultValue: 0
ErrorAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: !Sub "${AWS::StackName}-errors"
Namespace: !Sub "${AWS::StackName}-log-errors"
MetricName: Errors
Statistic: Maximum
ComparisonOperator: GreaterThanThreshold
Period: 1 # 1 minute
EvaluationPeriods: 1
Threshold: 0
TreatMissingData: notBreaching
ActionsEnabled: yes
์ง๊ธ ๊ฒฝ๋ณด ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ ์ ์์ต๋๋ค. ๋กค๋ฐฑ ๋๊ตฌ ์์๋ฅผ ์คํํ ๋ ํธ๋ฆฌ๊ฑฐ:
ALARM_ARN=$1
ROLLBACK_TRIGGER=$(cat <<EOF
{
"RollbackTriggers": [
{
"Arn": "$ALARM_ARN",
"Type": "AWS::CloudWatch::Alarm"
}
],
"MonitoringTimeInMinutes": 1
}
EOF
)
aws cloudformation create-change-set
--change-set-name "$CHANGE_SET_NAME"
--stack-name "$STACK_NAME"
--template-body "$TPL_PATH"
--change-set-type "UPDATE"
--rollback-configuration "$ROLLBACK_TRIGGER"
๋ ์จ 5: ์ต์ ๋ฒ์ ์ ํ ํ๋ฆฟ์ ๋ฐฐํฌํด์ผ ํฉ๋๋ค.
์ต์ ๋ฒ์ ์ด ์๋ cloudformation ํ ํ๋ฆฟ์ ๋ฐฐํฌํ๋ ๊ฒ์ ์ฝ์ง๋ง ๊ทธ๋ ๊ฒ ํ๋ฉด ๋ง์ ํผํด๋ฅผ ์ ๊ฒ ๋ฉ๋๋ค. ์ด๋ฐ ์ผ์ด ํ ๋ฒ ์ผ์ด๋ฌ์ต๋๋ค. ๊ฐ๋ฐ์๊ฐ Git์ ์ต์ ๋ณ๊ฒฝ ์ฌํญ์ ํธ์ํ์ง ์์๊ณ ์์ ๋ ๋ชจ๋ฅด๊ฒ ์ด์ ๋ฒ์ ์ ์คํ์ ๋ฐฐํฌํ์ต๋๋ค. ์ด๋ก ์ธํด ์ด ์คํ์ ์ฌ์ฉํ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ ์ค์ง ์๊ฐ์ด ๋ฐ์ํ์ต๋๋ค.
์ปค๋ฐํ๊ธฐ ์ ์ ๋ธ๋์น๊ฐ ์ต์ ์ธ์ง ํ์ธํ๊ธฐ ์ํด ๊ฒ์ฌ๋ฅผ ์ถ๊ฐํ๋ ๊ฒ๋งํผ ๊ฐ๋จํ ๊ฒ๋ ๊ด์ฐฎ์ต๋๋ค(git์ด ๋ฒ์ ์ ์ด ๋๊ตฌ๋ผ๊ณ ๊ฐ์ ).
git fetch
HEADHASH=$(git rev-parse HEAD)
UPSTREAMHASH=$(git rev-parse master@{upstream})
if [[ "$HEADHASH" != "$UPSTREAMHASH" ]] ; then
echo "Branch is not up to date with origin. Aborting"
exit 1
fi
๊ตํ 6: ๋ฐํด๋ฅผ ์ฌ๋ฐ๋ช ํ์ง ๋ง์ธ์
๋ค์์ ์ฌ์ฉํ์ฌ ๋ฐฐํฌํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ผ ์ ์์ต๋๋ค. ๊ตฌ๋ฆ ํ์ฑ - ๊ทธ๊ฒ์ ๊ฐ๋จํฉ๋๋ค. aws cli ๋ช ๋ น์ ์คํํ๋ bash ์คํฌ๋ฆฝํธ๊ฐ ํ์ํฉ๋๋ค.
4๋ ์ ์ ๋ aws cloudformation create-stack ๋ช ๋ น์ด๋ผ๋ ๊ฐ๋จํ ์คํฌ๋ฆฝํธ๋ก ์์ํ์ต๋๋ค. ๊ณง ๋๋ณธ์ ๋ ์ด์ ๋จ์ํ์ง ์๊ฒ ๋์์ต๋๋ค. ๋ฐฐ์ด ๊ฐ ๊ตํ์ ์คํฌ๋ฆฝํธ๋ฅผ ์ ์ ๋ ๋ณต์กํ๊ฒ ๋ง๋ค์์ต๋๋ค. ์ด๋ ค์ธ ๋ฟ๋ง ์๋๋ผ ๋ฒ๊ทธ๋ ๋ง์์ต๋๋ค.
์ ๋ ํ์ฌ ์๊ท๋ชจ IT ๋ถ์์์ ๊ทผ๋ฌดํ๊ณ ์์ต๋๋ค. ๊ฒฝํ์ ๋ฐ๋ฅด๋ฉด ๊ฐ ํ์๋ Cloudformation ์คํ์ ๋ฐฐํฌํ๋ ๊ณ ์ ํ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ๊ฒ์ ๋์๋ค. ๋ชจ๋๊ฐ ๊ฐ์ ์ ๊ทผ ๋ฐฉ์์ ์ทจํ๋ฉด ๋ ์ข์ ๊ฒ์ ๋๋ค. ๋คํํ Cloudformation ์คํ์ ๋ฐฐํฌํ๊ณ ๊ตฌ์ฑํ๋ ๋ฐ ๋์์ด ๋๋ ๋ง์ ๋๊ตฌ๊ฐ ์์ต๋๋ค.
์ด ์์
์ ์ค์๋ฅผ ํผํ๋ ๋ฐ ๋์์ด ๋ ๊ฒ์
๋๋ค.
์ถ์ฒ : habr.com