CloudFormationに対応したEC2の起動テンプレートを試してみた

2017年11月リリース、2018年3月CloudFormationからも利用出来る様になった起動テンプレート(Launch Templates for Amazon EC2 instances)を利用して、 EC2インスタンスと、オートスケール環境の設置を試してみました。
2018.06.05

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

AWSチームのすずきです。

2017年のre:InventでリリースされたEC2の起動テンプレート(Launch Templates for Amazon EC2 instances)。 EC2インスタンスの初期設定をテンプレートとして管理、再利用することが可能になりました。

【速報】新機能 Launch Templates for Amazon EC2 instancesが公開されました #reinvent

[新機能]Launch Templates for Amazon EC2 instancesを試してみた #reinvent

2018年3月、起動テンプレートのCloudFormationサポートがアナウンスされていました。

今回、CloudFormationを利用して、起動テンプレートによるEC2インスタンスと、オートスケール環境の設置を試す機会がありましたので、紹介させていただきます。

利点

CloudFormationで起動テンプレートを利用する利点について、まず紹介させていただきます。

設定の簡略化

  • VPCサブネット指定のみでEC2インスタンスの起動する事が可能になりました。
  • ライセンスなどの関係でオートスケールの導入が難しいが、負荷分散構成をとるEC2インスタンスを多数設置する場合には有効と思われます。
    Type: AWS::EC2::Instance
    Properties:
      LaunchTemplate:
        LaunchTemplateId: !Ref 'Ec2InstanceLaunchTemplate'
        Version: !GetAtt 'Ec2InstanceLaunchTemplate.LatestVersionNumber'
      SubnetId: !Ref 'VpcEc2Subnet1'

バージョン管理

  • 起動テンプレートはバージョン管理機構を備えます。
  • CloudFormationで作成した起動テンプレートを更新(UpdateStack)すると、起動テンプレートのバージョン番号が一つ繰り上がります。
  • 今回、!GetAtt関数を利用し「LatestVersionNumber」を指定、該当起動テンプレートの最新のバージョンを利用する指定としました。
  • 切り戻しなどの場合、バージョン指定を行う事で、古い起動テンプレートを利用したEC2環境を構築できます。
    Type: AWS::EC2::Instance
    Properties:
      LaunchTemplate:
        LaunchTemplateId: !Ref 'Ec2InstanceLaunchTemplate'
        Version: 1
      SubnetId: !Ref 'VpcEc2Subnet1'
  • CloudFormationで作成した起動テンプレートは、AWSコンソール上で、作成日時、作業者情報、設定内容を確認可能です。

タグ管理

  • EC2インスタンス、およびEBSのタグを一括管理する事が可能になりました。
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateData:
        TagSpecifications:
        - ResourceType: instance
          Tags:
          - Key: Env
            Value: !Ref 'Env'
        - ResourceType: volume
          Tags:
          - Key: Env
            Value: !Ref 'Env'
  • EC2起動後に増設したEBSや、AMIからの起動時に「Delete on Termination」(併せて削除)指定を省略して作成したEBSは、EC2の撤去後に取り残され用途不明となる事がありましたが、EBSへの適切なタグ付与を行う事で回避しやすくなります。

EC2起動時にEBSボリュームへのタグの付与が可能に!

  • EBSボリュームに適切なタグを付与する事で、コスト配分タグを有効としたAWSアカウントでは、EBSやスナップショット費用についてもタグによるコスト計算が可能になります。

Amazon Elastic Block Store (Amazon EBS) でスナップショットのコスト配分をサポート

T2無制限オプション

  • 「T2」ファミリーのCPUクレジット枯渇を回避するオプション指定を起動テンプレートで設定する事が可能です。
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateData:
        CreditSpecification:
          CpuCredits: unlimited
  • 従来のLaunchConfigを利用したオートスケール環境では、T2無制限オプションの有効化のための「Userdata」設定などが必要。awscliのバージョンや、IAMロールの調整が必要でした。

T2 Unlimited(T2無制限)オプションをオートスケール環境で利用してみた

スポットインスタンス

  • スポット入札価格の省略が可能になりました。
  • スポット価格高騰、強制停止に至る場合の動作として、EBS上のデータ消失を回避する停止やハイバネーションの指定も可能になりました。
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateData:
        InstanceMarketOptions:
          SpotOptions:
            SpotInstanceType: one-time
            InstanceInterruptionBehavior: terminate
          MarketType: spot

[新機能] Amazon EC2でハイバネーションが可能になりました #reinvent

  • 従来の起動設定(LaunchConfig)でも、スポット価格明示することでスポットインスタンスの利用は可能でしたが、インスタンスファミリーの変更などがあった場合、高額すぎる入札や、廉価すぎる入札により、意図しない状態が発生する事がありました。
    Type: AWS::EC2::LaunchTemplate
    Properties:
      SpotPrice: 0.1

ストレージ設定

LinuxAMI

    Type: AWS::EC2::LaunchTemplate
    Properties:
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeSize: 8
            VolumeType: gp2
            DeleteOnTermination: true
        - DeviceName: /dev/xvdf
          Ebs:
            VolumeSize: 1
            VolumeType: gp2
            DeleteOnTermination: true

WindowsAMI

    Type: AWS::EC2::LaunchTemplate
    Properties:
      BlockDeviceMappings:
        - DeviceName: /dev/sda1
          Ebs:
            VolumeSize: 30
            VolumeType: gp2
            DeleteOnTermination: true
        - DeviceName: /dev/xvdf
          Ebs:
            VolumeSize: 1
            VolumeType: gp2
            DeleteOnTermination: true

ネットワーク設定

  • 今回オートスケール環境での利用を優先したため設定を省略しましたが、NetworkInterface プロパティタイプによるネットワーク設定は可能です。

  • Amazon EC2 LaunchTemplate NetworkInterface

まとめ

起動テンプレート(Launch Templates)の利用により、 AWSのアップデートで追加された多くのEC2の機能を活用し、より効率的な管理、利用が出来るようになりました。

特に、初期設定のみCloudFormationを利用、 リリース後のEC2インスタンスの更新、管理についてはAWSコンソールやCLIの利用が想定される環境では、 起動テンプレートの利用は大きな効果があると思われます。

また、起動テンプレートはスポットフリートや、AWS Auto Scalingとの連係にも利用することが可能です。 こちらは別の機会に改めて紹介させて頂きたいと思います。

https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-fleet-automatic-scaling.html

テンプレート

  • 今回紹介した環境のCloudFormationテンプレート(YAML)です。
  • オートスケールと非オートスケールで、同一設定の2台のEC2インスタンスが起動します。
  • AMIはAWS東京リージョンのAmazon Linux 2 LTS Candidate 2 をデフォルトのパラメータに反映しています
  • 既存のVPC(デフォルトVPC)とサブネット、SSH接続情報をパラメータで指定します
  • Userdataの動作確認の為、awslogs(CloudwatchLogsAgent)の設定を実施しています
AWSTemplateFormatVersion: '2010-09-09'
Description: ec2-LaunchTemplate-instance
Parameters:
  Env:
    Description: Choose the environment to create
    Type: String
    Default: development
    AllowedValues:
    - development
    - staging
    - production
  VpcId:
    Description: VPC ID
    Type: AWS::EC2::VPC::Id
  VpcSubnetCidr:
    Description: VPC subnet CIDR
    Type: String
    Default: 172.31.0.0/16
  StaticIPforSSH:
    Description: Static IP for SSH access
    Type: String
    Default: 127.0.0.1/32
  VpcEc2Subnet1:
    Description: EC2 subnet 1(AZ-a)
    Type: AWS::EC2::Subnet::Id
  VpcEc2Subnet2:
    Description: EC2 subnet 2(AZ-c)
    Type: AWS::EC2::Subnet::Id
  Ec2ImageId:
    Description: AMI ID
    Type: String
    Default: ami-2724cf58
  Ec2InstanceType:
    Description: EC2 InstanceType
    Type: String
    Default: t2.micro
  Ec2InstanceKeyName:
    Description: EC2 SSH KEY
    Type: AWS::EC2::KeyPair::KeyName
    Default: SSHKey
  Ec2InstanceTagName:
    Description: EC2 Tag Name
    Type: String
    Default: test-lt-ec2-20180605
  Ec2AutoscaleMinSize:
    Description: AutoScalingGroup MinSize
    Type: String
    Default: '1'
  Ec2AutoscaleMaxSize:
    Description: AutoScalingGroup MaxSize
    Type: String
    Default: '1'
  Ec2AutoscaleDesiredCapacity:
    Description: AutoScalingGroup DesiredCapacity
    Type: String
    Default: '1'
Resources:
  Ec2InstanceLaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateData:
        SecurityGroupIds:
        - !Ref 'Ec2SecurityGroup'
        - !Ref 'Ec2SecurityGroupSSH'
        TagSpecifications:
        - ResourceType: instance
          Tags:
          - Key: Env
            Value: !Ref 'Env'
        - ResourceType: volume
          Tags:
          - Key: Env
            Value: !Ref 'Env'
        - ResourceType: instance
          Tags:
          - Key: Name
            Value: !Ref 'AWS::StackName'
        UserData:
          Fn::Base64: |
            #!/bin/bash -xe
            date > /tmp/run.log
            yum update -y
            yum install jq awslogs -y
            chkconfig awslogs on
            service awslogs start
            if [ ! -f /swapfile ]; then dd if=/dev/zero of=/swapfile bs=1M count=128; chmod 600 /swapfile; mkswap /swapfile; swapon /swapfile; echo "/swapfile  swap  swap  defaults  0  0" >> /etc/fstab; fi
        InstanceInitiatedShutdownBehavior: terminate
        IamInstanceProfile:
          Arn: !GetAtt 'Ec2IAMProfile.Arn'
        KeyName: !Ref 'Ec2InstanceKeyName'
        ImageId: !Ref 'Ec2ImageId'
        Monitoring:
          Enabled: false
        CreditSpecification:
          CpuCredits: standard
        InstanceType: !Ref 'Ec2InstanceType'
        InstanceMarketOptions:
          SpotOptions:
            SpotInstanceType: one-time
            InstanceInterruptionBehavior: terminate
          MarketType: spot
        BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeSize: 8
            VolumeType: gp2
            DeleteOnTermination: true
        - DeviceName: /dev/xvdf
          Ebs:
            VolumeSize: 1
            VolumeType: gp2
            DeleteOnTermination: true

  Ec2Instance:
    Type: AWS::EC2::Instance
    Properties:
      LaunchTemplate:
        LaunchTemplateId: !Ref 'Ec2InstanceLaunchTemplate'
        Version: !GetAtt 'Ec2InstanceLaunchTemplate.LatestVersionNumber'
      SubnetId: !Ref 'VpcEc2Subnet1'
  Ec2InstanceAutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      VPCZoneIdentifier:
      - !Ref 'VpcEc2Subnet1'
      - !Ref 'VpcEc2Subnet2'
      LaunchTemplate:
        LaunchTemplateId: !Ref 'Ec2InstanceLaunchTemplate'
        Version: !GetAtt 'Ec2InstanceLaunchTemplate.LatestVersionNumber'
      MinSize: !Ref 'Ec2AutoscaleMinSize'
      MaxSize: !Ref 'Ec2AutoscaleMaxSize'
      DesiredCapacity: !Ref 'Ec2AutoscaleDesiredCapacity'
  Ec2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref 'VpcId'
      GroupDescription: allow ICMP via same VPC
      SecurityGroupIngress:
      - IpProtocol: icmp
        FromPort: -1
        ToPort: -1
        CidrIp: !Ref 'VpcSubnetCidr'
  Ec2SecurityGroupSSH:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref 'VpcId'
      GroupDescription: allow SSH via static IP
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 22
        ToPort: 22
        CidrIp: !Ref 'StaticIPforSSH'
  Ec2IAMRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - ec2.amazonaws.com
          Action:
          - sts:AssumeRole
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM
      Path: /
      Policies: !Ref 'AWS::NoValue'
      RoleName: !Ref 'AWS::NoValue'
  Ec2RolePolicies:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: Ec2RolePolicies
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Action:
          - s3:List*
          Resource:
          - '*'
        - Effect: Allow
          Action:
          - logs:CreateLogGroup
          - logs:CreateLogStream
          - logs:PutLogEvents
          - logs:DescribeLogStreams
          Resource:
          - arn:aws:logs:*:*:*
      Roles:
      - !Ref 'Ec2IAMRole'
  Ec2IAMProfile:
    Type: AWS::IAM::InstanceProfile
    DependsOn: Ec2IAMRole
    Properties:
      Path: /
      Roles:
      - !Ref 'Ec2IAMRole'