LoginSignup
6
8

More than 3 years have passed since last update.

Rails + Docker + ECSのビルド〜デプロイをCodePipelineで自動化した

Last updated at Posted at 2020-03-10

Docker + ECS + RailsのプロジェクトでCodePipelineを使用してデプロイまでを自動化したので、その知見をまとめたい。(ブルーグリーンデプロイではなく、通常のデプロイ時の方法)

RailsのプロジェクトだけれどCodePipeline基本的な使い方は、他の言語でもそれほど変わらないと思う

デプロイの流れ

githubにpushすれば自動でデプロイが開始される。デプロイは以下の流れで行うように作った。

  1. GitHubの特定のブランチ(masterなど)にpushする
  2. pushされたことがCodepipelineに通知されビルドが開始
    1. docker-composeを利用して、Dockerをbuildする
    2. Dockerイメージタグにコミット番号を付与して一意にする
    3. ビルドが完了したらECRにpushする
  3. ビルド完了後にECSにデプロイ通知がいく

CodePipelineの設定

CodePipelineはソース管理、ビルド、デプロイをパーツのようにつなげてCD/CIを管理することができるAWSのサービス。以下のサービスをつなぎ合わせて連携することができる

  • CodeCommit
  • CodeBuild
  • CodeDeploy

スクリーンショット_2020-03-09_14_55_31-2.png

CodeCommit

スクリーンショット_2020-03-09_15_24_30.png

まずはGithubで特定のリポジトリにpushされたときに検知できるようにする。ここではmasterがpushsされたときにビルドされる設定した。

ちなみにCodeCommitはGithub以外にも、ECRやS3などと連携することもできる。CodeCommit自体にコードを管理させることも可能。

CodeBuild

スクリーンショット_2020-03-09_15_29_41.png

CodeBuildではビルドプロジェクトというものを作成する。このビルドプロジェクトはOS環境や、ビルドコマンドを記載するbuildspec.ymlのパスを設定していく。ようはビルドの設定を管理している感じだ。

スクリーンショット 2020-03-09 15.38.01.png

Ubuntuでイメージが最新バージョンのものを使っておけば特に問題はないかと思う。buildspec.ymlはGithubにあげたプロジェクトに入れておく。そのパスをビルドプロジェクトで設定すればOK

buildspec.yml

ビルドするコマンドをyamlに書いていく。ビルドは以下のような流れで行う。

  1. ECRからDockerのイメージを取得
  2. コミットハッシュを取得する(コミットハッシュはDockerイメージタグとして使用する)
  3. .envに環境変数を追加していく
  4. docker-composeを利用してビルドする
  5. dbのmigrateを行う
  6. デプロイを通知する
version: 0.2

phases:
  install:
    runtime-versions:
      docker: 18
  pre_build:
    commands:
      - echo -------- Logging in to Amazon ECR... --------
      - aws --version
      - $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)
      - REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/hogehoge
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - IMAGE_TAG=${COMMIT_HASH:=latest}
  build:
    commands:
      - echo -------- Build started on `date` --------
      - echo -------- Building the Docker image... --------
      - echo AWS_ACCESS_KEY=$AWS_ACCESS_KEY >> .env
      - echo AWS_SECRET_KEY=$AWS_SECRET_KEY >> .env
      - echo ECS_ENV_NAME=$ECS_ENV_NAME >> .env
      - docker-compose -f docker-compose-$ECS_ENV_NAME.yml build
      - docker-compose -f docker-compose-$ECS_ENV_NAME.yml run --name hogehoge-image web sh -c "bundle exec rake db:create && bundle exec rake db:migrate"
      - docker commit hogehoge-image $REPOSITORY_URI:latest
      - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
  post_build:
    commands:
      - echo -------- Build completed on `date` --------
      - echo -------- Pushing the Docker images... --------
      - docker push $REPOSITORY_URI:latest
      - docker push $REPOSITORY_URI:$IMAGE_TAG
      - echo [\{\"name\":\"hogehoge\",\"imageUri\":\"$REPOSITORY_URI:$IMAGE_TAG\"\}] > imagedefinitions.json
artifacts:
  files: imagedefinitions.json

buildspec.ymlはpre_build、build、post_buildという3段階で処理を行う。一個ずつ分解して説明していく。

pre_build

ビルドする前の下準備。 GithubのURLとか、コミットハッシュとかをあとで使うので変数に入れている。ちなみにコミットハッシュはDockerイメージタグとして後で使う。

build
  - echo AWS_ACCESS_KEY=$AWS_ACCESS_KEY >> .env
  - echo AWS_SECRET_KEY=$AWS_SECRET_KEY >> .env
  - echo S3_BUCKET=$S3_BUCKET >> .env

環境変数を.envに書き込むようにしている。僕のRailsプロジェクトでは.envで環境変数を管理しており、CodeBuildでもdocker-composeが使用したいという理由でこの形にしている。このやり方がベストプラクティスではないような気がするので、もっと良い方法を見つけたい。

ちなみに環境変数はSystem Managerで管理している。環境変数についてはのちほどもう少し詳しく記載する。

  - docker-compose -f docker-compose-$ECS_ENV_NAME.yml build
  - docker-compose -f docker-compose-$ECS_ENV_NAME.yml run --name hogehoge-image web sh -c "bundle exec rake db:create && bundle exec rake db:migrate"
  - docker commit hogehoge-image $REPOSITORY_URI:latest
  - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:

あとはdocker-composeでビルドして、migrateして、Dockerイメージをcommitしているだけ。

ちなみに僕はDockerfile内にassets:precompileを行っているため、buildspec.ymlにはコマンドが書いていない。

post_build

ECSにデプロイするためには最終的にimagedefinitions.jsonというファイルを作成する必要がある。

  - docker push $REPOSITORY_URI:latest
  - docker push $REPOSITORY_URI:$IMAGE_TAG
  - echo [\{\"name\":\"hogehoge\",\"imageUri\":\"$REPOSITORY_URI:$IMAGE_TAG\"\}] > imagedefinitions.json
artifacts:
  files: imagedefinitions.json

imagedefinitions.jsonはnameとimageUirを関連付けたjsonを書いていく。複数環境あるときは当たり前だけど複数書いていく。

  • name
    • ECSのコンテナ名
  • imageUri
    • ECRのURL

ECSのタスク定義との関連は以下のようになる。

スクリーンショット_2020-03-09_16_34_33-2.png

補足

環境変数について

環境変数は秘匿化する必要があるためSystem Managerでパラメータを管理するようにした。

68017af5-fa1b-4fb2-8307-aadc174d004a-960x690r.png

安全な文字列を選択してパラメータを設定する。「名前」欄で設定した値をCodeBuildで使用する。

d9424b69-0d44-4230-afa8-c769f4a2e55a-960x573r-2.png

System Managerで設定した値をbuildspec.ymlで使用するために、CodeBuildに環境変数として設定する必要がある。これを設定しておくとbuildspec.ymlの中で$HOGEHOGEという値で使用できるようになる。

  • 名前
    • buildspec.ymlで使用する環境変数名
    • System Managerで設定した名前
  • 入力
    • 『パラメータ』を選択する
- echo AWS_ACCOUNT_ID=$AWS_ACCOUNT_ID >> .env
- echo AWS_ACCESS_KEY=$AWS_ACCESS_KEY >> .env
- echo AWS_SECRET_KEY=$AWS_SECRET_KEY >> .env

これでbuildspec.ymlにファイルの中でCodeBuildで設定した環境変数が利用できるようになる

デプロイしたときにタスク定義のバージョンは更新されていく

0ad3ec1b-5711-4351-a838-654d76ef1f81-960x596r.png

デプロイされるとimagedefinitions.jsonで設定したコンテナ名とイメージのURLでタスク定義のイメージが変更され、リビジョンが新しく更新されていく。タスクをリビジョンで管理するメリットとして「切り戻しが簡単になる」という点がある。

もしも本番で障害が発生したとき場合にもリビジョンを戻すだけで動作する。ただしDBのカラム変更などしているときは、DBをロールバックする必要もあるので注意が必要。

CodePipelineからデプロイを実行する

なんらかの理由でソースコードをpushせずにデプロイしたい場合は、Codepipelineの画面から直接行うことができる

c7177b94-b6e1-408f-84c4-de5ea8b817ad-960x598r.png

終わり

ECSは少人数の開発にこそ向いていると思う。ECSでスケールアップから障害復旧までまかせ、CodePipelineでデプロイを自動化しておけばインフラの運用をそれほど考慮しなくて済むようになる。アプリケーション層に集中して開発ができるようになる。

まだまだECSやCodePipelineに対しての知見が足りないので、また気づきがあったら書いていきたい

6
8
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
6
8