[AWS]CloudFormationを使いこなして早く帰るTips5選

コンニチハ、千葉です。

最近CloudFormationで20台くらい、どっかーんとEC2立てて気持ちよくなってる私です。 CloudFormationを利用すると、規模が大きかったり環境(dev/stg/prd)がたくさんあっても、かなり楽できます。そうです、早く帰れます。

ということで、使ったTipsをまとめておきます。今回は、よく構築するであろうEC2にフォーカスします。

Tips

Tips1:環境によってパラメータを変える

パラメータを変数化して、1つのCFnファイルで複数の環境(dev/stg/prd)を扱います。Mappingsを使います。コードの例です。*Mappingsに関連する部分だけ抜粋 CFnスタック作成するときに、prd/stgを選ぶと、Mappingsで定義したパラメータを利用できます。これで、1つのCFnでprd/stgを管理できます。

Mappings:
  prd:
      PublicSubnet1a:  { ID: subnet-xxxxx }
  stg:
      PublicSubnet1a:  { ID: subnet-xxxxx }
Parameters:
  Environment:
    Description: Type of this environment.
    Type: String
    Default: prd
    AllowedValues:
      - prd
      - stg
  BastionEc2Instance:
・・・省略
        - SubnetId: !FindInMap [ !Ref Environment, PublicSubnet1a, ID ]

Tips2:命名規則

リソース名など、最初から命名規則を決めて組み込みましょう。

Parameters:
  Environment:
    Description: Type of this environment.
    Type: String
    Default: prd
    AllowedValues:
      - prd
      - stg
Parameters:
  SystemName:
    Description: Name of this system.
    Type: String
    Default: chiba-system
・・・省略
      Tags:
        - Key: Name
          Value: !Sub ${SystemName}-${Environment}-web1

Tips3:ユーザーデータを使って初期構築windows編

windowsの初期構築です。ユーザーデータを使って、ホスト名、時刻同期、文字コード、タイムゾーンを設定します。 Mappings!Subをうまく組み合わせて変数を取り込んでます。ちょっと複雑。ただ、ログインしなくていいんですよ、圧倒的です。

Mappings:
  prd:
    OSSettings:
      TimeZone: "Tokyo Standard Time"
    BastionEc2Instance:
      HostName: chiba-bastion
・・・省略
      UserData:
        Fn::Base64:
          !Sub
        - |
          <powershell>
          # hostname
          Rename-Computer -NewName ${HostName} -Force

          # TimeZone
          Set-TimeZone -id "${TimeZone}"

          # time sync
          w32tm /config /manualpeerlist:169.254.169.123 /syncfromflags:manual /update
          </powershell>

        - { HostName: !FindInMap [ !Ref Environment, BastionEc2Instance, HostName ], TimeZone: !FindInMap [ !Ref Environment, OSSettings, TimeZone ] }

Tips4:ユーザーデータを使って初期構築Linux編

Linuxバージョンです。ホスト名、時刻同期、文字コード、タイムゾーンを設定します。

Mappings:
  prd:
    OSSettings:
      Locale: ja_JP.utf8
      TimeZone: "Asia/Tokyo"
    BastionEc2Instance:
      HostName: chiba-bastion
・・・省略
      UserData:
        Fn::Base64:
          !Sub
        - |
          #!/bin/sh -ex

          # timezon
          timedatectl set-timezone  ${TimeZone}

          # hostname
          hostnamectl set-hostname --static ${HostName}
          echo 'preserve_hostname: true' >> /etc/cloud/cloud.cfg

          # time sync
          yum erase 'ntp*'
          yum install chrony
          echo '#Add TimeSync' >> /etc/chrony.conf
          echo 'server 169.254.169.123 prefer iburst' >> /etc/chrony.conf
          systemctl start chrony
          systemctl enable chrony

          # locale
          localectl set-locale LANG=${Locale}

        - { HostName: !FindInMap [ !Ref Environment, BastionEc2Instance, HostName ], TimeZone: !FindInMap [ !Ref Environment, OSSettings, TimeZone ], Locale: !FindInMap [ !Ref Environment, OSSettings, Locale ] }

Tips5:注意:DeviceNameは気をつける

EC2構築時にDeviceNameを利用しますが、デバイス名を意識して指定する必要があります。指定を誤るとEC2が起動できなかったりします。インスタンスタイプ(仮想化方式の違い)やAMIによって違ったりします。こちらを参考にしたり、実際に手動でEC2を作るときに確認できます。

全部入りコード

これらをまとめたコードを置いておきます!

windows

windows1台構築します。EC2の設定に加え、ホスト名、時刻同期、タイムゾーンを設定します。

AWSTemplateFormatVersion: '2010-09-09'
Description: This CloudFormation template to create EC2 instances.

Mappings:
  prd:
    PublicSubnet1a:  { ID: subnet-xxxxxx }
    PublicSubnet1c:  { ID: subnet-xxxxxx }
    PrivateSubnet1a: { ID: subnet-xxxxxx }
    PrivateSubnet1c: { ID: subnet-xxxxxx }
    OSSettings:
      TimeZone: "Tokyo Standard Time"
    BastionEc2Instance:
      HostName: xxxx_ec2
      InstanceType: t3.small
      AmiId: ami-0a2de1c3b415889d2
      RootVolumeSize: 30
      PrivateIpAddress: xx.xx.xx.xx
      SecurityGroupId: sg-xxxxxxx

Parameters:
  Environment:
    Description: Type of this environment.
    Type: String
    Default: prd
    AllowedValues:
      - prd
      - stg
  SystemName:
    Description: Name of this system.
    Type: String
    Default: chiba-web
  KeyName:
    Type: AWS::EC2::KeyPair::KeyName
    Description: Name of an existing EC2 KeyPair to enable access to instances.

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
    - Label:
        default: Environment Configuration
      Parameters:
        - SystemName
        - Environment
    - Label:
        default: EC2 Instance Keypair Name
      Parameters:
        - KeyName

Resources:
  BastionEc2Instance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: !FindInMap [ !Ref Environment, BastionEc2Instance, InstanceType ]
      ImageId: !FindInMap [ !Ref Environment, BastionEc2Instance, AmiId ]
      KeyName: !Ref KeyName
      BlockDeviceMappings:
        - DeviceName: /dev/sda1
          Ebs:
            VolumeType: gp2
            VolumeSize: !FindInMap [ !Ref Environment, BastionEc2Instance, RootVolumeSize ]
            DeleteOnTermination: true
      NetworkInterfaces:
        - SubnetId: !FindInMap [ !Ref Environment, PublicSubnet1a, ID ]
          PrivateIpAddress: !FindInMap [ !Ref Environment, BastionEc2Instance, PrivateIpAddress ]
          AssociatePublicIpAddress: false
          GroupSet:
             - !FindInMap [ !Ref Environment, BastionEc2Instance, SecurityGroupId ]
          DeviceIndex: 0
          DeleteOnTermination: true
      DisableApiTermination: true
      UserData:
        Fn::Base64:
          !Sub
        - |
          <powershell>
          # hostname
          Rename-Computer -NewName ${HostName} -Force

          # TimeZone
          Set-TimeZone -id "${TimeZone}"

          # time sync
          w32tm /config /manualpeerlist:169.254.169.123 /syncfromflags:manual /update
          </powershell>

        - { HostName: !FindInMap [ !Ref Environment, BastionEc2Instance, HostName ], TimeZone: !FindInMap [ !Ref Environment, OSSettings, TimeZone ] }

      Tags:
        - Key: Name
          Value: !FindInMap [ !Ref Environment, BastionEc2Instance, HostName ]

Linux

Linux1台構築します。EC2の設定に加え、ホスト名、時刻同期、タイムゾーンを設定します。

AWSTemplateFormatVersion: '2010-09-09'
Description: This CloudFormation template to create EC2 instances.

Mappings:
  prd:
    PublicSubnet1a:  { ID: subnet-xxxxxx }
    PublicSubnet1c:  { ID: subnet-xxxxxx }
    PrivateSubnet1a: { ID: subnet-xxxxxx }
    PrivateSubnet1c: { ID: subnet-xxxxxx }
    OSSettings:
      Locale: ja_JP.utf8
      TimeZone: "Asia/Tokyo"
    BastionEc2Instance:
      HostName: xxxx_ec2
      InstanceType: t3.small
      AmiId: ami-0a2de1c3b415889d2
      RootVolumeSize: 50
      PrivateIpAddress: xx.xx.xx.xx
      SecurityGroupId: sg-xxxxxxx

Parameters:
  Environment:
    Description: Type of this environment.
    Type: String
    Default: prd
    AllowedValues:
      - prd
      - stg
  SystemName:
    Description: Name of this system.
    Type: String
    Default: chiba-web
  KeyName:
    Type: AWS::EC2::KeyPair::KeyName
    Description: Name of an existing EC2 KeyPair to enable access to instances.

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
    - Label:
        default: Environment Configuration
      Parameters:
        - SystemName
        - Environment
    - Label:
        default: EC2 Instance Keypair Name
      Parameters:
        - KeyName

Resources:
  # Bastion instance
  BastionEc2Instance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: !FindInMap [ !Ref Environment, BastionEc2Instance, InstanceType ]
      ImageId: !FindInMap [ !Ref Environment, BastionEc2Instance, AmiId ]
      KeyName: !Ref KeyName
      CreditSpecification:
        CPUCredits: standard
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeType: gp2
            VolumeSize: !FindInMap [ !Ref Environment, BastionEc2Instance, RootVolumeSize ]
            DeleteOnTermination: true
      NetworkInterfaces:
        - SubnetId: !FindInMap [ !Ref Environment, PublicSubnet1a, ID ]
          PrivateIpAddress: !FindInMap [ !Ref Environment, BastionEc2Instance, PrivateIpAddress ]
          AssociatePublicIpAddress: false
          GroupSet:
             - !FindInMap [ !Ref Environment, BastionEc2Instance, SecurityGroupId ]
          DeviceIndex: 0
          DeleteOnTermination: true
      DisableApiTermination: true
      UserData:
        Fn::Base64:
          !Sub
        - |
          #!/bin/sh -ex

          # timezon
          timedatectl set-timezone  ${TimeZone}

          # hostname
          hostnamectl set-hostname --static ${HostName}
          echo 'preserve_hostname: true' >> /etc/cloud/cloud.cfg

          # time sync
          yum erase 'ntp*'
          yum install chrony
          echo '#Add TimeSync' >> /etc/chrony.conf
          echo 'server 169.254.169.123 prefer iburst' >> /etc/chrony.conf
          systemctl start chrony
          systemctl enable chrony

          # locale
          localectl set-locale LANG=${Locale}

        - { HostName: !FindInMap [ !Ref Environment, BastionEc2Instance, HostName ], TimeZone: !FindInMap [ !Ref Environment, OSSettings, TimeZone ], Locale: !FindInMap [ !Ref Environment, OSSettings, Locale ] }
      Tags:
        - Key: Name
          Value: !FindInMap [ !Ref Environment, BastionEc2Instance, HostName ]

まとめ

仕事はサクッと終わらせて早く帰りたい派のみなさま。早く帰って家族と過ごしたり、自分のスキル磨いたり、ゲームしたり、温泉入ったりライフワークバランスを大切にしたいものです。