기준: DVA-C02 시험 가이드오류 제보
운영 중인 서비스에서 가장 위험한 순간은 언제일까. 트래픽 폭증? DB 장애? Google의 SRE 책은 서비스 장애의 약 70%가 운영 중인 시스템의 변경에서 비롯된다고 설명한다. 그래서 가장 위험한 순간은 "배포 직후"다. 이유는 단순하다 — 배포가 끝났다는 사실 자체가 "지금까지 안 보이던 변경이 한꺼번에 사용자에게 노출됐다"는 뜻이기 때문이다.
CodeDeploy는 그 위험한 순간을 안전하게 만드는 도구다. "한 번에 다 바꾸지 말고 조금씩, 검증하면서, 문제 발생 시 즉시 되돌릴 수 있도록" 배포를 진행한다. DVA-C02 시험에서 CodeDeploy는 주요 영역이다. 특히 배포 대상별 차이(EC2 vs Lambda vs ECS), Blue/Green vs Canary vs Linear의 구분, appspec.yml의 라이프사이클 훅 순서를 알아야 한다.
CodeDeploy가 풀려는 문제는 "파일을 서버에 복사한다"가 아니라 "여러 노드에서 일어나는 상태 전환을 안전하게 조율한다"이다. 이 차이가 중요하다. 단순 파일 복사는 rsync나 scp로 끝나지만, production 배포는 ① 트래픽을 잠깐 빼고 ② 기존 프로세스를 종료하고 ③ 새 파일을 받고 ④ 의존성을 설치하고 ⑤ 프로세스를 시작하고 ⑥ 헬스 체크를 하고 ⑦ 트래픽을 다시 넣는, 7단계 이상의 순서가 있는 작업이다. 한 단계라도 실패하면 다음 단계로 넘어가면 안 되고, 여러 인스턴스에 걸쳐 일관된 순서로 진행돼야 한다.
CodeDeploy는 각 배포를 하나의 state machine으로 모델링한다 — 각 인스턴스는 Pending → InProgress → Succeeded/Failed의 상태를 가지고, 라이프사이클 훅이 각 상태 전환의 trigger가 된다. CodeDeploy의 차별점은 "각 훅에서 사용자 정의 스크립트 실행"을 1급 시민으로 다룬다는 점이다.
CodeDeploy Agent는 EC2 인스턴스에서 1분마다 CodeDeploy 컨트롤 플레인에 polling한다("새 배포가 있나?"). HTTP long-polling이 아닌 짧은 폴링 모델인데, 이는 ① 에이전트 구현 단순성 ② AWS 서버 부하 분산 ③ 일시적 네트워크 단절에서의 회복성 때문이다. 단점은 "배포 시작 명령 후 실제 시작까지 평균 30초 지연"이다. "왜 CodeDeploy가 즉시 시작하지 않는가?"라는 질문의 답은 이 polling 모델이다.
CodeDeploy는 EC2/온프레미스, Lambda, ECS 세 가지 대상을 지원하는데, 내부 동작이 완전히 다르다. 같은 "CodeDeploy"라는 이름을 쓰지만 사실은 세 가지 별개 서비스에 가깝다.
| 차원 | EC2/온프레미스 | Lambda | ECS |
|---|---|---|---|
| Agent 필요 | ✅ CodeDeploy Agent | ❌ (Lambda 자체 별칭 사용) | ❌ (ECS 서비스가 처리) |
| 배포 단위 | 파일 + 스크립트 | 함수 버전 + 별칭 가중치 | Task definition revision |
| 배포 전략 | In-Place / Blue/Green | Canary / Linear / AllAtOnce | Blue/Green only |
| 트래픽 전환 메커니즘 | ALB Target Group swap (B/G) 또는 변경 없음 (In-Place) | Lambda alias의 routing-config | ALB Target Group swap |
| appspec 형식 | YAML (files + hooks 섹션) | YAML/JSON (Resources + Hooks 섹션) | JSON (TaskDefinition + LoadBalancerInfo) |
| 롤백 메커니즘 | 이전 revision 재배포 | alias 가중치 복원 | 이전 target group 재연결 |
⚠️ 함정: "ECS에서 In-Place 배포를 사용하려면?"이라는 질문은 함정이다. ECS는 CodeDeploy로 Blue/Green만 지원한다. ECS 자체의 rolling update 기능은 CodeDeploy를 안 쓰고 ECS 서비스가 직접 하는 별개 메커니즘이다. 시험에서 "CodeDeploy로 ECS Blue/Green"이 나오면 정답이지만, "CodeDeploy로 ECS In-Place"는 존재하지 않는다.
EC2 배포의 핵심은 라이프사이클 훅이다. CodeDeploy Agent가 각 단계를 순차적으로 실행하고, 각 단계에서 사용자가 정의한 스크립트가 실패하면 전체 배포가 실패 처리된다.
In-Place 배포 (7단계)
====================
1. ApplicationStop [user script] 기존 앱 정지
2. DownloadBundle [agent] S3/GitHub에서 revision 다운로드
3. BeforeInstall [user script] 설치 전 작업(백업 등)
4. Install [agent] appspec.yml의 files 섹션 복사
5. AfterInstall [user script] 설치 후 작업(권한, 설정)
6. ApplicationStart [user script] 앱 시작
7. ValidateService [user script] 헬스 체크
Blue/Green 배포 (추가 단계)
==========================
Blue 인스턴스: 1~7 + BeforeBlockTraffic → BlockTraffic → AfterBlockTraffic
Green 인스턴스: 1~7 + BeforeAllowTraffic → AllowTraffic → AfterAllowTraffic
DownloadBundle과 Install은 사용자 정의가 불가능한 agent 내부 단계다. 이를 모르면 "BeforeInstall에서 파일을 미리 받으려 했는데 안 됐다"고 오해하기 쉽다. Bundle은 zip/tar로 패키징돼 있고 agent가 /opt/codedeploy-agent/deployment-root/<deployment-group-id>/<deployment-id>/deployment-archive에 푼다. BeforeInstall은 그 압축 풀린 디렉터리에는 접근 가능하지만 destination(예: /var/www/html)으로 복사되기 직전 상태다.
ValidateService는 Kubernetes의 readiness/liveness probe와 같은 역할을 한다.
version: 0.0
os: linux
files:
- source: build/ # 압축 해제된 디렉터리 내 경로
destination: /var/www/myapp
- source: nginx.conf
destination: /etc/nginx/conf.d/myapp.conf
file_exists_behavior: OVERWRITE # DISALLOW | OVERWRITE | RETAIN
# DISALLOW: 기존 파일 있으면 실패 (안전)
# OVERWRITE: 무조건 덮어쓰기
# RETAIN: 기존 파일 유지, 신규만 복사
permissions:
- object: /var/www/myapp
pattern: "**"
owner: nginx
group: nginx
mode: 644
type:
- file
- object: /var/www/myapp/bin
pattern: "**"
mode: 755
type:
- file
hooks:
ApplicationStop:
- location: scripts/stop.sh
timeout: 60
runas: root
BeforeInstall:
- location: scripts/backup.sh
timeout: 30
AfterInstall:
- location: scripts/configure.sh
timeout: 120
ApplicationStart:
- location: scripts/start.sh
timeout: 60
ValidateService:
- location: scripts/healthcheck.sh
timeout: 30ApplicationStop 훅은 이전 revision의 스크립트가 실행된다. 즉 v1 → v2 배포 시 ApplicationStop은 v1에 들어있던 스크립트가 돌아간다. 이걸 모르고 "ApplicationStop에서 새 기능을 추가했는데 안 돌더라"는 경우가 있는데, 이번 배포에서 추가한 스크립트는 다음 배포 때 ApplicationStop으로 처음 실행된다. 시험에 나오는 미세한 동작이다.
CodeDeploy의 "Deployment Configuration"은 한 번에 몇 개 인스턴스에 동시 배포할지 결정한다.
| 구성 | 동시 배포 | "성공" 기준 | 시험 키워드 |
|---|---|---|---|
CodeDeployDefault.AllAtOnce | 전체 인스턴스 | 1개 이상 성공 시 deployment success | "가장 빠른 배포" |
CodeDeployDefault.HalfAtATime | 50% | 50% 이상 정상 | "균형" |
CodeDeployDefault.OneAtATime | 1개 | 모든 인스턴스 성공 | "가장 안전한 점진적" |
Custom: Min Healthy Hosts = 75% | 25%만 동시 배포 | 75% 이상 정상 | 큰 fleet의 점진적 |
AllAtOnce가 "1개만 성공해도 deployment 자체는 success"라는 정의는 직관과 어긋난다. 이유는 "deployment status"와 "service availability"가 별개라는 CodeDeploy의 철학 때문이다. 배포 자체는 끝났고 그 결과 fleet의 1개 인스턴스라도 새 버전을 받았다면 의도한 작업은 완료된 것 — 다만 그 결과로 서비스가 가용한지는 별개 문제이고 CloudWatch Alarm으로 판단해야 한다는 분리다. 운영 시 함정이 된다.
Netflix는 자체 배포 도구 Spinnaker에서 "Rolling Red/Black" 패턴을 쓰는데, 이는 CodeDeploy의 OneAtATime + ValidateService를 매우 엄격하게 검증하는 패턴과 본질이 같다. 한 인스턴스에 새 버전 배포 → 1-5분 트래픽 받게 두고 메트릭 관찰 → 정상이면 다음 인스턴스, 비정상이면 즉시 롤백. CodeDeploy의 OneAtATime + CloudWatch Alarm + Auto Rollback 조합으로 거의 동일한 효과를 만들 수 있다.
In-Place 배포는 같은 인스턴스의 파일을 교체한다. 빠르고 비용이 안 들지만 롤백이 어렵다 — 이전 파일이 이미 사라졌기 때문이다. Blue/Green은 새 ASG를 통째로 띄워 새 인스턴스에 v2를 배포하고, ALB Target Group을 새 ASG로 swap한다. 잘못되면 원래 ASG로 swap만 되돌리면 끝.
[ALB] ──── Production Listener (port 80)
│ │
│ ├── Target Group "blue" ← ASG-blue (v1, 인스턴스 3개)
│ │
│ └── Target Group "green" ← ASG-green (v2, 인스턴스 3개) [신규]
│
└── [배포 진행 시 Listener Rule이 green으로 전환]
Blue/Green의 trade-off:
| 항목 | 값 |
|---|---|
| 인프라 비용 | 검증 기간 동안 2배 |
| 롤백 시간 | 수 초 (Target Group swap만) |
| In-Place 대비 배포 시간 | 약간 더 김 (ASG 프로비저닝 + Warmup) |
| Stateful 워크로드 적합성 | 낮음 (인스턴스가 바뀌므로 EBS 데이터 손실) |
Blue/Green 배포 후 "원본(Blue) ASG는 어떻게 되는가?"가 시험에 나온다. 옵션은 두 가지. ① 즉시 종료(Terminate original instances immediately) ② 지정 시간 후 종료(Wait N minutes then terminate). 후자는 swap 후에도 잠깐 Blue를 살려두면 빠른 롤백이 가능한 안전망 역할이다. 보통 5분~1시간 사이로 설정한다.
Lambda는 EC2와 완전히 다른 메커니즘이다. 파일을 어디 복사하는 게 아니라 함수의 새 버전을 publish하고, alias의 routing-config로 트래픽 비율을 조정한다.
# Lambda alias 트래픽 분할 (Boto3)
import boto3
lambda_client = boto3.client('lambda')
# v1이 90%, v2가 10% 받도록 alias 'live' 설정
lambda_client.update_alias(
FunctionName='my-function',
Name='live',
FunctionVersion='1', # 기본 버전
RoutingConfig={
'AdditionalVersionWeights': {
'2': 0.1 # v2가 10%
}
}
)CodeDeploy의 Lambda 배포는 이 update_alias 호출을 미리 정의된 schedule로 자동화한다. 사전 정의 구성은 다음 9가지다.
| Configuration | 첫 전환 비율 | 두 번째 전환까지 대기 | 완전 전환 시점 |
|---|---|---|---|
LambdaAllAtOnce | 100% | - | 즉시 |
LambdaCanary10Percent5Minutes | 10% | 5분 | 5분 후 |
LambdaCanary10Percent10Minutes | 10% | 10분 | 10분 후 |
LambdaCanary10Percent15Minutes | 10% | 15분 | 15분 후 |
LambdaCanary10Percent30Minutes | 10% | 30분 | 30분 후 |
LambdaLinear10PercentEvery1Minute | 10% | 1분마다 +10% | 10분 후 |
LambdaLinear10PercentEvery2Minutes | 10% | 2분마다 +10% | 20분 후 |
LambdaLinear10PercentEvery3Minutes | 10% | 3분마다 +10% | 30분 후 |
LambdaLinear10PercentEvery10Minutes | 10% | 10분마다 +10% | 100분 후 |
Canary와 Linear의 차이는 시험의 핵심이다. Canary는 "10%를 N분 두고 본 다음 한 번에 90%로 가는" 2단계 전환이다. 처음 10%에서 문제 발생을 모니터링한 다음, 큰 결정 한 번으로 전체로 간다. Linear는 "10%, 20%, 30%, ..."로 매 N분마다 균등 증가한다. Canary는 빠르고 한쪽에 모이는 위험이 있는 반면, Linear는 느리고 점진적이다. AB 테스트가 필요한 경우 Linear가, 빠른 검증 후 전체 배포가 필요한 경우 Canary가 적합하다.
# Lambda appspec.yml
version: 0.0
Resources:
- MyLambdaFunction:
Type: AWS::Lambda::Function
Properties:
Name: my-function
Alias: live
CurrentVersion: 1
TargetVersion: 2
Hooks:
- BeforeAllowTraffic: pre-traffic-validation-fn # 트래픽 받기 전 검증
- AfterAllowTraffic: post-traffic-validation-fn # 전환 완료 후 검증훅에서 호출되는 Lambda 함수는 마지막에 반드시 PutLifecycleEventHookExecutionStatus API를 호출해 Succeeded/Failed를 보고해야 한다. 안 하면 timeout(기본 1시간)까지 배포가 멈춰 있는다.
def lambda_handler(event, context):
# event에 deploymentId와 lifecycleEventHookExecutionId가 들어있음
deployment_id = event['DeploymentId']
hook_execution_id = event['LifecycleEventHookExecutionId']
# 검증 로직 (smoke test, contract test 등)
success = run_smoke_test()
codedeploy = boto3.client('codedeploy')
codedeploy.put_lifecycle_event_hook_execution_status(
deploymentId=deployment_id,
lifecycleEventHookExecutionId=hook_execution_id,
status='Succeeded' if success else 'Failed'
)BeforeAllowTraffic 훅이 실패하면 새 버전 트래픽 0%로 즉시 롤백된다. AfterAllowTraffic 훅이 실패하면 이미 전환이 끝난 상태에서 다시 이전 버전으로 자동 롤백된다 — 즉 두 훅 모두 자동 롤백 trigger가 된다. 시험에 "Lambda 배포 중 검증 실패 시 동작"이 나오면 이 두 훅과 자동 롤백을 같이 본다.
배포 중 또는 직후에 에러율이 폭증하면 자동으로 롤백하고 싶다. CodeDeploy는 CloudWatch Alarm과 통합돼 있다.
# DeploymentGroup 설정 (CloudFormation 발췌)
DeploymentGroup:
AutoRollbackConfiguration:
Enabled: true
Events:
- DEPLOYMENT_FAILURE # 배포 자체 실패
- DEPLOYMENT_STOP_ON_ALARM # 알람 발생으로 정지
- DEPLOYMENT_STOP_ON_REQUEST # 사용자 수동 정지
AlarmConfiguration:
Enabled: true
Alarms:
- Name: lambda-error-rate-high
- Name: lambda-duration-high
IgnorePollAlarmFailure: falseIgnorePollAlarmFailure가 false이면 CloudWatch에서 알람 상태를 조회 못 할 때 배포가 fail-stop한다. 보안상 보수적이지만 CloudWatch 일시 장애 때 배포가 막힌다. true로 두면 알람 조회 실패 시 배포를 그냥 진행 — 가용성 우선. 둘 다 정답이 없고 운영 정책에 따라 다르다.
ECS는 CodeDeploy 사용 시 Blue/Green만 가능하다. 동작 원리는 EC2 Blue/Green과 비슷하지만, 인스턴스가 아니라 Task definition revision이 단위다.
[ALB Production Listener:80]
│
├── Target Group "blue" ← ECS Service의 Blue Tasks (revision N)
│
└── Target Group "green" ← ECS Service의 Green Tasks (revision N+1) [신규]
[ALB Test Listener:8080] ← 배포 중 Green을 검증할 수 있는 별도 Listener
│
└── Target Group "green"
배포 흐름:
ECS Blue/Green을 쓰려면 네 가지가 필요하다.
deploymentController.type을 CODE_DEPLOY로 설정한 가지라도 빠지면 배포 시작 자체가 실패한다. 시험에서 "ECS Blue/Green 사전 조건"이 나오면 이 4가지가 모두 답.
{
"version": 0.0,
"Resources": [
{
"TargetService": {
"Type": "AWS::ECS::Service",
"Properties": {
"TaskDefinition": "arn:aws:ecs:...:task-definition/myapp:42",
"LoadBalancerInfo": {
"ContainerName": "app",
"ContainerPort": 80
},
"PlatformVersion": "LATEST"
}
}
}
],
"Hooks": [
{ "BeforeInstall": "pre-validation-fn" },
{ "AfterInstall": "post-task-up-fn" },
{ "AfterAllowTestTraffic": "smoke-test-fn" },
{ "BeforeAllowTraffic": "final-check-fn" },
{ "AfterAllowTraffic": "post-deploy-fn" }
]
}| 차원 | CodeDeploy | Spinnaker | Argo Rollouts | Octopus Deploy |
|---|---|---|---|---|
| 호스팅 | AWS managed | self-hosted | Kubernetes-native | self-hosted/SaaS |
| 대상 | EC2/Lambda/ECS | 멀티클라우드 + K8s | K8s only | 멀티 + .NET 강함 |
| Canary | Lambda만 native, EC2는 fleet 부분 배포 | 풍부 (자동 분석) | 풍부 (Prometheus 통합) | 제한적 |
| 학습 곡선 | 낮음 | 매우 높음 | 중간 | 낮음 |
| IAM 통합 | 네이티브 | OIDC 가능 | K8s ServiceAccount | 외부 통합 |
# Amazon Linux 2 설치
sudo yum install -y ruby wget
wget https://aws-codedeploy-${REGION}.s3.${REGION}.amazonaws.com/latest/install
chmod +x ./install
sudo ./install auto
# Systemd로 관리
sudo systemctl enable codedeploy-agent
sudo systemctl start codedeploy-agent
sudo systemctl status codedeploy-agent
# 로그 위치 (디버깅 필수)
tail -f /var/log/aws/codedeploy-agent/codedeploy-agent.log
tail -f /opt/codedeploy-agent/deployment-root/deployment-logs/codedeploy-agent-deployments.log
# 특정 배포의 모든 hook 로그
ls /opt/codedeploy-agent/deployment-root/<deployment-group-id>/<deployment-id>/logs/Agent는 Ruby로 만들어져 있어 Ruby가 설치돼 있어야 동작한다. 최소 컨테이너 이미지에 Ruby가 없으면 agent 설치가 실패한다.
CodeDeploy의 본질은 "배포라는 위험한 순간을 여러 작은 단계로 쪼개고, 각 단계마다 검증과 롤백 메커니즘을 박아 놓는 것"이다. EC2에서는 라이프사이클 훅으로, Lambda에서는 alias 가중치로, ECS에서는 task definition swap으로 같은 사상을 다른 방식으로 구현한다.
다음 글에서는 이 모든 단계를 자동으로 흘려보내는 오케스트레이터 — CodePipeline — 을 본다. 소스 변경 감지부터 빌드, 테스트, 승인, 배포까지의 전체 흐름을 코드로 관리하는 방법, 그리고 멀티 리전·멀티 계정 파이프라인의 패턴까지 짚는다.
선택지를 클릭하면 정답·해설이 펼쳐집니다.
문제 1
정답 3개 선택EC2에 CodeDeploy를 사용하기 위한 필수 전제 조건을 모두 고르면?
문제 2
Lambda 함수를 새 버전으로 전환하면서 처음 10분간 10%만 트래픽을 보낸 후 한 번에 나머지를 전환하려 한다. 적합한 배포 구성은?
문제 3
appspec.yml의 라이프사이클 훅 중 새 버전이 실패해도 항상 실행되는 것은? (EC2 In-Place 기준)
문제 4
ECS 서비스에 CodeDeploy로 Blue-Green 배포를 구성하려 한다. 필수 사전 조건이 아닌 것은?
문제 5
EC2 fleet 100대에 In-Place 배포를 진행 중 CloudWatch Alarm ErrorRate > 5%가 발생했다. 자동 롤백이 동작하려면 어떤 설정이 필요한가?
문제 6
CodeDeploy의 Blue-Green 배포 후 원본 ASG를 5분 후에 종료하도록 설정했다. 그 5분 동안의 의미는?
문제 7
다음 중 CodeDeploy의 배포 대상이 아닌 것은?