AWS CloudFormation - 인프라 프로비저닝과 CreateStack

초코칩·2023년 1월 4일
1

AWS Cloud

목록 보기
2/7
post-thumbnail

목표

  • AWS CloudFormation 템플릿을 빌드하여 간단한 인프라 프로비저닝하고 CreateStack 작업 수행
  • Detect drift 명령을 사용하여 AWS CloudFormation 스택에서 드리프트 감지
  • AWS CloudFormation Change Set 생성 및 실행

AWS CloudFormation

AWS CloudFormation은 코드형 자동 인프라 프로비저닝 서비스입니다. 줄여서 IaC(Infrastructure as Code)라고도 부르기도 합니다,

인프라를 코드(IaC)로 작성할 경우 다음과 같은 이점이 있습니다.

  • 애플리케이션 코드처럼 버전 지정 및 관리가 가능하다.
  • 반복적으로 그리고 안정적으로 생성, 종료 및 재생성 가능
  • 애플리케이션의 최신 버전 테스트 필요성에 따라 생성 가능
  • 여러 환경의 생성 가능
  • 여러 고객에 대한 동일한 환경 생성 가능

아키텍처

아래의 아키텍처는 HTTP 접속이 가능한 아파치 웹서버입니다. 전용 VPC, 단일 공용 서브넷 및 소규모 Amazon Elastic Compute Cloud 인스턴스로 구성되었습니다. 필요한 인프라를 CloudFormation을 통해 구성해 봅시다.

AWS CloudFormation 템플릿 빌드

간단한 인프라 템플릿 작성

AWS Cloud9 접속합니다.

AWS CloudFormation에서는 클라우드 환경에서 리소스를 모델링하고 프로비저닝할 수 있도록 공용 언어를 제공합니다. CloudFormation 템플릿 언어를 사용하여 yaml 또는 json 형식으로 인프라를 코딩합니다.

simple-infrastructure.yaml을 통해 CloudFormation 템플릿을 구성합니다.

simple-infrastructure.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: >-
  AWS CloudFormation Simple Infrastructure Template
  VPC_Single_Instance_In_Subnet: This template will show how to create a VPC and
  add an EC2 instance with an Elastic IP address and a security group.
Parameters:
  VPCCIDR:
    Description: CIDR Block for VPC
    Type: String
    Default: 10.199.0.0/16
    AllowedValues:
      - 10.199.0.0/16
  PUBSUBNET1: 
  InstanceType:
    Description: WebServer EC2 instance type
    Type: String
    Default: t2.nano
    AllowedValues:
      - t2.nano
      - t2.micro
      - t2.small
    ConstraintDescription: must be a valid EC2 instance type.
  KeyName:
    Description: Keyname for the keypair that Qwiklab will use to launch EC2 instances
    Type: 'AWS::EC2::KeyPair::KeyName'
    ConstraintDescription: must be the name of the provided existing EC2 KeyPair.
  SSHLocation:
    Description: ' The IP address range that can be used to SSH to the EC2 instances'
    Type: String
    MinLength: '9'
    MaxLength: '18'
    Default: 0.0.0.0/0
    AllowedPattern: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})'
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
  LatestAmiId:
    Description: Find the current AMI ID using System Manager Parameter Store
    Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
  QwiklabLocale:
    Default: en
    Description: >-
      The locale of the student will be passed in to this parameter via the
      Qwiklab platform (via the student's browser)
    Type: String
Resources:
  VPC:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: 'true'
      EnableDnsHostnames: 'true'
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: CF lab environment
  Subnet:
    Type: 'AWS::EC2::Subnet'
    DependsOn: VPC
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Ref PUBSUBNET1
      MapPublicIpOnLaunch: 'true'
      AvailabilityZone: !Select 
        - '0'
        - !GetAZs ''
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: Public Subnet
  InternetGateway:
    Type: 'AWS::EC2::InternetGateway'
    DependsOn: VPC
    Properties:
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
  AttachGateway:
    Type: 'AWS::EC2::VPCGatewayAttachment'
    DependsOn: VPC
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway
  RouteTable:
    Type: 'AWS::EC2::RouteTable'
    DependsOn: VPC
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
  Route: 
  SubnetRouteTableAssociation: 
  NetworkAcl:
    Type: 'AWS::EC2::NetworkAcl'
    DependsOn:
      - VPC
      - InternetGateway
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
  InboundHTTPNetworkAclEntry:
    Type: 'AWS::EC2::NetworkAclEntry'
    DependsOn:
      - VPC
      - InternetGateway
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: '100'
      Protocol: '6'
      RuleAction: allow
      Egress: 'false'
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: '80'
        To: '80'
  InboundSSHNetworkAclEntry:
    Type: 'AWS::EC2::NetworkAclEntry'
    DependsOn:
      - VPC
      - InternetGateway
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: '101'
      Protocol: '6'
      RuleAction: allow
      Egress: 'false'
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: '22'
        To: '22'
  InboundResponsePortsNetworkAclEntry:
    Type: 'AWS::EC2::NetworkAclEntry'
    DependsOn:
      - VPC
      - InternetGateway
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: '102'
      Protocol: '6'
      RuleAction: allow
      Egress: 'false'
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: '1024'
        To: '65535'
  OutBoundHTTPNetworkAclEntry:
    Type: 'AWS::EC2::NetworkAclEntry'
    DependsOn:
      - VPC
      - InternetGateway
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: '100'
      Protocol: '6'
      RuleAction: allow
      Egress: 'true'
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: '80'
        To: '80'
  OutBoundHTTPSNetworkAclEntry:
    Type: 'AWS::EC2::NetworkAclEntry'
    DependsOn:
      - VPC
      - InternetGateway
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: '101'
      Protocol: '6'
      RuleAction: allow
      Egress: 'true'
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: '443'
        To: '443'
  OutBoundResponsePortsNetworkAclEntry:
    Type: 'AWS::EC2::NetworkAclEntry'
    DependsOn:
      - VPC
      - InternetGateway
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: '102'
      Protocol: '6'
      RuleAction: allow
      Egress: 'true'
      CidrBlock: 0.0.0.0/0
      PortRange:
        From: '1024'
        To: '65535'
  SubnetNetworkAclAssociation:
    Type: 'AWS::EC2::SubnetNetworkAclAssociation'
    Properties:
      SubnetId: !Ref Subnet
      NetworkAclId: !Ref NetworkAcl
  IPAddress:
    Type: 'AWS::EC2::EIP'
    DependsOn: AttachGateway
    Properties:
      Domain: vpc
      InstanceId: !Ref WebServerInstance
  InstanceSecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      VpcId: !Ref VPC
      GroupDescription: Enable SSH access via port 22 and HTTP via port 80
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: '22'
          ToPort: '22'
          CidrIp: !Ref SSHLocation
        - IpProtocol: tcp
          FromPort: '80'
          ToPort: '80'
          CidrIp: 0.0.0.0/0
  WebServerInstance:
    Type: 'AWS::EC2::Instance'
    DependsOn: AttachGateway
    Metadata:
      Comment: Install a simple application
      'AWS::CloudFormation::Init':
        config:
          packages:
            yum:
              httpd: []
          files:
            /var/www/html/index.html:
              content: !Join 
                - |+

                - - >-
                    <h1>Congratulations, you have successfully deployed a simple
                    infrastructure using AWS CloudFormation.</h1>
              mode: '000644'
              owner: root
              group: root
            /etc/cfn/cfn-hup.conf:
              content: !Join 
                - ''
                - - |
                    [main]
                  - stack=
                  - !Ref 'AWS::StackId'
                  - |+

                  - region=
                  - !Ref 'AWS::Region'
                  - |+

              mode: '000400'
              owner: root
              group: root
            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Join 
                - ''
                - - |
                    [cfn-auto-reloader-hook]
                  - |
                    triggers=post.update
                  - >
                    path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init
                  - 'action=/opt/aws/bin/cfn-init -v '
                  - '         --stack '
                  - !Ref 'AWS::StackName'
                  - '         --resource WebServerInstance '
                  - '         --region '
                  - !Ref 'AWS::Region'
                  - |+

                  - |
                    runas=root
              mode: '000400'
              owner: root
              group: root
          services:
            sysvinit:
              httpd:
                enabled: 'true'
                ensureRunning: 'true'
              cfn-hup:
                enabled: 'true'
                ensureRunning: 'true'
                files:
                  - /etc/cfn/cfn-hup.conf
                  - /etc/cfn/hooks.d/cfn-auto-reloader.conf
    Properties:
      InstanceType: !Ref InstanceType
      ImageId: !Ref LatestAmiId
      KeyName: !Ref KeyName
      Tags:
        - Key: Application
          Value: !Ref 'AWS::StackId'
        - Key: Name
          Value: Lab Host
      NetworkInterfaces:
        - GroupSet:
            - !Ref InstanceSecurityGroup
          AssociatePublicIpAddress: 'true'
          DeviceIndex: '0'
          DeleteOnTermination: 'true'
          SubnetId: !Ref Subnet
      UserData: !Base64 
        'Fn::Join':
          - ''
          - - |
              #!/bin/bash -xe
            - |
              yum update -y aws-cfn-bootstrap
            - '/opt/aws/bin/cfn-init -v '
            - '         --stack '
            - !Ref 'AWS::StackName'
            - '         --resource WebServerInstance '
            - '         --region '
            - !Ref 'AWS::Region'
            - |+

            - '/opt/aws/bin/cfn-signal -e $? '
            - '         --stack '
            - !Ref 'AWS::StackName'
            - '         --resource WebServerInstance '
            - '         --region '
            - !Ref 'AWS::Region'
            - |+

    CreationPolicy:
      ResourceSignal:
        Timeout: PT15M
Outputs:
  URL:
    Value: !Join 
      - ''
      - - 'http://'
        - !GetAtt 
          - WebServerInstance
          - PublicIp
    Description: Newly created application URL

템플릿에 포함된 섹션은 다음과 같은 목적으로 사용됩니다.

  • Description: 템플릿을 설명하는 텍스트 문자열입니다.
  • Parameters: 실행 시간에 템플릿에 전달하는 값입니다.
  • Resources: 스택 리소스와 그 속성을 지정합니다.
  • Outputs: 스택 실행이 성공적으로 완료된 후 스택 속성으로 반환되는 값입니다.

SIMPLE-INFRASTRUCTURE 템플릿을 사용하여 CREATESTACK 실행

AWS Cloud9 terminal에서 키 페어 이름을 검색하고 이를 이용해 환경 변수를 생성합니다.

keyPair=$(aws ec2 describe-key-pairs --output text --query KeyPairs[*].KeyName)
cd ~/environment/templates

터미널에 다음 명령을 실행하여 스택 생성 프로세스를 시작합니다.

aws cloudformation create-stack --stack-name AWSStudent-Lab1 \
--parameters ParameterKey=KeyName,ParameterValue=$keyPair \
ParameterKey=InstanceType,ParameterValue=t2.micro \
--template-body file://simple-infrastructure.yaml

create-stack을 성공적으로 실행하면 명령줄에 StackId가 반환됩니다.

describe-stacks 명령은 터미널에 많은 양의 정보를 반환합니다. 템플릿에서 정의한 모든 리소스, 빌드 프로세스의 현재 상태, describe-stacks를 실행할 때 사용할 수 있는 리소스의 특정 속성에 대한 정보를 제공합니다. AWS CloudFormation 대시보드에는 동일한 정보가 표시되지만 더 익숙한 형식으로 표시됩니다.

CloudFormation - Stacks(스택) - Status(상태)에서 생성한 스택 이름을 클릭합니다.

각 스택의 스택 정보, 출력, 이벤트를 확인할 수 있으며 출력의 url로 접속합니다.

Create Stack 작업의 상태가 CREATE_COMPLETE일 경우, Output 탭에서 표시된 URL을 웹 브라우저에서 확인 가능합니다.

정리

AWS CloudFormation을 통해 간단한 인프라를 구축했습니다. 다음 편에서는 외부의 변경을 감지하는 Drift Detection(드리프트 감지)에 대해 실습합니다.

profile
초코칩처럼 달콤한 코드를 짜자

0개의 댓글