LoginSignup
17
11

More than 3 years have passed since last update.

RailsアプリをECS FargateへデプロイするCI/CD pipelineを構築する

Posted at

この記事は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デプロイできるようにします。
Untitled_New_Diagram_-_Cacoo.png

アレンジポイント

re:Invent 2019のWorkshopでは「簡易的なPHPのWebページ」をデプロイしていましたが、
今回は「Ruby on Railsのアプリケーション」をデプロイします。

デプロイするのは下の画像のような、シンプルなScaffoldアプリです。
EcsRailsApp.png

準備

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アプリケーションのリポジトリに変更します。

ecs-blue-green-deployment.yaml
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秒に設定します。

templates/service.yaml
       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はこの後の手順で作成します。

templates/service.yaml
        - 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を選択させます。

templates/vpc.yaml
     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
yuuu_ecs-rails-app.png

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が動き出し、諸々のリソースが作成されます。
CloudFormation_-_スタック_ecs-rails-app1.png

しばらくするとCodePipelineが動き出します。
Githubリポジトリからソースコードを取得してビルドしている様子が確認できます。
CodePipeline_-_AWS_Developer_Tools.png

動作確認

デプロイが終わったらEC2のコンソールを開きます。(ECSではないので注意)
ロードバランサーの画面へ移動して、Stack名に設定した名前のロードバランサーを探します。
無事に見つかったらDNS名をコピーしましょう。
ロードバランサー___EC2_Management_Console.png

http://(ロードバランサーのDNS名)/posts へアクセスすると、アプリケーションが動いていることを確認できます。
EcsRailsApp.png

動作確認ができたらCodePipeline上でapproveをしておきましょう。
CodePipeline_-_AWS_Developer_Tools.png

Blue/Greenデプロイする

これから、Railsアプリケーションに変更を加えて、デプロイをします。

デプロイ後、プレ環境で変更が正しく動作することを確認できます。
CodePipelineの承認ボタンをクリックすると、ALBのリスナーとターゲットが入れ替わり
本番環境のURLで新しいバージョンのアプリケーションが閲覧できるようになります。

Untitled_mockmock_-_Cacoo.png

Untitled_mockmock_-_Cacoo.png

アプリケーションのコードを変更しデプロイする

今回は例として以下の通り変更します。

app/views/posts/index.html.erb
 <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

動作確認

次に本番環境へアクセスします。

http://(ロードバランサーのDNS名)/posts
EcsRailsApp.png

変更が反映される前の状態が残っています。

では次に、プレ環境を確認します。
このシステムではロードバランサーの8080ポートへアクセスするとスペア環境へ接続できるようになっています。
http://(ロードバランサーのDNS名):8080/posts
EcsRailsApp.png

変更が反映されていますね。


変更を承認すると、Lambda FunctionによってALBのリスナーとターゲットの関係がスワップされます。
本番環境が新しいバージョン、プレ環境が古いバージョンとなることが確認できました。

http://(ロードバランサーのDNS名)/posts
EcsRailsApp.png

http://(ロードバランサーのDNS名):8080/posts
EcsRailsApp.png

今後の課題

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/

17
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
11