はじめに
SREチームの手﨑です。
OfferBoxでは、コンテナのデプロイにAWS CodeDeployを利用したブルーグリーンデプロイ方式を採用しています。このデプロイ方式については、既に多くの情報が公開されていますが、改めてOfferBoxにおける使い方を詳細に整理していきたいと思います。
ブルーグリーンデプロイとは
ブルーグリーンデプロイメントは、同一の環境を2つ作成するデプロイメント戦略です。 1 つの環境 (ブルー) は現在のアプリケーション バージョンを実行し、もう1つの環境 (グリーン) は新しいアプリケーション バージョンを実行します。ブルーグリーンデプロイメントを使用すると、アプリケーションの可用性が向上し、展開が失敗した場合のロールバックプロセスが簡素化されるため、展開リスクが軽減されます。
参考リンク: Overview of Deployment Options on AWS
OfferBoxでは、ECSとCodeDeployを組み合わせたブルーグリーンデプロイを実施しています。CodeDeployには以下のようなデプロイワークフローが用意されており、ユーザーガイドを参考に各処理を整理していきます。
デプロイワークフローの概要
CodeDeployを使用したデプロイを開始すると以下のデプロイワークフローが開始されます。 デプロイ前の状態はブルー環境に本番用のトラフィックが流れている前提とします。
ライフサイクルイベント | 説明 | ブルー環境のタスク状態 | グリーン環境のタスク状態 |
---|---|---|---|
BeforeInstall | デプロイ前にLambda関数(オプション)を実行して前準備を処理 | RUNNING | まだ作成されていない |
Install | グリーン環境で新しいタスクを設定し、アプリケーションを起動、グリーン環境のターゲットグループを関連付け | RUNNING | PENDING -> RUNNING |
AfterInstall | アプリケーションが起動した後、Lambda関数(オプション)を実行 | RUNNING | RUNNING |
AllowTestTraffic | テストトラフィックをグリーン環境のタスクにルーティング | RUNNING (変化なし) | RUNNING (テストトラフィックを受ける) |
AfterAllowTestTraffic | テストトラフィックを利用してLambda関数(オプション)を実行 | RUNNING | RUNNING |
BeforeAllowTraffic | AfterAllowTestTrafficが正常終了した際にLambda関数(オプション)を実行 | RUNNING | RUNNING |
AllowTraffic | グリーン環境のタスクに本稼働トラフィックをルーティングし、ブルー環境からの切り替えを実行 | DRAINING -> STOPPED/削除 | RUNNING (本稼働トラフィックを受ける) |
AfterAllowTraffic | 本稼働トラフィックのルーティング後に、Lambda関数(オプション)を実行 | STOPPED/削除 | RUNNING |
環境の全体図は以下のようになります
デプロイ手順の詳細
デプロイ前の状態
- デプロイ前の状態はブルー環境に本番用のトラフィックが流れている前提とします。
GitHub ActionsによるCodeDeployのトリガー
- デプロイプロセスは、GitHubリポジトリの特定のブランチに対するマージによってトリガーします。
# Github ActionsによるCodeDeployの実行 - name: deploy uses: aws-actions/amazon-ecs-deploy-task-definition@v1 id: update-task-definition-deploy_app with: task-definition: ${{ task-definition }} service: app cluster: prod-cluster codedeploy-appspec: appspec/prod.json wait-for-service-stability: false codedeploy-application: AppECS-code-deploy codedeploy-deployment-group: DpgECS-deployment-group
デプロイ前準備/
BeforeInstall
- デプロイ前にLambda関数(オプション)を実行して前準備を処理します。
ECSに新しいタスクセットを作成/
Install
->AfterInstall
- グリーン環境のタスクセットを作成し、新しいアプリケーションバージョンがデプロイされます。
- AfterInstallのLambda関数(オプション)を実行します。
- デプロイの初期段階では、トラフィックはまだ旧タスクセットに向けられています。
テストトラフィックの開始/
AllowTestTraffic
->AfterAllowTestTraffic
->BeforeAllowTraffic
- CodeDeployがALBのテストリスナーを更新し、新しいタスクセットにトラフィックを向き先変更します。
- グリーン環境でテストトラフィックのテストが可能な状態となります。
- AfterAllowTestTraffic、BeforeAllowTraffic用のLambda関数(オプション)を実行します。
トラフィックの段階的な移行/
AllowTraffic
- >AfterAllowTraffic
- ALBのプロダクションリスナーを更新しグリーン環境のターゲットグループを関連付けます。
- CodeDeployの設定にcanaryやリニアタイプを指定している場合は本番用のトラフィックを段階的に新しいタスクセットに移行します。
- OfferBoxの場合は
CodeDeployDefault.ECSAllAtOnce
を設定しているため即時全件を移行しています。 - AfterAllowTrafficのLambda関数(オプション)を実行します。
ロールバック対応
- もし新しいタスクセットに問題が発生した場合、CodeDeployのロールバック機能を使用して、プロダクションリスナーの関連付けをブルー環境に戻すことでトラフィックを再度旧タスクセットに戻します。
- ロールバック起動中のブルー環境を利用するため迅速な切り替えが可能です。
旧タスクセットの削除
- モニタリング期間中に問題がなければ、旧タスクセットは停止され、自動的に削除されます。
以上がブルーグリーンデプロイのフローとなります。
次にデプロイされる際はブルー環境に対して同じことが実行されます。
デプロイ時のモニタリング
OfferBoxの環境ではモニタリングツールにDatadogを利用しており、デプロイ時に一意な値となるVersion タグを設定することでバージョン毎のエラー率やレイテンシーの悪化などを測定しやすくしています。
- versionタグはGitHub ActionsのWorkflowの中で設定しています。
- git tagによるバージョン管理は実施していないため、コミットハッシュをversionタグに割り当てています。
- name: Fill in the new image ID in the Amazon ECS task definition uses: aws-actions/amazon-ecs-render-task-definition@v1 id: render with: task-definition: task-def.json container-name: app image: app:${{ github.sha }} environment-variables: | DD_VERSION=${{ github.sha }}
現在抱えている課題と対策
CodeDeployによるブルーグリーンデプロイは非常に安定しており、特に問題なく日頃のデプロイを行うことができていますが、更に改良するために課題と対策について以下のアプローチを検討しています。
1. ロールバック時の手順が煩雑になる
- 課題
- 現在のデプロイ手順では、リポジトリ上でのマージ操作とデプロイが連動しているため、CodeDeploy上でのロールバック後にリポジトリ側でrevertを行うと再度デプロイが実行されてしまい、手順が煩雑になっています。
- 対策
- マージ操作とデプロイ操作を分離することで、不要なデプロイを防ぐと同時に、ロールバック手順を簡素化したいと考えています。また、チャットベースでリリース操作を可能にすることで、デプロイ作業の効率化を図りたいです。
2. 本番相当のトラフィックを検証する環境が不足している
- 課題
- インフラや大規模なアプリケーションの改修時に、テスト環境での負荷検証は行っているものの、テストデータなどはマスキングされているため、より本番環境に近いトラフィックを再現してテストできる状態が必要とされています。
- 対策
- Shadow Proxyのような本番環境のトラフィックをミラーリングしてテストできる環境を用意し、影響を最小限に抑えつつ新しい変更点の検証ができる環境を用意したいです。
これらの改善を通じて、OfferBoxのデプロイプロセスをより効率的で安全な方式へ移行していきたいと考えています