LoginSignup
2
4

ECS で複数のサービスを 1 個の ALB に共有してコスト最適化を実施する (異なるドメイン)

Last updated at Posted at 2023-09-06

はじめに

1 個の ALB を複数の ECS サービスで共有したい時ありませんか?私はあります。ECS サービスごとに ALB を立てると、ALB そのものの料金が気になったりしますし、Public IPv4 アドレスの新しい料金についても気になります。

image-20230905223453028.png

そこで、ALB を共有してコスト最適化をする選択肢があります。ここで疑問に思ったのが、ECS のデプロイ方式には、Blue/Green Deployment や Rolling Update がありますが、ALB を共有した時に問題なく動作するのか不安でした。結果、動作検証して問題なく動いたので、その設定方法などを紹介します。この記事は Blue/Green Deployment で ALB を共有する方法を紹介します。

忙しい人に向けて

  • 1 個の ALB を複数の ECS Service で共有してコスト最適化を図る
  • ECS のデプロイタイプについて、Rolling Update・Blue/Green Deployment の両方とも可能
  • Blue/Green Deployment は GUI を使った設定は出来ないので、AWS CLI や CloudFormations などの API 経由で行う

ALB を共有する際の上限について

ALB を複数のサービスで共有する際に、認識した方がよい上限についてです。ALB の Service Quota を全般的にご確認いただいた方が良いですが、とくに意識した方が良いと思うものを以下に記載します。

  • Application Load Balancer あたりの証明書 : 25
  • Application Load Balancer あたりのターゲットグループ : 100
  • Application Load Balancer あたりのルール (デフォルトルールを除く) : 100

ACM で証明書を発行する and ホストヘッダーを使用し異なるドメインを利用する場合は、25 ドメインが上限となる場合があります。上限緩和申請が可能なものもありますが、ある程度の数で ALB を分割するのも良いと思います。

構成図

次の構成図のように、ホストヘッダーを使い異なるドメインへのアクセスを利用しながら、ALB を共有する構成を確認していきます。ECS Service 1 と 2 の両方で Blue/Green の構成を作成します。

image.png

ALB の作成

まず共有する ALB を作成します。

image-20230905132242613.png

ALB を選択して Create

image-20230905132302808.png

ALB のパラメータを指定します。ターゲットグループは、この後の手順で削除するので、適当にダミーを指定します。

image-20230905171858721.png

ダミーのリスナーは削除します。

image-20230905134915973.png

ECR にコンテナイメージを作成

動作確認に必要な 2 種類 x 2 バージョンのコンテナイメージを ECR に格納します。

App1 のコンテナイメージ

image-20230905160039743.png

App2 のコンテナイメージ

image-20230905160027426.png

タスク定義作成

ECS で利用するタスク定義を作成します。

image-20230905135640434.png

Create

app01-ver1

image-20230905161514435.png

同様に、以下のタスク定義も作成します。

  • app1-ver2
  • app2-ver1
  • app2-ver2

ECS Service 1 個目に関する設定

ECS Service 1 個目を作成し、Blue/Green Deployment を有効化していきます。

Blue/Green 用の Target Group を 2 個作成し、ALB に紐づけ

AWS CLI から作成

1 個目

aws elbv2 create-target-group \
     --name hostbased-bluegreen-app01-a \
     --protocol HTTP \
     --port 80 \
     --target-type ip \
     --vpc-id vpc-0b8b2123a16d03528 \
     --region ap-northeast-1

2 個目

aws elbv2 create-target-group \
     --name hostbased-bluegreen-app01-b \
     --protocol HTTP \
     --port 80 \
     --target-type ip \
     --vpc-id vpc-0b8b2123a16d03528 \
     --region ap-northeast-1

1 個目の Target Group を、ALB のリスナーに紐づける

aws elbv2 create-listener \
     --load-balancer-arn arn:aws:elasticloadbalancing:ap-northeast-1:xxxxxxxxxxxx:loadbalancer/app/ecs-hostbased-bluegreen/fbb38640ab7af2b9 \
     --protocol HTTP \
     --port 80 \
     --default-actions Type=forward,TargetGroupArn="arn:aws:elasticloadbalancing:ap-northeast-1:xxxxxxxxxxxx:targetgroup/hostbased-bluegreen-app01-a/baa95a555069b8fb" \
     --region ap-northeast-1

コマンドの実行結果

image-20230905183526108.png

ECS Service 1個目の作成

Service の定義ファイルを作成します。deploymentController で CODE_DEPLOY を指定するのがポイントです。

cat <<'EOF' > ecs-service-hostbased-bluegreen-app01.json
{
    "cluster": "test-cluster01",
    "serviceName": "hostbased-bluegreen-app01",
    "taskDefinition": "hostbased-hello-ver-app01:1",
    "loadBalancers": [
        {
            "targetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:xxxxxxxxxxxx:targetgroup/hostbased-bluegreen-app01-a/baa95a555069b8fb",
            "containerName": "app1-ver1",
            "containerPort": 8080
        }
    ],
    "launchType": "FARGATE",
    "schedulingStrategy": "REPLICA",
    "deploymentController": {
        "type": "CODE_DEPLOY"
    },
    "platformVersion": "LATEST",
    "networkConfiguration": {
       "awsvpcConfiguration": {
          "assignPublicIp": "ENABLED",
          "securityGroups": [ "sg-0fbfac9c258b61337" ],
          "subnets": [ "subnet-0c4b7c352a610baf4", "subnet-0d9a1ae8571fa7c30", "subnet-0355f72e6534092bf" ]
       }
    },
    "desiredCount": 2
}
EOF

Service を作成します。

aws ecs create-service \
     --cli-input-json file://ecs-service-hostbased-bluegreen-app01.json \
     --region ap-northeast-1

作成されました

image-20230905181216109.png

CodeDeploy に関するリソース作成

CodeDeploy で 「Application」というリソースを定義します。

aws deploy create-application \
     --application-name ecs-hostbased-bluegreen-app01 \
     --compute-platform ECS

CodeDeploy で Deployment Group を作成します。ALB のリスナーを指定しつつ、2 つの Target Group を指定することで、Blue/Green の動作を指定しています。

cat <<'EOF' > ecs-hostbased-bluegreen-app01-deployment-group.json
{
    "applicationName": "ecs-hostbased-bluegreen-app01",
    "autoRollbackConfiguration": {
        "enabled": true,
        "events": [
            "DEPLOYMENT_FAILURE"
        ]
    },
    "blueGreenDeploymentConfiguration": {
        "deploymentReadyOption": {
            "actionOnTimeout": "CONTINUE_DEPLOYMENT",
            "waitTimeInMinutes": 0
        },
        "terminateBlueInstancesOnDeploymentSuccess": {
            "action": "TERMINATE",
            "terminationWaitTimeInMinutes": 1
        }
    },
    "deploymentGroupName": "ecs-hostbased-bluegreen-app01-dg",
    "deploymentStyle": {
        "deploymentOption": "WITH_TRAFFIC_CONTROL",
        "deploymentType": "BLUE_GREEN"
    },
    "loadBalancerInfo": {
        "targetGroupPairInfoList": [
            {
                "targetGroups": [
                    {
                        "name": "hostbased-bluegreen-app01-a"
                    },
                    {
                        "name": "hostbased-bluegreen-app01-b"
                    }
                ],
                "prodTrafficRoute": {
                    "listenerArns": [
                        "arn:aws:elasticloadbalancing:ap-northeast-1:xxxxxxxxxxxx:listener/app/ecs-hostbased-bluegreen/fbb38640ab7af2b9/4cccf92c5a04b5cd"
                    ]
                }
            }
        ]
    },
    "serviceRoleArn": "arn:aws:iam::xxxxxxxxxxxx:role/AWSCodeDeployRoleForECS",
    "ecsServices": [
        {
            "serviceName": "hostbased-bluegreen-app01",
            "clusterName": "test-cluster01"
        }
    ]
}
EOF

Deployment Group の作成

aws deploy create-deployment-group \
     --cli-input-json file://ecs-hostbased-bluegreen-app01-deployment-group.json

Route 53 の設定

Route 53 で、DNS 名を付与します。

image-20230905184109695.png

ECS Service 2 個目に関する設定

同様に、ECS Service 1 個目の設定を進めていきます。

Blue/Green 用の Target Group を 2 個作成し、ALB に紐づけ

AWS CLI から作成

1 個目

aws elbv2 create-target-group \
     --name hostbased-bluegreen-app02-a \
     --protocol HTTP \
     --port 80 \
     --target-type ip \
     --vpc-id vpc-0b8b2123a16d03528 \
     --region ap-northeast-1

2 個目

aws elbv2 create-target-group \
     --name hostbased-bluegreen-app02-b \
     --protocol HTTP \
     --port 80 \
     --target-type ip \
     --vpc-id vpc-0b8b2123a16d03528 \
     --region ap-northeast-1

ALB リスナールールを作成して、Target Group1 を紐づけます。リスナーを選択します。

image-20230905184458251.png

ALB の共有を行うために、ホストヘッダー ( host header ) で分岐をするため、リスナールールを追加します。

image-20230905184520554.png

好きなルール名を指定します。

image-20230905184539997.png

Add condition を押します。

image-20230905184555335.png

Host Header を選択し、「ecs-bluegreen-app02.sugiaws.tokyo」でアクセスしてきたことを条件に分岐ルールを指定します。

image-20230905184627870.png

Next を押します。

image-20230905184638215.png

ECS Service 2 側の Target Group を指定します。

image-20230905184658042.png

Next

image-20230905184710954.png

Create

image-20230905184719111.png

ECS Service 2個目の作成

ECS Service を作成するために定義ファイルを作成します

cat <<'EOF' > ecs-service-hostbased-bluegreen-app02.json
{
    "cluster": "test-cluster01",
    "serviceName": "hostbased-bluegreen-app02",
    "taskDefinition": "hostbased-hello-ver-app02:1",
    "loadBalancers": [
        {
            "targetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:xxxxxxxxxxxx:targetgroup/hostbased-bluegreen-app02-a/babcc21f145a8a4c",
            "containerName": "app2-ver1",
            "containerPort": 8080
        }
    ],
    "launchType": "FARGATE",
    "schedulingStrategy": "REPLICA",
    "deploymentController": {
        "type": "CODE_DEPLOY"
    },
    "platformVersion": "LATEST",
    "networkConfiguration": {
       "awsvpcConfiguration": {
          "assignPublicIp": "ENABLED",
          "securityGroups": [ "sg-0fbfac9c258b61337" ],
          "subnets": [ "subnet-0c4b7c352a610baf4", "subnet-0d9a1ae8571fa7c30", "subnet-0355f72e6534092bf" ]
       }
    },
    "desiredCount": 2
}
EOF

Service の作成

aws ecs create-service \
     --cli-input-json file://ecs-service-hostbased-bluegreen-app02.json \
     --region ap-northeast-1

作成されました

image-20230905184904288.png

CodeDeploy の リソース 作成

CodeDeploy で 「Application」というリソースを定義します。

aws deploy create-application \
     --application-name ecs-hostbased-bluegreen-app02 \
     --compute-platform ECS

CodeDeploy で Deployment Group を作成します。ALB のリスナーを指定しつつ、2 つの Target Group を指定することで、Blue/Green の動作を指定しています。

cat <<'EOF' > ecs-hostbased-bluegreen-app02-deployment-group.json
{
    "applicationName": "ecs-hostbased-bluegreen-app02",
    "autoRollbackConfiguration": {
        "enabled": true,
        "events": [
            "DEPLOYMENT_FAILURE"
        ]
    },
    "blueGreenDeploymentConfiguration": {
        "deploymentReadyOption": {
            "actionOnTimeout": "CONTINUE_DEPLOYMENT",
            "waitTimeInMinutes": 0
        },
        "terminateBlueInstancesOnDeploymentSuccess": {
            "action": "TERMINATE",
            "terminationWaitTimeInMinutes": 1
        }
    },
    "deploymentGroupName": "ecs-hostbased-bluegreen-app02-dg",
    "deploymentStyle": {
        "deploymentOption": "WITH_TRAFFIC_CONTROL",
        "deploymentType": "BLUE_GREEN"
    },
    "loadBalancerInfo": {
        "targetGroupPairInfoList": [
            {
                "targetGroups": [
                    {
                        "name": "hostbased-bluegreen-app02-a"
                    },
                    {
                        "name": "hostbased-bluegreen-app02-b"
                    }
                ],
                "prodTrafficRoute": {
                    "listenerArns": [
                        "arn:aws:elasticloadbalancing:ap-northeast-1:xxxxxxxxxxxx:listener/app/ecs-hostbased-bluegreen/fbb38640ab7af2b9/4cccf92c5a04b5cd"
                    ]
                }
            }
        ]
    },
    "serviceRoleArn": "arn:aws:iam::xxxxxxxxxxxx:role/AWSCodeDeployRoleForECS",
    "ecsServices": [
        {
            "serviceName": "hostbased-bluegreen-app02",
            "clusterName": "test-cluster01"
        }
    ]
}
EOF

Deployment Group の作成

aws deploy create-deployment-group \
     --cli-input-json file://ecs-hostbased-bluegreen-app02-deployment-group.json

Route 53 の設定

Route 53 で、DNS 名を付与します。

image-20230905185107199.png

動作確認 : Blue/Green Deployment を Service 1 個目に対して行う

ECS Service 1 に対して、Blue/Green Deployment を実施します。この時に、以下を確認します。

  • ECS Service 1 が切り替わること
  • ECS Service 2 は引き続き利用できること

image-20230906131840389.png

1 回目

ALB のリスナールールで見たときに、Default で指定されてる Target Group が「hostbased-bluegreen-app01-a」から、「hostbased-bluegreen-app01-b」に切り替わってほしいです。

image-20230905204804177.png

Blue/Green Deployment を行う前の curl です。ホストヘッダーで適切に分岐されています。

$ curl http://ecs-bluegreen-app01.sugiaws.tokyo/
Hello, App1 Version1
$ curl http://ecs-bluegreen-app02.sugiaws.tokyo/
Hello, App2 Version1

CodeDeploy 用の AppSpec ファイルを作成して、S3 にアップロードします。

cat <<'EOF' > appspec.yaml
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: "arn:aws:ecs:ap-northeast-1:xxxxxxxxxxxx:task-definition/hostbased-hello-ver-app01:3"
        LoadBalancerInfo:
          ContainerName: "app1-ver1"
          ContainerPort: 8080
        PlatformVersion: "LATEST"
EOF

S3 Bucket にアップロードします。

aws s3 cp ./appspec.yaml s3://awscli-ecs-bluegreen-bucket01/appspec.yaml

CodeDeploy 上で、Deployment を作成するためのファイルを作成

cat <<'EOF' > create-deployment.json
{
    "applicationName": "ecs-hostbased-bluegreen-app01",
    "deploymentGroupName": "ecs-hostbased-bluegreen-app01-dg",
    "revision": {
        "revisionType": "S3",
        "s3Location": {
            "bucket": "awscli-ecs-bluegreen-bucket01",
            "key": "appspec.yaml",
            "bundleType": "YAML"
        }
    }
}
EOF

Blue/Green Deployment を開始します。

aws deploy create-deployment \
     --cli-input-json file://create-deployment.json

CodeDeploy が動作する様子が見えます。

image-20230905205319948.png

一定時間後、Deploy が完了しています。

image-20230905205537744.png

ALB のリスナーを見ると、無事に Default の方の Target Group が入れ替わっています。

image-20230905205556184.png

curl でアクセスした結果です。「ecs-bluegreen-app01.sugiaws.tokyo」の方は version2 にバージョンアップできました。

$ curl http://ecs-bluegreen-app01.sugiaws.tokyo/
Hello, App1 Version2
$ curl http://ecs-bluegreen-app02.sugiaws.tokyo/
Hello, App2 Version1

2 回目

同じく、ECS Service 1 個目に対して、2 回目の Blue/Green Deployment を試してみます。ALB リスナールールの、次の Target Group が切り替わることを確認します。

image-20230905210928127.png

curl のレスポンスです。

$ curl http://ecs-bluegreen-app01.sugiaws.tokyo/
Hello, App1 Version2
$ curl http://ecs-bluegreen-app02.sugiaws.tokyo/
Hello, App2 Version1

CodeDeploy 用の AppSpec ファイルを作成して、S3 にアップロードします。

cat <<'EOF' > appspec.yaml
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: "arn:aws:ecs:ap-northeast-1:xxxxxxxxxxxx:task-definition/hostbased-hello-ver-app01:1"
        LoadBalancerInfo:
          ContainerName: "app1-ver1"
          ContainerPort: 8080
        PlatformVersion: "LATEST"
EOF

S3 Bucket にアップロード

aws s3 cp ./appspec.yaml s3://awscli-ecs-bluegreen-bucket01/appspec.yaml

CodeDeploy 上で、Deployment を作成するためのファイルを作成

cat <<'EOF' > create-deployment.json
{
    "applicationName": "ecs-hostbased-bluegreen-app01",
    "deploymentGroupName": "ecs-hostbased-bluegreen-app01-dg",
    "revision": {
        "revisionType": "S3",
        "s3Location": {
            "bucket": "awscli-ecs-bluegreen-bucket01",
            "key": "appspec.yaml",
            "bundleType": "YAML"
        }
    }
}
EOF

Blue/Green Deployment を開始します。

aws deploy create-deployment \
     --cli-input-json file://create-deployment.json

CodeDeploy が動作

image-20230905211132880.png

Deploy が完了

image-20230905211609430.png

無事に入れ替わっている

image-20230905211627169.png

curl の結果で、App1 だけ切り替わりました。

$ curl http://ecs-bluegreen-app01.sugiaws.tokyo/
Hello, App1 Version1
$ curl http://ecs-bluegreen-app02.sugiaws.tokyo/
Hello, App2 Version1

動作確認 : Blue/Green Deployment を Service 2 個目に対して行う

ECS Service 2 に対して、Blue/Green Deployment を実施します。この時に、以下を確認します。

  • ECS Service 2 が切り替わること
  • ECS Service 1 は引き続き利用できること

image.png

1 回目

ALB のリスナールールで見たときに、host header のリスナールールで指定されてる Target Group が「hostbased-bluegreen-app02-a」から、「hostbased-bluegreen-app02-b」に切り替わってほしいです。

image-20230905203446809.png

デプロイ前の curl

$ curl http://ecs-bluegreen-app01.sugiaws.tokyo/
Hello, App1 Version1
$ curl http://ecs-bluegreen-app02.sugiaws.tokyo/
Hello, App2 Version1

CodeDeploy 用の AppSpec ファイルを作成して、S3 にアップロードします。

cat <<'EOF' > appspec.yaml
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: "arn:aws:ecs:ap-northeast-1:xxxxxxxxxxxx:task-definition/hostbased-hello-ver-app02:3"
        LoadBalancerInfo:
          ContainerName: "app2-ver1"
          ContainerPort: 8080
        PlatformVersion: "LATEST"
EOF

S3 Bucket にアップロード

aws s3 cp ./appspec.yaml s3://awscli-ecs-bluegreen-bucket01/appspec.yaml

CodeDeploy 上で、Deployment を作成するためのファイルを作成

cat <<'EOF' > create-deployment.json
{
    "applicationName": "ecs-hostbased-bluegreen-app02",
    "deploymentGroupName": "ecs-hostbased-bluegreen-app02-dg",
    "revision": {
        "revisionType": "S3",
        "s3Location": {
            "bucket": "awscli-ecs-bluegreen-bucket01",
            "key": "appspec.yaml",
            "bundleType": "YAML"
        }
    }
}
EOF

Blue/Green Deployment を開始します。

aws deploy create-deployment \
     --cli-input-json file://create-deployment.json

CodeDeploy が動作する様子が確認できます。

image-20230905203409047.png

正常終了します。

image-20230905203833876.png

デプロイ後の curl です。App 2 側のみ切り変わっています。

$ curl http://ecs-bluegreen-app01.sugiaws.tokyo/
Hello, App1 Version1
$ curl http://ecs-bluegreen-app02.sugiaws.tokyo/
Hello, App2 Version2

想定通り、ALB のリスナールールを見ると、Host Header 側が切り替わっています。

image-20230905204033976.png

2 回目

同じく、ECS Service 2 個目に対して、2 回目の Blue/Green Deployment を試してみます。ALB リスナールールの、次の Target Group が切り替わることを確認します。

image-20230905204033977.png

CodeDeploy 用の AppSpec ファイルを作成して、S3 にアップロードします。

cat <<'EOF' > appspec.yaml
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: "arn:aws:ecs:ap-northeast-1:xxxxxxxxxxxx:task-definition/hostbased-hello-ver-app02:1"
        LoadBalancerInfo:
          ContainerName: "app2-ver1"
          ContainerPort: 8080
        PlatformVersion: "LATEST"
EOF

S3 Bucket にアップロード

aws s3 cp ./appspec.yaml s3://awscli-ecs-bluegreen-bucket01/appspec.yaml

CodeDeploy 上で、Deployment を作成するためのファイルを作成

cat <<'EOF' > create-deployment.json
{
    "applicationName": "ecs-hostbased-bluegreen-app02",
    "deploymentGroupName": "ecs-hostbased-bluegreen-app02-dg",
    "revision": {
        "revisionType": "S3",
        "s3Location": {
            "bucket": "awscli-ecs-bluegreen-bucket01",
            "key": "appspec.yaml",
            "bundleType": "YAML"
        }
    }
}
EOF

Blue/Green Deployment を開始します。

aws deploy create-deployment \
     --cli-input-json file://create-deployment.json

デプロイ後の curl です。App 2 側のみ切り変わっています。

$ curl http://ecs-bluegreen-app01.sugiaws.tokyo/
Hello, App1 Version1
$ curl http://ecs-bluegreen-app02.sugiaws.tokyo/
Hello, App2 Version1

ALB のリスナールールも切り替わっています。

image-20230905203446810.png

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