Đóng gói Docker
Một số generator (như ts#strands-agent và py#strands-agent) tạo ra Docker image được đẩy lên Amazon ECR và được sử dụng bởi hạ tầng AWS. Hướng dẫn này mô tả mẫu thiết kế mà chúng tuân theo để bạn có thể áp dụng cho các trường hợp sử dụng khác — ví dụ, chạy dự án py#fast-api trên Amazon ECS, hoặc triển khai máy chủ Express được container hóa.
Mẫu Thiết Kế
Phần tiêu đề “Mẫu Thiết Kế”Mẫu thiết kế được khuyến nghị có ba phần:
- Target
bundletrên dự án của bạn tạo ra một thư mục độc lập chứa các artifact runtime. Đối với TypeScript, đây là một JavaScript bundle đơn file đã được tree-shaken do Rolldown tạo ra; đối với Python, đây làrequirements.txtvà các dependency đã cài đặt do uv tạo ra. Dockerfiletối giản chỉ đơn giảnCOPYđầu ra bundle vào base image. Vì việc bundling đã xử lý tree-shaking và cài đặt dependency,Dockerfilekhông cần chạynpm installhoặcuv sync.- Target
dockersao chépDockerfilecùng với đầu ra bundle (để Docker build context chỉ chứa các file cần thiết tại runtime), sau đó chạydocker build.
Docker build context được ghi vào thư mục dist của dự án. Infrastructure as code của bạn (CDK hoặc Terraform) sau đó trỏ đến thư mục đó để đẩy image lên ECR.
TypeScript
Phần tiêu đề “TypeScript”Bundle Target
Phần tiêu đề “Bundle Target”Cấu hình target bundle gọi Rolldown. Nếu bạn bắt đầu từ ts#project, thêm phần sau vào project.json của bạn:
{ "targets": { "bundle": { "cache": true, "executor": "nx:run-commands", "outputs": ["{workspaceRoot}/dist/{projectRoot}/bundle"], "options": { "command": "rolldown -c rolldown.config.ts", "cwd": "{projectRoot}" }, "dependsOn": ["compile"] } }}Và một file rolldown.config.ts tại thư mục gốc của dự án:
import { defineConfig } from 'rolldown';
export default defineConfig([ { tsconfig: 'tsconfig.lib.json', input: 'src/index.ts', output: { file: '../../dist/packages/my-project/bundle/index.js', format: 'cjs', inlineDynamicImports: true, }, platform: 'node', },]);Chạy target bundle để tạo ra dist/packages/my-project/bundle/index.js:
pnpm nx bundle my-projectyarn nx bundle my-projectnpx nx bundle my-projectbunx nx bundle my-projectDockerfile
Phần tiêu đề “Dockerfile”Tạo Dockerfile trong thư mục source của dự án. File này chỉ làm việc COPY bundle vào Node base image, cộng với npm install bất kỳ package external nào không thể được bundle. Đặt bước RUN npm install trước lệnh COPY, để Docker có thể cache layer node_modules đã cài đặt và chỉ chạy lại khi danh sách dependency thực sự thay đổi:
FROM public.ecr.aws/docker/library/node:lts
WORKDIR /app
# Install packages that cannot be bundled (declared as "external" in rolldown.config.ts).# Kept above the COPY so this layer is cached and only invalidated when the install list changes.RUN npm install @aws/aws-distro-opentelemetry-node-autoinstrumentation@0.10.0
# Copy bundled applicationCOPY index.js /app
EXPOSE 8080
CMD ["node", "index.js"]Docker Target
Phần tiêu đề “Docker Target”Thêm target docker để:
- Sao chép
Dockerfilevào thư mục đầu ra bundle (để build context chỉ chứa bundle +Dockerfile), và - Chạy
docker build(tùy chọn cho CDK — xem bên dưới).
{ "targets": { "docker": { "cache": true, "executor": "nx:run-commands", "options": { "commands": [ "ncp packages/my-project/src/Dockerfile dist/packages/my-project/bundle/Dockerfile", "docker build --platform linux/arm64 -t my-scope-my-project:latest dist/packages/my-project/bundle" ], "parallel": false }, "dependsOn": ["bundle"] } }}Chạy target này tạo ra image local được tag my-scope-my-project:latest, được build từ context tối giản tại dist/packages/my-project/bundle/:
pnpm nx docker my-projectyarn nx docker my-projectnpx nx docker my-projectbunx nx docker my-projectPython
Phần tiêu đề “Python”Bundle Target
Phần tiêu đề “Bundle Target”Cấu hình target bundle sử dụng uv để export và cài đặt dependency cho nền tảng đích của bạn. Generator py#project và generator py#lambda-function đều cấu hình điều này cho bạn. Cấu hình target trông như sau:
{ "targets": { "bundle-arm": { "cache": true, "executor": "nx:run-commands", "outputs": ["{workspaceRoot}/dist/{projectRoot}/bundle-arm"], "options": { "commands": [ "uv export --frozen --no-dev --no-editable --project {projectRoot} --package my_project -o dist/{projectRoot}/bundle-arm/requirements.txt", "uv pip install -n --no-deps --no-installer-metadata --no-compile-bytecode --python-platform aarch64-manylinux_2_28 --target dist/{projectRoot}/bundle-arm -r dist/{projectRoot}/bundle-arm/requirements.txt" ], "parallel": false }, "dependsOn": ["compile"] } }}Chạy nx bundle my-project tạo ra dist/packages/my-project/bundle-arm/ chứa source của dự án, các dependency, và requirements.txt — mọi thứ image cần tại runtime.
Dockerfile
Phần tiêu đề “Dockerfile”Dockerfile đơn giản sao chép bundle vào Python base image. Vì uv đã cài đặt tất cả dependency vào thư mục bundle, bạn không cần chạy pip install bên trong image:
FROM public.ecr.aws/docker/library/python:3.12-slim
WORKDIR /app
# Copy bundled package (source + installed dependencies)COPY . /app
EXPOSE 8080
ENV PYTHONPATH=/appENV PATH="/app/bin:${PATH}"
CMD ["python", "-m", "my_project.main"]Docker Target
Phần tiêu đề “Docker Target”Thêm target docker sao chép Dockerfile vào thư mục đầu ra bundle, sau đó chạy docker build:
{ "targets": { "docker": { "cache": true, "executor": "nx:run-commands", "options": { "commands": [ "rimraf dist/packages/my-project/docker", "make-dir dist/packages/my-project/docker", "ncp dist/packages/my-project/bundle-arm dist/packages/my-project/docker", "ncp packages/my-project/src/Dockerfile dist/packages/my-project/docker/Dockerfile", "docker build --platform linux/arm64 -t my-scope-my-project:latest dist/packages/my-project/docker" ], "parallel": false }, "dependsOn": ["bundle-arm"] } }}Điều này xóa thư mục đầu ra, sau đó sao chép cả nội dung bundle và Dockerfile vào dist/.../docker, trở thành Docker build context.
pnpm nx docker my-projectyarn nx docker my-projectnpx nx docker my-projectbunx nx docker my-projectInfrastructure
Phần tiêu đề “Infrastructure”Kết nối thư mục build-context kết quả với infrastructure as code là giống nhau cho cả TypeScript và Python — chỉ có đường dẫn đến thư mục build-context khác nhau (dist/packages/my-project/bundle cho TypeScript, dist/packages/my-project/docker cho Python).
Sử dụng DockerImageAsset của CDK trỏ đến thư mục build-context. CDK sẽ build image và publish nó lên CDK asset ECR repository tại thời điểm deploy:
import { DockerImageAsset, Platform } from 'aws-cdk-lib/aws-ecr-assets';import { findWorkspaceRoot } from ':my-scope/common-constructs';import * as path from 'path';import * as url from 'url';
const image = new DockerImageAsset(this, 'MyImage', { directory: path.join( // Resolve from the compiled construct location to the workspace root findWorkspaceRoot(url.fileURLToPath(new URL(import.meta.url))), 'dist/packages/my-project/bundle', ), platform: Platform.LINUX_ARM64,});Helper findWorkspaceRoot được tạo bởi generator ts#infra và được export từ :my-scope/common-constructs. Nếu bạn không sử dụng shared constructs, bạn có thể hardcode đường dẫn đến thư mục dist tương đối với nơi cdk được gọi từ đó — thường là workspace root — và bỏ qua hoàn toàn lời gọi findWorkspaceRoot.
Sử dụng DockerImageAsset với bất kỳ AWS construct nào chấp nhận container image, ví dụ aws_ecs.ContainerImage.fromDockerImageAsset(image).
AWS provider của Terraform không có resource “build and push a Docker image” cấp một. Mẫu được sử dụng bởi các generator là:
- Một
aws_ecr_repositoryđể lưu trữ image. - Một
null_resourcevới provisionerlocal-execxác thực với ECR, re-tag image được build locally, và đẩy nó lên. - Resource downstream (ví dụ
aws_ecs_task_definition) tham chiếu"${aws_ecr_repository.repo.repository_url}:latest".
resource "aws_ecr_repository" "repo" { name = "my-project-repository" image_tag_mutability = "MUTABLE" force_delete = true}
# Invalidate the push whenever the locally-built image digest changesdata "external" "docker_digest" { program = ["sh", "-c", "echo '{\"digest\":\"'$(docker inspect my-scope-my-project:latest --format '{{.Id}}')'\"}'"]}
resource "null_resource" "docker_publish" { triggers = { docker_digest = data.external.docker_digest.result.digest repository_url = aws_ecr_repository.repo.repository_url }
provisioner "local-exec" { command = <<-EOT aws ecr get-login-password --region ${data.aws_region.current.id} \ | docker login --username AWS --password-stdin ${self.triggers.repository_url} docker tag my-scope-my-project:latest ${self.triggers.repository_url}:latest docker push ${self.triggers.repository_url}:latest EOT }}Block data.external.docker_digest đảm bảo null_resource chạy lại bất cứ khi nào hash của local image thay đổi, kích hoạt push mới cho mỗi thay đổi code có ý nghĩa.
Đọc Thêm
Phần tiêu đề “Đọc Thêm”ts#strands-agentgenerator — một ví dụ hoàn chỉnh về mẫu này cho TypeScript agent được triển khai lên Bedrock AgentCore Runtime.py#strands-agentgenerator — tương đương cho Python.- Tài liệu Rolldown — tham chiếu cấu hình cho TypeScript bundler.
- Tài liệu
uv— tham chiếu cho Python dependency export và install.