AWS CodePipeline　Blue/Green デプロイ時にエラーが発生した際の勘所

# はじめに
本記事はAWSのCodePipelineを利用している環境で、Blue/Green デプロイ時にエラーが発生した際の勘所について記載しています。

DevOpsの一環として、CodePipelineを用いてECS環境（ECS on EC2）におけるBlue/Green デプロイの導入を行う機会がありました。

CodePipelineの仕様に対する理解不足が多く、デプロイ時にエラーが発生してトライアンドエラーを繰り返したのでナレッジとしてまとめました。

## アップデート方式
クラウド上に構築したシステムに対するアップデート方式は、一括でアップデートを行うAll at onceを除くと、以下のようなデプロイ戦略があります。

- Rolling
- Bule/Green
- Canary

目的に応じてソフトウェアの新しいバージョンや変更を本番環境に展開するためのデプロイ戦略を選択することが重要です。アップデート頻度やコストに対してリターンが見込めない場合は、最適でない場合もあると思います。

ECSのデフォルトは、[Rolling](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/userguide/deployment-type-ecs.html)によるアップデートです。
Rollingによるアップデートは、追加リソースが不要なため、コストがからないことがメリットです。デメリットは、切り戻しが困難なことです。

### Blue/Green
Blue/Greenデプロイメントは、システムの可用性を最大化し、ダウンタイムを最小限に抑えることができます。

以下に、Blue/Greenデプロイメントのメリット及びデメリットについて記載します。

- **メリット**
    - **高可用性と信頼性向上**：Blue/Greenデプロイメントは、新しいバージョンのアプリケーションを現在の環境（Blue）とは別の環境（Green）に展開し、任意のタイミングでトラフィックを切り替えることができます。
    - **切り戻しが容易**：新しいバージョンに問題が発生した場合、本番環境を別の環境（Green）から現在の環境（Blue）に切り替えることで、簡単にロールバックできます。
    - **テストプロセスの強化**: Blue/Greenデプロイメントはテストプロセスを強化します。新しいバージョンは現在の環境（Blue）とは別の環境（Green）でテスト可能なため、現在の環境となる本番環境に影響を与えずにテストが可能です。
- **デメリット**
    - **追加のリソース**：Blue/Greenデプロイメントでは、現在の環境（Blue）とは別の環境（Green）の両方が稼働するため、追加のリソースが必要です。従って追加のコストが発生[^1]します。
    - **データの同期**：データベースなど何らかのデータストアを必要とする場合、データの同期と一貫性を維持することが難しい場合があります。そのため、データベースで管理するデータのマイグレーションを行う場合は注意[^2]が必要です。
    - **導入コスト**：AWSの場合はタスク定義のテンプレートやAppSpecファイルなど管理すべき資産が増えるため、Rollingと比較した場合AppSpecファイルの管理場所など検討事項が発生します。

::: note
基本的にRolling、Bule/Green、Canaryはサービス停止なしでアップデートが可能です。
Blue/Green方式と、Canaryの違いについては、Blue/Green方式が一括で切り替わるのに対して、Canary方式は徐々に環境が切り替わるのが特徴です。
:::

[^1]: RollingもBlue/Greenより短い時間ではあるものの、一時的に２つのタスクが稼働します。そのため、ECS on EC2の場合はデプロイ時に複数のタスクを起動するためのスペックが必要です。
[^2]: Rollingも一時的に異なる環境のコンテナが平行稼働するため、破壊的変更があるマイグレーションが発生する場合は、古いコンテナからはアクセスできないなどのエラーが懸念されます。

## Blue/Green デプロイ時にエラーが発生した際の勘所
Blue/Green デプロイ時にエラーが発生した際の勘所について以下に記載します。

### コンソールの注意事項
AWSではサービスによってコンソールの新旧画面が存在する場合があります。

コンソールの新旧を切り替える場合は、EC2を例にすると画面左上の「New EC2 Experience」を操作することで、切り替えができます。

![スクリーンショット 2023-09-29 8.15.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/f9ab4cde-f2ed-e445-c1c4-29ffa8551d47.png)

コンソールの新旧を切り替えた場合は、表示内容が大きく変わるため、設定項目が変化することから**注意**が必要です。

#### デプロイメントタイプでBlue/Green デプロイメントが選択できない場合
ECSでBlue/Green デプロイに対応するための設定を行う場合、コンソール画面の新旧によって表示内容が異なります。

例えば、新しいコンソール画面からECSを開いて新規にサービスを作成する場合、デプロイオプションのデプロイタイプを確認すると、ローリングアップデートがグレー表示になっていて操作することができません。

![スクリーンショット 2023-09-15 18.29.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/a018e99f-c9b1-bbd0-d20a-9b18d936d86f.png)

最初は起動タイプがEC2環境の場合、対応していないのかと勘違いしましたが、そんなことはありませんでした。

AWSのサポートにも確認しましたが、新しいコンソールの仕様とのことで、旧コンソール画面に切り替えることで選択できると回答いただきました。（将来的には改善されるのではないかと思います）

![スクリーンショット 2023-09-15 18.29.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/317d282c-3499-55f4-75b5-5109e45d96f8.png)

#### taskdef.jsonの作成
タスク定義ファイルとして`taskdef.json`ファイルを作成する場合、新コンソール画面から作成します。

旧コンソール画面の場合はJSONの形式が異なっているため、optionの項目が`null`になることから注意が必要です。

### サービスを更新したがデプロイできない場合
検証を行っている中、サービスを更新したがデプロイが開始されない事象が発生しました。

確認したところ、公式ドキュメントの[コンソールを使用したサービスの更新](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/update-service-console-v2.html)を参考に**サービスの設定から新しいデプロイの強制**をチェックしたら動きました。

>更新されたコンテナイメージをタスクに使用する場合は、そのイメージを使用して新しいタスク定義リビジョンを作成し、コンソールで [新しいデプロイの強制] オプションを使用して、サービスにデプロイできます。

![スクリーンショット 2023-09-15 18.53.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/9c77219a-5bce-cf1d-b54a-d8d69528ce04.png)

### CodePipelineのアクセスエラー
CodePipelineのDeployステージで、`AWSCodePipelineServiceRole`に対するアクセスエラーが発生しました。

`AWSCodePipelineServiceRole`はデフォルトで作成されるロールですが、Blue/Green デプロイを行うには権限が不足しています。

必要な権限については、公式ドキュメントの[AWS CodePipeline のためのアイデンティティおよびアクセス管理](https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/security-iam.html)から**CodePipeline サービスロールにアクセス許可を追加する**の7をご確認ください。

```json
{
    "Effect": "Allow",
    "Action": [
        "codedeploy:CreateDeployment",
        "codedeploy:GetDeployment",
        "codedeploy:GetApplication",
        "codedeploy:GetApplicationRevision",
        "codedeploy:RegisterApplicationRevision",
        "codedeploy:GetDeploymentConfig",
        "ecs:RegisterTaskDefinition",
        "ecs:TagResource"
    ],
    "Resource": "resource_ARN"
},
```

### BuildArtifactの例外発生
CodePipelineのDeployステージで、BuildArtifactの例外が発生しました。

CodePipelineはbuildspec上でアーティファクトを出力する必要があるため、`buildspec.yml`ファイルに以下に示すような設定の記述が存在するか確認しましょう。

```yml
files: 
    - appspec.yml
    - taskdef.json
```

### AppSpecファイルの拡張子
ymlファイルの拡張子は`.yaml`と`yml`があります。

CodePipelineの設定と、`buildspec.yml`ファイル及び`appspec.yml`間で整合が取れてない場合は、ファイルが認識できないため、`Exception while trying to read the AppSec artifact file from: BuildArtifact.`のようなエラーが出力されます。

- CodePipelineの設定
![スクリーンショット 2023-09-28 23.42.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/34b0ac31-a51f-37fd-2a75-013fd15b1ef7.png)

- `buildspec.yml`ファイル

```yml
files: 
    - appspec.yml
    - taskdef.json
```

### 1タスクで複数コンテナが稼働する場合
公式ドキュメントの[イメージ定義ファイルのリファレンス](https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/file-reference.html)を参考にしながら複数のコンテナイメージのペアをリストするようにファイルを構築することもできます。

1タスクで複数コンテナが稼働する場合は`taskdef.json`で指定する`image`に対する考慮が必要です。

以下は`taskdef.json`で指定する`image`に不備があったため、出力されたエラーメッセージです。

`Container.image contains invalid characters.`

端的に言うと、`buildspec.yml`ファイルのビルドプロセスで`taskdef.json`を出力するようにすることで、1タスクで複数コンテナの設定が可能です。

### CodePipelineのビルド時に実行するコマンドで注意すべきこと
上記に関連して、例えば`buildspec.yml`ファイルのビルドプロセスで`sed`コマンドを使用する場合、ECRのリポジトリは`/`が含まれるため、`sed`コマンドを使用する場合はデリミタに対する注意が必要です。

対応として、`sed`コマンドで使用するデリミタを`/`から`@`に変更することで解消できます。

### Tags can not be empty.
デプロイ時に`Tags can not be empty.`のようなエラーメッセージが出力された場合は、`taskdef.json`ファイルの以下行を削除します。

```json
 "tags": []
```

参考：[AWS pipeline, code deploy 'Tags cannot be empty'."](https://stackoverflow.com/questions/75817989/aws-pipeline-code-deploy-tags-cannot-be-empty)

### タイムアウト
「トラフィックを再ルーティングするタイミングを指定する」を有効にしている場合、CodeDeployは時間が過ぎても自動的にルーティングを実行してくれません。

後で確認すると、以下のようなエラーメッセージが出力されます。

`The deployment timed out while waiting for a notification to continue. This time out period is 1440 minutes.`

エラーを発生させないためには、指定した時間までにトラフィックの再ルーティングを実行するとが必要です。

# おわりに
デプロイを開始してビルドは成功したものの、デプロイでエラーが発生し、原因を究明してトライアンドエラーを繰り返す。

本記事を書くにあたり、他にもまだエラーが出ていたたと思います。
同じような事象が発生した方のご参考になれば幸いです。

## 参考
- [チュートリアル:Amazon ECR ソースと ECS to-デプロイを使用してパイプラインを作成する CodeDeploy](https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/tutorials-ecs-ecr-codedeploy.html)
- [ブルーグリーン・デプロイメントとは](https://www.redhat.com/ja/topics/devops/what-is-blue-green-deployment)
- [カナリアリリースとブルーグリーン・デプロイメント: ダウンタイム短縮に最適なデプロイ戦略は？](https://circleci.com/ja/blog/canary-vs-blue-green-downtime/)
- [Blue-Green デプロイメント、ローリング・デプロイメント、およびカナリア・デプロイメントのモデル化](https://www.ibm.com/docs/ja/urbancode-deploy/6.2.4?topic=processes-modeling-blue-green-rolling-canary-deployments)