column

コラム

【ECS/Fargate】複数のターゲットグループを登録したサービスのBlue/Greenデプロイ

こんにちは、クラウドCoEの 竹中です。
 
今回は、複数のターゲットグループが登録されたECS のServiceにおけるBlue/Greenデプロイについてお話しします。
 
 

サービスに複数のターゲットグループを登録する際の制約

2022/03/09のアップデートで、ECS UpdateService APIに機能が追加されました。

今までもサービスに複数のターゲットグループを登録することは可能だったものの、作成時に設定しなければ新しくサービスを作り直す必要がありました。

ターゲットグループの追加が劇的に簡単になるため、私としては待望のアップデートでした。

しかし、ターゲットグループが複数登録されているサービスにはデプロイ方法に制約があります。

以下、公式ドキュメントより抜粋
 
  • サービス定義での複数のターゲットグループの指定は、次の条件でのみサポートされます。
    • サービスでは、Application Load Balancer またはNetwork Load Balancer を使用する必要があります。
    • サービスでローリング更新 (ECS) のデプロイコントローラータイプを使用する必要があります。
 
つまり、1つのサービスに複数のターゲットグループが登録されている場合、デプロイ方法はローリングアップデートのみとなり、Blue/Greenデプロイは不可能な仕様になっています。
 
 

サービスとDeployment Groupの対応関係の整理

実際に2つのターゲットグループを登録したサービスに対して、Blue/Greenデプロイメントを設定してみました。
 
すると、CodeDeployのDeployment Groupの作成時に以下のようなエラーが出て失敗しました。
 
 

 

Blue/Greenデプロイの場合、Deployment Groupの作成に失敗します。
 
これは、Deployment Groupとサービスが1対1で対応しているためです。
 
 
   
 
 
ローリングアップデートはCodeDeployを使用せず、直接CodePipelineからECSのデプロイを行います。
 
Deployment Groupを作成する必要がないため、問題なく設定できます。
 
 
 
 

デプロイ方法の3つの選択肢

以上から、この場合にとり得るデプロイ方法の選択肢は3つあると思います。
 

①ローリングアップデートを採用する

この場合、複数のターゲットグループが登録されたサービスをデプロイできますが、当然Blue/Greenデプロイにはなりません。
 

②サービスを複数作成し、それぞれについてDeployment Groupを作成する

全てのタスクに対してBlue/Greenデプロイを行うことができるので、最も妥当な選択肢だと思います。
 
ただし、サービスが増える分タスク数も増えるので、リソースの過剰投資の恐れがあります。
 
 
 
 

③1つのターゲットグループに対してBlue/Greenデプロイを行い、もう1つはLambdaでデプロイする

要件上タスクを増やせない場合などはこちらの選択肢になると思います。
 
今回はこちらの方法を実装していきます。
 
 
 

実装

Deployment Groupの設定では、片方のALBを設定します。

その他のCodePipeline, CodeDeploy, CodeBuildについての設定は記載を省略します。

 

 
 
EventBridgeを以下のように設定します。
 
こうすることで、Blue/Greenデプロイした側のタスクが再起動した際などにターゲットを登録しなおすことができます。
 
 
 
Lambdaは以下のようになります。
 
環境変数は以下の通り設定しました。
 
INTERNAL_TG_ARN: Blue/Greenデプロイを行うターゲットグループのARN
INTERNET_TG_ARN: Lambdaによって更新されるターゲットグループのARN
 
 
import boto3
import json
import os
import socket

def lambda_handler(event, context):
    client = boto3.client('elbv2')

    INTERNET_TG_ARN = os.environ['INTERNET_TG_ARN']
    INTERNAL_TG_ARN = os.environ['INTERNAL_TG_ARN']

    new_ips = client.describe_target_health(TargetGroupArn=INTERNAL_TG_ARN)['TargetHealthDescriptions']
    old_ips = client.describe_target_health(TargetGroupArn=INTERNET_TG_ARN)['TargetHealthDescriptions']
    
    print(new_ips)
    print(old_ips)
    new_targets = list(map(lambda x: {'Id': x['Target']['Id'], 'Port': 80}, new_ips))
    client.register_targets(
        TargetGroupArn=INTERNET_TG_ARN,
        Targets=new_targets
    )

    old_targets = list(map(lambda x:{'Id': x['Target']['Id'], 'Port': 80}, old_ips))
    client.deregister_targets(
        TargetGroupArn=INTERNET_TG_ARN,
        Targets=old_targets)
        
    return {
        'statusCode': 200,
        'body': json.dumps('Target Updated')
    }
 
 
先程設定したEventBrideを、Lambda関数のトリガーとして設定します。
 
 
念のため、リソースベースのポリシーが設定されていることを確認します。
 
 
 
やや無理やりではありますが、1つのALBへのBlue/Greenデプロイが完了した際にLambdaでもう一つのALBにもデプロイすることで、2つのALBへの疑似Blue/Greenデプロイを実現できました。
 
 
 

RECOMMEND

おすすめ記事一覧