AWS CloudFormationでEC2にミドルまでセットアップする
CloudFormationでインフラのコード化だと言いつつ、EC2立てた後のミドルのセットアップはAnsibleあたりが必要なのだろうか、とかアプリのデプロイとはスクリプト分けたいよねとか、モヤモヤしてたらCloudFormationで出来るようなので試してみました。
参考
だいたい公式のチュートリアル通りでOK。
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/deploying.applications.html
UserData プロパティ
ここにEC2のプロビジョニングで実行できるシェルスクリプトが書けます。
極論これだけで全部賄うことも可能ですが、UpdateStackへの対応や、メンテやデバッグがしんどくなるので、cfn-init ヘルパースクリプトを使って実行内容をメタデータに記載する方法をとります。
Resources:
WebServerInstance:
Type: AWS::EC2::Instance
#(...)
Properties:
ImageId: ami-08847abae18baa040 # Amazon Linux 2 AMI (HVM), SSD Volume Type
#(...)
UserData: !Base64
Fn::Sub: |
#!/bin/bash
yum -y update
amazon-linux-extras install php7.2
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 WebServerInstance --region ${AWS::Region}
# Signal the status from cfn-init
# https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-signal.html
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region}
CreationPolicy:
ResourceSignal:
Timeout: PT5M
変数展開のためにFn:Subを使用。
ついでにyum update
とamazon-linux-extras
で管理されているリポジトリの有効化もやっておく。
あとはヘルパースクリプトのインストール、cfn-init
によるデプロイ、cfn-signal
で結果の通知。
何故かチュートリアルには#!/bin/bash -xe
のオプションが付いていたため、エラー発生時にcfn-signal
が実行されずにタイムアウト待ちになったため外しておきます。詳細をログに出したければ -xは残してもいいかも。
Metadata プロパティ
cfn-init
を実行すると、ここに記載するAWS::CloudFormation::Init
リソースの内容がデプロイされます。
Resources:
WebServerInstance:
Type: AWS::EC2::Instance
Metadata:
# https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-init.html
AWS::CloudFormation::Init:
config:
packages:
rpm:
mysql57-community-release: http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm
yum:
httpd: []
mysql-community-client: []
php: []
files:
# https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-hup.html
/etc/cfn/cfn-hup.conf:
content: !Sub |
[main]
stack=${AWS::StackId}
region=${AWS::Region}
mode: '000400'
owner: root
group: root
/etc/cfn/hooks.d/cfn-auto-reloader.conf:
content: !Sub |
[cfn-auto-reloader-hook]
triggers=post.update
path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init
action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region}
runas=root
mode: '000400'
owner: root
group: root
commands:
01_set_timezone:
command: timedatectl set-timezone Asia/Tokyo
ignoreErrors: false
02_install_composer:
command: /usr/bin/curl -s https://getcomposer.org/installer | /usr/bin/php && mv composer.phar /usr/local/bin/composer
env:
HOME: /root
cwd: '~'
test: test ! -e /usr/local/bin/composer
ignoreErrors: false
services:
sysvinit:
httpd:
enabled: true
ensureRunning: true
cfn-hup:
enabled: true
ensureRunning: true
files:
- /etc/cfn/cfn-hup.conf
- /etc/cfn/hooks.d/cfn-auto-reloader.conf
Properties:
#(...)
configSets
を使うと複数のconfigを記載できるようです。依存関係のコントロール用?今回は必要ないのでconfig
を使用。
packages
でyumのパッケージをインストール。rpm
で野良パッケージや追加リポジトリのインストールも可能です。
files
で設定ファイルの作成ができます。ここではUpdateStack用のcfn-hup
サービスに関する設定を追加。
commands
で任意のコマンドを実行できます。実行順はキー名の昇順になります。test
を書くと判定がtrueの場合のみ実行できます。コマンド実行時は環境変数が殆ど設定されていないので注意。env
に記載可能です。
services
に起動したいサービスを記載します。
UserDataのログは /var/log/cloud-init-output.logに、cfn-init
やcfn-hup
のログは/var/log/cfn-*.logに出力されているので、うまくいかない場合は参照しましょう。
UpdateStack時
AWS::CloudFormation::Init:
の内容に変更があった場合、UpdateStack時にcfn-hup
サービスが検知して、cfn-auto-reloader.confの内容(cfn-init
)を実行してくれる...ハズなんですが、実行してくれる時としてくれない時があって良くわかりませんでした。
2018-11-02追記
/var/log/cfn-hup.logを眺めていたら、どうやら変更監視は15分間隔で実施されているようでした。
UpdateStack後に次回のチェックを待つとちゃんと実行されました。
実行されなかった場合は、EC2にログインしてactionの内容を手動で実行すれば反映できます。
sudo /opt/aws/bin/cfn-init -v --stack yourstackname --resource WebServerInstance --region ap-northeast-1 # Tokyo
おわり
AWSの公式ドキュメントは必要なことは全部書いてあるはずなのに、実際やると結構苦労しますね。