この記事はFusic Advent Calendar 2019の20日目の記事です。
昨日は@Kta-Mの「究極のCloudFormationをたずねて三千里」でした。
CloudFormationでこのネタをやるとは、、、斬新なアイディアですね。
さて、12月2日〜6日にアメリカのラスベガスでAWS re:Invent 2019が開催されました。
私自身も参加して多数のWorkshopを体験することができました。
今回は、その中で最も実用的と感じたWorkshopの内容を、一部アレンジしてre:Playします。
作るもの
ECS Fargate上にアプリケーションをデプロイするCI/CD pipelineを構築します。
ECSは2つのサービスを起動し、Blue/Greenデプロイできるようにします。
アレンジポイント
re:Invent 2019のWorkshopでは「簡易的なPHPのWebページ」をデプロイしていましたが、
今回は「Ruby on Railsのアプリケーション」をデプロイします。
デプロイするのは下の画像のような、シンプルなScaffoldアプリです。
準備
AWS CLIのインストール
以下URLを参考にインストールしてください。バージョン2が評価リリースされていますが、この記事ではバージョン1を使います。
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv1.html
GithubのAccess Tokenを作る
以下URLの(c)および(d)の手順を実施してください。
CloudFormationテンプレートをCloneする
AWSリソース一式を構築するCloudFormationテンプレートが用意されているので、これをCloneします。
また、branchを fargate
に切り替えておきます。
$ git clone https://github.com/awslabs/ecs-blue-green-deployment
$ cd ecs-blue-green-deployment
$ git checkout fargate
Workshopのときとはリージョンや起動するアプリが異なるので、いくつか修正を施しました。
取得するGithubリポジトリをWorkshopで使用したものから、今回私が作成したRailsアプリケーションのリポジトリに変更します。
Parameters
GitHubRepo:
Type: String
- Default: ecs-demo-php-simple-app
+ Default: ecs-rails-app
Description: The repo name of the sample service.
AllowedPattern: "[A-Za-z0-9_.-]*"
MaxLength: 50
Railsアプリケーションの場合、コンテナが起動してから assets:precompile
を実行するため、HelthCheckが通るようになるまで時間がかかります。
このため、HelthCheckの猶予時間を360秒に設定します。
LaunchType: FARGATE
TaskDefinition: !Ref TaskDefinition
LoadBalancers:
- - ContainerName: simple-app
+ - ContainerName: rails-app
ContainerPort: 80
TargetGroupArn: !Ref TargetGroup
+ HealthCheckGracePeriodSeconds: 360
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED #MENTION DISABLED if in private subnet with NAT gateway
Logを閲覧できるようにしないとデバッグがしづらいのでCloudWatch Logsへ書き出すように設定を加えています。
コンテナのWorkDirやEntrypoint、環境変数の定義を追加しています。
※master.keyはこの後の手順で作成します。
- FARGATE
ExecutionRoleArn: !GetAtt TaskIamRole.Arn
ContainerDefinitions:
- - Name: simple-app
+ - Name: rails-app
Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${Repository}:${Tag}
+ LogConfiguration:
+ LogDriver: awslogs
+ Options:
+ awslogs-group: /ecs/logs/ecs-rails-app-group
+ awslogs-region: !Ref "AWS::Region"
+ awslogs-stream-prefix: rails-app
+ WorkingDirectory: /app
EntryPoint:
- - /usr/sbin/apache2
- - -D
- - FOREGROUND
+ - /bin/sh
+ - -c
+ - /app/script/entrypoint_production.sh
Essential: true
Memory: 512
PortMappings:
- ContainerPort: 80
Environment:
- Name: Tag
- Value: !Ref Tag
\ No newline at end of file
+ Value: !Ref Tag
+ - Name: RAILS_ENV
+ Value: production
+ - Name: RAILS_SERVE_STATIC_FILES
+ Value: true
+ - Name: RAILS_LOG_TO_STDOUT
+ Value: true
+ - Name: RAILS_MASTER_KEY
+ Value: <master.keyの内容を設定>
東京リージョンではap-northeast-1bが選択できないため、ap-northeast-1cを選択させます。
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
- AvailabilityZone: !Select [ 1, !GetAZs ]
+ AvailabilityZone: !Select [ 2, !GetAZs ]
MapPublicIpOnLaunch: true
CidrBlock: !Ref Subnet2CIDR
Tags:
※参考までにこちらでも変更を確認できるようにしています。
https://github.com/yuuu/ecs-blue-green-deployment/commit/4294e098993511de134221758e834d5b9ecc674b
デプロイ対象のリポジトリを準備する
上記で入力した名前のリポジトリを、自分のGithubアカウント配下に置く必要があります。
今回は私が用意したリポジトリをforkしてお使いください。
https://github.com/yuuu/ecs-rails-app
config/master.keyを生成するために、以下コマンドを入力してください。
$ rm config/credentials.yml.enc
$ EDITOR=vi bundle exec rails credentials:edit
(エディタが起動したら :wq を入力)
$ git commit -m 'update credentials'
$ git push origin master
無事に生成できたら前述の templates/service.yaml
へ反映しておきましょう。
CloudFormation用のS3バケットを作成する
CloudFormationで使うS3バケットを作成します。S3バケットは他人と重複しない名前を適当に設定してください。
$ aws s3 mb s3://<unique-bucket-name>
デプロイする
環境構築&デプロイ
準備が完了しましたのでいよいよデプロイします。
先ほどCloneした ecs-blue-green-deployment
のディレクトリへ移動して以下コマンドを実行します。
実行すると対話形式でパラメータの入力を求められるので、下記に従って入力してください。
$ bin/deploy
+ echo -n 'Enter S3 Bucket to host the templates and scripts > '
Enter S3 Bucket to host the templates and scripts > + read bucket
(作成したバケット名を入力)
+ echo -n 'Enter stackname to create or update the stack > '
Enter stackname to create or update the stack > + read stackname
(任意のstacknameを入力)
+ echo -n 'Enter GitHub User > '
Enter GitHub User > + read GitHubUser
(Github ユーザ名を入力)
+ echo -n 'Enter GitHubToken > '
Enter GitHubToken > + read GitHubToken
(作成したGithub Access Tokenを入力)
CloudFormationが動き出し、諸々のリソースが作成されます。
しばらくするとCodePipelineが動き出します。
Githubリポジトリからソースコードを取得してビルドしている様子が確認できます。
動作確認
デプロイが終わったらEC2のコンソールを開きます。(ECSではないので注意)
ロードバランサーの画面へ移動して、Stack名に設定した名前のロードバランサーを探します。
無事に見つかったらDNS名をコピーしましょう。
http://(ロードバランサーのDNS名)/posts
へアクセスすると、アプリケーションが動いていることを確認できます。
動作確認ができたらCodePipeline上でapproveをしておきましょう。
Blue/Greenデプロイする
これから、Railsアプリケーションに変更を加えて、デプロイをします。
デプロイ後、プレ環境で変更が正しく動作することを確認できます。
CodePipelineの承認ボタンをクリックすると、ALBのリスナーとターゲットが入れ替わり
本番環境のURLで新しいバージョンのアプリケーションが閲覧できるようになります。
アプリケーションのコードを変更しデプロイする
今回は例として以下の通り変更します。
<p id="notice"><%= notice %></p>
-<h1>Posts</h1>
+<h1>Posts!!!</h1>
<table>
<thead>
このソースコードをpushしましょう。
まもなくデプロイが始まるので、デプロイ完了となるまで待ちます。
$ git add .
$ git commit -m 'change h1 text'
$ git push origin master
動作確認
次に本番環境へアクセスします。
変更が反映される前の状態が残っています。
では次に、プレ環境を確認します。
このシステムではロードバランサーの8080ポートへアクセスするとスペア環境へ接続できるようになっています。
http://(ロードバランサーのDNS名):8080/posts
変更が反映されていますね。
変更を承認すると、Lambda FunctionによってALBのリスナーとターゲットの関係がスワップされます。
本番環境が新しいバージョン、プレ環境が古いバージョンとなることが確認できました。
http://(ロードバランサーのDNS名):8080/posts
今後の課題
Workshopの後で知ったのですが、 ECS自体にBlue/Greenデプロイ機能が存在している そうです。
今回はLambdaを使ってALBのリスナーとターゲットを入れ替えていますが、ECSの機能を使った方がよりスマートにBlue/Greenデプロイできるはずです。
現状、RDBはsqlite3を使っていますが、実際の本番環境でsqlite3を使うことはほぼ無いので、RDSインスタンスへの接続も一度試してみたいところです。
また、CodeBuildでのビルド時間、CodeDeployでのデプロイ時間が 合計10分以上かかっている ので、もう少し短縮する必要がありますね。
まとめ
re:Invent 2019のWorkshopの内容をアレンジすることで、RailsアプリをECS FargateへデプロイするCI/CD pipelineを構築することができました。
こういったWorkshopに多数参加できるre:Invent 2019はとても有意義だったなと改めて実感しました。
この経験を今後の開発に活かせるよう、精進します。
参考
https://cicd-with-aws-fargate-lambda-bg.workshop.aws/en/intro.html
https://www.enisias.cloud/docker/152/