RACCOON TECH BLOG

株式会社ラクーンホールディングスのエンジニア/デザイナーから技術情報をはじめ、世の中のためになることや社内のことなどを発信してます。

CloudFormation入門 – ハマリポイント・注意点

こんにちは。インフラ担当のいせです。
弊社が運用しているサービスのなかのいくつかはAWS上で動いています。
当初はWebコンソールでリソースの作成や設定変更を行っていたのですが、だんだん規模が大きくなるなかで手作業での設定には限界を感じ、CloudFormation(以下CFN)を利用することにしました。
今回はCFNの社内向け勉強会の資料をブログとしてまとめてみます。

CFNとは

ざっくりした使い方

新規作成

  1. テンプレートを作成します。(テンプレートの詳しい話は後述)
  2. Webコンソールにログインして、CFNの管理画面を開きます。
  3. 「スタックの作成」をクリックします。
  4. テンプレートをアップロードして、「次へ」をクリックします。
  5. スタックの名前とテンプレートに応じてパラメータを入力して「次へ」をクリックします。
  6. スタックオプションの設定をする画面が表示されますが、とりあえずはいじらなくて問題ないので、再び「次へ」をクリックします。
  7. 確認画面が表示されるので、内容に問題がなければ「スタックの作成」をクリックします。

設定変更

  1. 新規作成時に使用したテンプレートをもとに、変更したい内容を反映したテンプレートを作成します。
  2. Webコンソールにログインして、CFNの管理画面を開きます。
  3. 変更したいスタックを選択し、「更新する」をクリックします。
  4. テンプレートをアップロードして、「次へ」をクリックします。
  5. 必要に応じてパラメータを修正し、「次へ」をクリックします。
  6. スタックオプションの設定をする画面が表示されますが、とりあえずはいじらなくて問題ないので、再び「次へ」をクリックします。
  7. 確認画面が表示されるので、内容に問題がなければ「実行」をクリックします。

スタックについて

テンプレートの書き方

まず最初にやるべきこと

テンプレートの例: VPNの中にSubnetを一つ作る

AWSTemplateFormatVersion: '2010-09-09'
Resources:
    vpc:                                   # 1.
        Type: AWS::EC2::VPC                # 2.
        Properties:                        # 3.
            CidrBlock: 10.0.0.0/16
            EnableDnsHostnames: true
            EnableDnsSupport: true
            InstanceTenancy: default
            Tags:
                - Key: Name
                  Value: vpc

    subnetPublicA:                            # 1.
        Type: AWS::EC2::Subnet                # 2.
        Properties:                           # 3.
            CidrBlock: 10.0.0.0/24
            VpcId: !Ref vpc
            AvailabilityZone: ap-northeast-1a
            Tags:
                - Key: Name
                  Value: subnetPublicA

AWSTemplateFormatVersion

Resourcesセクション

1. リーソスの名前

2. Type

3. Properties


AWSはややこしいことにリソースを特定する方法が主に3つあります
  1. リソースの種別ごとに固有のID
    • 例えばVPC ID(vpc-xxxxxxxxxxxxxxxxxx)やEC2インスタンスID(i-xxxxxxxx)などが該当します。
    • リソースを作成すると自動的に採番されるIDです。
  2. 名前
    • ユーザーが命名できるIDで、重複するとエラーになるようになっているためアカウント内orリージョン内でリソースを一意に識別できます。
    • EC2インスタンスなどに設定できる名前は実はただのタグで重複チェックされず、したがってリソースを識別するためには使用できません。
    • ただしタグで検索した結果、サービスごとに固有のIDやARNを取得することはできるので、間接的にIDとして使用する場合もあります。
    • IAM Roleの名前(例:AWSServiceRoleForRDS)などがこれに該当します。
  3. ARN
    • AWSのどのサービスでも共通のフォーマットでリソースを特定することができるIDです。
    • フォーマットは大抵の場合、arn:aws:サービス名:リージョン:アカウントID:リソースの種類/リソースの種別ごとに固有のID or 名前となっています。
    • IAM Roleであればarn:aws:iam::123456789012:role/AWSServiceRoleForRDS
    • VPCであればarn:aws:ec2:ap-northeast-1:123456789012:vpc/vpc-xxxxxxxxxxxxxxxxxx
    • EC2インスタンスならarn:aws:ec2:ap-northeast-1:123456789012:instance/i-xxxxxxxx

4. Condition

Conditions:
    isStaging: !Equals [!Ref "AWS::AccountId", 123456789012]

Resources:
    vpcStagingOnly:
        Type: AWS::EC2::VPC
        Properties:
            CidrBlock: 10.0.0.0/16
            EnableDnsHostnames: true
            EnableDnsSupport: true
            InstanceTenancy: default
        Condtion: isStaging

Parametersセクション

Parameters:
    envName:                       # 1.
        Type: String               # 2.

    ipVpc:                         # 1.
        Type: String               # 2.
        Default: 10.0.0.0/16       # 3.
        AllowedPattern: "[./0-9]+" # 4.

Resources:
    vpc:
        Type: AWS::EC2::VPC
        Properties:
            CidrBlock: !Ref ipVpc
            EnableDnsHostnames: true
            EnableDnsSupport: true
            InstanceTenancy: default
            Tags:
                - Key: Name
                  Value: !Sub "${envName}-vpc"

1. パラメータの名前

2. Type

3. Default

4. AllowedPattern

Mappingsセクション

Parameters:
    envName:
        Type: String

Mappings:
    ip:
        staging:
            vpc: 10.10.0.0/16
            subnet: 10.10.1.0/24
        production:
            vpc: 192.168.0.0/16
            subnet: 192.168.1.0/24

Resources:
    vpc:
        Type: AWS::EC2::VPC
        Properties:
            CidrBlock: !FindInMap [ip, !Ref envName, vpc]
            EnableDnsHostnames: true
            EnableDnsSupport: true
            InstanceTenancy: default

    subnetPublicA:
        Type: AWS::EC2::Subnet
        Properties:
            CidrBlock: !FindInMap [ip, !Ref envName, subnet]
            VpcId: !Ref vpc
            AvailabilityZone: ap-northeast-1a
    CidrBlock:
        Fn::FindInMap:
            - ip
            - !Ref envName
            - vpc

スタックをまたいで値を参照するには

# IAMのテンプレート
Outputs:
    outputEc2ProfileEc2AppServer:                # 1.
        Value: !Ref ec2ProfileEc2AppServer       # 2.
        Export:
            Name: exportEc2ProfileEc2AppServer   # 3.

Resources:
    roleEc2AppServer:
        Type: AWS::IAM::Role
        Properties:
            RoleName: roleEc2AppServer
            AssumeRolePolicyDocument:
                Version: 2012-10-17
                Statement:
                    - Effect: Allow
                      Principal:
                          Service:
                              - ec2.amazonaws.com
                      Action:
                          - sts:AssumeRole
            Path: /

    ec2ProfileEc2AppServer:
        Type: AWS::IAM::InstanceProfile
        Properties:
            InstanceProfileName: ec2ProfileEc2AppServer
            Path: /
            Roles:
                - !Ref roleEc2AppServer
# EC2のテンプレート
Resources:
    ec2AppServer:
        Type: AWS::EC2::Instance
        Properties:
            LaunchTemplate:
                LaunchTemplateId: !Ref ec2TemplateAppServer
                Version: !GetAtt ec2TemplateAppServer.LatestVersionNumber
            IamInstanceProfile:
                Name: !ImportValue exportEc2ProfileEc2AppServer
1. Outputの名前
2. Value
3. Export > Name

Outputの注意点

1. Outputの使用は極力避けるべき
2. Outputの参照は一方向にすべき

CFNで設定変更するときの確認ポイント

[
  {
    "resourceChange": {
      "logicalResourceId": "elbListenerAppHttps",   # 変更対象
      "action": "Modify",                           # 何をしようとしているか
      "physicalResourceId": "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:listener/app/elb/123456789012/xxxxxxxxxxxx",
      "resourceType": "AWS::ElasticLoadBalancingV2::Listener",
      "replacement": "False",        # これがtrueだとリソースが一度削除されて作り直されるのでサービスが停止する可能性あり
      "moduleInfo": null,
      "details": [                   # 変更の内容
        {
          "target": {
            "name": "Certificates",
            "requiresRecreation": "Never",
            "attribute": "Properties"
          },
          "causingEntity": null,
          "evaluation": "Static",
          "changeSource": "DirectModification"
        }
      ],
      "scope": [
        "Properties"
      ]
    },
    "type": "Resource"
  }
]

まとめ

今回はCloudFormationの基礎とハマリポイント・注意点について見ていきました。
意外なところで落とし穴があったりして難しそうだなと思われたかもしれませんが、今となっては、だからといって手動でひとつひとつ設定していくわけにも行きません。
弊社のAWS環境は小規模な方だと思いますが、それでもCFNのテンプレートは数千行に及んでいました。
結局銀の弾丸はないということなのかなと感じています。

一緒にラクーンのサービスを作りませんか? 採用情報を詳しく見る

関連記事

運営会社:株式会社ラクーンホールディングス(c)2000 RACCOON HOLDINGS, Inc