2
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?

ECS Service ConnectでもBlue/Greenができるようになったので検証してみた

Last updated at Posted at 2025-08-16

はじめに

ECS Service Connectとは

AWS Cloud Mapが提供する名前空間を使用して論理名でECSのサービス(を構成するECSタスク)間を接続することができる機能です。

Service Connectを設定したサービスを作成、または更新するとECSタスクにService Connectプロキシがsidecarとしてデプロイされます。

Service Connectプロキシはアプリケーションからの論理名による他のサービスへの接続を仲介し、サービスを構成するタスク群に対して負荷分散を行います。論理名とタスクのIPアドレスとの紐づけはAWS Cloud Mapで管理されます。

image.png

ECS Service ConnectのBlue/Green

ECSではかねてからCodeDeployでBlue/Greenデプロイを行うことができました。しかし、Service Connectには対応しておらず採用の障壁となっていました。そんな中、2025/7にECSネイティブなBlue/Green機能がリリースされ、Service ConnectでもBlue/Greenデプロイが可能になりました。

ECSの従来のBlue/GreenデプロイはELBの機能を利用したもの(Listenerを二つ用意し、Weightの割合を変化させる)でした。それに対して、Service ConnectのBlue/Greenデプロイがどのように機能するのかイメージできなかったので検証してみました。

検証

1. 初期状態

まず、検証した構成について説明します。ECSクラスタで2つのサービスが実行されています。

scbg.drawio.png

nginx-server

1つ目のサービスはnginx-serverです。このサービスはService Connectの「クライアントとサーバー」として設定しています。Service Connectのクライアントからhttp://my-app にアクセスするとProxy経由でBlueのタスクに接続されます。

サービスのService Connectの設定
スクリーンショット 2025-08-16 120650.jpg

Blueのタスクは"Blue"という文字列をHTTPレスポンスとして返します。

HTTPレスポンス

<html><body><h1 style=\"color: blue; text-align: center; font-size: 48px;\">Blue</h1></body></html>

Cloud Mapを見てみるとサービス属性が2つ設定されています。AWS_ECS_ROUTE_PRODAWS_ECS_ROUTE_TESTです。前者はヘッダなしで接続する場合、後者はx-canary-test: beta-versionというヘッダを付与して接続する場合のルールです(ヘッダのkey/valueは任意で設定可能です)。現在は両方Blueのタスクに紐づいています。

image.png

nginx-client

2つ目のサービスはnginx-clientです。このサービスはService Connectの「クライアント」として設定しています。

http://my-app に対して5秒おきにヘッダなし/あり両方のパターンで接続するように設定しています。下図はサービスのログです。ヘッダなしの接続が本番トラフィック、ヘッダありの接続がテストトラフィックです。現在は両方Blueのタスクに接続しています。
image.png

hook-function

検証構成の右上にあるのはECSサービスデプロイのLifeCyclehook用のLambda関数です。ECSのBlue/Greenデプロイの各段階で呼び出すことが可能で、レスポンスによってデプロイの成否を制御することができます。現在は常に成功hookStatus: SUCCEDEDを返すよう設定しています。

import json
import boto3

def handler(event, context):
    print(f"{json.dumps(event)}")
    print(event['lifecycleStage'])

    return {
        'hookStatus': 'SUCCEEDED'
        #'hookStatus': 'FAILED'
    }

2. サービスの更新

ではBlue/Greenの挙動を見ていきましょう。タスク定義を新たに作成し、BlueではなくGreenという文字列をレスポンスとして返すよう設定します。そのタスク定義を用いてサービス: nginx-serverを更新します。

scbg2.drawio.png

2-1. Hook: PRE_SCALE_UP (18:25:03)

まず起こるのはLambdaの実行です。新しいタスクがデプロイされる前に実行されます。LifeCycleのステージはPRE_SCALE_UPです。

Lambda Log:

YYYY-MM-DDT09:25:03.383Z
{"executionDetails": {"testTrafficWeights": {}, "productionTrafficWeights": {}, "serviceArn": "arn:aws:ecs:us-west-2:123456789012:service/blue-green-cluster/nginx-server", "targetServiceRevisionArn": "arn:aws:ecs:us-west-2:123456789012:service-revision/blue-green-cluster/nginx-server/1573819219887221329"}, "executionId": "52761202-4fbb-4468-b574-aa4591649806", "lifecycleStage": "PRE_SCALE_UP", "resourceArn": "arn:aws:ecs:us-west-2:123456789012:service-deployment/blue-green-cluster/nginx-server/p2DSH1QN1B1P5c7hsazwf"}
YYYY-MM-DDT09:25:03.383Z
PRE_SCALE_UP

2-2. Greenタスク起動(18:26:24)

そして次に新しいタスク定義に基づくGreenのタスクが起動します。

ECS Event Log:

YYYY年MM月DD日 18:25 (UTC+9:00)
service nginx-server has started 1 tasks: task a3e692948db94f7e9eca4832ad4002a2

nginx-server Log:

YYYY年MM月DD日 18:26
2025/08/06 09:26:24 [notice] 1#1: start worker process 7
2025/08/06 09:26:24 [notice] 1#1: start worker process 8  
2025/08/06 09:26:24 [notice] 1#1: using the "epoll" event method

2-3. Hook: POST_SCALE_UP (18:27:09)

Greenタスクの起動が完了すると再度Lambdaが実行されます。LifeCycleのステージはPOST_SCALE_UPです。

Lambda Log:

YYYY-MM-DDT09:27:09.129Z
{"executionDetails": {"testTrafficWeights": {}, "productionTrafficWeights": {}, "serviceArn": "arn:aws:ecs:us-west-2:123456789012:service/blue-green-cluster/nginx-server", "targetServiceRevisionArn": "arn:aws:ecs:us-west-2:123456789012:service-revision/blue-green-cluster/nginx-server/1573819219887221329"}, "executionId": "2bbdca3f-078f-49c8-b4bb-2fd0a82a7649", "lifecycleStage": "POST_SCALE_UP", "resourceArn": "arn:aws:ecs:us-west-2:123456789012:service-deployment/blue-green-cluster/nginx-server/p2DSH1QN1B1P5c7hsazwf"}
YYYY-MM-DDT09:27:09.129Z
POST_SCALE_UP

3. テストトラフィックのルーティング

次の段階ではテストトラフィックのルーティングが変更されます。

scbg3.drawio.png

3-1. サービス属性の更新(18:27:25)

まずCloud Mapのサービス属性AWS_ECS_ROUTE_TESTの値が変更されます。タスクの紐づきがBlueからGreenに変更されました。

Service Discovery Log:

{
 "eventTime": "YYYY-MM-DDT09:27:25Z",
  "eventSource": "servicediscovery.amazonaws.com",
  "eventName": "UpdateServiceAttributes",
  "requestParameters": {
    "serviceId": "srv-bhdknysg5s3cyjop",
    "attributes": {
      "AWS_ECS_ROUTE_TEST": "{\"name\":\"test\",\"type\":\"HTTP\",\"priority\":0,\"match\":{\"headers\":[{\"name\":\"x-canary-test\",\"value\":{\"exact\":\"beta-version\"}}]},\"weightedTargets\":[{\"weight\":100,\"target\":[\"arn:aws:ecs:us-west-2:123456789012:service-revision/blue-green-cluster/nginx-server/1573819219887221329\"]}]}"
    }
  }
}

3-2. Hook: TEST_TRAFFIC_SHIFT(18:27:25)

サービス属性の変更とほぼ時を同じくしてLambdaが実行されます。LifeCycleのステージはTEST_TRAFFIC_SHIFTです。

Lambda Log:

YYYY-MM-DDT09:27:25.050Z
{"executionDetails": {"testTrafficWeights": {"arn:aws:ecs:us-west-2:123456789012:service-revision/blue-green-cluster/nginx-server/5592795917093244871": 0, "arn:aws:ecs:us-west-2:123456789012:service-revision/blue-green-cluster/nginx-server/1573819219887221329": 100}, "productionTrafficWeights": {}, "serviceArn": "arn:aws:ecs:us-west-2:123456789012:service/blue-green-cluster/nginx-server", "targetServiceRevisionArn": "arn:aws:ecs:us-west-2:123456789012:service-revision/blue-green-cluster/nginx-server/1573819219887221329"}, "executionId": "d96e0dc6-26e9-4c41-a401-225cc072dbea", "lifecycleStage": "TEST_TRAFFIC_SHIFT", "resourceArn": "arn:aws:ecs:us-west-2:123456789012:service-deployment/blue-green-cluster/nginx-server/p2DSH1QN1B1P5c7hsazwf"}
YYYY-MM-DDT09:27:25.050Z
TEST_TRAFFIC_SHIFT

3-3. テストトラフィックのルーティング変更(18:27:33)

サービス属性の更新から8秒後にnginx-clientのテストトラフィックがGreenにフォワードされはじめました。

nginx-client Log:

YYYY-MM-DD 09:27:28
本番トラフィック: Blue
テストトラフィック: Blue

YYYY-MM-DD 09:27:33
本番トラフィック: Blue
テストトラフィック: Green

nginx-server Log:

YYYY年MM月DD日 18:27
127.0.0.1 - - [06/Aug/2025:09:27:33 +0000] "GET / HTTP/1.1" 200 100 "-" "curl/8.3.0" "-"

8秒ほどのタイムラグはいつもあるものなのかな?と、別途nginx-clientのタスクを10個に増やし、1s間隔でルーティングの変化を確認する検証をしてみました。

結果、すべてのタスクでタイムラグが確認できました。また、ルーティングが変更されるまでの時間はタスクによって最大14sの差がありました。

各々のタスク(のService Connect Proxy)がCloud Mapと非同期的に設定を同期していそうです。

表. サービス属性が更新されてからルーティングが変更されるまでの時間

タスクNo. #01 #02 #03 #04 #05 #06 #07 #08 #09 #10
時間 08s 12s 13s 16s 17s 04s 03s 11s 13s 15s

4. テストトラフィックの移行完了

サービス属性の更新からおよそ40秒経った頃にLambdaが実行されました。LifeCycleのステージはPOST_TEST_TRAFFIC_SHIFTで、テストトラフィックが完全に移行したことを示しています。

scbg3-2.drawio.png

4-1. Hook: POST_TEST_TRAFFIC_SHIFT(18:28:11)

Lambda Log:

YYYY-MM-DDT09:28:11.220Z
{"executionDetails": {"testTrafficWeights": {}, "productionTrafficWeights": {}, "serviceArn": "arn:aws:ecs:us-west-2:123456789012:service/blue-green-cluster/nginx-server", "targetServiceRevisionArn": "arn:aws:ecs:us-west-2:123456789012:service-revision/blue-green-cluster/nginx-server/1573819219887221329"}, "executionId": "ad6df153-cfe7-4442-9574-ffdfe06454a7", "lifecycleStage": "POST_TEST_TRAFFIC_SHIFT", "resourceArn": "arn:aws:ecs:us-west-2:123456789012:service-deployment/blue-green-cluster/nginx-server/p2DSH1QN1B1P5c7hsazwf"}
YYYY-MM-DDT09:28:11.220Z
POST_TEST_TRAFFIC_SHIFT

5. 本番トラフィックのルーティング

本番トラフィックのルーティングは先の「3.テストトラフィックのルーティング」と同じ形で進みます。

scbg4.drawio.png

5-1. サービス属性の更新(18:28:26)

CloudMapのAWS_ECS_ROUTE_PRODのサービス属性が更新され、紐づくタスクがBlueからGreenに変わります。

Service Discovery Log:

{
  "eventTime": "YYYY-MM-DDT09:28:26Z",
  "eventSource": "servicediscovery.amazonaws.com",
  "eventName": "UpdateServiceAttributes", 
  "requestParameters": {
    "serviceId": "srv-bhdknysg5s3cyjop",
    "attributes": {
      "AWS_ECS_ROUTE_PROD": "{\"name\":\"prod\",\"type\":\"HTTP\",\"priority\":1,\"weightedTargets\":[{\"weight\":100,\"target\":[\"arn:aws:ecs:us-west-2:123456789012:service-revision/blue-green-cluster/nginx-server/1573819219887221329\"]}]}"
    }
  }
}

5-2. Hook: PRODUCTION_TRAFFIC_SHIFT(18:28:27)

サービス属性の変更とほぼ時を同じくしてLambdaが実行されます。LifeCycleのステージはPRODUCTION_TRAFFIC_SHIFTです。

Lambda Log:

YYYY-MM-DDT09:28:27.002Z
{"executionDetails": {"testTrafficWeights": {}, "productionTrafficWeights": {"arn:aws:ecs:us-west-2:123456789012:service-revision/blue-green-cluster/nginx-server/5592795917093244871": 0, "arn:aws:ecs:us-west-2:123456789012:service-revision/blue-green-cluster/nginx-server/1573819219887221329": 100}, "serviceArn": "arn:aws:ecs:us-west-2:123456789012:service/blue-green-cluster/nginx-server", "targetServiceRevisionArn": "arn:aws:ecs:us-west-2:123456789012:service-revision/blue-green-cluster/nginx-server/1573819219887221329"}, "executionId": "abc779f1-7462-4727-beba-ebfeec44e41f", "lifecycleStage": "PRODUCTION_TRAFFIC_SHIFT", "resourceArn": "arn:aws:ecs:us-west-2:123456789012:service-deployment/blue-green-cluster/nginx-server/p2DSH1QN1B1P5c7hsazwf"}
YYYY-MM-DDT09:28:27.002Z
PRODUCTION_TRAFFIC_SHIFT

5-3. 本番トラフィックのルーティング変更(18:28:38)

サービス属性の変更から11秒して、nginx-clientの本番トラフィックの接続先がGreenのタスクに変わりました。

nginx-client Log:

## Client Log
YYYY-MM-DD 09:28:38
本番トラフィック: Green
テストトラフィック: Green

YYYY-MM-DD 09:28:33
本番トラフィック: Blue
テストトラフィック: Green

6. ベイク期間

ECSの本番トラフィックがすべて切り替わると、ベイク期間に入る。ベイク期間はBlueとGreenの環境が並行して存在する期間である。ベイク期間の長さは任意に設定することができ、ここでは3分に設定しています。

scbg4-2.drawio.png

6-1. Hook: POST_PRODUCTION_TRAFFIC_SHIFT(18:29:13)

ECSの本番トラフィックがすべて切り替わると、POST_PRODUCTION_TRAFFIC_SHIFTのライフサイクルステージがキックされLambdaが起動する。

Lambda Log:

YYYY-MM-DDT09:29:13.527Z
{
  "executionId": "603a3cb8-7203-4291-a853-d8997818e0e9",
  "lifecycleStage": "POST_PRODUCTION_TRAFFIC_SHIFT"
}

7. Blue環境の削除

最終段階としてBlueのタスクが削除されます。
scbg5.drawio.png

7-1. インスタンスの登録解除(18:32:31)

Blue環境のタスクがService Discoveryから登録解除され、新しいリクエストが送信されないようになります。この操作により、Blue環境は完全にトラフィックから切り離されます。

Service Discovery Log:

{
    "eventTime": "YYYY-MM-DDT09:32:31Z",
    "eventSource": "servicediscovery.amazonaws.com",
    "eventName": "DeregisterInstance",
    "awsRegion": "us-west-2",
    "sourceIPAddress": "ecs.amazonaws.com",
    "userAgent": "ecs.amazonaws.com",
    "requestParameters": {
        "serviceId": "srv-bhdknysg5s3cyjop",
        "instanceId": "f6d38933c38847b79a670d71c25d75a8"
    },
}

7-2. Blueタスク停止(18:32:55)

最後にBlueのタスクが停止して完了です。

ECS Event Log:

YYYY年MM月DD日 18:32 (UTC+9:00)
service nginx-server has stopped 1 running tasks: task f6d38933c38847b79a670d71c25d75a8

YYYY年MM月DD日 18:34 (UTC+9:00)
service nginx-server deployment ecs-svc/1573819219887221329 deployment completed.
service nginx-server has reached a steady state.

nginx-server Log:

YYYY年MM月DD日 18:32
2025/08/06 09:32:55 [notice] 9#9: exit

検証(ロールバック発生時)

せっかくなのでロールバックの時の挙動も確認しようと思います。先の図の6-1. POST_PRODUCTION_TRAFFIC_SHIFTで失敗を意味するhookStatus: FAILEDがLambdaから返ってきたとします。

scbg6.drawio.png

7f. 本番トラフィックのロールバック

では説明していきます。hookStatus: FAILEDが返却されるとECSはロールバックを開始します。

scbg7f.drawio.png

ロールバックが開始されたことはマネコンから確認可能です。

スクリーンショット 2025-08-08 002453.jpg

7f-1. サービス属性の変更(00:18:44)

まずCloudMapのAWS_ECS_ROUTE_PRODのサービス属性が更新され、紐づくタスクがGreenからBlueに戻ります。

Service Discovery Log:

{
"eventTime": "YYYY-MM-DDT15:18:44Z",
"eventSource": "servicediscovery.amazonaws.com",
"eventName": "UpdateServiceAttributes",
"awsRegion": "us-west-2",
"sourceIPAddress": "ecs.amazonaws.com",
"userAgent": "ecs.amazonaws.com",
"requestParameters": {
    "serviceId": "srv-bhdknysg5s3cyjop",
    "attributes": {
        "AWS_ECS_ROUTE_PROD": "{\"name\":\"prod\",\"type\":\"HTTP\",\"priority\":1,\"weightedTargets\":[{\"weight\":100,\"target\":[\"arn:aws:ecs:us-west-2:551312704990:service-revision/blue-green-cluster/nginx-server/7308461668976403387\"]}]}"
    }
}

7f-2. hook: PRODUCTION_TRAFFIC_SHIFT(00:18:44)

サービス属性の変更とほぼ時を同じくしてLambdaが実行されます。LifeCycleのステージはPRODUCTION_TRAFFIC_SHIFTです。

Lambda Log:

YYYY-MM-DDT15:18:44.160Z
{"executionDetails": {"testTrafficWeights": {}, "productionTrafficWeights": {"arn:aws:ecs:us-west-2:551312704990:service-revision/blue-green-cluster/nginx-server/9126904758940771806": 0, "arn:aws:ecs:us-west-2:551312704990:service-revision/blue-green-cluster/nginx-server/7308461668976403387": 100}, "serviceArn": "arn:aws:ecs:us-west-2:551312704990:service/blue-green-cluster/nginx-server", "targetServiceRevisionArn": "arn:aws:ecs:us-west-2:551312704990:service-revision/blue-green-cluster/nginx-server/9126904758940771806"}, "executionId": "7f3302f7-250d-4e95-be64-d76ce2720b3d", "lifecycleStage": "PRODUCTION_TRAFFIC_SHIFT", "resourceArn": "arn:aws:ecs:us-west-2:551312704990:service-deployment/blue-green-cluster/nginx-server/JnAjY1hkpfbWXUdPp-DK5"}
YYYY-MM-DDT15:18:44.160Z
PRODUCTION_TRAFFIC_SHIFT

7f-3. 本番トラフィックのルーティング変更(00:18:57)

サービス属性の変更から13秒して、nginx-clientの本番トラフィックの接続先が再びBluenのタスクに変わりました。

nginx-client Log:

## Client Log
YYYY-MM-DD 15:18:57
本番トラフィック: Blue
テストトラフィック: Green

YYYY-MM-DD 15:18:52
本番トラフィック: Green
テストトラフィック: Green

8f. テストトラフィックのロールバック

scbg8f.drawio.png

8f-1. サービス属性の変更(00:19:30)

CloudMapのAWS_ECS_ROUTE_TESTのサービス属性が更新され、紐づくタスクがGreenからBlueに戻ります。

Service Discovery Log:

{
"eventTime": "YYYY-MM-DDT15:19:30Z",
"eventSource": "servicediscovery.amazonaws.com",
"eventName": "UpdateServiceAttributes",
"awsRegion": "us-west-2",
"sourceIPAddress": "ecs.amazonaws.com",
"userAgent": "ecs.amazonaws.com",
"requestParameters": {
    "serviceId": "srv-bhdknysg5s3cyjop",
    "attributes": {
        "AWS_ECS_ROUTE_TEST": "{\"name\":\"test\",\"type\":\"HTTP\",\"priority\":0,\"match\":{\"headers\":[{\"name\":\"x-canary-test\",\"value\":{\"exact\":\"beta-version\"}}]},\"weightedTargets\":[{\"weight\":100,\"target\":[\"arn:aws:ecs:us-west-2:123456789012:service-revision/blue-green-cluster/nginx-server/7308461668976403387\"]}]}"
    }
},

8f-2. hook: TEST_TRAFFIC_SHIFT(00:19:30)

サービス属性の変更とほぼ時を同じくしてLambdaが実行されます。LifeCycleのステージはTEST_TRAFFIC_SHIFTです。

Lambda Log:

YYYY-MM-DDT15:19:30.575Z
{"executionDetails": {"testTrafficWeights": {"arn:aws:ecs:us-west-2:123456789012:service-revision/blue-green-cluster/nginx-server/9126904758940771806": 0, "arn:aws:ecs:us-west-2:123456789012:service-revision/blue-green-cluster/nginx-server/7308461668976403387": 100}, "productionTrafficWeights": {}, "serviceArn": "arn:aws:ecs:us-west-2:123456789012:service/blue-green-cluster/nginx-server", "targetServiceRevisionArn": "arn:aws:ecs:us-west-2:123456789012:service-revision/blue-green-cluster/nginx-server/9126904758940771806"}, "executionId": "f79df137-6f4a-4095-a965-245f03b3bf3a", "lifecycleStage": "TEST_TRAFFIC_SHIFT", "resourceArn": "arn:aws:ecs:us-west-2:123456789012:service-deployment/blue-green-cluster/nginx-server/JnAjY1hkpfbWXUdPp-DK5"}

YYYY-MM-DDT15:19:30.575Z
TEST_TRAFFIC_SHIFT

8f-3. テストトラフィックのルーティング変更(00:19:57)

サービス属性の変更から30秒ほどして、nginx-clientの本番トラフィックの接続先が再びBlueのタスクに変わりました。

nginx-client Log:

## Client Log
YYYY-MM-DD 15:19:57
本番トラフィック: Blue
テストトラフィック: Blue

YYYY-MM-DD 15:18:57
本番トラフィック: Blue
テストトラフィック: Green

8f-4. Greenタスクの削除(00:22)

最後にGreenタスクが削除され元の状態に戻ります

ECS Event log:

YYYY年MM月DD日 00:22 (UTC+9:00)
service nginx-server has reached a steady state.
YYYY年MM月DD日 00:22 (UTC+9:00)
service nginx-server deployment ecs-svc/7308461668976403387 deployment completed.
YYYY年MM月DD日 00:20 (UTC+9:00)
service nginx-server has stopped 1 running tasks: task 2b6d3ee95cce45f5aea1769be8610036.

まとめ、所感など

  • Cloud Mapの設定が変更されてから、実際にService Connectプロキシが新しいタスクにトラフィックをルーティングするまでにタイムラグがありました。タイムラグの大きさはタスクによって異なりました
    • 各々のタスク(Service Connect Proxy)が非同期にCloud Mapの設定を見に行って自身の設定を更新しているように思いました
      • 分散アーキテクチャの設計として納得感がありつつ、その場合、本番トラフィックは一気にではなく徐々に切り替わることになりますね
      • ロールバック発生時も本番トラフィックが元に戻るまでタイムラグがありました。障害を検知したら可能な限り速やかにロールバックしたい場合にはリスナールールの変更だけで済むALBを利用したほうが良さそうです
  • ロールバック発生時にもライフサイクルフックが呼び出されますが、仮にそれが失敗した場合にどうなるのかは改めて調べてみたいです

おわりに

本記事がどなたかのお役に立てば幸いです!

2
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
2
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?