Amazon ECSの耐障害性を考える

# はじめに
この記事はDevOps on AWS大全の一部です。
DevOps on AWS大全の一覧は[こちら](https://qiita.com/tech4anyone/items/b06f88035d27c6ef13b2)。

この記事ではAmazon ECSを耐障害性の観点から超詳細解説しています。

具体的には以下流れで説明します。

- Amazon ECSとは
- Amazon ECSのスケーラビリティ
- Amazon ECSのストレージ
- Amazon ECSのロギング


AWSの区分でいう「Level 200：トピックの入門知識を持っていることを前提に、ベストプラクティス、サービス機能を解説するレベル」の内容です。

# この記事を読んでほしい人
- Amazon ECSを採用するときのベストプラクティスを説明できるようになりたい人
- Amazon ECSの耐障害性に不安を感じている人
- AWS Certified DevOps Engineer Professionalを目指している人


# Amazon ECSとは
Amazon ECSは、Dockerコンテナの効率的なデプロイと管理を可能にするマネージドサービスです。
ECSは、タスクと呼ばれるコンテナ実行単位を使い、アプリケーションのスケーラビリティと柔軟性を向上させます。

よくAmazon EKSと混乱する人がいますがEKSはKubernetesでDockerコンテナの効率的なデプロイと管理を実現しています。
一方、ECSではAWS独自の方法でDockerコンテナを管理しています。

なお、ECSを利用してコンテナを起動する先はEC2とAWS Fargateを選択可能です。

## ECS on EC2
まず、EC2上でコンテナを起動するECS on EC2です。

![](https://storage.googleapis.com/zenn-user-upload/3f621249e101-20231122.png)

ECS on EC2を利用する最も大きなメリットはコンテナが起動しているサーバにアクセスできることです。
一方、最も大きなデメリットはEC2のプロビジョニングと管理を行う必要がありインフラストラクチャ部分の管理工数がかかることです。

そのため、ECSを利用する際にはまず後述するECS on Fargateの利用検討から行い、どうしても要件を満たせない場合のみECS on EC2を利用するのがベストプラクティスです。

なお、ECS on EC2を利用する場合、ECSクラスタにノードとして認識してもらうためのECS Agentをインストールする必要があります。

## ECS on Fargate
次にFargate上でコンテナを起動するECS on Fargateです。

![](https://storage.googleapis.com/zenn-user-upload/0869842587e7-20231122.png)

ECS on Fargateと異なり、EC2インスタンスの管理が不要なサーバレス型になっています。
そのため、コンテナのメリットを最大限に享受でき、インフラストラクチャ部分の管理が不要になります。

ユーザが行うことはタスク定義と呼ばれる定義を作成するだけです。

## タスク定義
タスク定義はイメージの場所などを記載したコンテナ定義や要求CPU・メモリ、ネットワークモードなどを記述するコンテナ群定義です。

タスク定義には様々な内容を記載する必要がありますがここではピックアップしてIAM周りの定義を説明します。

![](https://storage.googleapis.com/zenn-user-upload/241bde4e5a66-20231122.png)

まず、ECS on EC2でだけ必要なIAMロール、Instance Profileについてです。
これは、EC2にアタッチされたIAMロールでECSを使うときに限らずEC2からAWSリソースを呼び出す際には必ず必要となるIAMロールです。

例えば、CloudWatchLogsを呼び出す、という場合にはInstance Profileに適切な権限を与える必要があります。

そしてECS on EC2とECS on Fargateの両方で必要なのがタスクロールとタスク実行ロールです。
Amazon ECSにおいて、タスクロールとタスク実行ロールは異なる役割を果たします。
タスクロールは、ECSタスクがAWSサービスと連携する際に使用されるIAMロールであり、AWSサービスへのアクセス権を定義します。

一方で、タスク実行ロールはEC2インスタンスまたはFargateタスクがECSサービスと通信するためのIAMロールです。
タスク実行ロールはタスク内のコンテナがAWSサービスへアクセスする権限を与え、セキュアな環境での運用を可能にします。

参考までにECS on Fargateでnginxを起動するタスク定義を以下に記載します。
```json
{
  "family": "nginx-task",
  "executionRoleArn": "arn:aws:iam::your-account-id:role/ecs-task-execution-role",
  "taskRoleArn": "arn:aws:iam::your-account-id:role/ecs-task-role",
  "containerDefinitions": [
    {
      "name": "nginx-container",
      "image": "nginx:latest",
      "cpu": 256,
      "memory": 512,
      "essential": true,
      "portMappings": [
        {
          "containerPort": 80,
          "hostPort": 80
        }
      ]
    }
  ]
}
```

タスクロールとタスク実行ロールが両方定義されていることがわかると思います。
なお、タスク定義はECS on EC2とon Fargateいずれでも必要です。

# Amazon ECSのストレージ
ECSはコンテナサービスになるので何も考えずに使うとストレージは揮発性です。
そのため、永続ストレージは明示的に準備する必要があります。
その際、ECS on EC2でもECS on FargateでもつかるベストプラクティスはAmazon EFSです。

![](https://storage.googleapis.com/zenn-user-upload/0e0e5620e6e1-20231122.png)

Amazon EFSを利用する場合、コンテナが水平方向にスケーリングするときの共有ストレージとしても利用できるので、レイテンシーの要件が厳しくなければ基本はEFSを利用しましょう。

ただし、サブミリ秒のレイテンシーを必要とする場合にはEBSを永続ストレージとして利用する必要があります。
その場合、ECSの起動先がEC2に限定される点に注意が必要です。

# Amazon ECSのスケーラビリティ
Amazon ECSはECS on EC2とECS on Fargateともに自動でコンテナをスケールイン/アウトさせることが可能です。

以下ではコンテナリソースとしてCPUあるいはRAMの使用率でスケールさせる場合のネットワークリソースとしてALBのリクエストカウントでスケールさせる場合を説明します。

## コンテナリソース
コンテナリソースとしてCPUあるいはRAMの使用率でスケールさせる場合の考え方はECS on EC2とECS on Fargateで若干異なります。

まずは、ECS on EC2です。
![](https://storage.googleapis.com/zenn-user-upload/ea16c38740c6-20231122.png)

ECS on EC2の場合、コンテナを起動するEC2がAutoScalingGroupに入っているのがわかると思います。
ECS on EC2の場合はインフラストラクチャ部分の管理が必要になるので1)EC2インスタンスのスケーリング、2)タスクのスケーリングの2つを考える必要があります。

昔は2つのスケーリングを連動させるのために工夫が必要でしたが、現在はキャパシティプロバイダと呼ばれる機能を一緒に使うことでタスクのスケーリングだけを考えればよくなっています。

キャパシティプロバイダを使うとタスクのスケーリングに合わせてEC2のスケーリングもやってくれるため、EC2インスタンスのスケーリングを考える必要がなくなります。
そのため、キャパシティプロバイダを使うアーキテクチャがベストプラクティスです。

次にECS on Fargateです。
![](https://storage.googleapis.com/zenn-user-upload/4d73a715896e-20231122.png)

こちらはEC2インスタンスがない分単純で、タスクのスケーリングだけを考えれば済みます。
ECS on Fargateの場合キャパシティプロバイダなしでもスケーリングできますが、ベストプラクティスに沿って、キャパシティプロバイダありのアーキテクチャにしています。

ECS on Fargateでキャパシティプロバイダを使うメリットはFargateオンデマンドとFargateスポットインスタンスを組み合わせたキャパシティプランを簡単に作れることです。

これによりコスト最適が進むため、今回のアーキテクチャではキャパシティプロバイダありで書きました。

## ネットワークリソース
ネットワークリソースとしてALBのリクエストカウントでスケールさせる場合は特筆すべき点がありません。
![](https://storage.googleapis.com/zenn-user-upload/63b7a15b5642-20231122.png)

ECSで使えるLBはALB、NLB、CLBの三種類です。
ただし、ECSタスクはアプリケーションオートスケーリングになるのでリクエスト数に応じたタスクのスケーリングはALBのみ対応です。

なお、NLBは高いスループットを求められる場合に採用しますがCLBは現在非推奨なので特別な要件がなければ使うべきではありません。
これは通常LBを選択するときと同じです。

# Amazon ECSのロギング
最後にECSのロギングについてです。
コンテナはタスクの起動と停止が絶え間なく起こるのでロギングについてを耐障害性という観点に追加しました。

ECSでロギングをするためにはawslogs、splunlkまたはawsfirelensのlogging driverを有効化する必要があります。
例えば、awslogs driverを有効化したうえで、タスク定義にlogConfigurationを追加するとCloudWatchLogsに出力することが可能です。

タスク定義にlogConfigurationを追記すると以下のようになります。

```diff json
{
  "family": "nginx-task",
  "executionRoleArn": "arn:aws:iam::your-account-id:role/ecs-task-execution-role",
  "taskRoleArn": "arn:aws:iam::your-account-id:role/ecs-task-role",
  "containerDefinitions": [
    {
      "name": "nginx-container",
      "image": "nginx:latest",
      "cpu": 256,
      "memory": 512,
      "essential": true,
      "portMappings": [
        {
          "containerPort": 80,
          "hostPort": 80
        }
      ],
+     "logConfiguration": {
+       "logDriver": "awslogs",
+       "options": {
+         "awslogs-group": "/ecs/nginx-task",
+         "awslogs-region": "your-region",
+         "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ]
}
```

なお、ロギングに関して高度な処理を行いたい場合にはサイドカーコンテナを利用する必要があります。

![](https://storage.googleapis.com/zenn-user-upload/f4b9ef1b02f4-20231122.png)

サイドカーコンテナにはFluentdかFluent Bitが選択可能です。
awslogs logging driverはカスタマイズ性が低く、ログ種別ごとに異なる場所へログを格納する、CloudWatchLogs以外に転送する、という実プロジェクトではほぼ必須の要件を満たすことができません。

そのため、実プロジェクトにおけるベストプラクティスはサイドカーコンテナを利用したロギングだと思ってよいでしょう。

# まとめ

この記事ではAmazon ECSを耐障害性の観点から超詳細解説しました。

- Amazon ECSとは
- Amazon ECSのストレージ
- Amazon ECSのスケーラビリティ
- Amazon ECSのロギング

次回はEKSの耐障害性を考えます。