CloudFormation

提供: 技術wiki
ナビゲーションに移動 検索に移動

CloudFormationはAWS上で構築するインフラ作業を自動化するサービスである。 AWS上に構築するリソースをテンプレートファイルとして定義し、それをCloudFormationに取り込ませインフラを構築していく。 取り込んだテンプレートはCloudFormation上でスタックとして管理され、以降内容の更新が可能。 テンプレートはjsonもしくはymlで定義する。 ここではymlで定義する前提で説明する。

使い方

マネジメントコンソール上からCloudFormationのサービスを選び、作成したテンプレートファイルをアップロードする。

テンプレート

テンプレートの記述ルール。 テンプレートは以下4つの項目を定義する。

Parameters:
CloudFormationを使用する際の引数を定義。
画面上から入力できるものもあれば、テンプレートをネストした際に親テンプレートから引き継ぐことが可能。
Mappings:
CloudFormation内で使用する定数を定義。
Resources:
CloudFormationで作成するリソースを定義。
Outputs:
CloudFormation実行後に出力する項目を定義。

パラメータ定義

テンプレートに渡すパラメータの定義。

定義

変数名と型を定義する。

Parameters:
  Prefix : {"Type" : "String"}
  AccountId : {"Type" : "Number"}

参照

組み込み関数のRefで参照する。 ※パラメータに限らず定義した変数は基本的にRefで参照可能。

{"Ref" : "Prefix"}

マッピング定義

テンプレート内でキーと値をセットで定数を定義する。

定義

3階層でキーと値を定義する。

Mappings:
  Develop:
    Prefix:
      Lerge: "Develop"
    TempleteURL:
      VPC: "s3-ap-northeast-1.amazonaws.com/templete/01_vpc.yml"
      EC2: "s3-ap-northeast-1.amazonaws.com/templete/02_ec2.yml"

参照

組み込み関数Fn::FindInMapを使用して参照する。

{"Fn::FindInMap": [ Develop, Prefix, Lerge ] }

リソース定義

構築するAWSリソースを定義する。

※コードは下記のサンプル参照

出力項目定義

テンプレートを適用した結果の変数を出力する。 出力した内容はマネジメントコンソールで確認する。 ネストしていた子スタックが値を出力した場合は親スタックで参照可能。

定義

出力変数名を定義し、Valueに組み込み関数のRefで値を設定する。

Outputs:
  VPCId:
    Value: { Ref : VPC }
  EIP:
    Value: { Ref : EIP }

ネストについて

ここでいうCloudFormationのネストとはテンプレート内で別のテンプレートを呼び出すことである。 このテンプレートからスタックを作成した際に自動で別のスタックが作成される。 大規模なインフラを構築する際にはネストを利用してテンプレートを記述することを勧める。 例えば、共通項目を別のテンプレートとして作成することで流用が可能となり保守性が上がる。 また、一つのテンプレートに対して記述するリソース定義の量を減らすことで可読性が上がる。

定義の仕方

リソース定義としてスタックを定義する。

Mappings:
  Develop:
    TempleteURL:
      VPC: "https://s3-ap-northeast-1.amazonaws.com/templete/01_vpc.yml"
    Prefix:
      Upper: Develop-
      Lower: develop-
    VPC:
      CidrBlock: "10.254"
    VPN:
      CidrBlock: "192.168"
Resources:
  VPC:
    Type: AWS::CloudFormation::Stack
    DeletionPolicy: Delete
    Properties:
      TemplateURL: {"Fn::FindInMap": [ Develop, TempleteURL, VPC ] }
      Parameters:
        PrefixUpper: {"Fn::FindInMap": [ Develop, Prefix, Upper ] }
        CidrBlock: {"Fn::FindInMap": [ Develop, VPC, CidrBlock ] }
        VPNCidrBlock: {"Fn::FindInMap": [ Develop, VPN, CidrBlock ] }

削除ポリシーについて

スタックを削除したときのリソースの振る舞いを設定する項目。 リソースを作成するときに設定することができる。 デフォルトだとスタックを削除したときに一緒にリソースも削除する。 基本的にデフォルトのままで問題ない。 ※誤ってスタック自体が削除されないようユーザー管理は徹底すること

削除ポリシー(DeletionPolicy)
ポリシー 振る舞い
Delete 削除する。※デフォルト
Retain 削除しない。
Snapshot スナップショットを取得してから削除する。

サンプル

以下、リソースを作成する際のサンプルである。 このサンプルは実際にインフラ構築を実施した際に使用したテンプレートを元にしている。 このためマップやパラメータの参照が多くなっているがこの部分は固定値にしてしまっても良い。

Stack

VPC:
 Type: AWS::CloudFormation::Stack
   DeletionPolicy: Delete
   Properties:
    TemplateURL: {"Fn::FindInMap": [ Develop, TempleteURL, VPC ] }
    Parameters:
      PrefixLarge: {"Fn::FindInMap": [ Develop, Prefix, Large ] }
      CidrBlock: {"Fn::FindInMap": [ Develop, VPC, CidrBlock ] }

VPC

VPC:
  Type: AWS::EC2::VPC
  Properties:
    CidrBlock: {"Fn::Join" : ["", [{"Ref" : "CidrBlock"}, ".0.0/16"]]}
    EnableDnsHostnames: true
    EnableDnsSupport: true
    InstanceTenancy: default
    Tags:
    - Key: Name
      Value: {"Fn::Join" : ["", [{"Ref" : "Prefix"}, "VPC"]]}
Subnet:
  Type: AWS::EC2::Subnet
  Properties:
    VpcId: { Ref: VPC }
    AvailabilityZone: ap-northeast-1a
    CidrBlock: {"Fn::Join" : ["", [{"Ref" : "CidrBlock"}, ".0.0/24"]]}
    Tags:
    - Key: Name
      Value: {"Fn::Join" : ["", [{"Ref" : "Prefix"}, "Subnet"]]}

EC2(SecurityGroup)

セキュリティグループはルールを設定する方法が2つある。 セキュリティグループ自身を作成する際に指定する方法と後付けをする方法。 他のセキュリティグループを参照するルールを設定する場合は後付けを勧める。

WebServerSG:
  Type: AWS::EC2::SecurityGroup
  Properties:
    VpcId: { Ref: VPC }
    GroupDescription: {"Fn::Join" : ["", [{"Ref" : "Prefix"}, "WebServer-SG"]]}
    Tags:
    - Key: Name
      Value: {"Fn::Join" : ["", [{"Ref" : "Prefix"}, "WebServer-SG"]]}
    SecurityGroupEgress:
    - { IpProtocol: tcp, FromPort: 80,   ToPort: 80,   CidrIp: 0.0.0.0/0 }
    - { IpProtocol: tcp, FromPort: 443,  ToPort: 443,  CidrIp: 0.0.0.0/0 }
    - { IpProtocol: udp, FromPort: 53,   ToPort: 53,   CidrIp: 0.0.0.0/0 }
    - { IpProtocol: udp, FromPort: 123,  ToPort: 123,  CidrIp: 0.0.0.0/0 }
WebServerSGIngress:
  Type: AWS::EC2::SecurityGroupIngress
  Properties: { GroupId: {Ref : WebServerSG}, IpProtocol: tcp, FromPort: 22,ToPort: 22, CidrIp: 0.0.0.0/0 }
WebServerSGEgress:
  Type: AWS::EC2::SecurityGroupEgress
  Properties: { GroupId: {Ref : WebServerSG}, IpProtocol: tcp, FromPort: 443,   ToPort: 443,   SourceSecurityGroupId: { Ref : ApiServerSG } }

EC2(Instance/ELB)

WebServerLB:
  Type: AWS::ElasticLoadBalancing::LoadBalancer
  DeletionPolicy: Delete
  Properties:
    LoadBalancerName: {"Fn::Join" : ["", [{"Ref" : "Prefix"}, "WebServer-LB"]]}
    Subnets: 
    - { Ref : PublicSubnetA }
    - { Ref : PublicSubnetC }
    Scheme: internal
    CrossZone: TRUE
    ConnectionDrainingPolicy:
      Enabled: TRUE
      Timeout: 5
    HealthCheck:
      HealthyThreshold: 2
      Interval: 30
      Target: HTTP:8080/index.html
      Timeout: 5
      UnhealthyThreshold: 2
    SecurityGroups: 
    - { Ref : WebServerLBSG }
    Listeners:
    - InstancePort: 8080
      LoadBalancerPort: 80
      Protocol: HTTP
      InstanceProtocol: HTTP
    - InstancePort: 8080
      LoadBalancerPort: 443
      Protocol: HTTPS
      InstanceProtocol: HTTP
      SSLCertificateId: {Ref : SSLCertificateId}
      PolicyNames:
      - AppCookieStickinessPolicy
    AppCookieStickinessPolicy:
    - PolicyName: AppCookieStickinessPolicy
      CookieName: JSESSIONID
    AccessLoggingPolicy:
      S3BucketName: {"Fn::Join" : ["", ["system-", {"Ref" : "Prefix"}, "logs"]]}
      S3BucketPrefix: loadbalancerLog
      Enabled: true
      EmitInterval: 5

S3

s3contents:
  Type: AWS::S3::Bucket
  Properties:
    BucketName: {"Fn::Join" : ["", ["systemtest-",{"Ref" : "Prefix"}, "contents"]]}
    AccessControl: BucketOwnerFullControl
s3contentsBucketPolicy: 
  Type: AWS::S3::BucketPolicy
  Properties: 
    Bucket: 
      Ref: s3contents
    PolicyDocument: 
      Statement :
      - 
        Effect: "Allow"
        Sid: "IPAllow"
        Principal: "*"
        Action: 
          - "s3:GetObject"
        Resource: {"Fn::Join" : ["", ["arn:aws:s3:::systemtest-", {"Ref" : "Prefix"}, "contents/*"]]}
        Condition:
          IpAddress:
            aws:SourceIp: { "Fn::FindInMap": [ Develop, Bucket, SourceIp ] }

SNS

SNS:
  Type: AWS::SNS::Topic
  Properties:
    DisplayName: {"Fn::Join" : ["", [{"Ref" : "Prefix"}, "system-sns"]]}
    TopicName: {"Fn::Join" : ["", [{"Ref" : "Prefix"}, "system-sns"]]}

SQS

QueuePushNotificationDeadLetterQueue:
  Type: AWS::SQS::Queue
  DeletionPolicy: Delete
  Properties:
    QueueName: {"Fn::Join" : ["", [{"Ref" : "PrefixUpper"}, "Push-Notification-Dead-Letter-Queue"]]}
    VisibilityTimeout: 30
QueuePushNotificationQueue:
  Type: AWS::SQS::Queue
  DeletionPolicy: Delete
  Properties:
    QueueName: {"Fn::Join" : ["", [{"Ref" : "PrefixUpper"}, "Push-Notification-Queue"]]}
    VisibilityTimeout: 30
    ReceiveMessageWaitTimeSeconds: 20
    RedrivePolicy:
      deadLetterTargetArn: {"Fn::GetAtt" : [ "QueuePushNotificationDeadLetterQueue" , "Arn" ]}
      maxReceiveCount: 1

RDS

RDSはパラメータグループやサブネットグループ(必要ならオプショングループも)のみ作成する。 インスタンスは手動で削除・復元をするためCloudFormationで管理しないことを勧める。

DBSubnetGroup:
  Type: AWS::RDS::DBSubnetGroup
  Properties:
    DBSubnetGroupDescription : {"Fn::Join" : ["", [{"Ref" : "Prefix"}, "SubnetGroup"]]}
    SubnetIds: [{"Ref" : "PrivateSubnetA"}, {"Ref" : "PrivateSubnetC"}]
ParameterGroup:
  Type: AWS::RDS::DBParameterGroup
  Properties: 
    Description: {"Fn::Join" : ["", [{"Ref" : "Prefix"}, "pg"]]}
    Family: mysql5.6
    Parameters:
      binlog_cache_size: 32768
      binlog_format: MIXED
      character_set_client: utf8
      character_set_connection: utf8
      character_set_database: utf8
      character_set_filesystem: utf8
      character_set_results: utf8
      character_set_server: utf8
      collation_connection: utf8_general_ci
      explicit_defaults_for_timestamp: 1
      general_log: 1
      slow_query_log: 1
      innodb_buffer_pool_size: "{DBInstanceClassMemory*3/4}"
      innodb_flush_method: O_DIRECT