ファイルトリガーでAWS Elastic Beanstalkのデプロイを自動実行する
超おはようございます、暑さにはめっきり弱い城内です。 冬の寒さも苦手ですが、夏の暑さは汗をかくのが嫌ですね。
はじめに
さて、今回は以下の記事に出てきたBeanstalkのアプリケーションデプロイを自動化するスクリプトを紹介したいと思います。
上記で紹介されているものと何が違うかというと、ファイルをトリガーにデプロイを実行するという点です。 今回のスクリプトをEC2上で定期実行しておけば、SSHログインすることなく、SFTP等でトリガーファイルをアップロードするだけでアプリケーションをデプロイできるようになります。
構成図
今回は、以前記事にしたGravを使ったCMSシステムを使用します。もちろん、WordPressでも実装することは可能です。
セットアップ
手順
セットアップ手順は、以下のステップになります。
- MasterサーバにIAMロールを付与する
- BashスクリプトをMasterサーバに配置する
- BashスクリプトをCronに設定する(ここでトリガーファイルの格納先とファイル名が決まる)
- トリガーファイルを作成する
IAMロール
IAMロールのポリシーは、以下の感じになります。 (もっと制限することも可能ですが、Beanstalkの内部処理に関わってきますので、必要なものを洗い出すのは結構大変です)
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "elasticbeanstalk:CreateApplicationVersion", "elasticbeanstalk:UpdateEnvironment" ], "Effect": "Allow", "Resource": [ "arn:aws:elasticbeanstalk:ap-northeast-1:123456789012:applicationversion/grav-test/*", "arn:aws:elasticbeanstalk:ap-northeast-1:123456789012:environment/grav-test/grav-test-prod" ], "Condition": { "StringEquals": { "elasticbeanstalk:InApplication": [ "arn:aws:elasticbeanstalk:ap-northeast-1:123456789012:application/grav-test" ] } } }, { "Action": [ "s3:*", "autoscaling:*", "cloudformation:*", "ec2:*", "elasticloadbalancing:*" ], "Effect": "Allow", "Resource": "*" } ] }
スクリプト
スクリプトは、以下のBashスクリプトになります。前提として、AWS CLIが使用できる(リージョン指定をお忘れなく)ようになってさえいれば、特に変数定義等の必要もなくそのまま使用できるはずです。 これをMasterサーバの任意のディレクトリに配置してください。
#!/bin/bash ### Variables label_prefix="grav_" ### Function function usage { echo "Usage: ${0##*/} trigger_file" exit 0 } function myecho { echo "${0##*/}: $1" } ### Main if [ $# -ne 1 ]; then usage fi trigger_file=$1 if [ ! -f $trigger_file ]; then myecho "$(date '+%Y/%m/%d %H:%M:%S') [INFO] $trigger_file does not exist." exit 0 fi error_file="${trigger_file}.err" if [ -f $error_file ]; then myecho "$(date '+%Y/%m/%d %H:%M:%S') [WARNING] The deployment was canceled because an error file existed." exit 1 fi myecho "$(date '+%Y/%m/%d %H:%M:%S') [INFO] EB auto deploy start." grep -ve "^#" -e "^$" $trigger_file | while read source_dir region s3_path eb_app eb_env pre_cmd do export AWS_DEFAULT_REGION=$region s3_bucket="${s3_path%%/*}" s3_key="${s3_path#*/}" if [ "$s3_bucket" = "$s3_key" ]; then s3_key="" else s3_key="${s3_key}/" fi if [ "$pre_cmd" ]; then ($pre_cmd) if [ $? -ne 0 ]; then myecho "[ERROR] PreExec command failed." | tee -a $error_file continue fi fi ver_label="${label_prefix}$(date '+%Y%m%d%H%M%S')" (cd $source_dir && zip -ry /tmp/${ver_label}.zip . 1>/dev/null && aws s3 cp /tmp/${ver_label}.zip s3://${s3_path}/ && rm -f /tmp/${ver_label}.zip) if [ $? -ne 0 ]; then myecho "[ERROR] Application upload failed." | tee -a $error_file continue fi aws elasticbeanstalk create-application-version --application-name $eb_app --version-label $ver_label --source-bundle S3Bucket=${s3_bucket},S3Key=${s3_key}${ver_label}.zip if [ $? -ne 0 ]; then myecho "[ERROR] EB application regist failed." | tee -a $error_file continue fi aws elasticbeanstalk update-environment --environment-name $eb_env --version-label $ver_label if [ $? -ne 0 ]; then myecho "[ERROR] EB application deploy failed." | tee -a $error_file continue fi done if [ -f $error_file ]; then rc=1 else rc=0 rm -f $trigger_file fi myecho "$(date '+%Y/%m/%d %H:%M:%S') [INFO] EB auto deploy end.(RC=${rc})" exit $rc
スクリプトの仕組みとしては、引数に渡されるトリガーファイルを読み込んで、そこに定義されている情報を基に、Beanstalkのデプロイを実行するだけです(トリガーファイルの定義内容は後述します)。 そして、処理が成功した場合は、最後にトリガーファイルが削除されます。もし処理が失敗した場合は、エラーファイルを出力し、次回以降の処理がスキップされるようになっています。トリガーファイルは削除されないため、エラーファイルを削除すれば、次のサイクルから処理が再開されます。
Cron設定
MasterサーバにアップロードしたスクリプトをCronに設定します。 今回はrootユーザに設定しましたが、権限の設定をうまく調整すれば別のユーザでも可能です。
# crontab -e 0,15,30,45 * * * * /root/scripts/eb_auto_deploy.sh /eb_deploy/grav.tsv >> /eb_deploy/logs/eb_auto_deploy_`date "+\%Y\%m\%d"`.log 2>&1
15分毎の実行で、トリガーファイルは/eb_deploy/grav.tsvにしています。あとは、一応ログも出力しておきます。 (今回の設定では、別途ログパージの仕組みが必要になります)
トリガーファイル
トリガーファイルのフォーマットは、タブ区切りで1レコードに以下の項目を記述します。 ※なぜタブ区切りなの!?と思われる方は、スクリプトを修正しちゃってください(笑)。私も気に入ってはいませんので。
- Beanstalkにデプロイするソースディレクトリ(絶対パス)
- リージョン名
- S3バケットの格納先(バケット名を含むパス)
- Beanstalkのアプリケーション名
- Beanstalkの環境名
- 自動デプロイ処理の前に事前実行したいコマンドライン(ワンライナーで)
以下のような内容になります(先頭に「#」でコメントアウトできます)。
# Source directory Region S3 path Elastic Beanstalk application Elastic Beanstalk environment PreExec command /var/www/html ap-northeast-1 cm-grav-test/resource grav-test grav-test-prod cd /var/www/html && sudo php bin/grav clear-cache --all && aws s3 sync /var/www/html/user/images s3://cm-grav-test/user/images
実行
あとは、grav.tsvファイルを/eb_deployディレクトリにおいて、時を待つだけです。
# ls -l total 8 -rw-r--r-- 1 root root 291 Jul 29 20:41 grav.tsv drwxr-xr-x 2 root root 4096 Jul 30 00:00 logs # date Thu Jul 30 00:02:40 JST 2015 ... # date Thu Jul 30 00:18:04 JST 2015 # ls -l total 4 drwxr-xr-x 2 root root 4096 Jul 30 00:04 logs # cat logs/eb_auto_deploy_20150730.log eb_auto_deploy.sh: 2015/07/30 00:00:01 [INFO] /eb_deploy/grav.tsv does not exist. eb_auto_deploy.sh: 2015/07/30 00:15:01 [INFO] EB auto deploy start. upload: /tmp/grav_20150730001501.zip to s3://cm-grav-test/resource/grav_20150730001501.zip { "ApplicationVersion": { "ApplicationName": "grav-test", "VersionLabel": "grav_20150730001501", "SourceBundle": { "S3Bucket": "cm-grav-test", "S3Key": "resource/grav_20150730001501.zip" }, "DateUpdated": "2015-07-29T15:15:03.857Z", "DateCreated": "2015-07-29T15:15:03.857Z" } } { "ApplicationName": "grav-test", "EnvironmentName": "grav-test-prod", "VersionLabel": "grav_20150730001501", "Status": "Updating", "EnvironmentId": "e-12345abcde", "EndpointURL": "11.22.33.44", "SolutionStackName": "64bit Amazon Linux 2015.03 v1.4.3 running PHP 5.6", "CNAME": "grav-test-prod.elasticbeanstalk.com", "Health": "Grey", "AbortableOperationInProgress": true, "Tier": { "Version": " ", "Type": "Standard", "Name": "WebServer" }, "DateUpdated": "2015-07-29T15:15:04.535Z", "DateCreated": "2015-07-26T15:24:02.735Z" } eb_auto_deploy.sh: 2015/07/30 00:15:05 [INFO] EB auto deploy end.(RC=0)
上記がサーバ上の実行結果で、以下がマネージメントコンソール上での実行結果です。
うまくデプロイできていますね。
さいごに
そんなに大した仕組みではありませんが、SFTPだけでデプロイができるというのは、Beanstalk利用の敷居を下げられるものだと思っていますので、もしよかったらぜひ使ってみてください!