はじめに
- チュートリアルをなぞりながらECS/FargateでBlue/Greenデプロイをしてみたときのメモです。
デプロイ
構成イメージ
手順
1. 事前構築手順
1-1. ネットワークの構築
- Terraformで作成
- VPCの作成
- DNSホスト名有効化(エンドポイントのため)
- サブネットの作成
- インターネットゲートウェイの作成
- -> ルートテーブルへ追加する
- エンドポイント(Interface)の作成
- -> エンドポイント用のSGを作成する
- ecr.dkr
- ecr.api
- logs
- エンドポイント(Gateway)の作成
- -> ルーティングテーブルへ追加する
- s3
1-2. ALBの構築
- Terraformで作成
- ALB用のSG作成
- ALBの作成
- ターゲットグループの作成
- 2つ作成する(blue用とgreen用)
- このターゲットグループは、サービスで設定されている元のタスクにトラフィックをルーティングする
- 後続で作成するECSサービスで、このターゲットグループを指定する
- リスナーの作成
- ターゲットグループにリクエストを転送するデフォルトルールが関連付けられた、ロードバランサーのリスナーを作成する
1-3. ECRの構築
- Terraformで作成
- リポジトリの作成
- タグのイミュータビリティを有効化
- 同じタグでpushしても更新されない
- プッシュ時にスキャン
- タグのイミュータビリティを有効化
- 補足
- ecs cliの場合push時の作成もできるが、管理上+リポジトリ設定のため、Terraformで構築する
1-4. ECSクラスターの構築
- Terraformで作成
- Clusterの作成
- Fargateの場合 -> 「ネットワーキングのみ」
- 補足
- Cluster: TaskとServiceをグルーピングする概念。アプリケーションごと、環境ごとに用意する。
1-5. ECSサービスの構築
- Terraformで作成
- ECSサービス用のSG(ALBのSGからのインバウンド)
- ECSサービスの作成
- 初回は先にタスク定義ファイルの登録が必要?(「タスク定義ファイルの更新と登録」)
- タスク定義バージョンの指定
- タスクの数の指定(
desired-count
)
- ex. CLIで作成する場合
ecs-service.json
{
"cluster": "ecs-example-cluster",
"serviceName": "ecs-example-service",
"taskDefinition": "ecs-task-def",
"loadBalancers": [
{
"targetGroupArn": "arn:aws:elasticloadbalancing:region:aws_account_id:targetgroup/ecs-example-tg1/XXXXXXXX",
"containerName": "ecs-example",
"containerPort": 80
}
],
"launchType": "FARGATE",
"schedulingStrategy": "REPLICA",
"deploymentController": {
"type": "CODE_DEPLOY"
},
"platformVersion": "LATEST",
"networkConfiguration": {
"awsvpcConfiguration": {
"assignPublicIp": "ENABLED",
"securityGroups": [ "sg-abcd1234" ],
"subnets": [ "subnet-abcd1234", "subnet-abcd5678" ]
}
},
"desiredCount": 2
}
# 保留中でサービスが作成される
$ aws ecs create-service --cli-input-json file://ecs-service.json
- 補足
- Serviceを作成せずTaskを実行することも可能(バッチ処理など)
- Service: 指定したtaskDefinitionからtaskを生成する
- Serviceで定義するもの: ネットワーク、VPC、タスク定義
- ネットワークモードで awsvpc を使う場合はプライベートサブネットを使う
- ECS Serviceを作成する際に「deployment_controller」の設定で「type = "CODE_DEPLOY"」を指定する
1-6. CodeDeploy Applicationの構築
- Terraformで作成
- ex. CLIで作成する場合
aws deploy create-application \
--application-name ecs-example-deploy-application \
--compute-platform ECS
1-7. CodeDeploy Deployment Groupの構築
- Terraformで作成
- Deployment Groupを作成する
- Blue/Greenを指定
- ターゲットグループ(Blue/Green)とリスナーを指定
- その他、切り替え方式などを指定
- ex. CLIで作成する場合
- CLIを実行するIAMの権限に注意(code deploy, pass roleが必要)
deployment-group.json
{
"applicationName": "ecs-example-deploy-application",
"autoRollbackConfiguration": {
"enabled": true,
"events": [ "DEPLOYMENT_FAILURE" ]
},
"blueGreenDeploymentConfiguration": {
"deploymentReadyOption": {
"actionOnTimeout": "CONTINUE_DEPLOYMENT",
"waitTimeInMinutes": 0
},
"terminateBlueInstancesOnDeploymentSuccess": {
"action": "TERMINATE",
"terminationWaitTimeInMinutes": 5
}
},
"deploymentGroupName": "ecs-example-deploy-group",
"deploymentStyle": {
"deploymentOption": "WITH_TRAFFIC_CONTROL",
"deploymentType": "BLUE_GREEN"
},
"loadBalancerInfo": {
"targetGroupPairInfoList": [
{
"targetGroups": [
{
"name": "ecs-example-tg1"
},
{
"name": "ecs-example-tg2"
}
],
"prodTrafficRoute": {
"listenerArns": [
"arn:aws:elasticloadbalancing:region:aws_account_id:listener/app/ecs-example-alb/e5ba62739c16e642/665750bec1b03bd4"
]
}
}
]
},
"serviceRoleArn": "arn:aws:iam::aws_account_id:role/ecsCodeDeployRole",
"ecsServices": [
{
"serviceName": "ecs-example-service",
"clusterName": "ecs-example-cluster"
}
]
}
$ aws deploy create-deployment-group --cli-input-json file://deployment-group.json
2. CI/CD手順
2-1. ビルド実行
- GitLabRunnerなどで、コードのマージをトリガーに、①コンテナのビルドとECRへのpush+②タスク定義ファイルの更新と登録を実施する
2-1-1. コンテナのビルドとECRへのpush
# 手動の場合
# イメージビルド
# もしもどうしても環境ごとに変数を変えたい場合は、ARGインストラクションを使う(参考: [Use argument in Dockerfile](https://blog.lorentzca.me/use-argument-in-dockerfile/) )
$ docker build ./ -t ecs-example
# (参考)ローカルで確認
$ docker run --name hogehoge ecs-example -p 8080:80
# ECRにログイン
$ aws ecr get-login-password --region us-east-2 | docker login --username AWS --password-stdin XXX.dkr.ecr.us-east-2.amazonaws.com
# タグ付け(ローカルで)
# commit hash値を使う
$ docker tag ecs-example:latest XXX.dkr.ecr.us-east-2.amazonaws.com/ecs-example:v1
# ECRにプッシュ
$ docker push XXX.dkr.ecr.us-east-2.amazonaws.com/ecs-example:v1
2-2-2. タスク定義ファイルの更新と登録
- タスク定義ファイルの更新
- 基本的には
image
のtagを更新していく
- 基本的には
ecs-task-def.json
{
"family": "ecs-task-def",
"executionRoleArn": "arn:aws:iam::123456789012:role/ecs-exec-role",
"networkMode": "awsvpc",
"requiresCompatibilities": [ "FARGATE" ],
"cpu": "256",
"memory": "512",
"containerDefinitions": [
{
"name": "ecs-example",
"image": "XXX.dkr.ecr.us-east-2.amazonaws.com/ecs-example:v1",
"essential": true,
"memory": 384,
"memoryReservation": 128,
"portMappings": [
{
"containerPort": 80
}
]
}
]
}
- タスク定義ファイルの登録
-
file://
を忘れない
-
aws ecs register-task-definition --cli-input-json file://ecs-task-def.json
2-2-3. appspec.yamlファイルの更新とS3アップロード
- appspec.yamlの更新
- タスク定義のバージョン情報を更新する
appspec.yaml
version: 0.0
Resources:
- TargetService:
Type: AWS::ECS::Service
Properties:
TaskDefinition: "arn:aws:ecs:region:aws_account_id:task-definition/ecs-task-definition:7"
LoadBalancerInfo:
ContainerName: "ecs-example"
ContainerPort: 80
PlatformVersion: "LATEST"
- appspec.yamlのアップロード
$ aws s3 cp ./appspec.yaml s3://deploy-bucket/appspec.yaml
- 補足
- Task: コンテナの集合体
- TaskDefinition: docker-compose的なもの
- TaskDefinitionで定義するもの: タスクメモリ、CPU、コンテナ1のイメージURI・ポートマッピング・ログ設定
- タスク定義パラメータはこちらを参照
2-2. デプロイ実行
- 事前にcreate-deployment.jsonを作成しておく(更新不要)
{
"applicationName": "ecs-example-deploy-application",
"deploymentGroupName": "ecs-example-deploy-group",
"revision": {
"revisionType": "S3",
"s3Location": {
"bucket": "deploy-bucket",
"key": "appspec.yaml",
"bundleType": "YAML"
}
}
}
- CodeDeployのデプロイを作成する(デプロイ実行)
$ aws deploy create-deployment \
--cli-input-json file://create-deployment.json
2-3. 動作確認
- テストポートで動作確認する
2-4. Blue/Green切り替え
- 確認が完了したらCodeDeploy画面で『トラフィックの再ルーティング』を実行する。→ロールバックの必要がないと判断したら『元のタスクセットの終了』を選択する。