みなさん、CloudFormation使ってますか?
AWSで仮想サーバーを動かしたいな~という時に、『EC2インスタンスを立ち上げて……』『Elastic IPアドレスを作成してEC2インスタンスへ関連付けして……』『Elastic IPアドレスのIPv4アドレスとドメインをDNSレコードで結びつけて……』……
1回だけならまだしも、EC2インスタンスを色々な種類で試したいときには、ちょっと面倒ですよね。
CloudFormationはテンプレートファイルに従ってAWSやサードパーティのリソースを作成・設定してくれるすごいやつです。
今回はCloudFormationを使って、AWS Marketplaceに存在するAmazon マシンイメージ(AMI)からリソースを作成してみます。
AWS Marketplaceについて
AWSで実行するソフトウェアを購入できるオンラインストアのことです。
このストア上には無料 / 有料AMIも数多く存在します。
EC2コンソールのインスタンス起動ウィザードを表示すると、こんな画面が現れますよね。
デフォルトでは左ペインで『クイックスタート』が選ばれていて、ここでは一般的なAMIが表示されます。
左ペインで『AWS Marketplace』を選ぶと、AWS Marketplace上に存在するAMIが表示されます。
CloudFormation慣れしている方なら、ここで「あ~タイトルの横にあるami-XXXXXXってやつがAMI IDだから、それをCloudFormationのテンプレートで当てはめるだけだな!」となるかもしれませんが、AWS Marketplaceではそれはできません。
(そもそもタイトルの横にAMI IDが書かれていない)
これをどうするかがこの投稿の内容です。
CloudFormationについて
この投稿ではCloudFormationの初歩的な文法等はあまり触れません。
CloudFormationを一回も利用したことがなく、何も知らないという方は、他記事を読んで簡単に概要を知っておくと良いかもしれません。
【参考】
はじめてのAWS CloudFormationチュートリアル - Qiita
初学者のためのAWS入門(2) CloudFormation入門-1 - Qiita
(2017年12月時点) 私的 CloudFormation ベストプラクティス - Qiita
事前準備
- EC2インスタンスで使用するキーペア
作成はEC2コンソールの『キーペアを作成』画面から行えます。
手順
例として、さっきの画像にちらっと写っていた『WordPress Certified by Bitnami』のAMIを立ち上げる手順を記載します。
AWS Marketplaceで目的のAMIのページを探す
まずはAWS Marketplaceにアクセスして、検索バーなどを駆使して目的の商品ページまで移動します。
右上に『Typical Total Price』と書かれていますが、これはEC2インスタンス代も含めた値段です。
『Pricing』部分を確認すると……
どうやらこのAMIを利用するソフトウェア代は無料のようですね。安心して使えます。
Subscribeを行う
CloudFormationで対象のAMIを利用するには、Subscribeが必要となります。
Subscribeせずに手に入れたAMI IDをCloudFormationで利用すると、EC2インスタンス作成部分でエラーが発生します。
というわけで、さきほどのページの右上の『Continue to Subscribe』を押します。
条件(Terms)・値段が記載されているので確認し、『Accept Terms』をクリックします。
クリック後しばらく待つと、Subscribeが完了しこの画面になります。
『Continue to Configuration』を押すと、念願のAMI ID表示画面となります。
ソフトウェアバージョン、リージョン(日本ならAsia Pacific (Tokyo)でOK)を選び、AMI IDをメモっておきましょう。
CloudFormationテンプレートファイルの作成
あとはCloudFormationテンプレートファイルを作成し、そこでAMI IDを利用するだけです。
…………と、終わってしまうのは流石に不親切なので、以下の内容でリソースを作成するテンプレートファイルを記載しておきます。
AWS Marketplaceの色々なAMIで使用できるように汎用的なテンプレートにしています。
色んなAMIを触る際の手間を失くすためのテンプレートとして作っているため、こだわりたい場合はこれを参考に書き換えてみてください。
-
パラメーター
- プロジェクト名
この値を利用してリソースに名前をつけます。 - AMI ID
このIDでEC2インスタンスを作成します。 - インスタンスタイプ
記入したインスタンスタイプでEC2インスタンスを作成します。
インスタンスタイプによってスペック・値段が変わるため、どれにするかは『インスタンスタイプ - Amazon EC2 | AWS』を見て決定してください。
お試しであれば、汎用の最低スペックであるt2.nanoか、無料枠があるならそのひとつ上のt2.microで良いと思います。 - キーペア
EC2インスタンスとSSH接続する際に利用するキーペア。 - SSH接続可能なIPアドレス
EC2インスタンスのSSHポート(ポート22)へアクセスできるIPアドレス。
デフォルト値の0.0.0.0/0はどのIPからでもアクセス可能です。 - ドメイン名
後述するパラメータの『Route53のホストゾーン』内に、ドメインとElastic IPを結びつけるDNSレコードを作成します。 - Route53のホストゾーン
このホストゾーン内に上記DNSレコードを作成します。
- プロジェクト名
-
作成するリソース
- EC2インスタンス
- インスタンスに関連付けるElastic IP
- インスタンスに関連付けるセキュリティグループ
セキュリティグループとはインスタンスのファイアウォールみたいなものです。
今回はポート80(HTTP) / ポート443(HTTPS) /ポート22(SSH)を解放しています。 - Route53のDNSレコード
もし、ドメインを持っていない・IPだけで一旦試してみたい・Route53以外でドメインを管理しているからRoute53部分は必要ないという方は、自分のGitHubリポジトリにRoute53部分を削除したテンプレートを用意しています。
AWSTemplateFormatVersion: 2010-09-09
Description: Create AWS Marketplace insntace.
Parameters:
ProjectName:
Description: Name of this project. [ProjectName] is used for the naming of each resource.
Type: String
MinLength: "1"
Default: WordPress
EC2InstanceAmiId:
Description: AMI ID of AWS Marketplace.
Type: String
MinLength: "1"
EC2InstanceType:
Description: "WebServer EC2 instance type.\n
Learn more >> https://aws.amazon.com/jp/ec2/instance-types/"
Type: String
MinLength: "1"
Default: t2.micro
EC2InstanceKeyName:
Description: "Name of an existing EC2 KeyPair to enable SSH access to EC2 instance.\n
If you want to use a new EC2 KeyPair, create it from EC2 console.\n
>> https://console.aws.amazon.com/ec2/v2/home#CreateKeyPair:"
Type: "AWS::EC2::KeyPair::KeyName"
MinLength: "1"
ConstraintDescription: must be the name of an existing EC2 KeyPair.
SSHLocation:
Description: The IP address range that can be used to SSH to EC2 instance.
Type: String
MinLength: "9"
MaxLength: "18"
Default: 0.0.0.0/0
AllowedPattern: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})'
ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
DomainName:
Description: The domain name used to add DNS record to Route 53's hosted zone.
Type: String
MinLength: "1"
AllowedPattern: '[a-zA-Z0-9\-\.]*'
ConstraintDescription: only domain characters.
Route53HostedZoneID:
Description: The Route 53's hosted zone ID to add domain's DNS record to Route 53's hosted zone.
Type: "AWS::Route53::HostedZone::Id"
Metadata:
"AWS::CloudFormation::Interface":
ParameterGroups:
- Label:
default: Project Configuration
Parameters:
- ProjectName
- Label:
default: Amazon EC2 Configuration
Parameters:
- EC2InstanceAmiId
- EC2InstanceType
- EC2InstanceKeyName
- SSHLocation
- Label:
default: Route53 Configuration
Parameters:
- DomainName
- Route53HostedZoneID
Resources:
EC2SecurityGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupName: !Sub "SecurityGroup-${ProjectName}"
Tags:
- Key: Name
Value: !Sub "SecurityGroup-${ProjectName}"
GroupDescription: Enable HTTP / HTTPS / SSH access
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: "80"
ToPort: "80"
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: "443"
ToPort: "443"
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: "22"
ToPort: "22"
CidrIp: !Ref SSHLocation
EC2Instance:
Type: "AWS::EC2::Instance"
Properties:
ImageId: !Ref EC2InstanceAmiId
InstanceType: !Ref EC2InstanceType
SecurityGroups:
- !Ref EC2SecurityGroup
KeyName: !Ref EC2InstanceKeyName
Tags:
- Key: Name
Value: !Sub "Instance-${ProjectName}"
EC2EIP:
Type: "AWS::EC2::EIP"
Properties:
InstanceId: !Ref EC2Instance
Tags:
- Key: Name
Value: !Sub "EIP-${ProjectName}"
Route53Record:
Type: "AWS::Route53::RecordSet"
Properties:
HostedZoneId: !Ref Route53HostedZoneID
Name: !Ref DomainName
Type: A
TTL: "900"
ResourceRecords:
- !Ref EC2EIP
Outputs:
WebsiteURL:
Description: "URL using EC2 instance's [PublicDnsName]"
Value: !Join [ "", [ "http://", !GetAtt EC2Instance.PublicDnsName ] ]
ポイントとしては、CreationPolicyやヘルパースクリプトを使用していないところです。
Amazon Linux 2のEC2インスタンスを作成するテンプレート例として、以下のようにCloudFormationヘルパースクリプトから成功シグナルを送る実装がよくあります。
AWSのCloudFormationテンプレートのLAMP環境構築サンプルとかであるやつですね。
Resources:
EC2Instance:
Type: "AWS::EC2::Instance"
Properties:
ImageId: !Ref EC2InstanceAmiId
InstanceType: !Ref EC2InstanceType
SecurityGroups:
- !Ref EC2SecurityGroup
KeyName: !Ref KeyName
Tags:
- Key: Name
Value: !Sub "Instance-${ProjectName}"
UserData: !Base64
"Fn::Join":
- ""
- - "#!/bin/bash -xe\n"
- "yum update -y aws-cfn-bootstrap\n"
- "\n"
- "set +e\n"
- "/opt/aws/bin/cfn-init -v"
- !Sub " --stack ${AWS::StackName}"
- " --resource EC2Instance"
- " --configsets setup"
- !Sub " --region ${AWS::Region}"
- "\n"
- "# Signal the status from cfn-init\n"
- "/opt/aws/bin/cfn-signal -e $?"
- !Sub " --stack ${AWS::StackName}"
- " --resource EC2Instance"
- !Sub " --region ${AWS::Region}\n"
CreationPolicy:
ResourceSignal:
Timeout: PT10M
Metadata:
"AWS::CloudFormation::Init":
configSets:
setup:
- install_cfn
install_cfn:
files:
/etc/cfn/cfn-hup.conf:
content: !Join
- ""
- - "[main]\n"
- !Sub "stack=${AWS::StackId}\n"
- !Sub "region=${AWS::Region}\n"
mode: "000400"
owner: root
group: root
/etc/cfn/hooks.d/cfn-auto-reloader.conf:
content: !Join
- ""
- - "[cfn-auto-reloader-hook]\n"
- "triggers=post.update\n"
- "path=Resources.EC2Instance.Metadata.AWS::CloudFormation::Init\n"
- "action=/opt/aws/bin/cfn-init -v"
- !Sub " --stack ${AWS::StackName}"
- " --resource EC2Instance"
- " --configsets setup"
- !Sub " --region ${AWS::Region}\n"
- "runas=root\n"
mode: "000400"
owner: root
group: root
services:
sysvinit:
cfn-hup:
enabled: "true"
ensureRunning: "true"
files:
- /etc/cfn/cfn-hup.conf
- /etc/cfn/hooks.d/cfn-auto-reloader.conf
Amazon Linux 2以外のOSはヘルパースクリプトはインストールされていないので、AWS Marketplaceに網羅的に対応するために上記例のような実装はテンプレートに入れていません。
AWS Marketplaceで販売されているAMIにヘルパースクリプトをインストールするのも、チューニング済みAMIに不要なものを付け加えているような感じがしますしね。
curlでSignalResource APIを叩くことも考えましたが、必要なパラメーターが多くなりすぎるということでやめました。
という感じで、UserData処理やCreationPolicyによる成功シグナル待機などは行っていません。
(同じようにしているテンプレートを見つけられなかったため少し不安です。もしツッコミどころがありましたらコメントお願いします)
もし対象のAWS MarketplaceのAMIが決まっていて、それ専用にテンプレートファイルを作成するという場合には、色々処理を追加してしまってもよいと思います。
以降の手順
あとはもうCloudFormationコンソールの『スタックの作成』画面からテンプレートを指定してパラメーターを入力して実行するだけで、リソースが出来上がっちゃいます。
この部分や構築後にSSH接続する方法が分からない場合は、さきほどのCloudFormation記事やEC2インスンタンス記事を読んでみてください。
まとめ
AWS MarketplaceのAMI IDを利用するためには、そのソフトウェアへSubscribeする必要がある。