はじめに
1 個の ALB を複数の ECS サービスで共有したい時ありませんか?私はあります。ECS サービスごとに ALB を立てると、ALB そのものの料金が気になったりしますし、Public IPv4 アドレスの新しい料金についても気になります。
そこで、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 の構成を作成します。
ALB の作成
まず共有する ALB を作成します。
ALB を選択して Create
ALB のパラメータを指定します。ターゲットグループは、この後の手順で削除するので、適当にダミーを指定します。
ダミーのリスナーは削除します。
ECR にコンテナイメージを作成
動作確認に必要な 2 種類 x 2 バージョンのコンテナイメージを ECR に格納します。
App1 のコンテナイメージ
App2 のコンテナイメージ
タスク定義作成
ECS で利用するタスク定義を作成します。
Create
app01-ver1
同様に、以下のタスク定義も作成します。
- 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
コマンドの実行結果
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
作成されました
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 名を付与します。
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 を紐づけます。リスナーを選択します。
ALB の共有を行うために、ホストヘッダー ( host header ) で分岐をするため、リスナールールを追加します。
好きなルール名を指定します。
Add condition を押します。
Host Header を選択し、「ecs-bluegreen-app02.sugiaws.tokyo」でアクセスしてきたことを条件に分岐ルールを指定します。
Next を押します。
ECS Service 2 側の Target Group を指定します。
Next
Create
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
作成されました
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 名を付与します。
動作確認 : Blue/Green Deployment を Service 1 個目に対して行う
ECS Service 1 に対して、Blue/Green Deployment を実施します。この時に、以下を確認します。
- ECS Service 1 が切り替わること
- ECS Service 2 は引き続き利用できること
1 回目
ALB のリスナールールで見たときに、Default で指定されてる Target Group が「hostbased-bluegreen-app01-a」から、「hostbased-bluegreen-app01-b」に切り替わってほしいです。
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 が動作する様子が見えます。
一定時間後、Deploy が完了しています。
ALB のリスナーを見ると、無事に Default の方の Target Group が入れ替わっています。
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 が切り替わることを確認します。
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 が動作
Deploy が完了
無事に入れ替わっている
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 は引き続き利用できること
1 回目
ALB のリスナールールで見たときに、host header のリスナールールで指定されてる Target Group が「hostbased-bluegreen-app02-a」から、「hostbased-bluegreen-app02-b」に切り替わってほしいです。
デプロイ前の 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 が動作する様子が確認できます。
正常終了します。
デプロイ後の 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 側が切り替わっています。
2 回目
同じく、ECS Service 2 個目に対して、2 回目の Blue/Green Deployment を試してみます。ALB リスナールールの、次の Target Group が切り替わることを確認します。
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 のリスナールールも切り替わっています。