CloudFormationに対応したEC2の起動テンプレートを試してみた
はじめに
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への適切なタグ付与を行う事で回避しやすくなります。
- 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ロールの調整が必要でした。
スポットインスタンス
- スポット入札価格の省略が可能になりました。
- スポット価格高騰、強制停止に至る場合の動作として、EBS上のデータ消失を回避する停止やハイバネーションの指定も可能になりました。
Type: AWS::EC2::LaunchTemplate Properties: LaunchTemplateData: InstanceMarketOptions: SpotOptions: SpotInstanceType: one-time InstanceInterruptionBehavior: terminate MarketType: spot
- 従来の起動設定(LaunchConfig)でも、スポット価格明示することでスポットインスタンスの利用は可能でしたが、インスタンスファミリーの変更などがあった場合、高額すぎる入札や、廉価すぎる入札により、意図しない状態が発生する事がありました。
Type: AWS::EC2::LaunchTemplate Properties: SpotPrice: 0.1
ストレージ設定
- EBSの増量設定を定義する事が可能です。
-
ルートデバイスとして「sda1」を利用するWindowsの設定例
- Device Naming on Windows Instances
LinuxAMI
- ルートデバイスとして「xvda」を利用する、Linuxの設定例
- Linux インスタンスでのデバイスの名前付け
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
- ルートデバイスとして「sda1」を利用するWindowsの設定例
- Device Naming on Windows Instances
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 プロパティタイプによるネットワーク設定は可能です。
まとめ
起動テンプレート(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'