Amazon ECS による Web アプリの設定

2023年11月19日

はじめに

Aamazon ECS/AWS Fargate で Web アプリを設定することを考える。ここでは Streamlit を利用した Web アプリを想定。ECR にコンテナイメージを用意しているものとする。

構成

コンテナアプリを Fargate で公開する。接続には CloudFront を介する。Fargate と CloudFront のあいだにロードバランサー (ALB) を挟む。

ロードバランサーの設定

EC2

  • [ターゲットグループ]-[ターゲットグループの作成]
  • "ターゲットタイプの選択" で "IP アドレス" を選択。
  • "ターゲットグループ名" を設定。
  • ポートを設定。Streamlit なら 8501 など。
  • ヘルスチェック: /healthz
  • [次へ]
  • IP アドレスを指定。この場合は、タスクのプライベート IP の範囲を入力。
  • [ターゲットグループの作成]。
  • [ロードバランサー]-[ロードバランサーの作成]
  • "Application Load Balancer" で [作成]。
  • "ロードバランサー名" を設定。
  • "スキーム" で "インターネット向け" を選択。
  • "ネットワークマッピング" で VPC やサブネット (パブリックサブネット) を設定。
  • "セキュリティグループ" を設定。
    • CloudFront からのインバウンドを許可する (ソースのところで "cloudfront" などと入れると出てくる)。
  • "リスナーとルーティング" でポートを設定。HTTP の 80。
  • [ロードバランサーの作成]。

エンドポイントの作成

VPC

  • [エンドポイント] の [エンドポイントを作成]。
  • ecr で検索し、"com.amazonaws.ap-northeast-1.ecr.api" を選択。
  • VPC を選択。適切なサブネット (プライベートサブネット)、セキュリティグループを設定。
  • [エンドポイントを作成]。

以下も同様に作成。

  • "com.amazonaws.ap-northeast-1.ecr.dkr"
  • "com.amazonaws.ap-northeast-1.logs"
  • "com.amazonaws.ap-northeast-1.s3" の Gateway 型

ECS の設定

クラスター

  • Amazon ECS の [クラスター] の [クラスターの作成]。
  • "クラスター名" を適当に設定。
  • "インフラストラクチャ" で [AWS Fargate] が有効になっているのを確認。
  • [作成]。

タスク定義

  • "タスク定義ファミリー" で名前を設定する。
  • Amazon ECS の [タスク定義] の [新しいタスク定義の作成]-[新しいタスク定義の作成]。
  • "インフラストラクチャの要件" の "起動タイプ" で [AWS Fargate] が有効になっているのを確認。
  • "コンテナ - 1" で名前と、ECR の URI を設定。
  • ポートマッピングを設定。Streamlit なら 8501 など。
  • "読み取り専用" はオフにする (オンにするとたぶんエラーが出る)。
  • [作成]。
    ※既存のものを作り直すときは、[新しいリビジョンの作成] でよい。

クラスター

  • クラスターを選択。
  • [サービス]-[作成]。
  • "環境" の "コンピューティングオプション" で "キャパシティープロバイダー戦略" と "起動タイプ" があるが、とりあえずここでは "起動タイプ" を選んでおく。
  • "デプロイ設定" の "アプリケーションタイプ" で "サービス" を選ぶ。
  • "ファミリー" でタスク定義ファミリーを選択。
  • "サービス名" を指定。
  • VPC、サブネット、セキュリティグループを設定。サブネットはエンドポイントで設定しているものを指定すること。
  • パブリック IP をオフにする。
  • ロードバランサーの設定を行う。
  • [作成]。

うまくいかない場合

[クラスター]-[サービス]-[デプロイ] にタスクを起動しようとした形跡があるはずなので、それを確認する。

※タスクを起動しなおす場合は、CloudFormation にサービスのスタックが残るようなので、名前を変えるか、スタックを削除する。

エラー

ResourceInitializationError: unable to pull secrets or registry auth: execution resource retrieval failed: unable to retrieve ecr registry auth: service call has been retried 3 time(s): RequestError: send request failed caused by: Post "https://api.ecr.ap-northeast-1.amazonaws.com/": dial tcp 3.112.65.177:443: i/o timeout. Please check your task network configuration.

エンドポイント (ecr.api) を設定していないために起こるらしい。

ResourceInitializationError: unable to pull secrets or registry auth: execution resource retrieval failed: unable to retrieve ecr registry auth: service call has been retried 5 time(s): RequestError: send request failed caused by: Post "https://api.ecr.ap-northeast-1.amazonaws.com/": dial tcp: lookup api.ecr.ap-northeast-1.amazonaws.com on 10.108.48.2:53: no such host. Please check your task network configuration.

タスクとエンドポイントのサブネットが一致していないために起こるらしい。

CannotPullContainerError: pull image manifest has been retried 5 time(s): failed to resolve ref 292072304900.dkr.ecr.ap-northeast-1.amazonaws.com/codebuild-docker-test2:latest: failed to do request: Head "https://292072304900.dkr.ecr.ap-northeast-1.amazonaws.com/v2/codebuild-docker-test2/manifests/latest": dial tcp 52.196.216.64:443: i/o timeout

これもエンドポイント (ecr.dkr) を設定していないために起こるらしい。

CannotPullContainerError: containerd: pull command failed: panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x40 pc=0x1369f41] goroutine 1 [running]: main.(*puller).pullWithClient(0xc000573bc0, {0x18dc8a0, 0xc0005c0cc0}, {0x18d6988, 0xc0003d0000}, 0xc00038ac30, {0x18d6938, 0xc000419840}) /root/go/src/github.com/aws/two/puller/pull.go:198 +0x501 main.(*puller).Pull(0xc000573bc0, {0x18dc8a0, 0xc000397d10}, 0xc00038ac30, {0x18d6938, 0xc000419840}) /root/go/src/github.com/aws/two/puller/pull.go:147 +0x2a7 main.(*puller).pullImage(0x18dc8a0?, {0x18dc8a0, 0xc000397d10}, 0xc00038ac30, {0x18d6938?, 0xc000419840?}) /root/go/src/github.com/aws/two/puller/pull.go:350 +0x47 main.main() /root/go/src/github.com/aws/two/puller/main.go:75 +0x587 : exit status 2

読み取り専用設定のせい?

ResourceInitializationError: failed to validate logger args: : signal: killed

CloudWatch Logs のエンドポイントが必要。

つながらなかったら、Reachability Analizer で調べる。

CloudFront の設定

CloudFront

  • [ディストリビューションを作成]
  • "オリジンドメイン" でロードバランサーを選択する。
  • オリジンリクエストポリシーで "AllViewer" を選択。(これを入れないと WebSocket 通らない)
  • 適当に設定して [ディストリビューションを作成]。

参考