AWS CDK で Amazon ECR に Docker イメージを push して Amazon ECS のタスク定義に使う
AWS CDK で Amazon ECR の特定のリポジトリに Docker イメージを push して Amazon ECS のタスク定義に使う。
環境
- aws-cdk v2.56.1
- aws-cdk-lib v2.56.1
- cdk-ecr-deployment v2.5.6
本文
ディレクトリ構成
ルート
|
|-app
| |-(アプリケーションのソースコード)
|
|-cdk
| |-(cdk init でできたファイル群)
|
|-docker
| |-go
| |- Dockerfile.prod
|
|-.dockerignore
|-(その他のファイル)
Dockerfile
前述のディレクトリ構成の「ルート」を Docker の build context とし、Dockerfile.prod の親階層にあるアプリケーションのソースコードをコピーする設計となっている。
FROM golang:1.19 AS build-env
WORKDIR /app
COPY app ./
RUN go mod tidy && \
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o server server.go
FROM gcr.io/distroless/static-debian11
WORKDIR /app
COPY --from=build-env /app/server ./
ENTRYPOINT ["./server"]
.dockerignore
Docker イメージビルド時に無視するファイルを指定する。
ポイントとして、今回は build context に cdk ディレクトリが含まれているので cdk ディレクトリは無視する必要がある。
無視しないと cdk/cdk.out のファイルの中身で ENAMETOOLONG: name too long
というエラーが出る。
# このファイルがある階層のファイルは全部無視
.gitignore
.env.*
env.*
*.yaml
*.md
# cdk は無視
cdk/*
# ローカル開発用のファイルを無視
docker/db/*
app/tmp/*
# ローカル開発時に生成される機密ファイルを無視
app/*.json
cdk/lib/app-stack.ts
ECR のリポジトリを CDK で作成することもできるのだが、cdk deploy
と cdk destroy
を繰り返したら「同名のリポジトリは作成できない」というエラーになったので、既存のリポジトリを取得するようにした。
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import { DockerImageAsset } from "aws-cdk-lib/aws-ecr-assets";
import { ECRDeployment, DockerImageName } from "cdk-ecr-deployment";
import { Repository } from "aws-cdk-lib/aws-ecr";
import {
ContainerImage,
FargateTaskDefinition,
LogDriver,
} from "aws-cdk-lib/aws-ecs";
import { Role, ServicePrincipal, ManagedPolicy } from "aws-cdk-lib/aws-iam";
import { LogGroup } from "aws-cdk-lib/aws-logs";
import path = require("path");
export class AppStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const repositoryName = "sololog-back-repository";
// 既存のリポジトリを取得
const repository = Repository.fromRepositoryName(
this,
"SolologBackRepository",
repositoryName
);
// directory には build context を指定
const image = new DockerImageAsset(this, "SolologBackImageAsset", {
directory: path.join(__dirname, "..", ".."),
file: path.join("docker", "go", "Dockerfile.prod"),
});
new ECRDeployment(this, "SolologBackImage", {
src: new DockerImageName(image.imageUri),
dest: new DockerImageName(
`${cdk.Aws.ACCOUNT_ID}.dkr.ecr.${cdk.Aws.REGION}.amazonaws.com/${repositoryName}:latest`
),
});
const taskExecutionRole = new Role(this, "SolologBackTaskExecutionRole", {
roleName: "sololog-back-task-execution-role",
assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"),
managedPolicies: [
ManagedPolicy.fromAwsManagedPolicyName(
"service-role/AmazonECSTaskExecutionRolePolicy"
),
],
});
const taskRole = new Role(this, "SolologBackTaskRole", {
roleName: "sololog-back-task-role",
assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"),
managedPolicies: [
// 私のタスクは SSM にアクセスするので指定しているが、アクセスしないなら不要
ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMReadOnlyAccess"),
],
});
const taskDefinition = new FargateTaskDefinition(
this,
"SolologBackTaskDef",
{
taskRole,
executionRole: taskExecutionRole,
cpu: 256,
memoryLimitMiB: 512,
}
);
const logGroup = new LogGroup(this, "ServiceLogGroup", {
// 固定名にすると deploy と destroy を繰り返したときエラーになるので適当に乱数をつけておく
logGroupName: `sololog-back-logs-${Math.random()}`,
});
taskDefinition.addContainer("SolologBackAppContainer", {
image: ContainerImage.fromEcrRepository(repository, "latest"),
portMappings: [
{
containerPort: 8080,
},
],
logging: LogDriver.awsLogs({
streamPrefix: "app",
logGroup,
}),
});
// 以下略
}
}
以上