Skip to content

연습: 스택 업데이트


AWS CloudFormation에서는 기존 스택의 리소스 속성을 업데이트할 수 있다. 이러한 변경은 CloudWatch 경보의 경보 임계값 업데이트 같은 단순 구성 변경부터, Amazon EC2 인스턴스에서 실행 중인 Amazon Machine Image(AMI) 업데이트 같은 복잡한 변경에 이르기까지 다양하다. 템플릿에 포함된 여러 AWS 리소스를 업데이트할 수 있으며, 다른 리소스에 대한 지원을 계속 추가하고 있다.

이 단원에서는 실행 중인 스택의 간단한 업데이트 진행 과정을 살펴본다. 실행 중인 소프트웨어에 버전 제어를 사용하듯이 템플릿을 사용하여 AWS 인프라 구성에 버전 제어 시스템을 사용하는 방법을 보여준다. 다음 단계를 살펴본다.

  1. 초기 스택 생성 - 기본 Amazon Linux AMI를 사용하여 스택을 생성하고, AWS CloudFormation 헬퍼 스크립트를 사용하여 Apache Web Server 및 간단한 PHP 애플리케이션을 설치한다.

  2. 애플리케이션 업데이트 - CloudFormation을 사용하여 애플리케이션의 파일 중 하나를 업데이트하고 소프트웨어를 배포한다.

  3. 인스턴스 유형 업데이트 - 기존 Amazon EC2 인스턴스의 인스턴스 유형을 변경한다.

  4. Amazon EC2 인스턴스에서 AMI 업데이트 - 스택에서 Amazon EC2 인스턴스용 Amazon 머신 이미지(AMI)를 변경한다.

  5. 인스턴스에 키 페어 추가 - Amazon EC2 키 페어를 인스턴스에 추가한 다음, 인스턴스에 대한 SSH 액세스를 허용하도록 보안 그룹을 업데이트한다.

  6. 스택의 리소스 변경 - 스택에서 리소스를 추가 및 제거하고, 템플릿을 업데이트하여 이러한 스택을 자동 조정 및 로드 밸런싱된 애플리케이션으로 변환한다.


1. 간단한 애플리케이션

이 단원의 나머지 내용에서 사용할 수 있는 스택을 생성하는 것부터 시작한다. 간단한 템플릿 하나를 제공했으며, 이 템플릿은 Apache Web Server에 호스팅되고 Amazon Linux AMI에서 실행 중인 단일 인스턴스 PHP 웹 애플리케이션을 시작한다.

Apache Web Server, PHP 및 간단한 PHP 애플리케이션은 기본적으로 Amazon Linux AMI에 설치되는 CloudFormation 헬퍼 스크립트를 통해 모두 설치된다. 다음 템플릿 코드 조각에서는 설치할 패키지와 파일(여기서는 Amazon Linux AMI용 Yum 리포지토리의 Apache Web Server 및 PHP 인프라)을 설명하는 메타데이터를 표시한다. 이 코드 조각에서는 Apache Web Server가 실행 중임을 보장하는 Services 섹션도 보여준다. Amazon EC2 인스턴스 정의의 Properties 섹션에서 UserData 속성에는 cfn-init를 호출하여 패키지 및 파일을 설치하는 CloudInit 스크립트가 들어 있다.

  "WebServerInstance": {
    "Type" : "AWS::EC2::Instance",
    "Metadata" : {
      "AWS::CloudFormation::Init" : {
        "config" : {
          "packages" : {
            "yum" : {
              "httpd"             : [],
              "php"               : []
            }
          },

          "files" : {

            "/var/www/html/index.php" : {
              "content" : { "Fn::Join" : ["", [
                "<?php\n",
                "echo '<h1>AWS CloudFormation sample PHP application</h1>';\n",
                "echo '<p>", { "Ref" : "WelcomeMessage" }, "</p>';\n",
                "?>\n"
              ]]},
              "mode"    : "000644",
              "owner"   : "apache",
              "group"   : "apache"
            },
          },

          :

          "services" : {
            "sysvinit" : {
              "httpd"    : { "enabled" : "true", "ensureRunning" : "true" }
            }
          }
        }
      }
    },

    "Properties": {
      :
      "UserData"       : { "Fn::Base64" : { "Fn::Join" : ["", [
        "#!/bin/bash\n",
        "yum install -y aws-cfn-bootstrap\n",

        :

        "# Install the files and packages from the metadata\n",
        "/opt/aws/bin/cfn-init -v ",
        "         --stack ", { "Ref" : "AWS::StackName" },
        "         --resource WebServerInstance ",
        "         --region ", { "Ref" : "AWS::Region" }, "\n", 
        :
      ]]}}
    }
  },

애플리케이션 자체는 전체가 템플릿 안에 정의되어 있는 "Hello, World" 예제이다. 실제 애플리케이션의 경우, 파일이 Amazon S3, GitHub 또는 다른 리포지토리에 저장되어 템플릿에서 참조될 수도 있다. CloudFormation은 패키지(예: RPM 또는 RubyGems)를 다운로드하고 개별 파일을 참조하며 .zip.tar 파일의 압축을 풀어서 Amazon EC2 인스턴스에 애플리케이션 아티팩트를 생성할 수 있다.

이 템플릿은 cfn-hup 데몬이 Amazon EC2 인스턴스용 메타데이터에 정의된 구성의 변경 내용을 수신하도록 구성하고 활성화한다. cfn-hup 데몬을 사용하여 Apache 또는 PHP 버전 등 애플리케이션 소프트웨어를 업데이트하거나, 에서 PHP 애플리케이션 파일 자체를 업데이트할 수 있다. 템플릿에 들어 있는 동일 리소스의 다음 코드 조각은 메타데이터에 대한 변경 내용이 감지되는 경우 cfn-hup를 호출하여 소프트웨어를 업데이트하도록 cfn-hub를 구성하는 데 필요한 요소를 보여준다.

  "WebServerInstance": {
    "Type" : "AWS::EC2::Instance",
    "Metadata" : {
      "AWS::CloudFormation::Init" : {
        "config" : {

            :

          "files" : {

            :

            "/etc/cfn/cfn-hup.conf" : {
              "content" : { "Fn::Join" : ["", [
                "[main]\n",
                "stack=", { "Ref" : "AWS::StackName" }, "\n",
                "region=", { "Ref" : "AWS::Region" }, "\n"
              ]]},
              "mode"    : "000400",
              "owner"   : "root",
              "group"   : "root"
            },

            "/etc/cfn/hooks.d/cfn-auto-reloader.conf" : {
              "content": { "Fn::Join" : ["", [
                "[cfn-auto-reloader-hook]\n",
                "triggers=post.update\n",
                "path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init\n",
                "action=/opt/aws/bin/cfn-init -s ", { "Ref" : "AWS::StackId" }, " -r WebServerInstance ",
                " --region     ", { "Ref" : "AWS::Region" }, "\n",  
                "runas=root\n"
              ]]}
            }
          },
          :
    },

    "Properties": {

         :

      "UserData"       : { "Fn::Base64" : { "Fn::Join" : ["", [

        :

        "# Start up the cfn-hup daemon to listen for changes to the Web Server metadata\n",
        "/opt/aws/bin/cfn-hup || error_exit 'Failed to start cfn-hup'\n",  

        :
      ]]}}
    }
  },

이 템플릿은 스택을 완성하기 위해 Amazon EC2 보안 그룹을 생성한다.

{
  "AWSTemplateFormatVersion" : "2010-09-09",

  "Description" : "AWS CloudFormation Sample Template: Sample template that can be used to test EC2 updates. **WARNING** This template creates an Amazon Ec2 Instance. You will be billed for the AWS resources used if you create a stack from this template.",

  "Parameters" : {

    "InstanceType" : {
      "Description" : "WebServer EC2 instance type",
      "Type" : "String",
      "Default" : "t2.small",
      "AllowedValues" : [ 
        "t1.micro", 
        "t2.nano", 
        "t2.micro", 
        "t2.small", 
        "t2.medium", 
        "t2.large", 
        "m1.small", 
        "m1.medium", 
        "m1.large", 
        "m1.xlarge", 
        "m2.xlarge", 
        "m2.2xlarge", 
        "m2.4xlarge", 
        "m3.medium", 
        "m3.large", 
        "m3.xlarge", 
        "m3.2xlarge", 
        "m4.large", 
        "m4.xlarge", 
        "m4.2xlarge", 
        "m4.4xlarge", 
        "m4.10xlarge", 
        "c1.medium", 
        "c1.xlarge", 
        "c3.large", 
        "c3.xlarge", 
        "c3.2xlarge", 
        "c3.4xlarge", 
        "c3.8xlarge", 
        "c4.large", 
        "c4.xlarge", 
        "c4.2xlarge", 
        "c4.4xlarge", 
        "c4.8xlarge", 
        "g2.2xlarge", 
        "g2.8xlarge", 
        "r3.large", 
        "r3.xlarge", 
        "r3.2xlarge", 
        "r3.4xlarge", 
        "r3.8xlarge", 
        "i2.xlarge", 
        "i2.2xlarge", 
        "i2.4xlarge", 
        "i2.8xlarge", 
        "d2.xlarge", 
        "d2.2xlarge", 
        "d2.4xlarge", 
        "d2.8xlarge", 
        "hi1.4xlarge", 
        "hs1.8xlarge", 
        "cr1.8xlarge", 
        "cc2.8xlarge", 
        "cg1.4xlarge"
      ],
      "ConstraintDescription" : "must be a valid EC2 instance type."
    }
  },

  "Mappings" : {
    "AWSInstanceType2Arch" : {
      "t1.micro"    : { "Arch" : "HVM64"  },
      "t2.nano"     : { "Arch" : "HVM64"  },
      "t2.micro"    : { "Arch" : "HVM64"  },
      "t2.small"    : { "Arch" : "HVM64"  },
      "t2.medium"   : { "Arch" : "HVM64"  },
      "t2.large"    : { "Arch" : "HVM64"  },
      "m1.small"    : { "Arch" : "HVM64"  },
      "m1.medium"   : { "Arch" : "HVM64"  },
      "m1.large"    : { "Arch" : "HVM64"  },
      "m1.xlarge"   : { "Arch" : "HVM64"  },
      "m2.xlarge"   : { "Arch" : "HVM64"  },
      "m2.2xlarge"  : { "Arch" : "HVM64"  },
      "m2.4xlarge"  : { "Arch" : "HVM64"  },
      "m3.medium"   : { "Arch" : "HVM64"  },
      "m3.large"    : { "Arch" : "HVM64"  },
      "m3.xlarge"   : { "Arch" : "HVM64"  },
      "m3.2xlarge"  : { "Arch" : "HVM64"  },
      "m4.large"    : { "Arch" : "HVM64"  },
      "m4.xlarge"   : { "Arch" : "HVM64"  },
      "m4.2xlarge"  : { "Arch" : "HVM64"  },
      "m4.4xlarge"  : { "Arch" : "HVM64"  },
      "m4.10xlarge" : { "Arch" : "HVM64"  },
      "c1.medium"   : { "Arch" : "HVM64"  },
      "c1.xlarge"   : { "Arch" : "HVM64"  },
      "c3.large"    : { "Arch" : "HVM64"  },
      "c3.xlarge"   : { "Arch" : "HVM64"  },
      "c3.2xlarge"  : { "Arch" : "HVM64"  },
      "c3.4xlarge"  : { "Arch" : "HVM64"  },
      "c3.8xlarge"  : { "Arch" : "HVM64"  },
      "c4.large"    : { "Arch" : "HVM64"  },
      "c4.xlarge"   : { "Arch" : "HVM64"  },
      "c4.2xlarge"  : { "Arch" : "HVM64"  },
      "c4.4xlarge"  : { "Arch" : "HVM64"  },
      "c4.8xlarge"  : { "Arch" : "HVM64"  },
      "g2.2xlarge"  : { "Arch" : "HVMG2"  },
      "g2.8xlarge"  : { "Arch" : "HVMG2"  },
      "r3.large"    : { "Arch" : "HVM64"  },
      "r3.xlarge"   : { "Arch" : "HVM64"  },
      "r3.2xlarge"  : { "Arch" : "HVM64"  },
      "r3.4xlarge"  : { "Arch" : "HVM64"  },
      "r3.8xlarge"  : { "Arch" : "HVM64"  },
      "i2.xlarge"   : { "Arch" : "HVM64"  },
      "i2.2xlarge"  : { "Arch" : "HVM64"  },
      "i2.4xlarge"  : { "Arch" : "HVM64"  },
      "i2.8xlarge"  : { "Arch" : "HVM64"  },
      "d2.xlarge"   : { "Arch" : "HVM64"  },
      "d2.2xlarge"  : { "Arch" : "HVM64"  },
      "d2.4xlarge"  : { "Arch" : "HVM64"  },
      "d2.8xlarge"  : { "Arch" : "HVM64"  },
      "hi1.4xlarge" : { "Arch" : "HVM64"  },
      "hs1.8xlarge" : { "Arch" : "HVM64"  },
      "cr1.8xlarge" : { "Arch" : "HVM64"  },
      "cc2.8xlarge" : { "Arch" : "HVM64"  }
    },

    "AWSRegionArch2AMI" : {
      "us-east-1"        : {"HVM64" : "ami-0ff8a91507f77f867", "HVMG2" : "ami-0a584ac55a7631c0c"},
      "us-west-2"        : {"HVM64" : "ami-a0cfeed8", "HVMG2" : "ami-0e09505bc235aa82d"},
      "us-west-1"        : {"HVM64" : "ami-0bdb828fd58c52235", "HVMG2" : "ami-066ee5fd4a9ef77f1"},
      "eu-west-1"        : {"HVM64" : "ami-047bb4163c506cd98", "HVMG2" : "ami-0a7c483d527806435"},
      "eu-west-2"        : {"HVM64" : "ami-f976839e", "HVMG2" : "NOT_SUPPORTED"},
      "eu-west-3"        : {"HVM64" : "ami-0ebc281c20e89ba4b", "HVMG2" : "NOT_SUPPORTED"},
      "eu-central-1"     : {"HVM64" : "ami-0233214e13e500f77", "HVMG2" : "ami-06223d46a6d0661c7"},
      "ap-northeast-1"   : {"HVM64" : "ami-06cd52961ce9f0d85", "HVMG2" : "ami-053cdd503598e4a9d"},
      "ap-northeast-2"   : {"HVM64" : "ami-0a10b2721688ce9d2", "HVMG2" : "NOT_SUPPORTED"},
      "ap-northeast-3"   : {"HVM64" : "ami-0d98120a9fb693f07", "HVMG2" : "NOT_SUPPORTED"},
      "ap-southeast-1"   : {"HVM64" : "ami-08569b978cc4dfa10", "HVMG2" : "ami-0be9df32ae9f92309"},
      "ap-southeast-2"   : {"HVM64" : "ami-09b42976632b27e9b", "HVMG2" : "ami-0a9ce9fecc3d1daf8"},
      "ap-south-1"       : {"HVM64" : "ami-0912f71e06545ad88", "HVMG2" : "ami-097b15e89dbdcfcf4"},
      "us-east-2"        : {"HVM64" : "ami-0b59bfac6be064b78", "HVMG2" : "NOT_SUPPORTED"},
      "ca-central-1"     : {"HVM64" : "ami-0b18956f", "HVMG2" : "NOT_SUPPORTED"},
      "sa-east-1"        : {"HVM64" : "ami-07b14488da8ea02a0", "HVMG2" : "NOT_SUPPORTED"},
      "cn-north-1"       : {"HVM64" : "ami-0a4eaf6c4454eda75", "HVMG2" : "NOT_SUPPORTED"},
      "cn-northwest-1"   : {"HVM64" : "ami-6b6a7d09", "HVMG2" : "NOT_SUPPORTED"}
    }
  },

  "Resources" : {

    "WebServerInstance": {  
      "Type" : "AWS::EC2::Instance",
      "Metadata" : {
        "Comment" : "Install a simple PHP application",
        "AWS::CloudFormation::Init" : {
          "config" : {
            "packages" : {
              "yum" : {
                "httpd"             : [],
                "php"               : []
              }
            },

            "files" : {

              "/var/www/html/index.php" : {
                "content" : { "Fn::Join" : ["", [
                  "<?php\n",
                  "echo '<h1>AWS CloudFormation sample PHP application</h1>';\n",
                  "?>\n"
                ]]},
                "mode"    : "000644",
                "owner"   : "apache",
                "group"   : "apache"
              },


              "/etc/cfn/cfn-hup.conf" : {
                "content" : { "Fn::Join" : ["", [
                  "[main]\n",
                  "stack=", { "Ref" : "AWS::StackId" }, "\n",
                  "region=", { "Ref" : "AWS::Region" }, "\n"
                ]]},
                "mode"    : "000400",
                "owner"   : "root",
                "group"   : "root"
              },

              "/etc/cfn/hooks.d/cfn-auto-reloader.conf" : {
                "content": { "Fn::Join" : ["", [
                  "[cfn-auto-reloader-hook]\n",
                  "triggers=post.update\n",
                  "path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init\n",
                  "action=/opt/aws/bin/cfn-init -s ", { "Ref" : "AWS::StackId" }, " -r WebServerInstance ",
                                                   " --region     ", { "Ref" : "AWS::Region" }, "\n",
                  "runas=root\n"
                ]]}
              }
            },

            "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": {
        "ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" },
                          { "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] },
        "InstanceType"   : { "Ref" : "InstanceType" },
        "SecurityGroups" : [ {"Ref" : "WebServerSecurityGroup"} ],
        "UserData"       : { "Fn::Base64" : { "Fn::Join" : ["", [
             "#!/bin/bash -xe\n",
             "yum install -y aws-cfn-bootstrap\n",

             "# Install the files and packages from the metadata\n",
             "/opt/aws/bin/cfn-init -v ",
             "         --stack ", { "Ref" : "AWS::StackName" },
             "         --resource WebServerInstance ",
             "         --region ", { "Ref" : "AWS::Region" }, "\n",

             "# Start up the cfn-hup daemon to listen for changes to the Web Server metadata\n",
             "/opt/aws/bin/cfn-hup || error_exit 'Failed to start cfn-hup'\n",  

             "# Signal the status from cfn-init\n",
             "/opt/aws/bin/cfn-signal -e $? ",
             "         --stack ", { "Ref" : "AWS::StackName" },
             "         --resource WebServerInstance ",
             "         --region ", { "Ref" : "AWS::Region" }, "\n"
        ]]}}        
      },
      "CreationPolicy" : {
        "ResourceSignal" : {
          "Timeout" : "PT5M"
        }
      }
    },

    "WebServerSecurityGroup" : {
      "Type" : "AWS::EC2::SecurityGroup",
      "Properties" : {
        "GroupDescription" : "Enable HTTP access via port 80",
        "SecurityGroupIngress" : [
          {"IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "CidrIp" : "0.0.0.0/0"}
        ]
      }      
    }          
  },

  "Outputs" : {
    "WebsiteURL" : {
      "Description" : "Application URL",
      "Value" : { "Fn::Join" : ["", ["http://", { "Fn::GetAtt" : [ "WebServerInstance", "PublicDnsName" ]}]] }
    }
  }
}

이 예제에서는 단일 Amazon EC2 인스턴스를 사용하지만, Elastic Load Balancer 및 Auto Scaling 그룹을 사용하여 애플리케이션 서버 모음을 관리하는 복잡한 솔루션에서도 이와 동일한 메커니즘을 사용할 수 있다. 하지만 Auto Scaling 그룹의 경우 특별히 고려해야 할 사항이 있다.


2. 초기 스택 생성

이 예제에서는 AWS Management Console을 사용하여 동일 템플릿에서 초기 스택을 생성한다.

Warning

이 절차를 완료하면 라이브 AWS 서비스가 배포된다. 이러한 서비스가 실행되는 동안은 표준 사용 요금이 부과된다.

AWS Management Console에서 스택을 생성하려면

  1. 이전 템플릿을 복사하여 텍스트 파일로서 시스템에 로컬로 저장한다. 이후 단계에서는 이 파일을 사용해야 하므로 이 위치를 적어둔다.

  2. https://console.aws.amazon.com/cloudformation에서 CloudFormation 콘솔에 로그인한다.

  3. [Create New Stack]을 선택한다.

  4. 새 스택 생성 마법사의 템플릿 선택 화면에서 이름 필드에 UpdateTutorial을 입력한다. 동일한 페이지에서 [Amazon S3에 템플릿 업로드(Upload a template to Amazon S3)]를 선택하고 첫 번째 단계에서 다운로드한 파일을 찾은 후 [다음(Next)]을 선택한다.

  5. 파라미터 지정(Specify Parameters) 화면의 인스턴스 유형 상자에 t1.micro를 입력한다. 다음을 선택한다.

  6. [옵션(Options)] 화면에서 [다음(Next)]을 선택한다.

  7. [검토(Review)] 화면에서 모든 설정이 원하는 대로 지정되었는지 확인하고 나서 [생성(Create)]을 선택한다.

스택 상태가 CREATE_COMPLETE가 된 후에는 출력 탭에 웹 사이트의 URL이 표시된다. WebsiteURL 출력 값을 선택하면 새 PHP 애플리케이션의 작동이 표시된다.


3. 애플리케이션 업데이트

이제 스택을 배포했으며, 애플리케이션을 업데이트한다. 애플리케이션에서 출력되는 텍스트를 조금 변경해 본다. 이렇게 하기 위해 이 템플릿 코드 조각에 표시된 대로 index.php 파일에 echo 명령을 추가한다.

"WebServerInstance": {
      "Type" : "AWS::EC2::Instance",
      "Metadata" : {
        "AWS::CloudFormation::Init" : {
          "config" : {
              :

            "files" : {

              "/var/www/html/index.php" : {
                "content" : { "Fn::Join" : ["", [
                  "<?php\n",
                  "echo '<h1>AWS CloudFormation sample PHP application</h1>';\n",
                  "echo '<p>Updated version via UpdateStack</p>';\n ",
                  "?>\n"
                ]]},
                "mode"    : "000644",
                "owner"   : "apache",
                "group"   : "apache"
              },

              :

      }
    },

텍스트 편집기를 사용하여 로컬로 저장한 템플릿 파일을 수동으로 편집한다.

이제 스택을 업데이트한다.

AWS Management Console에서 스택을 업데이트하려면

  1. https://console.aws.amazon.com/cloudformation에서 AWS CloudFormation 콘솔에 로그인한다.

  2. AWS CloudFormation 대시보드에서 이전에 생성한 스택을 선택한 다음 [스택 업데이트(Update Stack)]를 선택한다.

  3. [스택 업데이트(Update Stack)] 마법사의 [템플릿 선택(Select Template)] 화면에서 [Amazon S3에 템플릿 업로드(Upload a template to Amazon S3)]를 선택한 다음, 수정한 템플릿을 선택하고 나서 [다음(Next)]을 선택한다.

  4. [옵션(Options)] 화면에서 [다음(Next)]을 선택한다.

  5. 스택에 스택 정책이 없으므로 [다음(Next)]을 선택한다. 재정의 정책 없이 모든 리소스를 업데이트할 수 있다.

  6. [검토(Review)] 화면에서 모든 설정이 원하는 대로 지정되었는지 확인한 후 [업데이트(Update)]을 선택한다.

AWS Management Console에서 스택을 업데이트하는 경우 초기 스택을 생성하는 데 사용된 파라미터가 스택 업데이트 마법사의 파라미터 페이지에 미리 채워진다. aws cloudformation update-stack 명령을 사용하는 경우 처음에 스택을 생성하는 데 사용했던 파라미터에 대해 동일한 값을 입력해야 한다.

스택이 UPDATE_COMPLETE 상태이면 WebsiteURL 출력 값을 다시 선택하여 애플리케이션에서 적용한 변경 내용을 확인한다. 기본적으로 cfn-hup 데몬은 15분마다 실행되므로 스택이 업데이트되고 나서 애플리케이션이 변경하는 데 최대 15분이 걸릴 수 있다.

업데이트된 리소스 세트를 확인하려면 AWS CloudFormation 콘솔로 이동한다. 이벤트 탭에서 스택 이벤트를 살펴본다. 이 특수한 경우에는 Amazon EC2 인스턴스 WebServerInstance의 메타데이터가 업데이트되어 변경 내용이 없는지 확인하기 위해 AWS CloudFormation에서 다른 리소스(WebServerSecurityGroup)를 다시 평가했다. 다른 스택 리소스는 수정되지 않았다. AWS CloudFormation은 스택 내에서 스택 변경에 영향을 받는 리소스만 업데이트한다. 이러한 변경에는 속성이나 메타데이터 변경과 같은 직접적인 변경도 있고, Ref, GetAtt 또는 기타 내장 템플릿 함수를 통한 데이터 흐름이나 종속성으로 인한 간접적인 변경도 있을 수 있다.

이 간단한 업데이트가 이 과정을 보여주지만, Amazon EC2 인스턴스로 배포되는 파일 및 패키지에 대한 보다 복잡한 변경도 가능하다. 예를 들면 MySQL에 대한 PHP 지원과 함께 인스턴스에 MySQL을 추가해야 할 수도 있다. 이렇게 하려면 추가 서비스와 함께 추가적인 패키지 및 파일을 구성에 추가하고 나서 변경 내용을 배포하도록 스택을 업데이트하기만 하면 된다. 다음 템플릿 코드 조각에서는 변경 내용이 빨간색으로 강조 표시되어 있다.

    "WebServerInstance": {
      "Type" : "AWS::EC2::Instance",
      "Metadata" : {
        "Comment" : "Install a simple PHP application",
        "AWS::CloudFormation::Init" : {
          "config" : {
            "packages" : {
              "yum" : {
                "httpd"             : [],
                "php"               : [],
                "php-mysql"         : [],
                "mysql-server"      : [],
                "mysql-libs"        : [],
                "mysql"             : []
              }
            },

            :

            "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"]},
                "mysqld"   : { "enabled" : "true", "ensureRunning" : "true" }
              }
            }
          }
        }
      },

      "Properties": {
           :
      }
    }

CloudFormation 메타데이터를 업데이트하여 애플리케이션에 사용되는 패키지의 새 버전으로 업데이트한다. 이전 예제에서는 각 패키지의 버전 속성이 비어 있어서 cfn-init가 패키지의 최신 버전을 설치해야 했다.

     "packages" : {
       "yum" : {
         "httpd"             : [],
         "php"               : []
      }

원할 경우 패키지의 버전 문자열을 지정할 수 있다. 이후 업데이트 스택 호출에서 버전 문자열을 변경하는 경우 패키지의 새 버전이 배포된다. 다음은 RubyGems 패키지의 버전 번호를 사용하는 예이다. 버전 관리를 지원하는 패키지마다 특정 버전이 있을 수 있다.

  "packages" : {
    "rubygems" : {
      "mysql"           : [],
      "rubygems-update" : ["1.6.2"],
      "rake"            : ["0.8.7"],
      "rails"           : ["2.3.11"]
    }
    }

1) Auto Scaling 그룹 업데이트

템플릿에서 Auto Scaling 그룹을 사용하는 경우, Amazon EC2 인스턴스 리소스와 반대로, 애플리케이션 업데이트는 똑같은 방식으로 작동하지만 AWS CloudFormation에서 Auto Scaling 그룹의 Amazon EC2 인스턴스에 대해 동기화나 직렬화를 제공하지 않는다. 각 호스트의 cfn-hup 데몬은 독립적으로 실행되며 고유 일정에 따라 애플리케이션을 업데이트한다. cfn-hup를 사용하여 인스턴스의 구성을 업데이트하는 경우, 각 인스턴스는 고유의 일정에 따라 cfn-hup 후크를 실행한다. 다음을 고려해야 한다:

  • cfn-hup 변경이 Auto Scaling 그룹의 모든 Amazon EC2 인스턴스에서 동시에 실행되는 경우, 업데이트하는 동안 서비스를 사용할 수 없다.

  • cfn-hup 변경이 서로 다른 시간에 실행하는 경우 소프트웨어의 이전 및 새 버전을 동시에 실행할 수 있다.

이러한 문제를 방지하기 위해 Auto Scaling 그룹 내 인스턴스에 대한 강제 롤링 업데이트를 고려해 본다. 자세한 내용은 UpdatePolicy 속성 섹션을 참조한다.


4. 리소스 속성 변경

AWS CloudFormation을 사용하여 스택 내 기존 리소스의 속성을 변경할 수 있다. 다음 단원에서는 특정 문제를 해결하는 다양한 업데이트에 대해 설명한다. 하지만 스택 업데이트를 지원하는 리소스의 속성을 필요 시 수정할 수 있다.

1) 인스턴스 유형 업데이트

지금까지 생성한 스택에는 t1.micro Amazon EC2 인스턴스가 사용된다. 새로 생성한 웹 사이트의 트래픽 양이 t1.micro 인스턴스에서 처리할 수 있는 것보다 점점 더 많아지고 있어서 m1.small 인스턴스 유형으로 이동하려 한다고 가정해 보자. 인스턴스 유형의 아키텍처가 변경되는 경우 다른 AMI로 인스턴스가 생성된다. 템플릿에서 매핑을 살펴보면 t1.micro 및 m1.small 둘 다 동일한 아키텍처이며 동일한 Amazon Linux AMI가 사용됨을 알 수 있다.

  "Mappings" : {
    "AWSInstanceType2Arch" : {
      "t1.micro"    : { "Arch" : "HVM64"  },
      "t2.nano"     : { "Arch" : "HVM64"  },
      "t2.micro"    : { "Arch" : "HVM64"  },
      "t2.small"    : { "Arch" : "HVM64"  },
      "t2.medium"   : { "Arch" : "HVM64"  },
      "t2.large"    : { "Arch" : "HVM64"  },
      "m1.small"    : { "Arch" : "HVM64"  },
      "m1.medium"   : { "Arch" : "HVM64"  },
      "m1.large"    : { "Arch" : "HVM64"  },
      "m1.xlarge"   : { "Arch" : "HVM64"  },
      "m2.xlarge"   : { "Arch" : "HVM64"  },
      "m2.2xlarge"  : { "Arch" : "HVM64"  },
      "m2.4xlarge"  : { "Arch" : "HVM64"  },
      "m3.medium"   : { "Arch" : "HVM64"  },
      "m3.large"    : { "Arch" : "HVM64"  },
      "m3.xlarge"   : { "Arch" : "HVM64"  },
      "m3.2xlarge"  : { "Arch" : "HVM64"  },
      "m4.large"    : { "Arch" : "HVM64"  },
      "m4.xlarge"   : { "Arch" : "HVM64"  },
      "m4.2xlarge"  : { "Arch" : "HVM64"  },
      "m4.4xlarge"  : { "Arch" : "HVM64"  },
      "m4.10xlarge" : { "Arch" : "HVM64"  },
      "c1.medium"   : { "Arch" : "HVM64"  },
      "c1.xlarge"   : { "Arch" : "HVM64"  },
      "c3.large"    : { "Arch" : "HVM64"  },
      "c3.xlarge"   : { "Arch" : "HVM64"  },
      "c3.2xlarge"  : { "Arch" : "HVM64"  },
      "c3.4xlarge"  : { "Arch" : "HVM64"  },
      "c3.8xlarge"  : { "Arch" : "HVM64"  },
      "c4.large"    : { "Arch" : "HVM64"  },
      "c4.xlarge"   : { "Arch" : "HVM64"  },
      "c4.2xlarge"  : { "Arch" : "HVM64"  },
      "c4.4xlarge"  : { "Arch" : "HVM64"  },
      "c4.8xlarge"  : { "Arch" : "HVM64"  },
      "g2.2xlarge"  : { "Arch" : "HVMG2"  },
      "g2.8xlarge"  : { "Arch" : "HVMG2"  },
      "r3.large"    : { "Arch" : "HVM64"  },
      "r3.xlarge"   : { "Arch" : "HVM64"  },
      "r3.2xlarge"  : { "Arch" : "HVM64"  },
      "r3.4xlarge"  : { "Arch" : "HVM64"  },
      "r3.8xlarge"  : { "Arch" : "HVM64"  },
      "i2.xlarge"   : { "Arch" : "HVM64"  },
      "i2.2xlarge"  : { "Arch" : "HVM64"  },
      "i2.4xlarge"  : { "Arch" : "HVM64"  },
      "i2.8xlarge"  : { "Arch" : "HVM64"  },
      "d2.xlarge"   : { "Arch" : "HVM64"  },
      "d2.2xlarge"  : { "Arch" : "HVM64"  },
      "d2.4xlarge"  : { "Arch" : "HVM64"  },
      "d2.8xlarge"  : { "Arch" : "HVM64"  },
      "hi1.4xlarge" : { "Arch" : "HVM64"  },
      "hs1.8xlarge" : { "Arch" : "HVM64"  },
      "cr1.8xlarge" : { "Arch" : "HVM64"  },
      "cc2.8xlarge" : { "Arch" : "HVM64"  }
    },
    "AWSRegionArch2AMI" : {
      "us-east-1"        : {"HVM64" : "ami-0ff8a91507f77f867", "HVMG2" : "ami-0a584ac55a7631c0c"},
      "us-west-2"        : {"HVM64" : "ami-a0cfeed8", "HVMG2" : "ami-0e09505bc235aa82d"},
      "us-west-1"        : {"HVM64" : "ami-0bdb828fd58c52235", "HVMG2" : "ami-066ee5fd4a9ef77f1"},
      "eu-west-1"        : {"HVM64" : "ami-047bb4163c506cd98", "HVMG2" : "ami-0a7c483d527806435"},
      "eu-west-2"        : {"HVM64" : "ami-f976839e", "HVMG2" : "NOT_SUPPORTED"},
      "eu-west-3"        : {"HVM64" : "ami-0ebc281c20e89ba4b", "HVMG2" : "NOT_SUPPORTED"},
      "eu-central-1"     : {"HVM64" : "ami-0233214e13e500f77", "HVMG2" : "ami-06223d46a6d0661c7"},
      "ap-northeast-1"   : {"HVM64" : "ami-06cd52961ce9f0d85", "HVMG2" : "ami-053cdd503598e4a9d"},
      "ap-northeast-2"   : {"HVM64" : "ami-0a10b2721688ce9d2", "HVMG2" : "NOT_SUPPORTED"},
      "ap-northeast-3"   : {"HVM64" : "ami-0d98120a9fb693f07", "HVMG2" : "NOT_SUPPORTED"},
      "ap-southeast-1"   : {"HVM64" : "ami-08569b978cc4dfa10", "HVMG2" : "ami-0be9df32ae9f92309"},
      "ap-southeast-2"   : {"HVM64" : "ami-09b42976632b27e9b", "HVMG2" : "ami-0a9ce9fecc3d1daf8"},
      "ap-south-1"       : {"HVM64" : "ami-0912f71e06545ad88", "HVMG2" : "ami-097b15e89dbdcfcf4"},
      "us-east-2"        : {"HVM64" : "ami-0b59bfac6be064b78", "HVMG2" : "NOT_SUPPORTED"},
      "ca-central-1"     : {"HVM64" : "ami-0b18956f", "HVMG2" : "NOT_SUPPORTED"},
      "sa-east-1"        : {"HVM64" : "ami-07b14488da8ea02a0", "HVMG2" : "NOT_SUPPORTED"},
      "cn-north-1"       : {"HVM64" : "ami-0a4eaf6c4454eda75", "HVMG2" : "NOT_SUPPORTED"},
      "cn-northwest-1"   : {"HVM64" : "ami-6b6a7d09", "HVMG2" : "NOT_SUPPORTED"}
    }

이전 섹션에서 수정한 템플릿을 사용하여 인스턴스 유형을 변경해 보자. InstanceType이 템플릿의 입력 파라미터이므로 템플릿을 수정할 필요가 없다. [스택 업데이트(Stack Update)] 마법사의 [파라미터 지정(Specify Parameters)] 페이지에서 파라미터 값을 변경할 수 있다.

AWS Management Console에서 스택을 업데이트하려면

  1. https://console.aws.amazon.com/cloudformation에서 AWS CloudFormation 콘솔에 로그인한다.

  2. CloudFormation 대시보드에서 이전에 생성한 스택을 선택한 다음 [스택 업데이트(Update Stack)]를 선택한다.

  3. [스택 업데이트(Update Stack)] 마법사의 [템플릿 선택(Select Template)] 화면에서 [현재 템플릿 사용(Use current template)]을 선택하고 나서 [다음(Next)]을 선택한다.

    세부 정보 지정 페이지에서 파라미터 지정 섹션은 초기 스택을 생성하는 데 사용된 파라미터로 미리 채워진다.

  4. InstanceType 텍스트 상자의 값을 t1.micro에서 m1.small로 변경한다. 그런 다음 다음을 선택한다.

  5. [옵션(Options)] 화면에서 [다음(Next)]을 선택한다.

  6. 스택에 스택 정책이 없으므로 [다음(Next)]을 선택한다. 재정의 정책 없이 모든 리소스를 업데이트할 수 있다.

  7. [검토(Review)] 화면에서 모든 설정이 원하는 대로 지정되었는지 확인한 후 [업데이트(Update)]을 선택한다.

인스턴스를 시작했다가 중지하여 EBS 지원 Amazon EC2 인스턴스의 인스턴스 유형을 동적으로 변경할 수 있다. AWS CloudFormation에서는 인스턴스 유형을 업데이트하고 인스턴스를 다시 시작하여 변경을 최적화하려고 하므로 인스턴스 ID가 변경되지 않는다. 하지만 인스턴스가 다시 시작되는 경우 인스턴스의 퍼블릭 IP 주소가 변경된다. 변경 후 탄력적 IP 주소가 올바르게 바인딩되도록 하기 위해 에서 탄력적 IP 주소도 업데이트한다. 콘솔의 이벤트 탭에서 변경 내용을 확인할 수 있다.

AWS Management Console에서 인스턴스 유형을 확인하려면 Amazon EC2 콘솔을 열고 해당 인스턴스를 찾는다.

2) Amazon EC2 인스턴스에서 AMI 업데이트

이제 인스턴스에서 실행 중인 Amazon 머신 이미지(AMI)를 어떻게 변경하는지 살펴본다. HVM64 인스턴스 유형인 t2.medium 같은 새 Amazon EC2 인스턴스 유형을 사용하도록 스택을 업데이트하여 AMI 변경을 시작한다.

이전 섹션에서처럼 기존 템플릿을 사용하여 예제 스택에 사용되는 인스턴스 유형을 변경한다. [Stack Update] 마법사의 [Specify Parameters] 페이지에서 [Instance Type] 값을 변경한다.

이 경우 AMI를 수정하기 위해 단순히 인스턴스를 시작했다가 중지할 수 없으므로 AWS CloudFormation은 이를 리소스의 변경 불가능 속성에 대한 변경으로 간주한다. 변경 불가능한 속성을 변경하려면 AWS CloudFormation에서 대체 리소스(이 경우에는 새 AMI를 실행 중인 Amazon EC2 인스턴스)를 시작해야 한다.

새 인스턴스를 실행한 후에는 AWS CloudFormation에서 새 리소스를 가리키도록 스택의 다른 리소스를 업데이트한다. 새로운 모든 리소스가 생성되면 이전 리소스가 삭제되며, 이를 UPDATE_CLEANUP이라는 프로세스라고 한다. 현재는 업데이트의 결과로서 스택 내 인스턴스의 인스턴스 ID와 애플리케이션 URL이 변경되었다. Event 테이블의 이벤트에는 리소스가 대체되었음을 나타내는 "Requested update has a change to an immutable property and hence creating a new physical resource" 설명이 포함되어 있다.

업데이트할 애플리케이션 코드를 AMI에 작성한 경우 새 애플리케이션을 로드하도록 동일한 스택 업데이트 메커니즘을 사용하여 AMI를 업데이트할 수 있다.

스택의 인스턴스에 대한 AMI를 업데이트하려면

  1. 애플리케이션이나 운영 체제 변경 내용을 포함하는 새 AMI를 생성한다. 자세한 내용은 Linux 인스턴스용 Amazon EC2 사용 설명서의 고유 AMI 생성을 참조한다.

  2. 새 AMI ID를 통합하도록 템플릿을 업데이트한다.

  3. 애플리케이션 업데이트에 설명된 대로 AWS Management Console에서 스택을 업데이트하거나 AWS 명령 aws cloudformation update-stack을 사용하여 업데이트한다.

스택 업데이트 시, CloudFormation은 AMI ID가 변경되었음을 감지하고 나서 위에서 트리거했을 때와 동일한 방식으로 스택 업데이트를 시작한다.

3) Auto Scaling 그룹에 대한 Amazon EC2 시작 구성 업데이트

Amazon EC2 인스턴스가 아니라 Auto Scaling 그룹을 사용하려는 경우 실행 중인 인스턴스를 업데이트하는 프로세스가 조금 다르다. Auto Scaling 리소스를 사용하는 경우 인스턴스 유형 또는 AMI ID 같은 인스턴스의 구성이 Auto Scaling 시작 구성에 캡슐화된다. 이전 섹션에서 인스턴스 리소스를 변경할 때와 동일한 방식으로 시작 구성을 변경할 수 있다. 하지만 시작 구성은 Auto Scaling 그룹의 실행 중인 Amazon EC2 인스턴스에 영향을 미치지 않는다. 업데이트된 시작 구성은 업데이트 후에 생성되는 새 인스턴스에만 적용된다.

시작 구성에 대한 변경 사항을 Auto Scaling 그룹의 모든 인스턴스로 전파하려는 경우 업데이트 속성을 사용할 수 있다. 자세한 내용은 UpdatePolicy 속성 섹션을 참조한다.


5. 리소스 속성 추가

지금까지는 템플릿 안에 있는 리소스의 기존 속성에 대한 변경을 살펴보았다. 템플릿에 처음에 지정되지 않았던 속성을 추가할 수도 있다. 이를 설명하기 위해 Amazon EC2 키 페어를 기존 EC2 인스턴스에 추가하고 나서 Amazon EC2 보안 그룹에서 포트 22를 열어서 Secure Shell(SSH)을 사용하여 인스턴스에 액세스할 수 있도록 한다.

1) 인스턴스에 키 페어 추가

Amazon EC2 인스턴스에 SSH 액세스를 추가하려면

  1. 템플릿에 기존 Amazon EC2 키 페어 및 SSH 위치의 이름을 전달할 추가 파라미터 두 개를 추가한다.

      "Parameters" : {
    
        "KeyName" : {
          "Description" : "Name of an existing Amazon EC2 key pair for SSH access",
          "Type": "AWS::EC2::KeyPair::KeyName"
        },
        "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."
        }  
        :
      },
    
  2. KeyName 속성을 Amazon EC2 인스턴스에 추가한다.

              "WebServerInstance": {
              "Type" : "AWS::EC2::Instance",
              :
              "Properties": {
              :
              "KeyName" : { "Ref" : "KeyName" },
              :
              }
              },
    
  3. 포트 22 및 SSH 위치를 Amazon EC2 보안 그룹의 수신 규칙에 추가한다.

        "WebServerSecurityGroup" : {
          "Type" : "AWS::EC2::SecurityGroup",
          "Properties" : {
            "GroupDescription" : "Enable HTTP and SSH",
            "SecurityGroupIngress" : [
              {"IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : { "Ref" : "SSHLocation"}},
              {"IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "CidrIp" :  "0.0.0.0/0"}
            ]
          }
        },    
    
  4. 애플리케이션 업데이트에 설명된 대로 AWS Management Console에서 스택을 업데이트하거나 AWS 명령 aws cloudformation update-stack을 사용하여 업데이트한다.


6. 스택의 리소스 변경

시간이 지나면 애플리케이션 요구 사항이 변경될 수 있다. AWS CloudFormation에서는 스택을 구성하는 리소스 세트를 변경할 수 있다. 이를 보여주기 위해 리소스 속성 추가의 단일 인스턴스 애플리케이션을 가져와서 스택을 업데이트하여 이 애플리케이션을 자동 조정 및 로드 밸런싱된 애플리케이션으로 변환한다.

이렇게 하면 탄력적 IP 주소를 사용하여 간단한 단일 인스턴스 PHP 애플리케이션이 생성된다. 이제 업데이트 중에 리소스를 변경하여 이 애플리케이션을 자동 조정 및 로드 밸런싱된 고가용 애플리케이션으로 변환한다.

  1. 탄력적 로드 밸런서 리소스를 추가한다.

        "ElasticLoadBalancer" : {
          "Type" : "AWS::ElasticLoadBalancing::LoadBalancer",
          "Properties" : {
            "CrossZone" : "true",
            "AvailabilityZones" : { "Fn::GetAZs" : "" },
            "LBCookieStickinessPolicy" : [ {
              "PolicyName" : "CookieBasedPolicy",
              "CookieExpirationPeriod" : "30"
            } ],
            "Listeners" : [ {
              "LoadBalancerPort" : "80",
              "InstancePort" : "80",
              "Protocol" : "HTTP",
              "PolicyNames" : [ "CookieBasedPolicy" ]
            } ],
            "HealthCheck" : {
              "Target" : "HTTP:80/",
              "HealthyThreshold" : "2",
              "UnhealthyThreshold" : "5",
              "Interval" : "10",
              "Timeout" : "5"
            }
          }
        }
    
  2. 템플릿의 EC2 인스턴스를 Auto Scaling 시작 구성으로 변환한다. 속성이 동일하므로 유형 이름만 변경하면 된다.

    "WebServerInstance": {
      "Type" : "AWS::EC2::Instance",
    

    CNAME:

    "LaunchConfig": {
      "Type" : "AWS::AutoScaling::LaunchConfiguration",
    

    템플릿의 명확성을 위해 리소스의 이름을 WebServerInstance에서 LaunchConfig로 변경했으므로, cfn-init 및 cfn-hup에 의해 참조되는 리소스 이름을 업데이트해야 한다(단순히 WebServerInstance를 검색하여 LaunchConfig로 대체, cfn-signal의 경우 제외). cfn-signal의 경우, 다음 스크립트에서처럼 인스턴스가 아니라 그룹(WebServerGroup)에 신호를 전송해야 한다.

                "# Signal the status from cfn-init\n",
                "/opt/aws/bin/cfn-signal -e $? ",
                "         --stack ", { "Ref" : "AWS::StackName" },
                "         --resource WebServerGroup ",
                "         --region ", { "Ref" : "AWS::Region" }, "\n"
    
  3. Auto Scaling 그룹 리소스를 추가한다.

        "WebServerGroup" : {
          "Type" : "AWS::AutoScaling::AutoScalingGroup",
          "Properties" : {
            "AvailabilityZones" : { "Fn::GetAZs" : "" },
            "LaunchConfigurationName" : { "Ref" : "LaunchConfig" },
            "MinSize" : "1",
            "DesiredCapacity" : "1",
            "MaxSize" : "5",
            "LoadBalancerNames" : [ { "Ref" : "ElasticLoadBalancer" } ]
          },
          "CreationPolicy" : {
            "ResourceSignal" : {
              "Timeout" : "PT15M"
            }
          },
          "UpdatePolicy": {
            "AutoScalingRollingUpdate": {
              "MinInstancesInService": "1",
              "MaxBatchSize": "1",
              "PauseTime" : "PT15M",
              "WaitOnResourceSignals": "true"
            }
          }
        }
    
  4. 로드 밸런서에서 인스턴스로 나가는 트래픽을 차단하도록 보안 그룹 정의를 업데이트한다.

        "WebServerSecurityGroup" : {
          "Type" : "AWS::EC2::SecurityGroup",
          "Properties" : {
            "GroupDescription" : "Enable HTTP access via port 80 locked down to the ELB and SSH access",
            "SecurityGroupIngress" : [
              {"IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "SourceSecurityGroupOwnerId" : {"Fn::GetAtt" : ["ElasticLoadBalancer", "SourceSecurityGroup.OwnerAlias"]},
    "SourceSecurityGroupName" : {"Fn::GetAtt" : ["ElasticLoadBalancer", "SourceSecurityGroup.GroupName"]}},
              {"IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : { "Ref" : "SSHLocation"}}
            ]
          }
        }
    
  5. 애플리케이션 위치로서 탄력적 로드 밸런서의 DNS 이름을 반환하도록 출력을 업데이트한다.

    "WebsiteURL" : {
      "Value" : { "Fn::Join" : ["", ["http://",
          { "Fn::GetAtt" : [ "WebServerInstance", "PublicDnsName" ]}]]},
      "Description" : "Application URL"
    }
    

    CNAME:

    "WebsiteURL" : {
      "Value" : { "Fn::Join" : ["", ["http://",
          { "Fn::GetAtt" : [ "ElasticLoadBalancer", "DNSName" ]}]]},
      "Description" : "Application URL"
    }
    

참고로, 다음 샘플은 전체 템플릿을 보여준다. 이 템플릿을 사용하여 스택을 업데이트하는 경우 단순한 단일 인스턴스 애플리케이션을 고가용, multi-AZ, 자동 조정 및 로드 밸런싱된 애플리케이션으로 변환한다. 업데이트해야 할 리소스만 변경되므로, 이 애플리케이션에 대한 데이터 스토어가 있을 것이고 데이터가 변경되지 않은 상태로 유지될 것이다. 이제 AWS CloudFormation을 사용하여 요구 사항이 변경될 때 스택을 확장하거나 개선할 수 있다.

{
  "AWSTemplateFormatVersion" : "2010-09-09",

  "Description" : "AWS CloudFormation Sample Template: Sample template that can be used to test EC2 updates. **WARNING** This template creates an Amazon Ec2 Instance. You will be billed for the AWS resources used if you create a stack from this template.",

  "Parameters" : {

    "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."
    },

    "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."
    },

    "InstanceType" : {
      "Description" : "WebServer EC2 instance type",
      "Type" : "String",
      "Default" : "t2.small",
      "AllowedValues" : [ 
        "t1.micro", 
        "t2.nano", 
        "t2.micro", 
        "t2.small", 
        "t2.medium", 
        "t2.large", 
        "m1.small", 
        "m1.medium", 
        "m1.large", 
        "m1.xlarge", 
        "m2.xlarge", 
        "m2.2xlarge", 
        "m2.4xlarge", 
        "m3.medium", 
        "m3.large", 
        "m3.xlarge", 
        "m3.2xlarge", 
        "m4.large", 
        "m4.xlarge", 
        "m4.2xlarge", 
        "m4.4xlarge", 
        "m4.10xlarge", 
        "c1.medium", 
        "c1.xlarge", 
        "c3.large", 
        "c3.xlarge", 
        "c3.2xlarge", 
        "c3.4xlarge", 
        "c3.8xlarge", 
        "c4.large", 
        "c4.xlarge", 
        "c4.2xlarge", 
        "c4.4xlarge", 
        "c4.8xlarge", 
        "g2.2xlarge", 
        "g2.8xlarge", 
        "r3.large", 
        "r3.xlarge", 
        "r3.2xlarge", 
        "r3.4xlarge", 
        "r3.8xlarge", 
        "i2.xlarge", 
        "i2.2xlarge", 
        "i2.4xlarge", 
        "i2.8xlarge", 
        "d2.xlarge", 
        "d2.2xlarge", 
        "d2.4xlarge", 
        "d2.8xlarge", 
        "hi1.4xlarge", 
        "hs1.8xlarge", 
        "cr1.8xlarge", 
        "cc2.8xlarge", 
        "cg1.4xlarge"
      ],
      "ConstraintDescription" : "must be a valid EC2 instance type."
    }
  },

  "Mappings" : {
    "AWSInstanceType2Arch" : {
      "t1.micro"    : { "Arch" : "HVM64"  },
      "t2.nano"     : { "Arch" : "HVM64"  },
      "t2.micro"    : { "Arch" : "HVM64"  },
      "t2.small"    : { "Arch" : "HVM64"  },
      "t2.medium"   : { "Arch" : "HVM64"  },
      "t2.large"    : { "Arch" : "HVM64"  },
      "m1.small"    : { "Arch" : "HVM64"  },
      "m1.medium"   : { "Arch" : "HVM64"  },
      "m1.large"    : { "Arch" : "HVM64"  },
      "m1.xlarge"   : { "Arch" : "HVM64"  },
      "m2.xlarge"   : { "Arch" : "HVM64"  },
      "m2.2xlarge"  : { "Arch" : "HVM64"  },
      "m2.4xlarge"  : { "Arch" : "HVM64"  },
      "m3.medium"   : { "Arch" : "HVM64"  },
      "m3.large"    : { "Arch" : "HVM64"  },
      "m3.xlarge"   : { "Arch" : "HVM64"  },
      "m3.2xlarge"  : { "Arch" : "HVM64"  },
      "m4.large"    : { "Arch" : "HVM64"  },
      "m4.xlarge"   : { "Arch" : "HVM64"  },
      "m4.2xlarge"  : { "Arch" : "HVM64"  },
      "m4.4xlarge"  : { "Arch" : "HVM64"  },
      "m4.10xlarge" : { "Arch" : "HVM64"  },
      "c1.medium"   : { "Arch" : "HVM64"  },
      "c1.xlarge"   : { "Arch" : "HVM64"  },
      "c3.large"    : { "Arch" : "HVM64"  },
      "c3.xlarge"   : { "Arch" : "HVM64"  },
      "c3.2xlarge"  : { "Arch" : "HVM64"  },
      "c3.4xlarge"  : { "Arch" : "HVM64"  },
      "c3.8xlarge"  : { "Arch" : "HVM64"  },
      "c4.large"    : { "Arch" : "HVM64"  },
      "c4.xlarge"   : { "Arch" : "HVM64"  },
      "c4.2xlarge"  : { "Arch" : "HVM64"  },
      "c4.4xlarge"  : { "Arch" : "HVM64"  },
      "c4.8xlarge"  : { "Arch" : "HVM64"  },
      "g2.2xlarge"  : { "Arch" : "HVMG2"  },
      "g2.8xlarge"  : { "Arch" : "HVMG2"  },
      "r3.large"    : { "Arch" : "HVM64"  },
      "r3.xlarge"   : { "Arch" : "HVM64"  },
      "r3.2xlarge"  : { "Arch" : "HVM64"  },
      "r3.4xlarge"  : { "Arch" : "HVM64"  },
      "r3.8xlarge"  : { "Arch" : "HVM64"  },
      "i2.xlarge"   : { "Arch" : "HVM64"  },
      "i2.2xlarge"  : { "Arch" : "HVM64"  },
      "i2.4xlarge"  : { "Arch" : "HVM64"  },
      "i2.8xlarge"  : { "Arch" : "HVM64"  },
      "d2.xlarge"   : { "Arch" : "HVM64"  },
      "d2.2xlarge"  : { "Arch" : "HVM64"  },
      "d2.4xlarge"  : { "Arch" : "HVM64"  },
      "d2.8xlarge"  : { "Arch" : "HVM64"  },
      "hi1.4xlarge" : { "Arch" : "HVM64"  },
      "hs1.8xlarge" : { "Arch" : "HVM64"  },
      "cr1.8xlarge" : { "Arch" : "HVM64"  },
      "cc2.8xlarge" : { "Arch" : "HVM64"  }
    },
    "AWSRegionArch2AMI" : {
      "us-east-1"        : {"HVM64" : "ami-0ff8a91507f77f867", "HVMG2" : "ami-0a584ac55a7631c0c"},
      "us-west-2"        : {"HVM64" : "ami-a0cfeed8", "HVMG2" : "ami-0e09505bc235aa82d"},
      "us-west-1"        : {"HVM64" : "ami-0bdb828fd58c52235", "HVMG2" : "ami-066ee5fd4a9ef77f1"},
      "eu-west-1"        : {"HVM64" : "ami-047bb4163c506cd98", "HVMG2" : "ami-0a7c483d527806435"},
      "eu-west-2"        : {"HVM64" : "ami-f976839e", "HVMG2" : "NOT_SUPPORTED"},
      "eu-west-3"        : {"HVM64" : "ami-0ebc281c20e89ba4b", "HVMG2" : "NOT_SUPPORTED"},
      "eu-central-1"     : {"HVM64" : "ami-0233214e13e500f77", "HVMG2" : "ami-06223d46a6d0661c7"},
      "ap-northeast-1"   : {"HVM64" : "ami-06cd52961ce9f0d85", "HVMG2" : "ami-053cdd503598e4a9d"},
      "ap-northeast-2"   : {"HVM64" : "ami-0a10b2721688ce9d2", "HVMG2" : "NOT_SUPPORTED"},
      "ap-northeast-3"   : {"HVM64" : "ami-0d98120a9fb693f07", "HVMG2" : "NOT_SUPPORTED"},
      "ap-southeast-1"   : {"HVM64" : "ami-08569b978cc4dfa10", "HVMG2" : "ami-0be9df32ae9f92309"},
      "ap-southeast-2"   : {"HVM64" : "ami-09b42976632b27e9b", "HVMG2" : "ami-0a9ce9fecc3d1daf8"},
      "ap-south-1"       : {"HVM64" : "ami-0912f71e06545ad88", "HVMG2" : "ami-097b15e89dbdcfcf4"},
      "us-east-2"        : {"HVM64" : "ami-0b59bfac6be064b78", "HVMG2" : "NOT_SUPPORTED"},
      "ca-central-1"     : {"HVM64" : "ami-0b18956f", "HVMG2" : "NOT_SUPPORTED"},
      "sa-east-1"        : {"HVM64" : "ami-07b14488da8ea02a0", "HVMG2" : "NOT_SUPPORTED"},
      "cn-north-1"       : {"HVM64" : "ami-0a4eaf6c4454eda75", "HVMG2" : "NOT_SUPPORTED"},
      "cn-northwest-1"   : {"HVM64" : "ami-6b6a7d09", "HVMG2" : "NOT_SUPPORTED"}
    }
  },

  "Resources" : {

    "ElasticLoadBalancer" : {
      "Type" : "AWS::ElasticLoadBalancing::LoadBalancer",
      "Properties" : {
        "CrossZone" : "true",
        "AvailabilityZones" : { "Fn::GetAZs" : "" },
        "LBCookieStickinessPolicy" : [ {
          "PolicyName" : "CookieBasedPolicy",
          "CookieExpirationPeriod" : "30"
        } ],
        "Listeners" : [ {
          "LoadBalancerPort" : "80",
          "InstancePort" : "80",
          "Protocol" : "HTTP",
          "PolicyNames" : [ "CookieBasedPolicy" ]
        } ],
        "HealthCheck" : {
          "Target" : "HTTP:80/",
          "HealthyThreshold" : "2",
          "UnhealthyThreshold" : "5",
          "Interval" : "10",
          "Timeout" : "5"
        }
      }
    },

    "WebServerGroup" : {
      "Type" : "AWS::AutoScaling::AutoScalingGroup",
      "Properties" : {
        "AvailabilityZones" : { "Fn::GetAZs" : "" },
        "LaunchConfigurationName" : { "Ref" : "LaunchConfig" },
        "MinSize" : "1",
        "DesiredCapacity" : "1",
        "MaxSize" : "5",
        "LoadBalancerNames" : [ { "Ref" : "ElasticLoadBalancer" } ]
      },
      "CreationPolicy" : {
        "ResourceSignal" : {
          "Timeout" : "PT15M"
        }
      },
      "UpdatePolicy": {
        "AutoScalingRollingUpdate": {
          "MinInstancesInService": "1",
          "MaxBatchSize": "1",
          "PauseTime" : "PT15M",
          "WaitOnResourceSignals": "true"
        }
      }
    },

    "LaunchConfig": {  
      "Type" : "AWS::AutoScaling::LaunchConfiguration",
      "Metadata" : {
        "Comment" : "Install a simple PHP application",
        "AWS::CloudFormation::Init" : {
          "config" : {
            "packages" : {
              "yum" : {
                "httpd"             : [],
                "php"               : []
              }
            },

            "files" : {

              "/var/www/html/index.php" : {
                "content" : { "Fn::Join" : ["", [
                  "<?php\n",
                  "echo '<h1>AWS CloudFormation sample PHP application</h1>';\n",
                  "echo 'Updated version via UpdateStack';\n ",
                  "?>\n"
                ]]},
                "mode"    : "000644",
                "owner"   : "apache",
                "group"   : "apache"
              },


              "/etc/cfn/cfn-hup.conf" : {
                "content" : { "Fn::Join" : ["", [
                  "[main]\n",
                  "stack=", { "Ref" : "AWS::StackId" }, "\n",
                  "region=", { "Ref" : "AWS::Region" }, "\n"
                ]]},
                "mode"    : "000400",
                "owner"   : "root",
                "group"   : "root"
              },

              "/etc/cfn/hooks.d/cfn-auto-reloader.conf" : {
                "content": { "Fn::Join" : ["", [
                  "[cfn-auto-reloader-hook]\n",
                  "triggers=post.update\n",
                  "path=Resources.LaunchConfig.Metadata.AWS::CloudFormation::Init\n",
                  "action=/opt/aws/bin/cfn-init -s ", { "Ref" : "AWS::StackId" }, " -r LaunchConfig ",
                                                   " --region     ", { "Ref" : "AWS::Region" }, "\n",
                  "runas=root\n"
                ]]}
              }
            },

            "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": {
        "ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" },
                          { "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] },
        "InstanceType"   : { "Ref" : "InstanceType" },
        "KeyName"        : { "Ref" : "KeyName" },
        "SecurityGroups" : [ {"Ref" : "WebServerSecurityGroup"} ],
        "UserData"       : { "Fn::Base64" : { "Fn::Join" : ["", [
             "#!/bin/bash -xe\n",
             "yum install -y aws-cfn-bootstrap\n",

             "# Install the files and packages from the metadata\n",
             "/opt/aws/bin/cfn-init -v ",
             "         --stack ", { "Ref" : "AWS::StackName" },
             "         --resource LaunchConfig ",
             "         --region ", { "Ref" : "AWS::Region" }, "\n",

             "# Start up the cfn-hup daemon to listen for changes to the Web Server metadata\n",
             "/opt/aws/bin/cfn-hup || error_exit 'Failed to start cfn-hup'\n",  

             "# Signal the status from cfn-init\n",
             "/opt/aws/bin/cfn-signal -e $? ",
             "         --stack ", { "Ref" : "AWS::StackName" },
             "         --resource WebServerGroup ",
             "         --region ", { "Ref" : "AWS::Region" }, "\n"
        ]]}}        
      }
    },

    "WebServerSecurityGroup" : {
      "Type" : "AWS::EC2::SecurityGroup",
      "Properties" : {
        "GroupDescription" : "Enable HTTP access via port 80 locked down to the ELB and SSH access",
        "SecurityGroupIngress" : [
          {"IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "SourceSecurityGroupOwnerId" : {"Fn::GetAtt" : ["ElasticLoadBalancer", "SourceSecurityGroup.OwnerAlias"]},"SourceSecurityGroupName" : {"Fn::GetAtt" : ["ElasticLoadBalancer", "SourceSecurityGroup.GroupName"]}},
          {"IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : { "Ref" : "SSHLocation"}}
        ]
      }
    }          
  },

  "Outputs" : {
    "WebsiteURL" : {
      "Description" : "Application URL",
      "Value" : { "Fn::Join" : ["", ["http://", { "Fn::GetAtt" : [ "ElasticLoadBalancer", "DNSName" ]}]] }
    }
  }
}

7. 가용성 및 영향 고려 사항

속성마다 스택의 리소스에 미치는 영향이 다르다. CloudFormation을 사용하여 속성을 업데이트할 수 있지만, 변경하기 전에 다음 질문을 고려해야 한다.

  1. 업데이트가 리소스 자체에 어떤 영향을 미치는가? 예를 들어 경보 임계값 업데이트는 업데이트 중에 경보를 비활성화한다. 아시다시피, 인스턴스 유형을 변경하려면 인스턴스를 중지했다가 다시 시작해야 한다. AWS CloudFormation은 기본 리소스에 대해 업데이트 또는 수정 작업을 사용하여 리소스를 변경한다. 업데이트 영향을 이해하려면 특정 리소스에 대한 설명서를 확인해야 한다.

  2. 변경이 가능한가? 불가능한가? Amazon EC2 인스턴스에서 AMI 변경과 같이 리소스 속성에 대한 일부 변경은 기본 서비스에서 지원되지 않는다. 가능한 변경의 경우, CloudFormation은 기본 리소스에 대해 업데이트 또는 수정 유형의 API를 사용한다. 불가능한 속성 변경의 경우, CloudFormation은 업데이트된 속성을 사용하여 새 리소스를 생성하고 나서 이전 리소스를 삭제하기 전에 새 리소스를 스택에 연결한다. CloudFormation이 스택 리소스의 중단 시간을 줄이려고 노력하더라도, 리소스 대체는 다단계 프로세스이며 시간이 걸리는 작업이다. 스택 구성 중에는 애플리케이션이 부분적으로만 작동한다. 예를 들면 요청을 처리하거나 데이터베이스에 액세스하지 못할 수도 있다.

8. 관련 리소스

CloudFormation을 사용하여 애플리케이션 시작 및 다른 구성 및 배포 서비스(예: Puppet및 Opscode Chef)와의 통합에 대한 자세한 내용은 다음 백서를 참조한다.

이 단원 전체에서 사용된 템플릿은 "Hello, World" PHP 애플리케이션이다. 이 템플릿 라이브러리에는 cfn-hup 및 cfn-init를 사용하여 ElasticCache와 PHP 애플리케이션을 통합함으로써 Amazon ElastiCache 캐시 클러스터 구성 변경에 응답하는 방법을 보여주는 Amazon ElastiCache 샘플 템플릿이 있으며, 이러한 모든 작업은 업데이트 스택을 통해 수행할 수 있다.


References