Share
Sign In

CloudFormation - Fundamentals

💡
YAML은 다들 아시죠?

Infrastructure as Code (IaC)

지금까지는 모든 작업을 콘솔에서 수동으로 진행하였다.
이제 인프라를 생성/수정/삭제/배포를 자동으로 진행하게 하고 싶다.
그래서 인프라를 코드로 표현하는 방식을 IaC라고 한다.
코드로 인프라를 관리하면 버전 관리, 정적 테스트 등 개발 과정의 이점을 가져갈 수 있다.
Do not reinvent the wheel!

CloudFormation (CFN)

AWS 리소스를 선언해서 생성하는 IaC 서비스이다. S3에 템플릿이 업로드 되어야 하며, 버전 업그레이드를 통해서만 수정이 가능하다.

장점

비용
리소스별로 비용을 추적할 수 있다.
CFN template 별로 비용을 예측할 수 있다.
특정 시간대에만 자동으로 삭제하고 다시 생성해 비용을 아낄 수 있다.
생산성
그때그때 삭제하고 다시 생성할 수 있다.
CFN template의 다이어그램을 자동으로 만들어준다.
VPC 스택, Network 스택 등 따로따로 관리할 수 있다.
선언형 프로그래밍 (리소스의 생성 순서를 관리할 필요 없다)

템플릿 배포

템플릿을 배포한 것을 스택이라고 한다.
수동 : CFN designer로 템플릿을 수정하고 콘솔로 input을 정하고 배포한다.
자동 : YAML 파일로 템플릿을 수정하고 CLI로 input을 정하고 배포한다.

템플릿 속성

Resources : AWS 리소스. 필수다! !Ref
Parameters : 동적 변수 !Ref
Mapping : 정적 변수, !FindInMap
Outputs : 다른 스택 참조용 변수 !ImportValue
Conditionals
Metadata

Parameters

지금 당장 결정할 수 없거나 재사용하고 싶은 변수를 파라미터로 선언한다.
그러면 템플릿을 교체하지 않고 파라미터만 수정해서 새로운 스택을 만들 수 있다.
아래 예시 외의 property도 일단 숙지해야 한다. 링크
Parameters: InstanceTypeParameter: Type: String Description: Enter t2.micro, m1.small. Default is t2.micro Default: t2.micro AllowedValues: - t2.micro - m1.small
Pseudo parameters : AWS::<parameter> 꼴로 되어있는, AWS에서 제공하는 파라미터이다. 링크

Reference

다른 리소스나 파라미터를 참조해야 할 때 Ref 함수를 사용한다.
!Ref <Parameter> : 파라미터의 값을 반환한다.
!Ref <Resource> : Physical ID 같이 리소스를 특정할 수 있는 값은 반환한다.
VpcId: Ref: MyVPC # 방법 1 VpcId: !Ref MyVPC # 방법 2

Resources

실제 AWS 컴포넌트를 생성하며, 템플릿을 만들 때 꼭 필요한 부분이다. AWS::aws-product-name::data-type-name 꼴로 쓴다. 링크
동적으로 리소스를 만들 수 없다. 무조건 Resource 당 하나가 만들어진다.
거의 모든 서비스를 만들 수 있다. AWS Lambda Custom Resoruces로 그 외의 서비스도 사용 가능하다.

Mappings

CFN 템플릿에서 고정된 변수이다. dev/prod 환경이나 리전, AMI 타입 등을 정의할 때 용이하다.
Fn::FindInMap 함수를 통해 Mapping을 참조한다.
!FindInMap [MapName, TopLevelKey, SecondLevelKey] 꼴로 쓴다.
Mappings: RegionMap: us-east-1: HVM64: ami-0ff8a91507f77f867 HVMG2: ami-0a584ac55a7631c0c us-west-1: HVM64: ami-0bdb828fd58c52235 HVMG2: ami-066ee5fd4a9ef77f1 Resources: myEC2Instance: Type: "AWS::EC2::Instance" Properties: ImageId: !FindInMap [RegionMap, !Ref "AWS::Region", HVM64] InstanceType: m1.small

Outputs

모든 스택은 다른 스택의 output을 참조해서 값을 가져올 수 있다.
예를 들어, 네트워크 스택은 VPC 스택에서 VPC ID와 Subnet ID를 가져온다.
다른 스택이 의존하고 있는 스택은 삭제할 수가 없다.
Fn::ImportValue 함수를 통해 다른 스택의 output 값을 불러온다.
Outputs: StackSSHSecurityGroup: Value: !Ref MyCompanyWideSSHSecurityGroup Export: Name: SSHSecurityGroup # 다른 스택에서 참조할 이름 --- Resources: MySecureInstance: Type: AWS::EC2::Instance Properties: ... SecurityGroups: - !ImportValue: SSHSecurityGroup

Conditions

Condition을 만들고 이를 리소스에 붙여, 해당 리소스를 생성할지 안할지 결정한다.
Conditions: CreateProdResources: !Equals [ !Ref EnvType, prod ] Resources: MonutPoint: Type: "AWS::EC2::VolumeAttachment" Condition: CreateProdResources
Logical Functions : Fn::And, Fn::Equals, Fn::If, Fn::Not, Fn::Or

Intrisic Functions

Fn::Ref :
Fn::GetAtt : 리소스의 속성을 가져온다. !GetAtt EC2Instance.AvailabilityZone 꼴로 쓴다.
Fn::FindInMap : Mapping의 값을 가져온다.
Fn::ImportValue : Outputs의 값을 가져온다.
Fn::Join : 문자열을 join한다. !Join [ ":", [ a, b, c ] ]a:b:c
Fn:Sub : Bash처럼 변수를 포함해 문자열을 만든다.
# Name: www.example.com/path Name: !Sub - 'www.${Domain}/${Path}' - Domain: example.com Path: home Name: !Sub 'arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:vpc/${vpc}'

User data

EC2 인스턴스의 유저 데이터는 Base64 저장이 되기 때문에 Fn::Base64 함수를 통한 변환이 필요하다. 추가로 /var/log/cloud-init-output.log 에 유저 데이터가 저장된다.
UserData: Fn::Base64: !Sub | #!/bin/bash -xe yum update -y aws-cfn-bootstrap /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfig --configsets wordpress_install --region ${AWS::Region} /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerGroup --region ${AWS::Region}

cfn-init

User data의 복잡한 스크립트를 대체하여 좀 더 읽기 좋게 바꾼 것을 말한다. cfn-init의 로그는 /var/log/cfn-init.log 파일에서 확인할 수 있다.
1.
인스턴스가 launch되면 User data의 명령을 실행한다.
2.
User data의 cfn-init 명령은 CloudFormation의 MyInstance 리소스에서 Metadata를 가져와 실행한다.
Resources: MyInstance: Type: AWS::EC2::Instance Properties: UserData: Fn::Base64: !Sub | #!/bin/bash -xe yum update aws-cfn-bootstrap /opt/aws/bin/cfn-init -s ${AWS:StackId} -r MyInstance --region ${AWS::Region} || error_exit 'Failed to run cfn-init' Metadata: AWS::CloudFormation::Init: config: packages: yum: httpd: [] # yum install httpd files: "/var/www/html/index.html": content: | <h1> Hello world from EC2 Instance! </h1> <p> This was created using cfn-init </p> mode: '000644' commands: hello: commnad: "echo 'Hello World'" cwd: "~" services: sysvinit: httpd: enabled: 'true' # service enable httpd ensureRunning: 'true' # service start httpd

cfn-signal & wait condition

cfn-signal : CFN에게 이 리소스가 잘 만들어졌다고 알리는 스크립트
WaitCondition : CFN에게 cfn-signal을 줘야지 생성이 완료되는 더미 리소스
Resources: MyInstance: ... # MyInstance Resource의 Base64에 아래 명령을 추가한다. /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource MyInstance --region ${AWS::Region} SampleWaitCondition: DependsOn: MyInstance # MyInstance가 생성 완료되어야 이 리소스를 생성 시작한다. CreationPolicy: # 아래 조건을 만족해야 이 리소스가 생성 완료된다. ResourceSignal: # cfn-signal을 기다린다. Timeout: PT2M # 2분 동안 기다린다. Type: AWS:CloudFormation::WaitCondition

CFN 스택 생성/업데이트 실패시

스택 생성 실패 (CreateStackAPI)
ROLLBACK : 기본값으로, 모두 지우고 다시 설치한다. 로그를 볼 수 있다.
DO_NOTHING : 롤백을 하지 않고 직접 트러블슈팅을 할 수 있도록 한다.
DELETE : 모두 지운다.
스택 업데이트 실패 (UpdateStack API)
이전 상태로 자동으로 롤백한다. 로그와 오류 메시지는 볼 수 있다.

Nested Stacks

스택 안의 스택이다. 😎 늘 밑바닥부터 만들 수 없으니 다른 사람들이 만든 템플릿을 가져다가 써야 한다.
Resources: myStack: Type: AWS::CloudFormation::Stack Properties: TemplateURL: https://... Parameters: # 해당 템플릿에서 요구하는 파라미터들 ...

ChangeSets

스택을 업데이트할 경우 어떤 리소스가 생성/수정/삭제될 것인지 보여준다. 실제 적용하기 전의 계획서라고 보면 된다. 단, 업데이트한 스택이 성공한다고 보장하지는 않는다.

DeletionPolicy

스택을 삭제하면 각 리소스별로 어떤 행동을 취할지 DeletionPolicy에 정의할 수 있다.
Retain : 리소스를 제거하지 않고 놔둔다.
Snapshot : 저장 공간의 스냅샷을 저장한다.
EBS Volume, ElastiCache Cluster, ElastiCache ReplicationGroup
RDS DBInstance, RDS DBCluster, Redshift Cluster
Delete : 디폴트이며, 리소스를 제거한다.
AWS::RDS::DBCluster는 스냅샷으로 저장한다.
S3 bucket은 삭제하기 전, 저장하고 있는 것이 없어야 한다.

TerminationProtection

어떤 스택에 Termination protection이 enable 되어 있으면 해당 스택은 삭제할 수 없다. 삭제하기 위해서는 termination protection 기능을 disable한 후 지워야 한다. 삭제를 좀 더 번거롭게 만들고 과정이다.