ECS on Fargate構成にする際の効果的なコスト削減案(Arm化含む)

少し前、アプリケーションをAPI Gateway + Lambda構成からELB(ALB) + ECS on Fargate構成に置き換えました。理由は、Lambdaの同時実行数を超えるほどのトラフィックが発生して、より安定したスケーリングが必要になったからです。

ECS on Fargateへの移行後、負荷への耐性は向上したのですが、Lambdaと比べると常時課金されるELBやFargateのコストが増加し、新しい悩みの種になってしまいました。

今回の記事では、コスト削減のために行ったいくつかの施策をまとめました。

## 1. 開発環境のリソースを最小限に抑える
Fargateの料金は、使用するvCPUとメモリのサイズに応じて課金されます。そのため、トラフィックが少ない開発環境では、リソースを最小限に抑えることでコスト削減が可能です。今回は、最低限のスペックとして`256(.25 vCPU), 512 MiB`を設定しました。

![スクリーンショット 2024-10-12 8.04.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/3251b407-e867-a9cc-47a9-7ee675e5e6d8.png)

**設定例:**
タスク定義の`cpu`と`memory`を以下のように設定します。

```
"cpu": 256,
"memory": 512,
```

開発環境のリソース要件が軽い場合、これにより大幅なコスト削減が期待できます。

## 2. Fargate Spotを利用する
Fargate Spotは通常のFargateよりも最大70 %安く利用できるため、開発環境ではFargate Spotを採用しました。本番環境では安定性を重視して通常のFargateを引き続き使用していますが、非本番環境でのコスト削減にはFargate Spotが有効です。

![スクリーンショット 2024-10-12 14.13.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/6177ab20-a1a4-14fd-4779-2e71184f00ef.png)

ECSサービスのコンピューティング設定で、以下のようにキャパシティープロバイダーを設定します。

**設定例:**
- キャパシティープロバイダー: `FARGATE_SPOT`
  - ベース: `1`
  - ウエイト: `2`
- キャパシティープロバイダー: `FARGATE`
  - ベース: `0`
  - ウエイト: `1`

![スクリーンショット 2024-10-12 8.14.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/ef032844-ed41-0715-6697-9b4374d2b08e.png)

### 設定の意図
Fargate Spotは通常のFargateに比べて大幅にコスト削減が可能ですが、SpotインスタンスはAWSの判断で突然停止する可能性があります。このため、開発環境など安定性をあまり重視しない環境ではFargate Spotのみで十分です。しかし、必要に応じて安定性を担保するために通常のFargateをフォールバックとして設定しています。

以上の設定例では、Spotインスタンスを優先的に使用し、万が一Spotが利用できない場合には通常のFargateを自動的に使用するようにしています。ベースの値を`0`に設定することで、Spotが利用できない際にのみ通常のFargateが利用されるようになります。

もしリスクを許容できるなら、Fargate Spotのみを使う設定にして、さらにコスト削減を狙うことも可能です。

- キャパシティープロバイダー: `FARGATE_SPOT`
  - ベース: `1`
  - ウエイト: `1`

この設定では、Fargate Spotのみを使用し、Spotインスタンスの停止リスクを完全に許容する構成となります。

## 3. 開発環境の夜間・休日停止
Fargateは使用時間に応じて課金されるため、開発環境のように稼働時間が限定される場合は、夜間や休日にタスクを停止することがコスト削減に直結します。

ECSタスクのスケジューリングには、EventBridgeとLambdaを使用したスケジュール設定を活用しました。この方法を使えば、稼働しない時間帯に自動でタスクを停止し、コストを削減することができます。

以下が以前書いた記事です。

https://qiita.com/suzuki0430/items/f84a794fad427875844b

## 4. コンテナアーキテクチャのArm化
Fargateでの料金はアーキテクチャにも依存します。`x86_64`からGravitonベースの`Arm`に変更することで、料金を約20 %削減することが可能です。
Gravitonは、AWSが独自に開発したArmベースのプロセッサで、より低コストで優れたパフォーマンスを提供します。

さらに、2024/9からはFargate SpotでもArmが利用可能になったので、開発環境でも`Arm`アーキテクチャを採用しました。

![スクリーンショット 2024-10-12 14.26.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/bf4af3a2-ff64-f55d-656c-e80e9f851a9d.png)

https://aws.amazon.com/jp/about-aws/whats-new/2024/09/amazon-ecs-graviton-based-spot-compute-fargate/

**CI/CDパイプラインでのクロスコンパイル設定:**
CI/CDワークフローにおいて、`docker buildx`を使ったクロスコンパイルを行うことで、簡単に`Arm`アーキテクチャに対応したDockerイメージをビルドできます。以下が設定例です。

```yml
      - name: Set up QEMU
        run: docker run --privileged --rm tonistiigi/binfmt --install all

      - name: Set up Docker Buildx
        run: docker buildx create --use

      - name: Build Docker Image
        run: docker buildx build --platform linux/arm64 --build-arg ENV=${{ env.ENV }} -t ${{ env.REPOSITORY_URI }}:${{ github.sha }} -f ./backend/Dockerfile.ecs ./backend --load
```

ここで、`--platform linux/arm64`オプションを指定することで、`Arm`アーキテクチャ向けにビルドされます。`docker buildx`は異なるアーキテクチャ向けのコンテナイメージを効率的にビルドできるため、`x86_64`と`Arm`の両方に対応したイメージを作成し、柔軟に運用できます。

**タスク定義でのアーキテクチャ指定:**

```
"runtimePlatform": {
  "cpuArchitecture": "ARM64",
  "operatingSystemFamily": "LINUX"
},
```

この変更により、Armアーキテクチャの利点を最大限活用し、コストを抑えられます。

## 5. Compute Savings Plansの利用(未対応)
まだ導入はしていませんが、将来的にCompute Savings Plansを利用する予定です。このプランを利用することで、EC2、Lambda、Fargate全体のコンピューティングコストを大幅に削減できます。1年または3年の期間契約で、一定のコンピューティング使用量に対する割引が提供されます。

https://aws.amazon.com/jp/savingsplans/compute-pricing/