コンテンツにスキップ

AWS CDK で ECS をデプロイ (Immutable Tag 運用)

やり方の概要

  • アクティブなタグは Parameter Store に保存
  • repository スタックで ECR リポジトリと初期イメージを生成してpush、Parameter を生成する
    • Parameter の初期値はビルドしたダミーイメージ (initial) とする。
  • ecr スタックで Parameter Store からタグを読んでデプロイ

Repository スタック

lib/repository/initial 配下に、初期イメージ生成用の Dockerfile を作成します。

FROM amd64/node:18-slim
RUN npm install -g install http-server
EXPOSE 80
ENTRYPOINT ["http-server", "-p", "80"]

lib/repository/stack.ts

import * as cdk from "aws-cdk-lib";
import * as ecr from "aws-cdk-lib/aws-ecr";
import * as ecr_deployment from "cdk-ecr-deployment";
import * as ecr_assets from "aws-cdk-lib/aws-ecr-assets";
import * as path from "path";
import {Construct} from "constructs";

const INITIAL_IMAGE_TAG = "initial"; // 初期値: 変更しないこと!

export class RepositoryStack extends cdk.Stack {
    public repository: ecr.IRepository;
    public imageTagParameterName: string;

    constructor(scope: Construct, id: string, props: cdk.StackProps) {
        super(scope, id, props);

        const repositoryName = "foo"; // リポジトリ名
        const imageTagParameterName = `/foo/imageTag`; // イメージタグ保存先 Parameter

        // 初期イメージ作成 (タグ: initial)
        const initialImage = new ecr_assets.DockerImageAsset(this, "InitialImageAssets", {
            directory: path.resolve(__dirname, INITIAL_IMAGE_TAG),
        });

        // ECR Repository
        const repository = new ecr.Repository(this, "Repository", {
            repositoryName: repositoryName,
        });
        new ecr_deployment.ECRDeployment(this, "WebInitialImage", {
            src: new ecr_deployment.DockerImageName(initialImage.imageUri),
            dest: new ecr_deployment.DockerImageName(repository.repositoryUriForTag(INITIAL_IMAGE_TAG)),
        });

        イメージタグは Parameter Store に保存する
        new ssm.StringParameter(this, "ImageTag", {
            parameterName: imageTagParameterName,
            stringValue: INITIAL_IMAGE_TAG, // 初期値
        });

        this.repository = repository;
        this.imageTagParameterName = imageTagParameterName;
    }
}

ECR スタック

lib/ecs/stack.ts

import * as cdk from "aws-cdk-lib";
import * as ecs from "aws-cdk-lib/aws-ecs";
import * as ecs_patterns from "aws-cdk-lib/aws-ecs-patterns";
import * as ssm from "aws-cdk-lib/aws-ssm";
import {Construct} from "constructs";

interface EcsStackProps extends cdk.StackProps {
    readonly repository: ecr.IRepository;
    readonly imageTagParameterName: string;
}

export class EcsStack extends cdk.Stack {
    constructor(scope: Construct, id: string, props: EcsStackProps) {
        super(scope, id, props);

        // Parameter
        const imageTagParameter = ssm.StringParameter.fromStringParameterName(this, "ImageTag", props.imageTagParameterName);

        // ECS
        const service = new ecs_patterns.ApplicationLoadBalancedFargateService(this, "Service", {
            taskImageOptions: {
                image: ecs.ContainerImage.fromEcrRepository(props.webRepository, imageTagParameter.stringValue),
                ...
            },
            ...
        });
    }
}

スタックの作成

const repositoryStack = new repositoryStack(app, "repository", {
    ...
});
const ecsStack = new ecsStack(app, "ecs", {
    ...,
    repository: repositoryStack.repository,
    imageTagParameterName: repositoryStack.imageTagParameterName,
});
ecsStack.addDependency(repositoryStack);