1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

EEM-① App Runner から ECS Express Mode 移行の苦労話(Terraform)

1
Last updated at Posted at 2026-04-10

はじめに

突然? App Runner の新規サービス登録終了のアナウンスが流れましたね。
約1ヶ月前という恐ろしいタイミングです。(一部フライングもあったので察しではありましたが)

以前公開されていたマイグレーション方法のページも 404 になってしまったし...。

背景

App Runner の新規サービス登録終了がアナウンスは以下。

移行先として ECS Express Mode を選定しました。

App Runner との主な違い

項目 App Runner ECS Express Mode
コンピュート マネージド ECS Fargate
ネットワーク VPC Connector 経由 パブリックサブネットに直接配置
LB 内蔵(不可視) ALB(自動作成・可視)
スケーリング MaxConcurrency ベース CPU/Memory ターゲットベース
Scale to Zero あり なし(min_task_count >= 1)
デプロイ ECR push で自動 外部パイプライン必要
WAF / サイドカー 不可
Terraform リソース aws_apprunner_service aws_ecs_express_gateway_service
Provider バージョン v5 系で可 v6 以上必須

概要

App Runner を稼働させたまま Express Mode を並行構築し、Route 53 加重ルーティングで段階的に切り替えます。バックエンド(RDS / ElastiCache 等)は共通なので並行稼働でき、問題があれば重みを戻すだけでロールバックできます。

移行イメージ

※ webapp と analytics の2つの App Runner で運用

# Before (App Runner)

  Route 53 ── App Runner (ALB 内蔵)
                (webapp / analytics)
# 移行中 (並行稼働)

             ┌── App Runner (既存)
  Route 53 ──┤     (webapp / analytics)
             └── ALB(自動作成) ── Express Mode
                                   (webapp / analytics)
# After (ECS Express Mode)

  Route 53 ── ALB(自動作成) ── Express Mode
                                (webapp / analytics)

移行手順

フェーズ 内容
Phase 0 Terraform 作成・IAM 設計
Phase 1 ECS Express 構築・疎通テスト
Phase 2 Route 53 加重ルーティングで段階的移行(10% → 50% → 100%)
Phase 3 完全切替・安定性確認
Phase 4 App Runner リソース削除

移行対象は App Runner 上の 2 サービスのみ。既存 ALB や RDS 等には手を入れません。


内容

サービス構築自体は難しくありませんでした。大変だったのはカスタムドメインでの公開です。Route 53 の設定で以下の問題に順番にぶつかりました。

  • エイリアスレコードが設定できない
    • App Runner と違い、Express Mode の ALB は Terraform から直接参照できず、エイリアスのターゲットに指定できない。ゾーンアペックスで運用している場合ここで詰みかける
      → 実際には ALB は内蔵ではなく独立したリソースなので、タグ逆引きで特定すればエイリアス設定は可能だった
  • HTTPS が使えない
    • ALB にカスタムドメイン用の ACM 証明書がアタッチされていない
  • ルーティングされない
    • リスナールールがカスタムドメインを考慮していない

これらを Terraform で解決するためのワークアラウンドを以降で説明します。

0. 最も重要な前提:ALB は「自動生成」されるが「自動管理」ではない

Express Mode を検討する上で最初に理解すべきことがあります。
Express Mode は ALB を自動で作ってくれますが、証明書の追加やリスナールールのカスタマイズは自分でやる必要があります。App Runner のように「ドメインを設定したら全部よしなにやってくれる」わけではありません。ALB が作られた後の面倒は自分で見る、という前提で設計しないと後からひっくり返ることになります。

公式でもこれに関して以下のように触れています。

「Understanding the shared responsibility model」セクションに以下の記述があります。

While Express Mode creates and configures resources like Amazon ECS services, load balancers, and auto scaling policies, all resources remain in your AWS account and are fully accessible for direct management.
(Express Mode はロードバランサー等を作成・構成するが、すべてのリソースはユーザーのアカウントに残り、直接管理できる)

1. Express Mode 固有のリソースと IAM ロール

aws_ecs_service ではなく aws_ecs_express_gateway_service を使います。Terraform AWS Provider v6.38 以上が必須です。

v6 では ALB の mutual_authentication { mode = "off" } が属性ごと消えており、書くとエラーになります。既存の ALB 定義がある場合は先に削除が必要です。

IAM ロールは通常の ECS の 2 つ(Execution / Task)に加え、ALB や SG を自動構成するための Infrastructure Role が必要です。

ロール 何に使うか Principal
Infrastructure Role ALB / SG / 証明書の自動構成 ecs.amazonaws.com
Execution Role ECR pull / CloudWatch Logs / Secrets 読み取り ecs-tasks.amazonaws.com
Task Role アプリ実行時の権限(S3, SQS, SES 等) ecs-tasks.amazonaws.com

infrastructure_role_arn が Express Mode 固有の設定項目です。

resource "aws_ecs_express_gateway_service" "webapp" {
  cluster                 = aws_ecs_cluster.express.name
  service_name            = local.prefix
  infrastructure_role_arn = aws_iam_role.infrastructure.arn  # Express Mode 固有
  execution_role_arn      = aws_iam_role.execution.arn
  task_role_arn           = aws_iam_role.task.arn

  primary_container {
    image          = "${var.app_ecr_repository_url}:latest"
    container_port = 3000
    ...
  }
  ...
}

2. ALB 自動管理と Terraform の衝突

Express Mode は ALB を自動で作りますが、Terraform からはその ALB を直接参照できません。ここが一番苦労したところです。

Security Group の自動注入

Express Mode は ALB 用の SG をタスクの network_configuration に自動で追加します。Terraform は毎回差分を検出するので、lifecycle で握りつぶす必要があります。

resource "aws_ecs_express_gateway_service" "webapp" {
  network_configuration {
    subnets         = var.public_subnet_ids
    security_groups = [aws_security_group.task.id]
  }

  lifecycle {
    ignore_changes = [
      network_configuration[0].security_groups,
    ]
  }
}

ALB のタグベース逆引き

ALB を Terraform から触るにはタグで逆引きするしかありません。Name タグは最初に作られたサービス名に依存して不安定だったので、AmazonECSManaged + environment + service の組み合わせに落ち着きました。

data "aws_lb" "express" {
  tags = {
    AmazonECSManaged = "true"
    environment      = var.env
    service          = local.prefix
  }

  depends_on = [
    aws_ecs_express_gateway_service.webapp,
    aws_ecs_express_gateway_service.analytics,
  ]
}

depends_on を忘れるとサービスより先に ALB を探しに行って失敗します。初回 apply でハマりました。

3. カスタムドメインのワークアラウンド

リスナールールにカスタムドメインの host-header 条件を足したかったのですが、Terraform のリソースでは実現できず、null_resource + local-exec で AWS CLI を直接叩いています。

resource "null_resource" "webapp_custom_domain" {
  count = var.custom_domain_webapp != null ? 1 : 0

  triggers = {
    custom_domain   = var.custom_domain_webapp
    rule_arn        = data.aws_lb_listener_rule.webapp[0].arn
    conditions_hash = local_file.webapp_conditions[0].content_sha256
  }

  provisioner "local-exec" {
    command = "aws elbv2 modify-rule --rule-arn ... --conditions file://..."
  }
}

ルールの priority を固定値で指定しているので、サービスが増えたり AWS 側の採番が変わると壊れるリスクがあります。

4. カスタム証明書の追加

既存のワイルドカード ACM 証明書を Express Mode の ALB にも使い回したいのですが、これも ALB の逆引きが前提です。

resource "aws_lb_listener_certificate" "express_additional" {
  listener_arn    = data.aws_lb_listener.express_https[0].arn
  certificate_arn = var.additional_certificate_arn
}

タグベース逆引きの安定性に依存する構造です。

5. Security Group 設計の制約

Express Mode が作る ALB の SG を Terraform から安定して参照できないため、タスク側の ingress は VPC CIDR 全体を許可しています。

resource "aws_security_group" "task" {
  ingress = [{
    cidr_blocks = [var.vpc_cidr_block]
    from_port   = 3000
    protocol    = "tcp"
    to_port     = 3000
    ...
  }]
}

ALB の SG だけに絞りたいところですが、現状では妥協しています。


終わりに

ECS Express Mode は App Runner の移行先として悪くない選択肢ですが、Terraform との相性には苦労します。ALB を直接参照できないため、タグ逆引きや null_resource + CLI といった力技が必要になります。

その代わり、WAF の適用、Container Insights でのメトリクス監視、サイドカーの追加など、App Runner ではできなかったことができるようになりました。差し引きで考えれば移行してよかったと思っています。

aws_ecs_express_gateway_service から ALB ARN やリスナールール ARN を直接取れるようになれば、ここに書いたワークアラウンドの大半は不要になるはずです。Provider のアップデートに期待しつつ、それまでは力技で乗り切っていきます。

【追記】
以下に追加記事を書いていますので宜しければご確認ください。

一言

ECS Express Mode で一旦構築完了したら ALB関連のリソースを import してしまった方が良いのかもしれません。ここは皆さんの方でも試してみては如何でしょうか。

実際にここまでやってみて、もし自分が次に同じ機会があれば import を選ぶと思います^^;

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?