コンテナワークロードでは CodeDeploy などで継続的なデリバリを行い、Auto Scaling を利用して負荷に応じて、動的にコンテナを増減させて運用されているのではないかと思います。特に ECS では CodeDeploy を利用して、Blue/Green デプロイメントを行うことができます。また、Auto Scaling ではスケーリングを判断する指標の1つとして Application Load Balancer(ALB)ターゲットグループ内のターゲットごとに完了したリクエストの数(ALBRequestCountPerTarget)を利用することができます。が、実は現時点ではそれらを一緒に使うと、そのままでは期待通りに動作してくれません。それぞれをうまく利用するために検討する機会がありましたので、考え方のベースとして記録に残しておきたいと思います。

本記事の内容をそのまま本番環境に適用せず、十二分に検証を行った上でご判断ください。

2024/03/20 更新

2023年3月に「Application Auto Scaling がターゲット追跡ポリシーに対する Metric Math に対応」というアップデートがありました。これにより、本記事のようにオートスケーリングポリシーの付け替えを行わずに (CodeDeploy のライフサイクルイベントフックを使わずに) 実現する案も可能になりました。 詳しくは以下の記事を参照ください。

[ECS Blue/Green デプロイでもリクエストカウント追跡のオートスケーリングを利用したい (Metric Math 編)]
https://blog.msysh.me/posts/2024/03/ecs-blue-green-deploy-with-request-count-per-target-tracking-autoscaling-with-metric-math.html

はじめに

冒頭のとおり、CodeDeploy による Blue/Green デプロイメントと、ECS の Auto Scaling においてスケーリングポリシーに「ターゲットの追跡」で ALB のターゲットごとのリクエストカウントをメトリクスとして利用すると、そのままでは期待した動作となりません。まずはその辺りの理由について書いておきたいと思います。

CodeDeploy による ECS Blue/Green の動作

CodeDeploy による ECS の Blue/Green はどのように実現されているか、簡単にいうと ALB に関連付けられる Target Group を切り替えることで実現されています。マネジメントコンソールなどから設定した場合、Target Group に -1-2 がついているかと思います。

CodeDeploy による Blue/Green デプロイメント

ターゲット追跡ポリシーでの ALBRequestCountPerTarget

一方、Auto Scaling の方を見てみます。ECS で利用できる Auto Scaling ポリシーのうち「ターゲット追跡スケーリングポリシー」があり、利用できるメトリクスの一つに ALBRequestCountPerTarget があります。ALBRequestCountPerTarget はざっくりいうとターゲット毎(タスク毎)のリクエスト数が指定した数になるようにタスク数を増減させていく動作となります(公式ドキュメント)。ALBRequestCountPerTarget はリクエストが増えてもコンテナの CPU やメモリの増加があまり変化がなく、ECSServiceAverageCPUUtilizationECSServiceAverageMemoryUtilization といったメトリクスではうまくスケールさせられない場合に利用できる選択肢の1つかと思います。

ALBRequestCountPerTarget による Auto Scaling

ECS Blue/Green と ALBRequestCountPerTarget を一緒に使うと期待通りにならない

CodeDeploy の Blue/Green の動作と、ALBRequestCountPerTarget による Auto Scaling の図を見てもらえればなんとなく想像いただけそうですが、Blue/Green により ALB に関連付けられる Target Group が変わってしまいながらも、Target Group に設定された Auto Scaling の設定までは付け変わってくれないため、実質的には Green 環境に変わったことで Auto Scaling の設定が外れてしまうような動作になってしまいます。

Blue/Green デプロイと ALBRequestCountPerTaret

Green 環境への切り替え後、マネジメントコンソールから「サービス」-「Auto Scaling」タブを見るとポリシーは残っているのように見えますが、後述する CLI で確認すると Blue 環境側の Target Group と関連付けられていることがわかります。

Blue/Green デプロイ後の Auto Scaing 設定

CLI で確認すると Auto Scaling の設定は Blue 側の Target Group(仮に Blue 側が Target Group 1、Green 側が Target Group 2 だったとします)に関連付けられたままになっていることがわかります。以下の出力例では説得力ないですが、CloudWatch Alarm からも辿ってみるとわかるのではないかと思います。

aws application-autoscaling describe-scaling-policies --service-namespace ecs --policy-names <Policy_Name>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
  "ScalingPolicies": [
    {
      //    :
      //  (略)
      //    :
      "TargetTrackingScalingPolicyConfiguration": {
          "TargetValue": 10.0,
          "PredefinedMetricSpecification": {
              "PredefinedMetricType": "ALBRequestCountPerTarget",
              "ResourceLabel": "app/<Alb_Name>/<Alb_Id>/targetgroup/<Target_Group_1_Name>/<Target_Group_1_Id>"
          },
          //    :
          //  (略)
          //    :
      },
      "Alarms": [
        {
            "AlarmName": "TargetTracking-service/<Cluster_Name>/<Service_Name>-AlarmHigh-XXXXXXXXXX",
            "AlarmARN": "arn:aws:cloudwatch:<Region>:<AWS_Account>:alarm:TargetTracking-service/<Cluster_Name>/<Service_Name>-AlarmHigh-XXXXXXXXXX"
        },
        {
            "AlarmName": "TargetTracking-service/<Cluster_Name>/<Service_Name>-AlarmLow-YYYYYYYYYY",
            "AlarmARN": "arn:aws:cloudwatch:<Region>:<AWS_Account>:alarm:TargetTracking-service/<Cluster_Name>/<Service_Name>-AlarmLow-YYYYYYYYYY"
        }
      ],
      "CreationTime": "2021-08-22T22:23:19.715000+09:00"
    }
  ]
}

Target Group 2 の方にも Auto Scaling の設定を…

じゃ Target Group 2 の方にも Auto Scaling の設定を入れれば… と思ってしまいますが、Auto Scaling が ECS サービスに関連付けられているからでしょうか、すでに存在しているとのことで設定できません。 Scaling ポリシーの追加

検討方針

ではどうしようか、となりますが、今回はデプロイが完了したタイミングで、Auto Scaling の設定を新しい Target Group 向けに設定し直す方針で検討してみました。幸い、CodeDeploy の「ライフサイクルイベントフック」で ECS デプロイにおける各イベントのタイミングで Lambda を実行することができますのでこちらを利用します。
ライフサイクルイベントフックについての公式ドキュメントは以下です。

CodeDeploy のライフサイクルイベントフックとは(ECS デプロイの場合)

ECS デプロイのライフサイクルイベントフックですが、詳細はドキュメントを見てもらえればと思いますので、ここでは各タイミングについて簡単に。

  • BeforeInstall
    • Green 環境のタスクセットが作成される前、Blue 環境しかない状態です。この時点ではロールバックはできません。
  • AfterInstall
    • Green 環境のタスクセットが作成され、Target Group に関連付けられた後のタイミングです。
      これ以降で、フックする Lambda の結果からロールバックさせることができます。
  • AfterAllowTestTraffic
    • Green 環境のタスクセットにテスト用トラフィックが流せるようになった後のタイミングです。
  • BeforeAllowTraffic
    • Green 環境のタスクセットに本番用トラフィックを流す前のタイミングです。
  • AfterAllowTraffic
    • Green 環境のタスクセットに本番用トラフィックを流した後のタイミングです。

表にしてみました(テストトラフィックはテストリスナーが設定されている場合に有効です)。

タイミング 本番トラフィックは? テストトラフィックは? ロールバック
BeforeInstall Blue 環境へ まだ流せない
(Green 環境がまだない)
不可
AfterInstall Blue 環境へ まだ流れない
(Green 環境はできた)
可能
AfterAllowTestTraffic Blue 環境へ Green 環境へ 可能
BeforeAllowTraffic Blue 環境へ Green 環境へ 可能
AfterAllowTraffic Green 環境へ もう流せない 可能

補足

ちなみに ALBRequestCountPerTarget ではなく、ECSServiceAverageCPUUtilizationECSServiceAverageMemoryUtilization をターゲット追跡のメトリクスに利用する場合は、ECS サービス全体でのメトリクスに基づいて判断してくれますので、今回のような「Auto Scaling ポリシーを付け替える」といった事をする必要はありません。

検討に際しての具体的なポイントなど

具体的に検討した上でのポイントなどを。

1. Blue/Green デプロイにおけるそもそもの考慮事項

公式ドキュメントにも記載されている通り、そもそも Blue/Green と Auto Scaling を併用するとデプロイに失敗する可能性があります。

  • サービス Auto Scaling と blue/green デプロイタイプを使用するように設定されたサービスでは、Auto Scaling はデプロイ中にブロックされませんが、状況によってはデプロイが失敗する場合があります。以下では、この動作について詳しく説明します。
    • サービスがスケーリング中の状態でデプロイが開始されると、グリーンタスクセットが作成され、CodeDeploy は Green タスクセットが定常状態になるまで最大 1 時間待機し、完了するまでトラフィックをシフトしません。
    • サービスが Blue/Green デプロイのプロセス中で、スケーリングイベントが発生した場合、トラフィックは 5 分間シフトし続けます。サービスが 5 分以内に定常状態にならない場合、CodeDeploy はデプロイを停止し、失敗としてマークします。

デプロイ失敗のリスク低減のため、デプロイ中は Auto Scaling を止めることにしようと思います。

2. ECS の Auto Scaling を操作する API は?

マネジメントコンソールからは「サービスの更新」により Auto Scaling を再設定することができるので、ECS の UpdateService APIとか使えばいけそうな雰囲気がありますが、ECS サービスの Auto Scaling は設定変更できません。

先ほども少し触れましたが、ECS サービスの Auto Scaling 設定は Application Auto Scaling を利用します。Application Auto Scaling はマネジメントコンソールからは利用できず、CLI、SDK などから操作できます。

例えば CLI などから以下のように実行すると ECS に設定されている Auto Scaling 設定が見れます。

$ aws application-autoscaling describe-scalable-targets --service-namespace ecs

また再掲になりますが以下の CLI で、設定されている Auto Scaling ポリシーが見れます。

$ aws application-autoscaling describe-scaling-policies --service-namespace ecs --policy-names <Policy_Name>

実際の Auto Scaling ポリシーの操作は CodeDeploy のライフサイクルイベントで

  • デプロイ前に DeleteScalingPolicy API で Auto Scaling ポリシー を削除し
  • デプロイ完了後に PutScalingPolicy API で新しい Auto Scaling ポリシー を入れ直す

ということをしてやろうと思います。

3. デプロイ失敗(ロールバック)時の対応

CodeDeploy のライフサイクルイベントで Auto Scaling ポリシーを削除/再設定してあげるわけですが、デプロイの途中でアプリの不具合などでロールバックする可能性も考えられます。デプロイのロールバックや、キャンセル(即時停止)をした場合は、以降のライフサイクルイベントがスキップされフック関数(Lambda)も呼び出されなくなりますので、Auto Scaling ポリシーが削除されたままになってしまうことも想定されます。

そのため、CodePipeline からの通知の仕組みを使って、デプロイの失敗時には SNS に通知し、Lambda を使って Auto Scaling ポリシーを入れ直す、ということをしてみたいと思います。ただし、ロールバックされることを前提としており、即時停止のケースは今回は検討の対象外にしました。

まとめると…

  • BeforeInstall のタイミングで Application Auto Scaling の DeleteScalingPolicy を利用して Auto Scaling ポリシー を削除。
  • AfterAllowTraffic のタイミングで Application Auto Scaling の PutScalingPolicy を利用して ALB に関連付けられた新しい Target Group で Auto Scaling ポリシーを設定。
  • ロールバックした場合は、通知を使って Auto Scaling ポリシーが外れたままにならないよう再設定する。

図にすると以下のような感じです。

BeforeInstall 時に Auto Scaling ポリシーを削除
BeforeInstall 時に Auto Scaling ポリシーを削除

AfterAllowTraffic 時に Auto Scaling ポリシーを設定
AfterAllowTraffic 時に Auto Scaling ポリシーを設定

ロールバックした場合は、通知を使って Auto Scaling ポリシーを再設定
デプロイ失敗時は Auto Scaling ポリシーを再設定

サンプルコード

せっかくなので試した Lambda のコードもサンプルとして紹介します。見易さ重視でエラー処理など省略しているとこもあるので参考程度にとどめてくださいませ。

appspec.yaml での Hooks セクション

CodeDeploy の appspec.yaml では Hooks セクションに以下のように設定します。ハイライト部のように各イベントで Lambda 関数名を指定します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: <TASK_DEFINITION>
        LoadBalancerInfo:
          ContainerName: "app"
          ContainerPort: 8080
Hooks:
  - BeforeInstall: BeforeInstall-Lambda
  - AfterAllowTraffic: AfterAllowTraffic-Lambda

BeforeInstall に設定する Lambda

Auto Scaling ポリシーを削除してやります。CodeDeploy 向けのお作法(codedeploy.put_lifecycle_event_hook_execution_status)はドキュメントのサンプルで確認ください。

環境変数には以下を設定してください。

  • POLICY_NAME : Auto Scaling Policy の名前
  • ECS_CLUSTER_NAME : ECS クラスタ名
  • ECS_SERVICE_NAME : ECS サービス名
  • LOG_LEVEL : ログレベル
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import json
import logging
import os
import sys
import boto3
from botocore.exceptions import ClientError

POLICY_NAME = os.getenv('POLICY_NAME')
ECS_CLUSTER_NAME = os.getenv('ECS_CLUSTER_NAME')
ECS_SERVICE_NAME = os.getenv('ECS_SERVICE_NAME')

logger = logging.getLogger(__name__)
logger.setLevel(os.getenv('LOG_LEVEL', 'WARNING'))
formatter = logging.Formatter("%(asctime)s %(name)s:%(lineno)s [%(levelname)s] %(funcName)s : %(message)s", "%Y-%m-%dT%H:%M:%S%z")

for handler in logger.handlers:
  handler.setFormatter(formatter)

codedeploy = boto3.client('codedeploy')
appAutoScaling = boto3.client('application-autoscaling')

def lambda_handler(event, context):

    deletePolicy()

    if 'DeploymentId' in event and 'LifecycleEventHookExecutionId' in event:
        logger.info('Invoked by CodeDeploy')
        res = codedeploy.put_lifecycle_event_hook_execution_status(
            deploymentId = event['DeploymentId'],
            lifecycleEventHookExecutionId = event['LifecycleEventHookExecutionId'],
            status = 'Succeeded'
        )
        logger.debug(f"codedeploy.put_lifecycle_event_hook_execution_status() response: {res}")

    return {
        'statusCode': 200,
        'body': json.dumps(res)
    }

def deletePolicy():
    try:
        res = appAutoScaling.delete_scaling_policy(
            PolicyName = POLICY_NAME,
            ServiceNamespace = 'ecs',
            ResourceId = f"service/{ECS_CLUSTER_NAME}/{ECS_SERVICE_NAME}",
            ScalableDimension = 'ecs:service:DesiredCount'
        )
        logger.debug(f"appAutoScaling.delete_scaling_policy() response: {res}")
    except ClientError as e:
        res = { 'error': e }
        logger.error(e)

    return res

AfterAllowTraffic に設定する Lambda

Lambda 実行時の ALB に関連付けられているターゲットグループをもとに Auto Scaling ポリシーを設定します。
(ちなみに、BeforeInstall でポリシーを削除してから、AfterAllowTraffic でポリシーを再設定していますが、削除しなくても PutScalingPolicy API で上書きできます。)

環境変数は以下を設定してします。

  • POLICY_NAME : Auto Scaling Policy の名前
  • ECS_CLUSTER_NAME : ECS クラスタ名
  • ECS_SERVICE_NAME : ECS サービス名
  • ALB_NAME : ALB の名前
  • TARGET_GROUP_NAME_1 : ターゲットグループ 1 の名前
  • TARGET_GROUP_NAME_2 : ターゲットグループ 2 の名前
  • REQUEST_COUNT_TARGET_VALUE ターゲット追跡として ALBRequestCountPerTarget に設定したいリクエスト数
  • SCALE_OUT_COOL_DOWN : スケールアウトのクールダウン期間(秒数)
  • SCALE_IN_COOL_DOWN : スケールインのクールダウン期間(秒数)
  • DISABLE_SCALE_IN : スケールインの無効化(True : スケールインしない / False : スケールインする)
  • LOG_LEVEL : ログレベル
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import json
import logging
import os
import re
import sys
import boto3
from botocore.exceptions import ClientError

POLICY_NAME = os.getenv('POLICY_NAME', '')
ECS_CLUSTER_NAME = os.getenv('ECS_CLUSTER_NAME', '')
ECS_SERVICE_NAME = os.getenv('ECS_SERVICE_NAME', '')
ALB_NAME = os.getenv('ALB_NAME', '')
TARGET_GROUP_NAME_1 = os.getenv('TARGET_GROUP_NAME_1', '')
TARGET_GROUP_NAME_2 = os.getenv('TARGET_GROUP_NAME_2', '')
REQUEST_COUNT_TARGET_VALUE = float(os.getenv('REQUEST_COUNT_TARGET_VALUE', '100.0'))
SCALE_OUT_COOL_DOWN = int(os.getenv('SCALE_OUT_COOL_DOWN', '300'))
SCALE_IN_COOL_DOWN = int(os.getenv('SCALE_IN_COOL_DOWN', '300'))
DISABLE_SCALE_IN = os.getenv('DISABLE_SCALE_IN', 'False')
DISABLE_SCALE_IN = False if DISABLE_SCALE_IN == 'False' else bool(DISABLE_SCALE_IN)

logger = logging.getLogger(__name__)
logger.setLevel(os.getenv('LOG_LEVEL', 'WARNING'))
formatter = logging.Formatter("%(asctime)s %(name)s:%(lineno)s [%(levelname)s] %(funcName)s : %(message)s", "%Y-%m-%dT%H:%M:%S%z")

for handler in logger.handlers:
    handler.setFormatter(formatter)

codedeploy = boto3.client('codedeploy')
appAutoScaling = boto3.client('application-autoscaling')
elbv2 = boto3.client('elbv2')

class AutoScalingPolicy:

    re_elb_arn = re.compile(r'arn:aws(-[^:]+)?:elasticloadbalancing:[^:]+:\d{12}:loadbalancer/(.+)$')
    re_targetGroup_arn = re.compile(r'arn:aws(-[^:]+)?:elasticloadbalancing:[^:]+:\d{12}:(targetgroup/.+)$')

    def __init__(self) -> None:
        self.logger = logger.getChild('AppTask')
        pass

    def get_load_balancer_arn(self, name):
        res = elbv2.describe_load_balancers(Names = [name])
        self.logger.debug(f"elbv2.describe_load_balancers() response: {res}")

        arn = res['LoadBalancers'][0]['LoadBalancerArn']
        self.logger.debug(f"Load Balancer ARN: {arn}")
        return arn

    def parse_resource_label_of_elb(self, arn):
        m = self.re_elb_arn.match(arn)
        self.logger.debug(f"Resource Lable of ELB: {m.group(2)}")
        return m.group(2)

    def get_current_target_group_arn(self, tgNames, elbArn):
        res = elbv2.describe_target_groups(Names = tgNames)
        self.logger.debug(f"elbv2.describe_target_groups() response: {res}")

        if len(res['TargetGroups']) > 1:
            # *** Attention ***
            # 本番トラフィックとテストトラフィックが有効な状態(AfterAllowTestTraffic から BeforeAllowTraffic までの間)で
            # 「デプロイの停止」(即時停止)を行うとロールバックされず、2つのターゲットグループが ALB に関連付けされたままになるので、
            # どちらのターゲットグループが本番用か elbv2.describe_target_groups だけでは判断できません。
            # 結果、正しく Auto Scaling ポリシーが設定できない場合があります
            self.logger.error('Both TG-1 and TG-2 are attached alb. Can not identify it for production traffic.')

        tgArn = [tg['TargetGroupArn'] for tg in res['TargetGroups'] if len(tg['LoadBalancerArns']) > 0 and tg['LoadBalancerArns'][0] == elbArn]
        self.logger.debug(f"Target Group ARN: {tgArn}")
        return tgArn[0] if len(tgArn) > 0 else 'No TargetGroup'

    def parse_resource_label_of_target_group(self, arn):
        m = self.re_targetGroup_arn.match(arn)
        self.logger.debug(f"Resource Lable of Target Group: {m}")
        return m.group(2)

    def getResourceLabel(self):
        # https://docs.aws.amazon.com/ja_jp/autoscaling/application/APIReference/API_PredefinedMetricSpecification.html
        elbArn = self.get_load_balancer_arn(ALB_NAME)
        elbResourceLabel = self.parse_resource_label_of_elb(elbArn)

        tgArn = self.get_current_target_group_arn(tgNames = [TARGET_GROUP_NAME_1, TARGET_GROUP_NAME_2], elbArn = elbArn)
        tgResourceLabel = self.parse_resource_label_of_target_group(tgArn)

        self.logger.info(f"New Resource Lable: {elbResourceLabel}/{tgResourceLabel}")
        return f"{elbResourceLabel}/{tgResourceLabel}"

    def put_policy(self):
        try:
            resourceLabel = self.getResourceLabel()
            res = appAutoScaling.put_scaling_policy(
                PolicyName = POLICY_NAME,
                ServiceNamespace = 'ecs',
                ResourceId = f"service/{ECS_CLUSTER_NAME}/{ECS_SERVICE_NAME}",
                ScalableDimension = 'ecs:service:DesiredCount',
                PolicyType = 'TargetTrackingScaling',
                TargetTrackingScalingPolicyConfiguration = {
                    'TargetValue': REQUEST_COUNT_TARGET_VALUE,
                    'PredefinedMetricSpecification' : {
                        'PredefinedMetricType': 'ALBRequestCountPerTarget',
                        'ResourceLabel': resourceLabel
                    },
                    'ScaleOutCooldown': SCALE_OUT_COOL_DOWN,
                    'ScaleInCooldown': SCALE_IN_COOL_DOWN,
                    'DisableScaleIn': DISABLE_SCALE_IN
                }
            )
            self.logger.debug(f"appAutoScaling.put_scaling_policy() response: {res}")
        except ClientError as e:
            res = { 'error': e }
            self.logger.error(e)

        return res

def lambda_handler(event, context):

    app = AutoScalingPolicy()
    app.put_policy()

    if 'DeploymentId' in event and 'LifecycleEventHookExecutionId' in event:
        logger.info('Invoked by CodeDeploy')
        res = codedeploy.put_lifecycle_event_hook_execution_status(
            deploymentId = event['DeploymentId'],
            lifecycleEventHookExecutionId = event['LifecycleEventHookExecutionId'],
            status = 'Succeeded'
        )
        logger.debug(f"codedeploy.put_lifecycle_event_hook_execution_status() response: {res}")

    return {
        'statusCode': 200,
        'body': json.dumps(res)
    }

デプロイ失敗(ロールバック)時に SNS 経由で実行される Lambda

ちょっと横着ですが、デプロイ失敗時に SNS 経由で起動する Lambda は AfterAllowTraffic のものをそのまま利用します。ロールバックされた後に Lambda が呼び出され、ALB に関連付けられているターゲットグループをもとに Auto Scaling ポリシーを再設定するというわけです。

注意点として、上記コード内のコメントにも書いていますが、本番トラフィックとテストトラフィックが有効な状態( AfterAllowTestTraffic から BeforeAllowTraffic までの間)でロールバックを伴わない「デプロイの停止」(即時停止)を行うと、2つのターゲットグループが ALB に関連付けされたままになるので、どちらのターゲットグループが本番トラフィック用かというのが elbv2.describe_target_groups だけでは判断できません。そういったケースにも対応する場合は、DynamoDB なども使ってステートを管理するとか、ALB リスナーのポートから判断しながら Auto Scaling ポリシーをリカバリする必要があるでしょう。

まとめ

CodeDeploy による Blue/Green デプロイメント利用時であっても、Auto Scaling で ALB のリクエストカウントのメトリクスを利用する方法について検討してみました。

最後に・・・

この投稿は個人的なものであり、所属組織を代表するものではありません。ご了承ください。
※本サンプルは、自己責任の範囲でご利用ください。