既存クラスターでFargate Spotを利用する際にはまったこと

# はじめに
すでにECS on Fargateで本番サービスを稼働している状態でしたが、利用ユーザーが増えてきて起動するコンテナの台数も増えてきたため、コストパフォーマンスを上げるためにFargate Spotの検討を始めました。
その際、既存クラスターに変更を加えようとしていくつかはまったので、その共有になります。

# 最初に試したこと
既存のクラスターでFargate Spotを利用するために、[AWS re:Postに投稿されていた記事](https://repost.aws/knowledge-center/ecs-fargate-spot-capacity-provider)を参考にしました。

内容としては、AWS CLIコマンドを実行してCapacity Providerの関連付けをするというものでした。
```bash
aws ecs put-cluster-capacity-providers \
        --cluster <Cluster_name> \
        --capacity-providers FARGATE FARGATE_SPOT \
        --default-capacity-provider-strategy capacityProvider=FARGATE,weight=1 capacityProvider=FARGATE_SPOT,weight=1 \
        --region <Region>
```

こちらの対応とECS Service側でFargate Spotを利用する設定をすることで、Fargate Spotを利用してタスクが起動するようになりました。
以下はAWSコンソール上でのクラスターのキャパシティープロバイダー設定です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237951/fa29d456-e941-7a6c-5220-44760492b22a.png)

# コードへの組み込み
とりあえずうまくいくことは分かったので、次にコード化するために必要なことを調べました。
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs-readme.html#fargate-capacity-providers

既存のcdkコード内でclusterを作成する際に、 `enableFargateCapacityProviders` propertyを `true` でセットする必要があると分かったので、以下のようにpropertyを追加して `cdk deploy` を実行しました。

```typescript
export class Ecs extends Construct {

  constructor(scope: cdk.Stack, id: string, props: EcsProps) {
    super(scope, id);

    const { serviceName, myEnv, vpc } = props;

    // Ecs Cluster
    const cluster = new ecs.Cluster(this, 'EcsCluster', {
      vpc: vpc,
      clusterName: `${serviceName}-${myEnv}-ecs-cluster`,
      enableFargateCapacityProviders: true // Capacity Providerを関連付けするために必要な設定
    })
```

すると、以下のようなエラーが発生し、更新できなくなってしまいました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237951/b905a78a-9526-264a-adc7-9aa8a5cb3f0a.png)

```
Resource handler returned message: "The cluster already contains capacity provider associations" (RequestToken: xxxx, HandlerErrorCode: AlreadyExists)
```
一応今回追加したpropertyをcdkで管理しなければこのままでも問題ないですが、別の環境を構築する際に手順が増えてしまうため、エラーの解消に動きます。

# エラー解消のために試したこと

元々参考にしていたre:Postの記事に、 `Dissociate Fargate Spot capacity provider with your cluster` という見出しで関連付けの解除ができる旨の記載があったため、以下のAWS CLIコマンドを実行しました。

```bash
aws ecs put-cluster-capacity-providers \
        --cluster <Cluster_name> \
        --capacity-providers FARGATE \
        --default-capacity-provider-strategy capacityProvider=FARGATE,weight=1 \
        --region <Region>
```
特に何も考えず実行した結果、以下のエラーが発生しました。

```bash
An error occurred (ResourceInUseException) when calling the PutClusterCapacityProviders operation: The specified capacity provider is in use and cannot be removed.
```

記載の通り、たしかにFargate Spotでタスクが起動している状態だったため、ECS Serviceを削除した上で、改めてコマンドを実行しました。
実行すると、コンソール上ではFargateのみがキャパシティープロバイダーとして指定されている状態になりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237951/a5127228-cf25-b7f8-8d01-43a2266b8b60.png)

この状態で再度 `cdk deploy` を実行しましたが、まあ予想通り最初と同様のエラーが発生しました。
```
Resource handler returned message: "The cluster already contains capacity provider associations" (RequestToken: xxxx, HandlerErrorCode: AlreadyExists)
```

このエラーを解消するには、キャパシティープロバイダーの設定を以下の状態にする必要がありますが、AWS CLIコマンドでも削除できず、当然コンソール上でもグレーアウトされているので元に戻せない状態です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237951/57419b6e-59e2-dbeb-9c03-2519bcd60e74.png)

# 結果どうしたか
最終的にはテスト環境だったということもあり、ECS Clusterを削除して作り直すという方法をとりました。
他にも困ってる人いそうだなと思って調べてみると、少し内容は違うもののGitHub Issuesでいくつか議論がされているようでした。
※workaroundもいくつか提案されていましたが、今回の場合は作り直した方が早いと判断しました。
https://github.com/aws/aws-cdk/issues/15230
https://github.com/aws/aws-cdk/issues/15366

# おわりに
みなさんも既存のリソースに変更を加える際は気を付けてください。
IaCでインフラをコード化しておき、いつでも捨てられる環境で試すことの大切さを再認識した良い機会でした。