[AWS] CloudFormation 활용하기

 CloudFormation


CloudFormation은 Amazon Web Services(AWS) 리소스를 자동으로 생성해 주는 서비스이다. 
사용하려는 AWS 리소스를 템플릿 파일로 작성하면, CloudFormation이 이를 분석해서 AWS 리소스를 생성한다. 
이렇게 생성된 리소스를 스택이라고 한다.

작동 방식 

CloudFormation 아래 그림과 같이 작동한다. 템플릿 작성, 템플릿 업로드, 스택 생성, 스택 설정 및 리소스 생성의 4단계이다.




특징 

  • 인프라를 코드로 관리한다. 
    • 인프라를 코드로 관리하는 것을 Infrastructure as code라고 한다. 코드를 Git과 같은 버전 관리 시스템으로 관리하면 개발자가 소스코드를 관리하는 것과 비슷하게 인프라를 관리할 수 있다.
  • 인프라 관리가 편리하다. 
    • 템플릿에 리소스를 정의하기만 하면 리소스를 생성하고 리소스가 서로 연계되도록 구성한다. 리소스가 더 이상 필요하지 않으면 스택을 삭제하는 것만으로 전체 리소스를 삭제할 수 있다.
  • 인프라 구성을 재사용한다. 
    • 한번 구성한 인프라는 얼마든지 다시 구축할 수 있다. 한 리전에서 사용한 템플릿을 다른 리전에서 동일하게 사용한다. 같은 리전에서 재사용하려면 스택 이름을 다르게 설정한다.
  • 사용 요금이 없다. 
    • 생성된 리소스에 대한 요금만 지불하면 되고 CloudFormation 자체에는 별도로 요금이 부과되지 않는다.

CloudFormation의 기본적인 Syntax 소개, 스택 생성 및 업데이트 실습

1. 스택 생성 



ec2.yml 파일
AWSTemplateFormatVersion: '2010-09-09'
Description: EC2 CloudFormation Template

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      -
        Parameters:
          - EC2Name
          - EC2InstanceType
          - AMIId
          - KeyName
          - VPC
          - Subnet
          - AllocateEIP

    ParameterLabels:
      EC2Name:
        default: EC2 Instance Name
      EC2InstanceType:
        default: Instance Type
      AMIId:
        default: AMI Id
      KeyName:
        default: EC2 Key Pair Name
      AllocateEIP:
        default: Elastic IP


# Custom Variable for the environment
Parameters:
  EC2Name:
    Description: Name tag for EC2
    Type: String

  # Keypair should be manually created prior to create cloudformation stack(EC2 -> KeyPair)
  KeyName:
    Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
    Type: AWS::EC2::KeyPair::KeyName
    ConstraintDescription: must be the name of an existing EC2 KeyPair

  EC2InstanceType:
    Description: Specify EC2 instance type
    Type: String

  VPC:
    Description: Specify VPC Id
    Type: AWS::EC2::VPC::Id

  Subnet:
    Description: Specify Subnet Id
    Type: AWS::EC2::Subnet::Id

  AMIId:
    Description: Specify AMI Id
    Type: AWS::EC2::Image::Id

  AllocateEIP:
    Description: Select "yes" if you want to assign EIP to an instance
    Type: String
    AllowedValues:
      - yes
      - no

Conditions:
  UseAllocateEIP: !Equals [!Ref AllocateEIP, yes]

Resources:

  # IAM roles assigned to EC2 instnaces
  IAMRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          -
            Effect: Allow
            Action:
              - sts:AssumeRole
            Principal:
              Service:
                - ec2.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM

  # IAM Instance profile assigned to EC2 instnaces
  InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /
      Roles:
        - !Ref IAMRole
    DependsOn: IAMRole

  # Security Group assigned to EC2 instnaces
  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Join ['-', [!Ref EC2Name, "sg"]]
      GroupDescription: !Join [' ', [!Ref EC2Name, "Instance", "Security Group"]]
      VpcId: !Ref VPC
      Tags:
        -
          Key: Name
          Value: !Join ['-', [!Ref EC2Name, "InstanceSecurityGroup"]]

  Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AMIId
      InstanceType: !Ref EC2InstanceType
      DisableApiTermination: false
      InstanceInitiatedShutdownBehavior: stop
      IamInstanceProfile: !Ref InstanceProfile
      Monitoring: false
      KeyName: !Ref KeyName
      SecurityGroupIds:
        - !Ref InstanceSecurityGroup
      SubnetId: !Ref Subnet
      Tags:
        -
          Key: Name
          Value: !Ref EC2Name
    DependsOn:
      - InstanceSecurityGroup
      - InstanceProfile
 
  ## Assign Elastic IP
  EIP:
    Type: AWS::EC2::EIP
    Condition: UseAllocateEIP

  ## Associate Elastic IP
  EIPAssociation:
    Type: AWS::EC2::EIPAssociation
    Condition: UseAllocateEIP
    Properties:
      AllocationId: !GetAtt EIP.AllocationId
      InstanceId: !Ref Instance
    DependsOn:
      - EIP
      - Instance





CloudFormation Stack Update

텍스트 에디터에서 ec2.yml 파일을 열고 Instance 블록을  수정한 후  아래 블록을 추가
생성한 stack에서 업데이트 , 변경한 ec2.yml파일을 교체 


같은 방식으로 ec2-lt.yml로 스택 생성



업데이트 확인 

Resource 내부에 코드를 붙여야한다 . 

결과 

CloudFormation Update Behaviors: 각 Resource의 속성 변경시 해당 Resource가 재시작 또는 재생성 될수 있다. 
EC2를 예로 들자면 UserData의 경우에는 Some Interruption으로 기존에 생성된 인스턴스를 유지하되 재시작 또는 리부팅이 일어날수 있고, LaunchTemplate의 경우에는 Replacement로 인스턴스가 교체된다. 
따라서 위의 예에서 Userdata에 스크립트를 추가했을때 반영이 되지 않았던 이유는 Userdata는 인스턴스 생성시에만 실행되기 때문이다. 



CFN 템플릿 재사용 방안 및 모듈화된 (Nested Stack) 스택 구성 실습

Mapping

ec-2 mapping.yml 을 업로드해서 앞 단계와같이 스택을 생성한다 .
생성된 인스턴스타입을 확인한다

ec-2 mapping.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: EC2 CloudFormation Template

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      -
        Parameters:
          - EC2Name
          - Environment
          - AMIId
          - KeyName
          - VPC
          - Subnet
          - AllocateEIP

    ParameterLabels:
      EC2Name:
        default: EC2 Instance Name
      Environment:
        default: Environment
      AMIId:
        default: AMI Id
      KeyName:
        default: EC2 Key Pair Name
      AllocateEIP:
        default: Elastic IP


# Custom Variable for the environment
Parameters:
  EC2Name:
    Description: Name tag for EC2
    Type: String

  # Keypair should be manually created prior to create cloudformation stack(EC2 -> KeyPair)
  KeyName:
    Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
    Type: AWS::EC2::KeyPair::KeyName
    ConstraintDescription: must be the name of an existing EC2 KeyPair

  Environment:
    Description: Specify Environment
    Type: String
    AllowedValues:
      - test
      - stag
      - prod

  VPC:
    Description: Specify VPC Id
    Type: AWS::EC2::VPC::Id

  Subnet:
    Description: Specify Subnet Id
    Type: AWS::EC2::Subnet::Id

  AMIId:
    Description: Specify AMI Id
    Type: AWS::EC2::Image::Id

  AllocateEIP:
    Description: Select "yes" if you want to assign EIP to an instance
    Type: String
    AllowedValues:
      - yes
      - no

Mappings:
  Specs:
    InstanceType:
      test: t2.micro
      stag: t2.medium
      prod: m5.large

Conditions:
  UseAllocateEIP: !Equals [!Ref AllocateEIP, yes]

Resources:

  # IAM roles assigned to EC2 instnaces
  IAMRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          -
            Effect: Allow
            Action:
              - sts:AssumeRole
            Principal:
              Service:
                - ec2.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM

  # IAM Instance profile assigned to EC2 instnaces
  InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /
      Roles:
        - !Ref IAMRole
    DependsOn: IAMRole

  # Security Group assigned to EC2 instnaces
  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Join ['-', [!Ref EC2Name, "sg"]]
      GroupDescription: !Join [' ', [!Ref EC2Name, "Instance", "Security Group"]]
      VpcId: !Ref VPC
      Tags:
        -
          Key: Name
          Value: !Join ['-', [!Ref EC2Name, "InstanceSecurityGroup"]]
 
  # Security Group Ingress Rule
  HTTPIngressRule:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref InstanceSecurityGroup
      IpProtocol: tcp
      FromPort: 80
      ToPort: 80
      CidrIp: 0.0.0.0/0
    DependsOn:
      - InstanceSecurityGroup

  LaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateData:
        DisableApiTermination: false
        InstanceInitiatedShutdownBehavior: stop
        IamInstanceProfile:
          Name: !Ref InstanceProfile
        ImageId: !Ref AMIId
        InstanceType: !FindInMap [Specs, InstanceType, !Ref Environment]
        Monitoring:
          Enabled: false
        KeyName: !Ref KeyName
        SecurityGroupIds:
          - !Ref InstanceSecurityGroup
        TagSpecifications:
          -
            ResourceType: instance
            Tags:
              -
                Key: Name
                Value: !Ref EC2Name
        UserData:
          Fn::Base64:
            !Sub |
                #!/bin/bash -xe
                sudo yum install -y httpd
                echo "<html>Hello-World!!</html>" > /tmp/index.html
                sudo cp /tmp/index.html /var/www/html/index.html
                sudo chmod 755 /var/www/html/index.html
                sudo service httpd start
                sudo chkconfig httpd on
      LaunchTemplateName: !Ref EC2Name
    DependsOn:
      - InstanceSecurityGroup
      - InstanceProfile

  Instance:
    Type: AWS::EC2::Instance
    Properties:
      LaunchTemplate:
        LaunchTemplateId: !Ref LaunchTemplate
        Version: !GetAtt LaunchTemplate.LatestVersionNumber
      SubnetId: !Ref Subnet
    DependsOn:
      - LaunchTemplate
 
  ## Assign Elastic IP
  EIP:
    Type: AWS::EC2::EIP
    Condition: UseAllocateEIP

  ## Associate Elastic IP
  EIPAssociation:
    Type: AWS::EC2::EIPAssociation
    Condition: UseAllocateEIP
    Properties:
      AllocationId: !GetAtt EIP.AllocationId
      InstanceId: !Ref Instance
    DependsOn:
      - EIP
      - Instance






업데이트 

현재 템플릿 사용을 선택, environment를 prod를 변경 후 다음 ,스택을 업데이트 한다 . 





t2.micro에서 m5.large로 바뀌었다 . 










댓글