はじめに
ecspressoを使いつつAWS CodePipeline上での承認フローステージを通してECSへデプロイする方法を検証しました。
前提
- 既存ですでに以下のリソースが存在している前提になります。
- Docker Image on ECR
- ALB
- ECS Cluster
- ECS Task Definition
- ECS Service
- CodePipelineに付与するIAMサービスロール
- 上記を構成するためのVPC、サブネット、セキュリティグループなど諸々
- CIツールはGitLab CI
- ECSはFargateタイプであり、ECS ServiceのデプロイではCodeDeploy(Blue/Green)を利用している。
ローカル環境
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.15.6
BuildVersion: 19G2021
$ ecspresso version
ecspresso v1.2.1
Contents
概要
概要として今回は全体として以下のようなアプリケーションのデプロイフローを構築しました。
CIツールであるGitLab CIにてECRへDockerイメージをPushすることとS3へecspressoからtaskdef.jsonとappspec.yamlを出力。
CodePipelineでは承認フローを設定し承認されたらECS(Blue/Green)にてタスク定義が反映されCodeDeployからアプリケーションがデプロイされます。
ecspressoの初期設定
ecspressoの初期設定を行います。
既存でECSには以下のようなECS ClusterとECS Serviceがあり、このアプリケーションのデプロイフローを構築する場合、以下のようなに実行します。1
$ ecspresso init --config config.yaml --region ap-northeast-1 --cluster ecspressopoc --service rjpf-ecspressopoc-service
上記を実行することで以下の3つのファイルが生成されます。
-
config.yaml
-> ecspresso 設定ファイル -
ecs-service-def.json
-> ECSサービスの構成を定義するecspresso用ファイル -
ecs-task-def.json
-> aws cliで得られるのと同様なECSタスク定義ファイル
config.yaml
と ecs-service-def.json
こちらの公式ドキュメントと同じ項目のファイルになっています。
config.yaml
とecs-service-def.json
に関しては修正する部分は特にありません。
ecs-task-def.json
ecs-task-def.json
もこちらの公式ドキュメントと同様に出力されています。
ここでCIツールを使って継続的にECS Task Definitionバージョンを更新できるように修正します。
ecspressoのテンプレート機能を利用します。2
ECR上のImageバージョンのimmutable性を保証するためにも"Image"
フィールドは以下のように修正します。
"image": "{{ must_env `ECR_IMAGE_URL` }}",
後述のCIツールのフロー内でECR_IMAGE_URL
環境変数を設定しCIでのecspressoコマンドにてECRへPushしたURLに置換してECS Task Definitionへ更新します。
また、アプリケーションの環境変数をデプロイ先の環境ごとに変えるためにECS Task Definitionの環境変数を設定する箇所も以下のようにしています。CIツールのフローは後述します。
"environment": [
{
"name": "DB_HOST",
"value": "{{ must_env `DB_HOST` }}"
}
],
CodePipelineのトリガーとなるS3バケットの作成
今回はCodePipelineのSource ActionとしてS3を利用するのでそのためのS3バケットを作成します。3
このS3は後述するCDKにて管理対象のリソースとして作成しています。
CIツール(GitLab CI) + ecspresso
以下が今回利用したGitLab CIでのYamlファイルになります。
ステップとして以下になります。
-
push
ステージにて -
docker build
で対象アプリケーションをビルドする。 - AWS ECRへ対象イメージをタグ名(Gitコミットハッシュ)付きでDOCKER PUSHする。
-
deploy
ステージにて -
ecspresso render
でECS Task Definitionファイルのtaskdef.json
をテンプレートから生成する。 -
ecspresso appspec
によりECS(Blue/Green)でのCodeDeployのためのappspec.yaml
を生成する。 - 上記2つのファイルをZIP化し上述のS3バケットへアップロードする。
variables:
IMAGE_TAG: ${CI_COMMIT_SHA}
ECSPRESSO_VERSION: "v1.2.1"
AWSCLI_VERSION: "1.18.134"
ECR_NAME: "ecspressopoc"
stages:
- push
- deploy
.push_variables: &push_variables
DOCKER_DRIVER: overlay2
.push_setup: &push_setup
image: docker:stable
services:
- docker:18.09-dind
before_script:
- apk add git
- apk -Uuv add groff less python3 py-pip
- pip install awscli
.push: &push
stage: push
script:
- docker build -t ${ECR_NAME} .
- aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com
- docker tag ${ECR_NAME}:latest ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${ECR_NAME}:${IMAGE_TAG}
- docker push ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${ECR_NAME}:${IMAGE_TAG}
.deploy_setup: &deploy_setup
image: python:3.8
before_script:
# install AWS CLI
- pip install --upgrade pip
- pip install --upgrade awscli==${AWSCLI_VERSION}
# install commands
- apt-get update -y && apt-get install -y curl unzip zip
# install ecspresso
- curl -sL -o ecspresso-${ECSPRESSO_VERSION}-linux-amd64.zip https://github.com/kayac/ecspresso/releases/download/${ECSPRESSO_VERSION}/ecspresso-${ECSPRESSO_VERSION}-linux-amd64.zip
- unzip ecspresso-${ECSPRESSO_VERSION}-linux-amd64.zip
- install ecspresso-${ECSPRESSO_VERSION}-linux-amd64 /usr/local/bin/ecspresso
- ecspresso version
.deploy: &deploy
stage: deploy
script:
- export ECR_IMAGE_URL=${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${ECR_NAME}:${IMAGE_TAG} # For ecspresso
- export DB_HOST=${DB_HOST}
- ecspresso --config config.yaml render --task-definition > taskdef.json
- ecspresso --config config.yaml appspec | sed -e '/TaskDefinition/ s/arn.*$/"<TASK_DEFINITION>"/' > appspec.yaml
- zip ecspresso-deploy-poc.zip appspec.yaml taskdef.json
- aws s3 cp ecspresso-deploy-poc.zip s3://dev-ecspresso-poc
.dev_variables: &dev_variables
AWS_ENV: "dev"
AWS_ACCOUNT_ID: "123456789012"
DB_HOST: ${DEV_DB_HOST} # variable is in GitLab CI setting
.dev_config: &dev_config
only:
- develop
tags:
- my-platform_dev_docker
allow_failure: false
dev-push:
variables:
<<: *dev_variables
<<: *push_variables
<<: *dev_config
<<: *push_setup
<<: *push
dependencies:
- dev-migrate-task
dev-deploy:
variables:
<<: *dev_variables
<<: *dev_config
<<: *deploy_setup
<<: *deploy
dependencies:
- dev-push
ecspresso render
とecs appspec
のそれぞれのサブコマンドについてはそれぞれ以下のページで詳細を確認できます。
- https://zenn.dev/fujiwara/articles/ecspresso-20201212
- https://zenn.dev/fujiwara/articles/ecspresso-20201218
CodePipeline with 承認フロー
CodePipelineをAWS CDKを利用して構築しました。
注意 ただし注意点として今回対象にしていたCodePipelineのECS Blue/Green Action ProviderがCDKでは2021年1月時点で未対応なのでその部分に関しては、私は現状ではそれ以前のステージまではCDKで対応しcdk deploy
後に手動で追加修正している状況です。GitHubでは https://github.com/aws/aws-cdk/issues/1559 のIssueが最新の動向になっています。
この記事ではCDKのインストール方法と基本的な利用方法には言及しません。
今回はCDKではTypeScriptを利用しました。対象のコードとしては以下になります。
import { Role } from "@aws-cdk/aws-iam";
import { Bucket, BlockPublicAccess } from "@aws-cdk/aws-s3";
import { Stack, Construct, StackProps } from "@aws-cdk/core";
import { Pipeline, Artifact } from "@aws-cdk/aws-codepipeline";
import {
S3SourceAction,
ManualApprovalAction
} from "@aws-cdk/aws-codepipeline-actions";
export class EcspressopocCpipeStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
// The code that defines your stack goes here
// Source step
//
// S3 bucket as source
const sourceBucket = new Bucket(this, 'ecspressopoc-bucket', {
versioned: true, // a Bucket used as a source in CodePipeline must be versioned
blockPublicAccess: BlockPublicAccess.BLOCK_ALL
});
// Source Action
const sourceOutput = new Artifact();
const sourceAction = new S3SourceAction({
actionName: 'S3Source',
bucket: sourceBucket,
bucketKey: 'ecspresso-deploy-poc.zip',
output: sourceOutput,
});
// Approval step
//
// Manual Approval Action
const approvalAction = new ManualApprovalAction({
actionName: 'ManualApproval',
});
// ECS Deploy step
// TODO: Waiting for ECS Blue Green Deployment in CDK
// Assemble pipeline
new Pipeline(this, "Pipeline", {
pipelineName: "ecspressopoc-cdk",
role: Role.fromRoleArn(this, 'plRole', 'arn:aws:iam::12345678901:role/ecspresso-poc'); // role which can access to ECS Blue/Green Deployment,
stages: [
{
stageName: "S3-Source",
actions: [sourceAction],
},
{
stageName: "Approval",
actions: [approvalAction],
}
],
});
}
}
上記では前述のようにGitLab CIがアップロードする先のS3バケットを以下の設定で作成しています。
-
versioned: true
← CodePipeline Source用のS3バケットは必ずVersioningをEnabledにしている必要がある。 -
blockPublicAccess: BlockPublicAccess.BLOCK_ALL
← 内部でしか利用しないのでPublicアクセスはすべてブロックにしておく。
手動承認に関して今回は最小限の設定です。より詳しい仕様は公式ドキュメントで確認できます。
上記をcdk deploy
によりデプロイした後に前述の理由から手動でECS(Blue/Green)でのデプロイ設定をします。以下スクショのようにしています。
CodePipelineでは以下のスクショのようになり、GitLab CIがS3へZIPファイルをアップロードすると処理が開始され、手動承認をするとアプリケーションがECSへデプロイされることを確認しました。
参考
-
詳細は https://zenn.dev/fujiwara/articles/f2314651691adcae5215.md 参照 ↩
-
テンプレート機能に関してはこちらの公式ドキュメントに詳細があります。 ↩
-
公式ドキュメント https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-S3.html ↩