CertNote/ 출퇴근 학습 노트
로드맵
KOEN
CLF-C02 · FoundationalCloud Practitioner - Foundational
DVA-C02 · AssociateDeveloper - Associate
SAA-C03 · AssociateSolutions Architect - Associate
SOA-C02 · AssociateCloudOps Engineer - Associate
  • Week 1
    • 1.운영자가 보는 AWS 인프라: 리전, AZ, 그리고 책임의 경계선
    • 2.IAM의 운영자 사용설명서: 사용자·그룹·역할·정책 평가 알고리즘
    • 3.IAM 심화: Permission Boundary, SCP, Identity Center로 짓는 가드레일
    • 4.AWS Organizations: 100개 계정을 한 사람이 운영하는 법
    • 5.Week 1 통합 복습: 운영자 시나리오로 다시 보는 첫 주
  • Week 2
  • Week 3
  • Week 4
  • Week 5
  • Week 6
    • 1.CloudFormation: 인프라를 코드로, 운영자의 시각으로
    • 2.Change Set, Drift Detection, Rollback Trigger: 안전한 CFn 운영의 3대 기둥
    • 3.Nested Stack, Cross-Stack Reference, StackSets: 대규모 IaC 운영
    • 4.Service Catalog, AppConfig, AppRegistry: 거버넌스와 동적 설정의 기술
    • 5.Week 6 종합 복습: CloudFormation 운영 완전 정복
  • Week 7
  • Week 8
  • Week 9
  • Week 10
  • Week 11
  • Week 12
SAP-C02 · ProfessionalSolutions Architect - Professional
DOP-C02 · ProfessionalDevOps Engineer - Professional
SCS-C03 · SpecialtySecurity - Specialty
MLA-C01 · AssociateMachine Learning Engineer - Associate
AIF-C01 · FoundationalAI Practitioner - Foundational
DEA-C01 · AssociateData Engineer - Associate
MLS-C01 · SpecialtyMachine Learning - Specialty
합격 후기

© CertNote · 주간랩스

  • 소개
  • 수정 기록
  • 문의
  • 이용약관
  • 개인정보처리방침
← SOA-C02/Week 6/Day 1

기준: SOA-C02 시험 가이드·오류 제보

SOA-C02· AssociateWeek 6 · Day 1읽기 약 21분

Day 1 - CloudFormation: 인프라를 코드로, 운영자의 시각으로

2011년 Netflix는 "Chaos Monkey"를 공개했다. 프로덕션 환경에서 무작위로 인스턴스를 종료시키는 도구다. 이 도구가 존재할 수 있었던 이유는 Netflix가 인프라를 코드로 관리했기 때문이다. 인스턴스가 죽어도 CloudFormation(또는 그 당시의 IaC 도구)이 자동으로 동일한 인스턴스를 다시 만들어냈다. 수동으로 구성된 인프라라면 Chaos Monkey는 재앙이었을 것이다.

CloudFormation은 AWS의 공식 IaC(Infrastructure as Code) 서비스다. 2011년 2월 출시됐으며, JSON 템플릿으로 시작해 현재는 YAML을 기본 형식으로 사용한다. SOA-C02에서 CloudFormation은 단순히 "인프라 생성 도구"가 아니라 "운영자가 변경을 안전하게 관리하고, 상태 drift를 감지하고, 실패 시 자동 복구하는" 운영 플랫폼으로 다뤄진다.

IaC의 역사와 CloudFormation의 위치

IaC 개념은 2006년 Mark Burgess(CFEngine 저자)의 논문 "In Search of Certainty"에서 체계화됐다. "시스템의 원하는 상태를 선언적으로 기술하라"는 원칙이 핵심이다. 2009년 Chef, 2011년 Puppet 엔터프라이즈, 2012년 Ansible, 2014년 Terraform이 등장했다. Terraform은 2014년 HashiCorp의 Mitchell Hashimoto가 "Write, Plan, Apply" 워크플로로 발표했는데, 이 패턴이 이후 IaC 업계의 표준이 됐다.

CloudFormation의 독특한 위치는 AWS에 완전히 네이티브라는 것이다. 다른 IaC 도구들이 AWS를 하나의 provider로 지원하는 반면, CloudFormation은 AWS 서비스 그 자체다. AWS가 새 서비스를 출시하면 해당 일 또는 수 주 내에 CloudFormation 리소스 타입이 추가된다. Terraform에서 신규 서비스를 지원하려면 커뮤니티 또는 HashiCorp의 Provider 업데이트가 필요하다.

IaC 도구 심화 비교:

특성CloudFormationTerraformPulumiCDKAnsible
언어JSON/YAMLHCLPython/TS/Go/C#TS/Python/Java/GoYAML + Jinja2
상태 관리AWS 내부 (Stack)S3+DynamoDB 파일Pulumi Cloud / S3CFn으로 변환없음(멱등 실행)
AWS 통합완전 네이티브Provider 경유Provider 경유CFn으로 변환boto3/module
멀티 클라우드XOOXO
드리프트 감지기본 내장terraform planpulumi refreshCDK DriftX
롤백자동수동수동자동(CFn 경유)X
출시 연도20112014201820192012
오픈소스XO(BSL)OOO

CloudFormation의 "Declarative" 방식은 수학의 집합론과 유사하다. "이 집합(인프라)에 이런 원소(리소스)들이 있어야 한다"고 선언하면, 현재 상태와 목표 상태의 차이를 시스템이 계산해 수렴시킨다. 명령형(Imperative) 방식인 Shell 스크립트가 "Step 1: EC2 생성, Step 2: SG 생성..."처럼 절차를 기술하는 것과 대조된다. 선언적 방식의 핵심은 멱등성(Idempotency): 같은 템플릿을 여러 번 적용해도 결과가 동일하다. 이 개념은 Dijkstra의 "분리 가능한 추상화(Separable Abstraction)" 원칙까지 거슬러 올라가며, 1970년대 하드웨어 기술 독립성(machine independence) 논쟁의 소프트웨어 버전이기도 하다.

CloudFormation 엔진은 내부적으로 DAG(Directed Acyclic Graph) 기반으로 리소스 의존성을 분석한다. !Ref와 !GetAtt로 리소스 간 의존성이 정의되면 CFn이 자동으로 위상 정렬(Topological Sort)을 수행해 어떤 리소스를 먼저 만들고 병렬로 처리할 수 있는지 결정한다. DependsOn을 명시적으로 추가하면 CFn이 자동 감지하지 못하는 암묵적 의존성(예: IAM 권한 전파 지연)을 수동으로 표현할 수 있다. 이 DAG 접근법은 GNU Make의 Makefile 의존성 분석, Apache Airflow의 DAG 스케줄링과 동일한 CS 이론 기반이다.

CloudFormation 엔진 내부 작동 원리

CloudFormation이 create-stack을 받으면 내부적으로 다음 과정을 거친다.

1단계: 템플릿 파싱과 검증

  • JSON/YAML 파싱 → 내부 표현으로 변환
  • 10개 섹션 구조 검증
  • Intrinsic Functions 해석 (재귀적 평가)
  • 순환 참조 탐지 (A → B → A 구조 차단)

2단계: 의존성 그래프 구성

  • !Ref와 !GetAtt에서 암묵적 의존성 추출
  • DependsOn에서 명시적 의존성 추가
  • DAG 유효성 검사 (순환 없는지)
  • 위상 정렬로 생성 순서 결정

3단계: 병렬 리소스 생성

  • 의존성 없는 리소스들을 동시에 생성 (최대 병렬 처리)
  • 의존 관계에 있는 리소스는 선행 리소스 완료 대기
  • 각 리소스 생성 API 호출 → 폴링으로 완료 확인
  • CloudWatch Events로 이벤트 스트림 방출

4단계: CreationPolicy 신호 대기 (있는 경우)

  • Count 수만큼 cfn-signal 수신 대기
  • Timeout 초과 시 실패 처리
  • 성공 신호 수신 시 다음 단계 진행

5단계: 스택 상태 최종화

  • 모든 리소스 성공 → CREATE_COMPLETE
  • 어느 리소스라도 실패 → ROLLBACK_IN_PROGRESS 시작
CloudFormation 내부 처리 흐름:

Template YAML
    │
    ▼
┌─────────────────────────────────────────┐
│  CFn Parser                             │
│  - JSON/YAML → IR (Internal Repr.)      │
│  - Intrinsic Function 평가              │
│  - Pseudo Parameter 치환                │
└──────────────────┬──────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────┐
│  Dependency Analyzer (DAG Builder)      │
│  !Ref / !GetAtt → 암묵 의존성           │
│  DependsOn → 명시 의존성               │
│  Topological Sort → 실행 계획          │
└──────────────────┬──────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────┐
│  Resource Executor (병렬)               │
│  ┌─────────┐  ┌─────────┐             │
│  │ IAM Role│  │  VPC    │  (병렬)      │
│  └────┬────┘  └────┬────┘             │
│       └─────┬──────┘                   │
│             ▼                          │
│        ┌─────────┐                     │
│        │EC2 inst │  (위 둘 완료 후)     │
│        └─────────┘                     │
└─────────────────────────────────────────┘

템플릿 10대 섹션: 운영자가 알아야 할 것만

CloudFormation Template은 최대 10개 섹션으로 구성된다. 필수는 Resources만이고 나머지는 선택이다. 이 사실이 시험에서 반복 출제된다.

AWSTemplateFormatVersion: '2010-09-09'  # 선택 (현재 유일한 값)
Description: 'My application stack'     # 선택
 
Metadata:           # 선택 - 콘솔 UI 힌트, 파라미터 그룹화
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "Database Configuration"
        Parameters: [DBInstanceClass, DBPassword]
 
Parameters:         # 선택 - 스택 생성 시 입력값
  EnvType:
    Type: String
    AllowedValues: [dev, staging, prod]
    Default: dev
    Description: "Deployment environment"
 
Mappings:           # 선택 - 룩업 테이블 (Region → AMI 등)
  RegionAMIMap:
    ap-northeast-2:
      AMI: ami-0c9c942bd7bf113a2
    us-east-1:
      AMI: ami-0c55b159cbfafe1f0
 
Conditions:         # 선택 - 조건부 리소스/속성
  IsProd: !Equals [!Ref EnvType, prod]
  IsNotDev: !Not [!Equals [!Ref EnvType, dev]]
 
Transform:          # 선택 - SAM, 매크로 변환
  - AWS::Serverless-2016-10-31
 
Resources:          # 필수 - 실제 AWS 리소스 정의
  MyBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub 'myapp-${EnvType}-${AWS::AccountId}'
 
Outputs:            # 선택 - 다른 스택에 노출하거나 콘솔에서 확인
  BucketArn:
    Value: !GetAtt MyBucket.Arn
    Export:
      Name: !Sub '${AWS::StackName}-BucketArn'
 
Rules:              # 선택 - Parameter 간 교차 유효성 검사
  MustUseProdDB:
    RuleCondition: !Equals [!Ref EnvType, prod]
    Assertions:
      - Assert: !Equals [!Ref DBInstanceClass, db.r5.large]
        AssertDescription: "Production must use db.r5.large"

Parameters: 입력값의 정교한 제어

Parameters는 템플릿을 재사용 가능하게 만드는 핵심이다. 동일한 템플릿으로 dev, staging, prod 환경을 만들 수 있다.

Parameter 타입 전체 목록:

타입설명예시
String일반 문자열"t3.medium"
Number정수 또는 부동소수점3306
List<Number>숫자 목록[80, 443]
CommaDelimitedList쉼표 구분 문자열"a,b,c"
AWS::EC2::KeyPair::KeyName계정의 기존 키 페어 이름my-key
AWS::EC2::SecurityGroup::Id계정의 기존 SG IDsg-abc123
AWS::EC2::Subnet::Id계정의 기존 서브넷 IDsubnet-abc
AWS::EC2::VPC::Id계정의 기존 VPC IDvpc-abc
AWS::SSM::Parameter::Value<String>SSM 파라미터에서 값 가져오기/myapp/config
AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>SSM에서 최신 AMI ID/aws/service/ami-amazon-linux-latest/...

동적 참조 (Parameter 없이 외부 값 직접 참조):

# Secrets Manager에서 직접 참조
DBPassword:
  !Sub '{{resolve:secretsmanager:prod/db/password:SecretString:password}}'
 
# Parameter Store에서 직접 참조
DBHost:
  !Sub '{{resolve:ssm:/myapp/prod/database/host}}'
 
# Parameter Store에서 암호화된 값 참조
DBPassword2:
  !Sub '{{resolve:ssm-secure:/myapp/prod/database/password}}'

AWS::SSM::Parameter::Value<AWS::EC2::Image::Id> 타입은 AWS가 공식적으로 운영하는 SSM 파라미터 경로(/aws/service/ami-amazon-linux-latest/...)에서 최신 AMI ID를 자동으로 가져온다. 이 경로의 값은 AWS가 새 AMI를 출시할 때마다 자동으로 업데이트한다. 즉, 같은 템플릿을 1년 후에 배포해도 항상 최신 Amazon Linux 2 AMI가 사용된다. 하드코딩이나 Mapping으로 AMI ID를 관리할 때 발생하는 "AMI ID가 deprecated됐다" 문제가 사라진다. 주의할 점은 스택 업데이트 없이 SSM 파라미터 값이 바뀐 경우, 스택을 다시 배포하기 전까지 반영되지 않는다는 것이다. 이는 Drift Detection에서 파라미터 변경이 감지되지 않는 맹점이기도 하다.

내장 함수 (Intrinsic Functions): 운영자 필수

함수문법용도
!Ref!Ref MyBucket파라미터 값, 리소스의 기본 식별자 (EC2면 Instance ID, S3면 버킷 이름)
!GetAtt!GetAtt MyBucket.Arn리소스의 특정 속성 (ARN, DNS 이름 등)
!Sub!Sub 'prefix-${EnvType}'문자열에 변수 삽입
!FindInMap!FindInMap [RegionAMIMap, !Ref AWS::Region, AMI]Mappings에서 값 조회
!If!If [IsProd, t3.large, t3.micro]조건에 따라 두 값 중 선택
!ImportValue!ImportValue 'network-stack-VpcId'다른 스택의 Export 값 가져오기
!Join!Join [':', [a, b, c]] → "a:b:c"목록을 구분자로 연결
!Select!Select [0, !GetAZs '']목록에서 인덱스로 선택
!GetAZs!GetAZs 'ap-northeast-2'리전의 AZ 목록
!Cidr!Cidr [10.0.0.0/16, 6, 12]CIDR 블록 자동 분할
!Base64!Base64 '#!/bin/bash ...'Base64 인코딩 (UserData용)
!Split!Split [',', 'a,b,c'] → [a, b, c]문자열을 구분자로 분리

함수 중첩 예시 (실전 패턴):

# AZ 목록에서 첫 번째 AZ의 서브넷 자동 선택
SubnetId: !Select
  - 0
  - !GetAZs ''
 
# 조건부 인스턴스 타입 + Sub 중첩
InstanceType: !If
  - IsProd
  - !Sub 'r5.${ProdInstanceSize}'
  - t3.micro
 
# CIDR 자동 분할 (VPC 서브넷 자동 생성)
CidrBlock: !Select
  - 0
  - !Cidr [!GetAtt VPC.CidrBlock, 6, 12]
  # VPC CIDR을 6개 /20 서브넷으로 분할, 첫 번째 선택

스택 라이프사이클: 모든 상태를 알아야 트러블슈팅이 된다

Stack 상태 전이도:

CREATE_IN_PROGRESS ──────────────► CREATE_COMPLETE
        │
        └──(실패)──► ROLLBACK_IN_PROGRESS ──► ROLLBACK_COMPLETE
                                                    │
                                                    └── [업데이트 불가, 삭제만 가능]

CREATE_COMPLETE ──(update-stack)──► UPDATE_IN_PROGRESS ──► UPDATE_COMPLETE
                                            │
                                            └──(실패)──► UPDATE_ROLLBACK_IN_PROGRESS
                                                                │
                                                                ├──(성공)──► UPDATE_ROLLBACK_COMPLETE
                                                                │
                                                                └──(실패)──► UPDATE_ROLLBACK_FAILED
                                                                                    │
                                                                                    └── continue-update-rollback 필요

UPDATE_COMPLETE ──(delete-stack)──► DELETE_IN_PROGRESS ──► DELETE_COMPLETE
                                            │
                                            └──(실패)──► DELETE_FAILED
                                                             │
                                                             └── retain 옵션으로 문제 리소스 제외 후 재삭제

중요 상태 설명:

상태원인운영자 조치
ROLLBACK_COMPLETE최초 스택 생성 실패 후 롤백 완료삭제 후 재생성. update-stack 불가
UPDATE_ROLLBACK_FAILED업데이트 롤백 중 추가 실패continue-update-rollback + resources-to-skip
DELETE_FAILED리소스 삭제 실패 (비어있지 않은 S3, 연결된 ENI 등)--retain-resources 로 문제 리소스 보존 후 재삭제
IMPORT_ROLLBACK_FAILED기존 리소스 Import 중 실패Import 대상 리소스 상태 확인 후 재시도
# ROLLBACK_COMPLETE 상태 Stack 삭제
aws cloudformation delete-stack --stack-name failed-stack
aws cloudformation wait stack-delete-complete --stack-name failed-stack
 
# UPDATE_ROLLBACK_FAILED 복구
aws cloudformation continue-update-rollback \
  --stack-name my-stack \
  --resources-to-skip LogicalResourceId1 LogicalResourceId2
 
# DELETE_FAILED 복구: 문제 리소스 보존하며 Stack 삭제
aws cloudformation delete-stack \
  --stack-name my-stack \
  --retain-resources ProblematicS3Bucket ProblematicENI
 
# Stack 이벤트로 실패 원인 확인
aws cloudformation describe-stack-events \
  --stack-name my-stack \
  --query 'StackEvents[?ResourceStatus==`CREATE_FAILED` || ResourceStatus==`UPDATE_FAILED`].[Timestamp,LogicalResourceId,ResourceStatusReason]' \
  --output table

⚠️ 함정: ROLLBACK_COMPLETE 상태는 처음 스택 생성이 실패한 경우의 최종 상태다. 이 상태에서는 update-stack이 불가능하고, 반드시 delete-stack 후 재생성해야 한다. 이를 모르고 update-stack을 시도하면 Stack is not in a valid state for UpdateStack 오류가 발생한다. continue-update-rollback은 UPDATE_ROLLBACK_FAILED 전용으로, ROLLBACK_COMPLETE에는 적용되지 않는다. 시험에서 "스택을 ROLLBACK_COMPLETE에서 복구하려면?" 문제의 정답은 항상 "삭제 후 재생성"이다.

Resource 속성: DeletionPolicy, UpdateReplacePolicy, CreationPolicy

DeletionPolicy: 스택 삭제 시 리소스 처리

MyDatabase:
  Type: AWS::RDS::DBInstance
  DeletionPolicy: Snapshot    # Stack 삭제 전 스냅샷 생성 후 인스턴스 삭제
  Properties: ...
 
MyBucket:
  Type: AWS::S3::Bucket
  DeletionPolicy: Retain       # Stack 삭제 시 버킷은 그대로 보존
  Properties: ...
 
MyCacheCluster:
  Type: AWS::ElastiCache::ReplicationGroup
  DeletionPolicy: Delete       # 기본값: Stack 삭제 시 리소스 삭제
  Properties: ...
DeletionPolicy 값동작사용 리소스
Delete (기본)스택 삭제 시 리소스 삭제임시 리소스
Retain스택 삭제 시 리소스 보존S3, DynamoDB, 중요 데이터
Snapshot스택 삭제 전 스냅샷 생성 후 삭제RDS, ElastiCache, EBS

UpdateReplacePolicy: 업데이트 시 리소스 교체가 발생할 때

UpdateReplacePolicy는 스택 업데이트 중 리소스 교체(Replacement)가 발생할 때 기존 리소스를 어떻게 처리할지 결정한다. DeletionPolicy는 스택 삭제 시 동작하고, UpdateReplacePolicy는 업데이트 시 교체 조건에서 동작한다.

MyDatabase:
  Type: AWS::RDS::DBInstance
  DeletionPolicy: Retain
  UpdateReplacePolicy: Snapshot  # 업데이트로 인해 DB가 교체될 때 기존 DB 스냅샷 후 삭제
  Properties:
    DBInstanceClass: db.t3.medium  # → db.r5.large로 변경 시 RDS 교체 발생
    MultiAZ: false                  # → true로 변경 시 RDS 교체 발생

리소스 교체(Replacement)가 발생하는 속성 변경 예시:

리소스변경 속성교체 발생 여부
AWS::RDS::DBInstanceDBInstanceClassTrue
AWS::RDS::DBInstanceMultiAZ (false→true)True
AWS::S3::BucketBucketNameTrue
AWS::EC2::InstanceImageId (AMI ID)True
AWS::EC2::InstanceInstanceType조건부 (EBS최적화 여부)
AWS::DynamoDB::TableTableNameTrue
AWS::ElastiCache::ReplicationGroupNumCacheClusters조건부

가상의 사례로, 2022년 E커머스 F사에서 CloudFormation 업데이트 중 RDS 인스턴스가 예상치 못하게 교체되는 사고가 발생했다. DB 인스턴스 클래스를 db.t3.medium에서 db.r5.xlarge로 변경하면서 동시에 MultiAZ: false를 MultiAZ: true로 변경했다. AWS는 MultiAZ 변경을 Replacement로 처리했고, UpdateReplacePolicy가 기본값 Delete로 설정되어 있어 기존 DB가 스냅샷 없이 삭제됐다. 최근 수동 스냅샷에서 4시간 치 데이터를 잃었다. 이후 모든 RDS에 DeletionPolicy: Snapshot과 UpdateReplacePolicy: Snapshot을 표준 정책으로 설정하고, Change Set 검토 시 Replacement: True 리소스를 반드시 확인하는 프로세스를 도입했다.

CreationPolicy: 리소스 생성 완료 신호 대기

EC2가 User Data 스크립트를 모두 실행하고 애플리케이션이 준비될 때까지 CloudFormation이 기다리게 하는 패턴이다.

WebServer:
  Type: AWS::EC2::Instance
  CreationPolicy:
    ResourceSignal:
      Count: 1
      Timeout: PT15M  # ISO 8601 Duration: 15분 대기
  Properties:
    ImageId: !Ref AmiId
    UserData:
      Fn::Base64: !Sub |
        #!/bin/bash
        yum update -y
        yum install -y httpd
        systemctl start httpd
        systemctl enable httpd
        echo "<h1>Hello from ${AWS::StackName}</h1>" > /var/www/html/index.html
        # 설정 완료 후 CloudFormation에 신호 전송
        /opt/aws/bin/cfn-signal \
          --exit-code $? \
          --stack ${AWS::StackName} \
          --resource WebServer \
          --region ${AWS::Region}

cfn-signal이 exit-code 0으로 신호를 보내면 CloudFormation이 CREATE_COMPLETE로 진행한다. exit-code 1이거나 Timeout 내에 신호가 오지 않으면 리소스 생성 실패로 처리된다.

cfn-signal 디버깅 패턴:

# cfn-signal 실패 시 EC2 시스템 로그 확인 (콘솔)
aws ec2 get-console-output --instance-id i-xxxx
 
# UserData 실행 로그 확인 (SSM Session Manager로 접속 후)
cat /var/log/cfn-init.log
cat /var/log/cfn-init-cmd.log
cat /var/log/cloud-init-output.log
 
# cfn-signal 성공 여부 CloudFormation 이벤트에서 확인
aws cloudformation describe-stack-events \
  --stack-name my-stack \
  --query 'StackEvents[?ResourceStatus==`CREATE_FAILED`].[LogicalResourceId,ResourceStatusReason]'

⚠️ 함정: cfn-signal Timeout은 ISO 8601 Duration 형식으로 표기한다. PT15M(15분), PT1H(1시간), PT1H30M(1시간 30분). 가장 많은 실수는 Timeout을 너무 짧게 설정하는 것이다. OS 패키지 업데이트(yum update -y)만 해도 리전/네트워크 상태에 따라 5-10분 걸릴 수 있다. 최소 PT15M을 권장하며, 무거운 소프트웨어(Java, Node.js 빌드 등)가 포함되면 PT30M 이상을 설정해야 한다. Timeout이 발생하면 이벤트에 "WaitCondition timed out" 또는 "Failed to receive N resource signal(s) within the specified duration" 메시지가 표시된다.

Auto Scaling Group의 UpdatePolicy (Rolling Update):

WebASG:
  Type: AWS::AutoScaling::AutoScalingGroup
  CreationPolicy:
    ResourceSignal:
      Count: !Ref DesiredCapacity
      Timeout: PT20M
  UpdatePolicy:
    AutoScalingRollingUpdate:
      MaxBatchSize: 2              # 한 번에 2개씩 교체
      MinInstancesInService: 1     # 최소 1개는 항상 서비스 중
      PauseTime: PT5M              # 배치 간 5분 대기 (WaitOnResourceSignals=false 시)
      WaitOnResourceSignals: true  # true면 PauseTime 대신 cfn-signal 대기
      SuspendProcesses:            # 롤링 업데이트 중 ASG 자동화 프로세스 일시 중지
        - HealthCheck
        - ReplaceUnhealthy
        - AZRebalance
  Properties:
    MinSize: 2
    MaxSize: 6
    DesiredCapacity: !Ref DesiredCapacity

AutoScalingRollingUpdate 핵심 파라미터 상호작용:

파라미터역할주의사항
MaxBatchSize한 번에 교체할 인스턴스 수DesiredCapacity보다 작아야 함
MinInstancesInService항상 유지해야 할 최소 인스턴스 수MaxBatchSize + MinInstancesInService ≤ MaxSize 권장
PauseTimeWaitOnResourceSignals=false 시 배치 간 대기WaitOnResourceSignals=true면 무시됨
WaitOnResourceSignalscfn-signal 대기 여부true면 각 인스턴스의 CreationPolicy 신호 대기
SuspendProcesses롤링 중 일시 중지할 ASG 프로세스AZRebalance 없으면 불균형 상태 발생 가능

CreationPolicy와 cfn-signal의 패턴은 분산 시스템의 "readiness probe" 개념과 동일하다. Kubernetes의 readinessProbe처럼, 애플리케이션이 준비됐다는 외부 신호를 받기 전까지 트래픽을 전달하지 않는다. CloudFormation에서는 이 신호가 cfn-signal이고, 신호를 받기 전까지 스택은 CREATE_IN_PROGRESS 상태를 유지하며 다음 리소스 생성으로 넘어가지 않는다. Rolling Update에서 WaitOnResourceSignals=true는 각 배치마다 이 readiness 체크를 반복해, "이 인스턴스가 정말 트래픽을 받을 준비가 됐는가"를 CloudFormation이 직접 확인하는 구조다. Martin Fowler의 "Blue/Green Deployment" 패턴이 클러스터 전체를 교체하는 반면, AutoScalingRollingUpdate는 인스턴스를 점진적으로 교체하는 "Canary Release"에 가깝다.

Stack Policy: 핵심 리소스 업데이트 차단

Stack Policy는 스택 업데이트 시 특정 리소스를 실수로 수정·삭제하는 것을 방지하는 방화벽이다. IAM 정책이 사람(Principal)의 API 접근을 제어하는 것과 달리, Stack Policy는 CloudFormation이 특정 리소스에 수행할 수 있는 작업을 제어한다.

{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "Update:*",
      "Principal": "*",
      "Resource": "*"
    },
    {
      "Effect": "Deny",
      "Action": ["Update:Replace", "Update:Delete"],
      "Principal": "*",
      "Resource": "LogicalResourceId/ProdDatabase"
    },
    {
      "Effect": "Deny",
      "Action": "Update:*",
      "Principal": "*",
      "Resource": "LogicalResourceId/ProdCacheCluster"
    }
  ]
}

Stack Policy Action 종류:

Action의미
Update:Modify기존 리소스의 속성을 In-Place로 수정
Update:Replace기존 리소스를 삭제하고 새로 생성 (Replacement)
Update:Delete리소스를 스택에서 제거하고 삭제
Update:*위 세 가지 모두
# Stack Policy 설정
aws cloudformation set-stack-policy \
  --stack-name my-prod-stack \
  --stack-policy-body file://stack-policy.json
 
# Stack Policy 일시 override (관리자 권한 필요)
aws cloudformation update-stack \
  --stack-name my-prod-stack \
  --template-body file://template.yaml \
  --stack-policy-during-update-body '{"Statement":[{"Effect":"Allow","Action":"Update:*","Principal":"*","Resource":"*"}]}'
 
# 현재 Stack Policy 확인
aws cloudformation get-stack-policy --stack-name my-prod-stack

Stack Policy의 평가 방식은 IAM 정책과 동일한 명시적 Deny 우선 원칙을 따른다. 기본값이 "모든 리소스 업데이트 허용"인 점이 IAM(기본값: 모든 것 거부)과 다르다. Stack Policy가 설정되지 않은 경우 모든 업데이트가 허용된다. 반면 Stack Policy가 한 번 설정되면, 명시적으로 허용(Effect: Allow)하지 않은 모든 리소스는 Deny된다. 따라서 특정 리소스만 차단하려면 반드시 "모든 리소스 허용" Statement를 명시적으로 추가해야 한다. 이 패턴은 방화벽 규칙의 "Allow All, Deny Specific"과 동일하다.

Pseudo Parameters: 항상 사용 가능한 내장 변수

# 사용 가능한 Pseudo Parameters
${AWS::AccountId}     # 현재 AWS 계정 ID
${AWS::Region}        # 현재 리전 (예: ap-northeast-2)
${AWS::StackId}       # Stack ARN
${AWS::StackName}     # Stack 이름
${AWS::URLSuffix}     # 도메인 접미사 (amazonaws.com 또는 중국은 amazonaws.com.cn)
${AWS::Partition}     # aws, aws-cn, aws-us-gov
${AWS::NoValue}       # 조건부로 속성을 제거할 때
 
# 활용 예시
BucketName: !Sub 'myapp-${AWS::AccountId}-${AWS::Region}-${EnvType}'
# → myapp-123456789012-ap-northeast-2-prod
 
# AWS::NoValue 활용: 조건부 속성 제거
MultiAZ: !If
  - IsProd
  - true
  - !Ref AWS::NoValue  # dev 환경에서는 MultiAZ 속성 자체를 제거 (기본값 false 사용)

Partition 활용 (GovCloud, 중국 리전 지원):

# 리전에 관계없이 올바른 ARN 형식 구성
PolicyArn: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/MyPolicy'
# us-east-1: arn:aws:iam::123456789012:policy/MyPolicy
# cn-north-1: arn:aws-cn:iam::123456789012:policy/MyPolicy
# us-gov-east-1: arn:aws-us-gov:iam::123456789012:policy/MyPolicy

트러블슈팅 패턴: 운영자의 일상

디버깅을 위한 롤백 비활성화:

# Stack 생성 시 실패해도 롤백하지 않음 → 실패한 리소스가 남아 직접 확인 가능
aws cloudformation create-stack \
  --stack-name debug-stack \
  --template-body file://template.yaml \
  --on-failure DO_NOTHING \
  --parameters ...
 
# 업데이트 시 롤백 비활성화
aws cloudformation update-stack \
  --stack-name my-stack \
  --template-body file://template.yaml \
  --disable-rollback

주의: DO_NOTHING/--disable-rollback은 비용이 계속 청구되므로 디버깅 후 반드시 정리한다.

템플릿 유효성 검사:

# Template 문법 검사 (AWS 서버에서 검증)
aws cloudformation validate-template \
  --template-body file://template.yaml
 
# cfn-lint로 로컬 검사 (더 상세한 검증)
pip install cfn-lint
cfn-lint template.yaml
 
# cfn-lint 규칙별 검사 (SOA 시험 관련 규칙)
cfn-lint template.yaml --include-checks W  # Warning 포함
cfn-lint template.yaml -r E3002            # 특정 규칙만 검사

CAPABILITY 플래그:

# IAM 리소스를 생성하는 Template에 필요
aws cloudformation create-stack \
  --capabilities CAPABILITY_IAM         # IAM 리소스 (이름 없는)
  --capabilities CAPABILITY_NAMED_IAM   # 이름 있는 IAM 리소스
  --capabilities CAPABILITY_AUTO_EXPAND # Transform(SAM/Macro) 사용
 
# 복수 지정 가능
aws cloudformation create-stack \
  --capabilities CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND

CAPABILITY가 없을 때 오류 메시지:

InsufficientCapabilities: Requires capabilities : [CAPABILITY_NAMED_IAM]

이 오류는 템플릿에 AWS::IAM::Role, AWS::IAM::Policy 등 IAM 리소스가 있는데 CAPABILITY 플래그를 지정하지 않은 경우 발생한다.

가상의 사례로, 2023년 핀테크 스타트업 G사에서 CI/CD 파이프라인에서 CloudFormation 배포가 갑자기 실패하기 시작했다. 오류는 "InsufficientCapabilities". 원인은 템플릿에 AWS::IAM::Role을 추가했는데 파이프라인 스크립트에 --capabilities CAPABILITY_IAM이 없었다. 수정은 간단했지만 프로덕션 배포가 30분 지연됐다. 이후 모든 파이프라인 스크립트에 --capabilities CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND를 기본으로 포함했다. CAPABILITY를 최대한 허용해도 CloudFormation이 실제로 IAM 리소스를 생성할 때만 영향을 미치므로 보안 위험이 없다.

Termination Protection과 운영 거버넌스

# Stack 실수 삭제 방지
aws cloudformation update-termination-protection \
  --stack-name my-prod-stack \
  --enable-termination-protection
 
# Termination Protection 상태 확인
aws cloudformation describe-stacks \
  --stack-name my-prod-stack \
  --query 'Stacks[0].EnableTerminationProtection'
 
# 삭제 전 해제 필요
aws cloudformation update-termination-protection \
  --stack-name my-prod-stack \
  --no-enable-termination-protection

Termination Protection이 활성화된 스택을 삭제하려 하면 오류가 발생한다:

TerminationProtection: Stack [my-prod-stack] cannot be deleted while TerminationProtection is enabled

Termination Protection은 인간 실수(Human Error) 방어 계층이다. 넷플릭스의 "Defense in Depth" 문화에서 단일 실수가 돌이킬 수 없는 결과를 초래하지 않도록 여러 확인 단계를 두는 원칙이다. CloudFormation에서 운영 거버넌스의 3개 레이어: (1) Stack Policy — CFn 엔진 레벨에서 특정 리소스 수정 차단, (2) Termination Protection — 스택 전체 삭제 차단, (3) IAM Permission Boundary — 사용자/역할이 CFn 스택 자체를 삭제하는 API 호출 차단. 세 레이어가 각각 다른 공격 표면을 방어한다.

CloudFormation에서 가장 위험한 상황은 Replacement를 모르고 수행하는 것이다. EC2 ImageId 변경, RDS MultiAZ 변경, DynamoDB TableName 변경이 모두 Replacement를 트리거한다. Change Set을 실행하면 각 리소스의 변경 유형이 Replacement: True / False / Conditional로 표시된다. Conditional은 런타임에 결정되는 경우로, 예를 들어 EC2 InstanceType 변경은 EBS 최적화 인스턴스 여부에 따라 Replacement가 결정된다. 운영자는 반드시 Change Set의 Replacement 필드를 확인하고 RDS나 DynamoDB가 True이면 UpdateReplacePolicy: Snapshot이 설정됐는지 검토해야 한다.

전체 흐름 그림

CloudFormation 운영자 워크플로
============================================================

[개발자/운영자]
    │ Template 작성 (YAML)
    ▼
[cfn-lint / validate-template]  ← 로컬+원격 검사
    │
    ▼
[create-change-set]  ← Replacement=True 리소스 확인
    │
    ▼
[execute-change-set]
    │
    ▼
┌─────────────────────────────────────────┐
│         CloudFormation Engine           │
│                                         │
│  1. Template 파싱 및 검증               │
│  2. DAG 의존성 분석                     │
│  3. 병렬 리소스 생성/수정/삭제          │
│  4. Stack Policy 체크                   │
│  5. CreationPolicy 신호 대기            │
│  6. 이벤트 스트림 생성                  │
└─────────────────────────┬───────────────┘
                          │
              ┌───────────┴───────────┐
              │ 성공                  │ 실패
              ▼                       ▼
        UPDATE_COMPLETE         UPDATE_ROLLBACK_IN_PROGRESS
                                      │
                                UPDATE_ROLLBACK_COMPLETE
                                (이전 상태로 복원)

이벤트 추적:
aws cloudformation describe-stack-events --stack-name xxx
  → 각 리소스별 상태 변화를 실시간으로 추적

📝 연습 문제

선택지를 클릭하면 정답·해설이 펼쳐집니다.

문제 1

CloudFormation Template에서 반드시 있어야 하는 섹션은?

문제 2

Stack이 ROLLBACK_COMPLETE 상태에 있다. 이 Stack을 동일한 이름으로 새 Template로 업데이트하려 한다. 어떻게 해야 하는가?

문제 3

EC2 인스턴스의 User Data 스크립트가 완료될 때까지 CloudFormation이 기다리게 하려면 어떤 설정이 필요한가?

문제 4

CloudFormation 운영자가 S3 버킷 리소스의 BucketName을 old-bucket에서 new-bucket으로 변경했다. Stack 업데이트 시 어떤 일이 발생하는가?

문제 5

Template에서 현재 리전에 해당하는 최신 Amazon Linux 2 AMI ID를 자동으로 사용하려 한다. 하드코딩이나 Mapping을 사용하지 않는 방법은?

문제 6

Auto Scaling Group을 CloudFormation으로 관리할 때 UpdatePolicy: AutoScalingRollingUpdate를 사용한다. WaitOnResourceSignals: true로 설정했을 때의 동작은?

문제 7

CloudFormation Stack Policy에 대한 설명으로 올바른 것은?

이전Week 5 복습: Systems Manager 종합 시나리오Week 5 · Day 5다음 Change Set, Drift Detection, Rollback Trigger: 안전한 CFn 운영의 3대 기둥Week 6 · Day 2

참고 공식 자료

  • SOA-C02 공식 시험 안내(새 탭에서 열림)
  • AWS CloudFormation 공식 문서(새 탭에서 열림)
  • Amazon EC2 공식 문서(새 탭에서 열림)
  • Amazon S3 공식 문서(새 탭에서 열림)
  • AWS IAM 공식 문서(새 탭에서 열림)
  • Amazon RDS · Aurora 공식 문서(새 탭에서 열림)

이 페이지

  • IaC의 역사와 CloudFormation의 위치
  • CloudFormation 엔진 내부 작동 원리
  • 템플릿 10대 섹션: 운영자가 알아야 할 것만
  • Parameters: 입력값의 정교한 제어
  • 내장 함수 (Intrinsic Functions): 운영자 필수
  • 스택 라이프사이클: 모든 상태를 알아야 트러블슈팅이 된다
  • Resource 속성: DeletionPolicy, UpdateReplacePolicy, CreationPolicy
  • Stack Policy: 핵심 리소스 업데이트 차단
  • Pseudo Parameters: 항상 사용 가능한 내장 변수
  • 트러블슈팅 패턴: 운영자의 일상
  • Termination Protection과 운영 거버넌스
  • 전체 흐름 그림
  • 연습 문제