ECR+App RunnerをCDKデプロイする - dyoshikawa’s blog
をさらに実戦に近づけるために、今度はNode.js Expressアプリをデプロイしてみる。
環境
- M1 Mac Big Sur
- node 16.15.0
- typescript 5.0.2
- aws-cdk-lib 2.69.0
- constructs 10.1.281
- @aws-cdk/aws-apprunner-alpha 2.69.0-alpha.0
- cdk-docker-image-deployment 0.0.195
- esbuild 0.17.12
コード
- esbuildでバンドルJSファイル生成
- 1のバンドルファイルを入れたDockerイメージをビルドする
- 2のDockerイメージをECRリポジトリに上げる
- 3のECRリポジトリを参照するApp Runnerサービスを起動
という方針。
CDKコード:
// lib/app-runner-stack-express.ts import { Construct } from "constructs"; import * as cdk from "aws-cdk-lib"; import * as apprunner from "@aws-cdk/aws-apprunner-alpha"; import * as imagedeploy from "cdk-docker-image-deployment"; export class AppRunnerExpressStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const repository = new cdk.aws_ecr.Repository(this, "expressRepository", { repositoryName: "express-repository2", removalPolicy: cdk.RemovalPolicy.DESTROY, }); new imagedeploy.DockerImageDeployment(this, "imageDeploy", { source: imagedeploy.Source.directory("./src/app-runner-express"), destination: imagedeploy.Destination.ecr(repository, { tag: "latest", }), }); const service = new apprunner.Service(this, "apprunnerService", { source: apprunner.Source.fromEcr({ imageConfiguration: { port: 3000 }, repository, tagOrDigest: "latest", }), }); new cdk.CfnOutput(this, "serviceUrl", { exportName: "serviceUrl", value: service.serviceUrl, }); } }
Dockerfile:
# src/app-runner-express/Dockerfile FROM --platform=linux/amd64 node:18-slim WORKDIR /app COPY main.js main.js CMD [ "node", "main.js" ]
Expressアプリケーションコード:
import express, { Request, Response } from "express"; const app = express(); const port = 3000; app.get("/", (req: Request, res: Response) => { res.send("Hello, World!"); }); app.listen(port, () => { console.log(`Server running at http://localhost:${port}`); });
package.json:
// package.json "scripts": { "build:app-runner": "esbuild src/app-runner-express/main.ts --platform=node --target=node18 --bundle --minify --outfile=./src/app-runner-express/main.js", },
esbuildでアプリケーションTSコードをJSファイルにバンドルするnpm scriptsを登録しておく。
あとはこんな感じでビルドとデプロイできる:
npm run build:app-runner && npx cdk deploy
ハマった点
最初、バンドルファイルを {PROJECT_ROOT}/dist/app-runner-express/main.js
に吐くようにしていた:
// package.json "scripts": { "build:app-runner": "esbuild src/app-runner-express/main.ts --platform=node --target=node18 --bundle --minify --outfile=./dist/app-runner-express/main.js", },
そしてDockerfileのCOPYは次のようにしていた:
# src/app-runner-express/Dockerfile COPY ../../dist/app-runner-express/main.js main.js
これで docker build src/app-runner-express
すると
=> ERROR [3/3] COPY ../../dist/app-runner-express/main.js main.js 0.0s ------ > [3/3] COPY ../../dist/app-runner-express/main.js main.js: ------ failed to compute cache key: failed to walk /var/lib/docker/tmp/buildkit-mountxxxxxxx/dist/app-runner-express: lstat /var/lib/docker/tmp/buildkit-mountxxxxxxxx/dist/app-runner-express: no such file or directory
とエラー。
Dockerビルド時のContext設定を合わせることで解決できるようだったが、今回はシンプルにDockerfileの隣にバンドルファイルを吐くようにすることで対応した。
// package.json "scripts": { - "build:app-runner": "esbuild src/app-runner-express/main.ts --platform=node --target=node18 --bundle --minify --outfile=./dist/app-runner-express/main.js", + "build:app-runner": "esbuild src/app-runner-express/main.ts --platform=node --target=node18 --bundle --minify --outfile=./src/app-runner-express/main.js", },
# src/app-runner-express/Dockerfile - COPY ../../dist/app-runner-express/main.js main.js + COPY main.js main.js