こんにちは、リードエンジニアの @dachi_023 です。今回はGitHub Actionsとecspressoでデプロイフローの構築をしたのでそれについて書いていきます。先に言っておくと簡単にセットアップできるし設定もシンプルなのでかなりおすすめです。
これまでのデプロイ
コネヒトではECS環境へのデプロイに silinternational/ecs-deploy を採用しています。CodeBuildもしくはTravis CI上からecs-deployを利用してECS環境にアプリケーションをデプロイする構成です。
CI/CDツールの乗り換え検討
これまでずっとTravis CIを利用してきました。しかし 料金体系の変更 があったり、CodeBuildでデプロイの改善 をしたり、アプリ開発ではBitriseを利用 し始めたりするなどTravis CIを選択する理由が減ってきました。それと、デプロイ起点が複数あることで各CI/CDサービスの挙動理解や、メンテ時の面倒さを生み出してしまっているかもなと今この記事を書きながら思いました。
そこで今回はGitHub Actionsが良いのでは、という判断をしました。ecspressoを導入したリポジトリでは乗り換え先の検証としてGitHub Actionsを使ってCIを実行しており、どうせやるなら1つのCI/CDサービスでやろうと思ったからです。これまでTravis CIで実行してきた処理はすべて移行可能ですし、CodeBuildで行っている各家庭の回線に依存しない手動デプロイも引き続き行うことができます。
あとはGitHubの各種イベントに連動させたり、HTTPリクエストで起動したりするのが簡単なのも良いです。機能面以外だと請求がGitHubにまとめられるというのも良いのではないでしょうか。(コード管理をGitHubでしている前提)
ECS設定をコード化したい
アプリケーションをECS環境にデプロイするという用途だけならecs-deployで十分です。しかし、サービス定義やタスク定義を管理する仕組みがないためアプリケーション側の都合でタスク定義を更新したい場合などにECSコンソールから更新をかけるかインフラチームへ依頼するという手順が必要でした。コード管理していないためPull Requestを使ったレビューを行うことができないという欠点もあります。
これらの課題を解決するため、ecs-deployをやめてecspressoを使ってみることにしました。
ecspresso
ecspressoはECSのサービス定義・タスク定義も更新可能で、その設定内容はJSONで管理できます。なのでリポジトリ内で管理して変更する際にはPull Requestを通してレビューすることも可能です。ecspressoがやってくれることや、その思想についての解説は以下の記事が非常に分かりやすかったです。
ecspresso advent calendar 2020 day 21 - やること、やらないこと
デプロイまでの手順
ここから先は既存のECS環境に対してecspressoを利用し設定ファイルの作成、デプロイフローの構築をするための手順になります。
ecspressoのインストール
Homebrew経由でインストール可能です。
$ brew install kayac/tap/ecspresso
定義ファイルを作成
以下コマンドを実行して設定ファイルを作成します。
$ ecspresso init \ --region my-ecs-region \ --cluster my-ecs-cluster \ --service my-ecs-service \ --config config.yaml
実行すると3つファイルが作成されるのでそれらをコミットしておきます。複数環境へのデプロイが想定される場合は ecspresso/{env}/
のような形でディレクトリを作成して格納しておくと管理しやすいです。
config.yaml
: ecspressoの設定ファイルecs-service-def.json
: サービス定義ecs-task-def.json
: タスク定義
Workflowの作成
デプロイフローなどは各開発チームによって手順が異なるのであくまで参考程度にですが、私のいる開発チームでは以下のような設定になっています。
# .github/workflows/push-main.yml # main ブランチにマージされたら development 環境へデプロイする name: Push main branch on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest env: IMAGE_TAG: latest steps: - name: Checkout uses: actions/checkout@v2 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: my-aws-region - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v1 - name: Build, tag, and push image to Amazon ECR env: DOCKER_BUILDKIT: 1 ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} ECR_REPOSITORY: my-repository run: | docker build . -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - uses: kayac/ecspresso@v0 with: version: v1.1.3 - name: Deploy to Amazon ECS run: | ecspresso deploy --config .ecspresso/development/config.yaml
- 最新コードのチェックアウト
- ECRへのログイン
- イメージのビルド・プッシュ
- ecspressoを利用してECSタスク定義の更新・デプロイ
という順で実行されていきます。実行される処理のほとんどは既存のGitHub Actionを利用・組み合わせただけなので非常に簡単ですし、シンプルなのでメンテも楽になります。
より便利にするために
ここまででデプロイに必要な最低限を設定してきました。ここからは必要ならやってみてね、というものになります。
Slackへのデプロイ結果の通知
Slack通知用のGitHub Actionを入れます。ざっと調べただけでも3〜4個あるのですが今回は 8398a7/action-slack を使うことにしました。以下の設定をジョブの一番最後に入れます。
# .github/workflows/push-main.yml - name: Notify slack of deployment result uses: 8398a7/action-slack@v3 with: status: ${{ job.status }} # このへんはドキュメントを読みながらお好みで設定してください fields: repo,commit,ref,workflow,message,took # 誰がデプロイ作業をしているのかが分かるように GitHub ユーザー名を設定しています author_name: ${{ github.actor }} text: | Deployment has ${{ (job.status == 'success' && 'succeeded') || (job.status == 'failure' && 'failed') || 'cancelled' }} to my-app. env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} if: always()
タスク定義の変更がなければ処理をスキップする
タスク定義にdiffが出てなかったらタスク定義の更新処理をスキップしたくなりました。ecspressoのドキュメントを読んでいたら --skip-task-definition
--force-new-deployment
という2つのオプションを見つけたのでこれをdiffがなければ付けるようにします*1。差分チェックは dorny/paths-filter というGitHub Actionを使っています。
# .github/workflows/push-main.yml - name: Check if file has changed uses: dorny/paths-filter@v2 id: changes with: filters: .github/file-filters.yml # 〜 中略 〜 - name: Deploy to Amazon ECS run: | ecspresso deploy --config .ecspresso/development/config.yaml ${{ (steps.changes.outputs.task-def == 'false' && '--skip-task-definition --force-new-deployment') || '' }}
# .github/file-filters.yml task-def: - .ecspresso/development/ecs-task-def.json
さいごに
ECS Scheduled Taskの管理をecscheduleでGitOps化しました などを推進してくれている @laugh_k に協力してもらいながら無事デプロイできるようになりました。また、ECSのようなインフラエンジニア以外も触れる機会の多い部分のコード化は非常に重要だなということを学びました。
参考リンク
PR
エンジニア募集しています!
この記事はShodo (https://shodo.ink) で執筆されました。
*1:ecspresso advent calendar 2020 day 4 - deploy を読むとデプロイ時の挙動に詳しくなれます。