/var/www/yatta47.log

/var/www/yatta47.log

やったのログ置場です。スクラップみたいな短編が多いかと。

ECSネイティブBlue/Greenで同一ListenerRuleにカナリア用TGを同居できない理由と回避策

ECSネイティブBlue/Greenで同一ListenerRuleにカナリア用TGを同居できない理由と回避策

ECSネイティブBlue/Greenデプロイは、advanced_configuration で指定した ListenerRule の forward config を排他的に管理します。同一 ListenerRule 上にカナリア用の3つ目の Target Group を同居させようとすると、ECSが forward config を上書きして消してしまいます。

ECS純正のCANARY/LINEARデプロイは使えますが、自前のTGとの同居はできません。

やるなら ListenerRule を分けるか、DNS/CDN レイヤーで振り分ける必要があります。

ECSネイティブB/Gで加重ルーティングを組もうとした

2025年7月にGAになったECSネイティブBlue/Green、CodeDeployが不要になってかなり楽になったんですよね。で、これを使いつつ ALB の加重ルーティングでカナリアリリースも組めないかなと調べていたんですが、3つの非互換性にぶつかりました。

端的に言うと、ECSネイティブB/G は ListenerRule を「自分の持ち物」として排他管理していて、blue と green の2つの Target Group だけが存在する世界を前提にしています。3つ目の TG が入ってくることを想定していません。

ECSネイティブB/GとALB加重ルーティングが共存できない3つの理由

forward config が丸ごと上書きされる

デプロイ完了時に ECS が ListenerRule の forward config を更新するんですが、このとき primary と alternate の2つの TG だけで構成し直します。

つまりこういうことが起きます。

デプロイ前:
  ListenerRule forward config:
    blue-tg   weight=80
    green-tg  weight=10
    canary-tg weight=10    ← 自前の加重ルーティング用

デプロイ完了時:
  ECS が ModifyRule を実行:
    blue-tg   weight=0
    green-tg  weight=100
    canary-tg → 消滅       ← forward config から除外される

ECS は「知らない TG」を消しているわけじゃなくて、「知ってる TG だけで forward config を構成し直す」という動きをしています。結果として3つ目以降の TG が消えるんですよね。

アクティブTGがblue/green以外だとデプロイが失敗する

ListenerRule 上で weight > 0 の TG が blue でも green でもない場合、そもそもデプロイ自体が通りません。

canary-tg に weight=10 が付いている状態でデプロイ開始
  ↓
ECS: 「ListenerRule上のアクティブTGが想定外」
  ↓
deployment failed: Service deployment rolled back because of
invalid networking configuration.

ECS はデプロイ開始時に ListenerRule の状態を検証していて、「blue=100/green=0」か「blue=0/green=100」のどちらか(トラフィックを受けるTGがちょうど1つ)でないと受け付けません。

60/40 のような中間状態も NG です。

Terraformで直しても次のデプロイで再上書きされる

「じゃあ Terraform で3 TG 構成に戻せばいいのでは」と思うかもしれませんが、次の ECS デプロイが走った瞬間にまた2 TG に上書きされます。

時系列:
  t1: terraform apply → ListenerRule = [blue, green, canary] ← 3TG構成
  t2: ECS deploy      → ListenerRule = [blue, green]         ← 2TGに上書き
  t3: terraform plan   → "canary-tg が消えてる" という drift を検出
  t4: terraform apply → ListenerRule = [blue, green, canary] ← また3TG
  t5: ECS deploy      → ListenerRule = [blue, green]         ← また上書き
  ... 無限ループ

Terraform と ECS が ListenerRule の管理権を奪い合う形になります。ignore_changes で逃げても本質的な解決にはなりません。

ECSネイティブB/Gでカナリアリリースを実現する回避策

同一 ListenerRule 上での共存は設計として成立しないので、別のアプローチが必要です。

  • ECS の deploymentConfiguration.strategyBLUE_GREEN から LINEAR または CANARY に変更する(5%ずつ、10分間隔など)。strategy の値は ROLLING / BLUE_GREEN / LINEAR / CANARY のどれか1つを選ぶ排他的な指定。LINEAR/CANARY は 2025年10月30日 GA(B/G の 2025年7月17日 GAとは別)。なお LINEAR/CANARY でも ListenerRule は ECS 管理下に入る点は同じなので、3TG 同居の問題を回避する形であって、ListenerRule を自由に使えるようになるわけではない
  • ListenerRule を分ける。カナリア用の TG は別の ListenerRule(別のパスやヘッダー条件)に紐づけて、ECS B/G が管理する Rule とは完全に分離する
  • ALB の前段に CloudFront や Route 53 の加重ルーティングを置いて、ALB レベルではなく DNS/CDN レベルで振り分ける

注意点

ECSネイティブB/Gを採用する場合、ListenerRule は ECS に「明け渡す」ものだと理解しておく必要があります。advanced_configuration に渡した ListenerRule は ECS の管理下に入るので、同じ Rule を Terraform や他のツールから触ろうとすると必ずコンフリクトします。

CrowdStrikeのタスク定義パッチングでも似たような話がありましたが、「誰がそのリソースのオーナーなのか」をはっきりさせておかないと、Terraform drift の無限ループにハマります。

まとめ

  • ECSネイティブB/G は ListenerRule の forward config を排他的に管理する
  • blue と green の2 TG しか存在できない。3つ目は消される、デプロイが拒否される、Terraform で直しても再上書きされる
  • 加重カナリアをやるなら、ListenerRule を分けるか、レイヤーを変える(DNS/CDN)
  • ListenerRule のオーナーシップを ECS に渡す前提で設計するのが正解

参考