AWSのEC2に自動デプロイする(github+circleci+CodeDeploy)
githubにpushしたとき、自動でデプロイし、失敗した場合はslackに通知する設定方法のメモです。
期待する動作
- githubにpush
- circleciでビルド
- CodeDeployでデプロイ
- もしデプロイに失敗したらslack通知
設定するもの
- s3
- EC2
- CodeDeploy
- circleci
- SNS
- lambda
デプロイ用s3バケット
デプロイ時のリビジョン保存用のバケットを用意する
EC2
- CodeDeploy Agentのインストール
- EC2用ロール
インスタンス
CodeDeployができるOSは以下
- AmazonLinux
- Red Hat Enterprise Linux
- Ubuntu Server
- Microsoft Windows Server
EC2でインスタンスの作成を選択したとき、クイックスタートで選択できるOSの様な気がします。
IAM
下記の権限を持つIAMをアタッチする
- AWSCodeDeployFullAccess
- AmazonS3ReadOnlyAccess
CodeDeploy Agentのインストール
EC2内で実行
sudo yum update sudo yum install aws-cli -y cd /home/ec2-user aws s3 cp s3://aws-codedeploy-ap-northeast-1/latest/install . --region ap-northeast-1 chmod +x ./install sudo ./install auto
CodeDeployの設定
- appspec.yml
- CodeDeploy用ロール
appspec.yml
デプロイ時の設定として appspec.yml
が必要です。
appspec.yml
は、gitで管理してるアプリのルートディレクトリに置きます。
appspec.yml のサンプル
version: 0.0 os: linux files: - source: / destination: /var/www/campaign-app hooks: BeforeInstall: - location: scripts/BeforeInstall.sh AfterInstall: - location: scripts/AfterInstall.sh timeout: 300 runas: root ApplicationStart: - location: scripts/ApplicationStart.sh timeout: 300 runas: root ValidateService: - location: scripts/ValidateService.sh timeout: 300 runas: root
BeforeInstall
や AfterInstall
など、各処理が実行される順番については↓に詳しく書いてありました。
dev.classmethod.jp
CodeDeploy用ロール
ポリシー AWSCodeDeployRole
を持つロール
githubとcircleciの連携
- circle.yml
- circleci用IAM
circle.yml のデプロイメント部分
deployment: deploy: branch: master # デプロイ対象にするブランチ名 codedeploy: # circleciでCodeDeployを使う codedeploy-aplication-name: # CodeDeployのアプリケーション名にする application_root: / region: ap-northeast-1 revision_location: revision_type: S3 # デプロイ履歴のソースコードをs3に保存する s3_location: bucket: bucket-name # デプロイ履歴のソースコードを保存するbucket名 key_pattern: {BRANCH}-{SHORT_COMMIT}.zip # 保存時のファイル名 deployment_group: all
CodeDeployではデプロイしたアプリを圧縮してs3に保存することができるので、s3へ保存するファイル名を決める必要があります。
ファイル名は
key_pattern: {BRANCH}-{SHORT_COMMIT}.zip
の用に定義することができます。
パラメータ | 意味 |
---|---|
BRANCH | デプロイするbranch名に変わる masterブランチなら master となる |
SHORT_COMMIT | デプロイ時のcommitIDになる |
circleciに設定するIAM
- AWSCodeDeployFullAccess の権限をもつIAM
デプロイ失敗時の通知設定
- SNS
- lambda function
設定方法
SNS
- トピックを作成する
CodeDeploy
- CodeDeployの設定でトリガーに 1.で作成したSNSを追加する
失敗時の通知をしたいので、イベントは Deployment fails
lambdaの設定
項目 | 設定 |
---|---|
設計図の選択 | sns-message |
トリガーの設定 | 作成したSNSトピック |
'use strict'; // Ref: https://gist.github.com/vgeshel/1dba698aed9e8b39a464 console.log('Loading function'); const https = require('https'); const url = require('url'); // to get the slack hook url, go into slack admin and create a new "Incoming Webhook" integration const slack_url = "{slackのwebhookurl}"; const region = 'ap-northeast-1' const codedeploy_url = 'https://' + region + '.console.aws.amazon.com/codedeploy/home?region=' + region + '#/deployments/' const slack_req_opts = url.parse(slack_url); slack_req_opts.method = 'POST'; slack_req_opts.headers = {'Content-Type': 'application/json'}; exports.handler = function(event, context) { (event.Records || []).forEach(function (rec) { if (rec.Sns) { var req = https.request(slack_req_opts, function (res) { if (res.statusCode === 200) { context.succeed('posted to slack'); } else { context.fail('status code: ' + res.statusCode); } }); req.on('error', function(e) { console.log('problem with request: ' + e.message); context.fail(e.message); }); var message = JSON.parse(rec.Sns.Message); var str = '*' + 'Application: ' + message.applicationName + ' deploymentGroupName: ' + message.deploymentGroupName + ' deploymentId: ' + message.deploymentId + '*' + ' ' + codedeploy_url + message.deploymentId; req.write(JSON.stringify({text: str})); // for testing: , channel: '@vadim' req.end(); } }); };
その他
Railsアプリをデプロイする時にハマったことがありました。
詳しくは調べてませんが、下のような順番でデプロイしてるんじゃないかなと思ってます。
- circleciでビルドした結果を圧縮し、s3にデプロイ
- s3から各EC2へソースコードをデプロイ
ここで問題になるのが、 circleci内で bundle install
した結果をそのままEC2にデプロイするということです。
ビルド環境(circleci)で bundle install
したときのパスがそのまま設定されているため、EC2インスタンスとcircleciのビルド時の環境が違うと、パスが通らないことが有りアプリの起動に失敗します。
解決策としては、CodeDeployでアプリ起動前に bundle install
をすることです。
bundle install --path ../vendor/bundle
の様な形で、デプロイするソースコード外にインストールする方法で回避しました。
参考
[公式チュートリアルではじめる]CircleCI+CodeDeployを使ったCD(継続的デプロイ) | Developers.IO
CircleCI、CodeDeploy、S3を使って、GitHubにpushしたらEC2にデプロイされる仕組みを作る - Qiita
CodeDeployのApplicationStopイベントフックはどう実行される? | Developers.IO
Amazon Web Services ブログ: AWS CodeDeployがアジア・パシフィック(東京)で利用可能になりました
AWS CodeDeployのデプロイ結果をSlackに流す | mediba Creator × Engineer Blog