こんにちは。
開発部の松田です。
最近、検証環境を簡単に作成できるようにCloudFormationのcfn-initを使用して構築しましたのでその方法をご紹介いたします。
CloudFormationとは?
CloudFormationではVPCやEC2などのリソースの設定をテンプレートファイルに書くことで、そこから簡単にリソースの作成が出来るようになります。
今回は、CloudFormationを使ったことはあるけど、cfn-initを使ったことが無いという人を対象に説明を進めていきますので、
CloudFormationについてわからない方は一度こちらを確認してみてください。
cfn-initとは?
CloudFormationにはEC2インスタンス内でソフトウェアをインストールしたり、メタデータを取得したりすることができる、4種類のヘルパースクリプトが存在します。
今回紹介するcfn-initはそのうちの一つになります。
CloudFormationヘルパースクリプトについて知りたい方はこちら
cfn-initを使うことで、CloudFormationで作成したEC2インスタンスを、パッケージのインストールやサービスを開始した状態で起動することができます。
cfn-initを使ってみる
さっそくcfn-initを使って、Apacheのインストールと簡単なHTMLファイルの作成、Apacheの起動までを行っていきます。
今回使用するテンプレートファイルには、EC2 1台を作成する為の設定のみ書かれています。
※VPCやSubnetは作成済みのものを使用することを想定しています。
まずはcfn-initを使用していないテンプレートと、cfn-initを使用しているテンプレートをそれぞれ見てみましょう。
cfn-initを使用していない場合の例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
AWSTemplateFormatVersion: "2010-09-09" Description: cfn-init-test Parameters: VpcId: Type: 'AWS::EC2::VPC::Id' Description: Your VPC Id SubnetId: Type: 'AWS::EC2::Subnet::Id' Description: SubnetId in your VPC KeyName: Type: 'AWS::EC2::KeyPair::KeyName' Description: Name of an existing EC2 KeyPair to enable SSH access to the instances Default: "your key name" FromIpAddress: Type: String Description: The IP address range that can be used to SSH and HTTP to the EC2 instances Default: 0.0.0.0/0 Resources: # ------------------------------------------------------------# # EC2 # ------------------------------------------------------------# EC2InstanceServer: Type: AWS::EC2::Instance Properties: ImageId: your AMI ID #Amazon Linux 2 AMI (HVM) - Kernel 5.10, SSD Volume Typeを使用 InstanceType: t3.micro BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeSize: 20 VolumeType: gp2 KeyName: !Ref KeyName NetworkInterfaces: - AssociatePublicIpAddress: true SubnetId: !Ref SubnetId DeviceIndex: "0" GroupSet: - !Ref SecurityGroup SecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Enable SSH, HTTP and zabbixagent SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: !Ref FromIpAddress - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: !Ref FromIpAddress - IpProtocol: tcp FromPort: 10051 ToPort: 10051 CidrIp: !Ref FromIpAddress VpcId: !Ref VpcId |
cfn-initを使用した場合の例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
AWSTemplateFormatVersion: "2010-09-09" Description: cfn-init-test Parameters: VpcId: Type: 'AWS::EC2::VPC::Id' Description: Your VPC Id SubnetId: Type: 'AWS::EC2::Subnet::Id' Description: SubnetId in your VPC KeyName: Type: 'AWS::EC2::KeyPair::KeyName' Description: Name of an existing EC2 KeyPair to enable SSH access to the instances Default: "your key name" FromIpAddress: Type: String Description: The IP address range that can be used to SSH and HTTP to the EC2 instances Default: 0.0.0.0/0 Resources: # ------------------------------------------------------------# # EC2 # ------------------------------------------------------------# EC2InstanceServer: Type: AWS::EC2::Instance Metadata: Comment: ApacheInstall AWS::CloudFormation::Init: configSets: Install: - "ApacheInstall" ApacheInstall: packages: yum: httpd: [] files: /var/www/html/index.html: content: !Sub | <p>Hello!</p> mode: '000644' owner: root group: root services: sysvinit: httpd: enabled: true ensureRunning: true Properties: ImageId: your AMI ID #Amazon Linux 2 AMI (HVM) - Kernel 5.10, SSD Volume Typeを使用 InstanceType: t3.micro BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeSize: 20 VolumeType: gp2 KeyName: !Ref KeyName NetworkInterfaces: - AssociatePublicIpAddress: true SubnetId: !Ref SubnetId DeviceIndex: "0" GroupSet: - !Ref SecurityGroup UserData: Fn::Base64: !Sub | #!/bin/bash -x yum -y update yum -y install aws-cfn-bootstrap # Install the files and packages from the metadata # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-init.html /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource EC2InstanceServer --region ${AWS::Region} SecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Enable SSH, HTTP and zabbixagent SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: !Ref FromIpAddress - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: !Ref FromIpAddress - IpProtocol: tcp FromPort: 10051 ToPort: 10051 CidrIp: !Ref FromIpAddress VpcId: !Ref VpcId |
2つのテンプレートの違いを簡単にまとめると、 MetadataセクションとUserdataセクションの有無になります。
違いが分かったところで、ここからは使い方を説明していきます。
基本的な使い方
cfn-initを使う際に書き込む必要がある箇所は主に二つです。
- Metadataセクションに実行したいコマンドを記述する。
- UserdataセクションでMetadataセクションに記述した内容を指定する。
次は、MetadataセクションとUserdataセクションの書き方について順番に説明していきます。
Metadataセクションの書き方
まず、Metadataセクションの書き方について説明します。
cfn-initを使用して実行したいコマンドは、このMetadataセクションに書きます。
サンプルテンプレートからMetadataセクションだけを抜き出したものがこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Metadata: Comment: ApacheInstall AWS::CloudFormation::Init: config: packages: yum: httpd: [] files: /var/www/html/index.html: content: !Sub | <p>Hello!</p> mode: '000644' owner: root group: root services: sysvinit: httpd: enabled: true ensureRunning: true |
それぞれの項目について説明していきます。
2行目 Comment
ここはコメントなので無くても構いませんが、何を実行するのか簡単に書いておきましょう。今回はApacheInstallとしています。
3行目 AWS::CloudFormation::Init
これはメタデータキーと呼ばれるものです。 cfn-init スクリプトを呼び出すと、このAWS::CloudFormation::Init内に書かれた内容を見に来ます。
4行目config
これはメタデータの論理IDになります。
configsetを使用しない場合、cfn-initはこのconfigという論理IDを探して、読み込みます。
今回はconfigsetを使用していないのでconfigと記述しています。
5行目以降 設定セクション
5行目以降は、実行するコマンドについて書かれています。
設定セクションは7種類あり、packages, groups, users, sources, files, commands, servicesの7つを使い分けて書いていくことになります。
また、各セクションのMetadata内での記述順番にかかわらず、packages, groups, users, sources, files, commands, servicesの順番で実行されて行くので、その点に注意して設定を行いましょう。
上記の順番では実行したい内容を実現できない場合にはconfigsetを使うことで、自由な順番でコマンドを実行させることができます。
長くなるので本記事では説明を省きます。
今回は、packages,files,servicesという3つのセクションを使って、実行したいコマンドを記述しています。
今回使用していないセクションも含めた各設定セクションの詳細やconfigsetについては、公式ドキュメントを参考にしてみてください。
ここからはpackages,files,servicesという3つのセクションについて説明していきます。
packagesセクション
packagesセクションでは、パッケージ形式やインストールするパッケージの指定、各パッケージのバージョンの指定をすることができます。
packagesセクションでサポートされているパッケージ形式はapt、msi、python、rpm、rubygems、yumになります。
今回はyum を使ってApacheの最新バージョンをインストールするので、以下のように書いています。空のリストを記述することで、最新バージョンを指定することができます。
1 2 |
yum: httpd: [] |
filesセクション
filesセクションでは、インスタンス上にファイルを作成することができます。ファイルの内容をセクション内で記述することも、URLから取得することもできます。
ここでは、Webアクセスした時にHello!と表示する/var/www/html/index.htmlを作成しています。
1 2 3 4 5 6 7 |
files: /var/www/html/index.html: content: !Sub | <p>Hello!</p> mode: '000644' owner: root group: root |
servicesセクション
servicesセクションでは、インスタンスが起動されるときにサービスを起動するか停止状態にするかを指定することができます。
今回はインスタンスを起動した瞬間からWebページにアクセスできるようApacheの起動、自動起動を設定しておきます。
enabledが自動起動設定、ensureRunningがインスタンス起動直後の起動設定になります。
1 2 3 4 5 |
services: sysvinit: httpd: enabled: true ensureRunning: true |
これで、Metadataセクションの設定が終わりました。
完成まであと少しです!!
Userdataセクションの書き方
Userdataセクションは、EC2起動時に実行されるシェルスクリプトになります。ここにcfn-init スクリプトを記述することで、Metadataセクションに書いた内容が実行されます。
1 2 3 4 5 6 7 8 |
UserData: Fn::Base64: !Sub | #!/bin/bash -x yum -y update yum -y install aws-cfn-bootstrap # Install the files and packages from the metadata # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-init.html /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource EC2InstanceServer --region ${AWS::Region} |
cfn-init スクリプトの部分を抜粋して説明します。
1 |
/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource EC2InstanceServer --region ${AWS::Region} |
–stack,–regionオプションにはそれぞれシェル変数を使用しています。
–resourceオプションには、今回作成するEC2の論理IDを入力します。
各オプションの詳細については、公式ドキュメントAWS CloudFormation cfn-initを確認してください。
これで、Userdataセクションの設定が終わりました。
完成です!
起動してみる
作成したテンプレートファイルを使ってEC2を起動してみましょう。
EC2が起動したら、インスタンスのパブリックIPアドレスにアクセスしてみます。
Hello!と表示されました。
まとめ
今回はcfn-initを使用して、Apacheが起動した状態のEC2を作成してみました。
個人的に感じたメリットとしては、 UserDataセクション内にシェルスクリプトで全てコマンドを書くよりも可読性が高いこと、修正や変更が容易であることが挙げられます。
CloudFormationでEC2を作成したことはあるけど、パッケージのインストールは手動でやっていたという方は、この機会にcfn-initを使ってみませんか?