Docker バンドリング
いくつかのジェネレーター(ts#strands-agentやpy#strands-agentなど)は、Amazon ECRにプッシュされ、AWSインフラストラクチャによって使用されるDockerイメージを生成します。このガイドでは、これらのジェネレーターが従うパターンについて説明し、他のユースケースに適用できるようにします。例えば、Amazon ECS上でpy#fast-apiプロジェクトを実行したり、コンテナ化されたExpressサーバーをデプロイしたりする場合などです。
推奨されるパターンには3つの要素があります:
bundleターゲット - プロジェクト上に配置され、ランタイムアーティファクトの自己完結型ディレクトリを生成します。TypeScriptの場合、これはRolldownによって生成されるツリーシェイクされた単一ファイルのJavaScriptバンドルです。Pythonの場合、これはuvによって生成されるrequirements.txtとインストールされた依存関係です。- 最小限の
Dockerfile- バンドル出力をベースイメージに単純にCOPYするだけです。バンドリングによってすでにツリーシェイキングと依存関係のインストールが処理されているため、Dockerfileはnpm installやuv syncを実行する必要がありません。 dockerターゲット -Dockerfileをバンドル出力と一緒にコピーし(Docker buildコンテキストがランタイムに必要なファイルのみを含むようにするため)、docker buildを実行します。
Docker buildコンテキストはプロジェクトのdistフォルダーに書き込まれます。その後、Infrastructure as Code(CDKまたはTerraform)がそのディレクトリを指定してイメージをECRにプッシュします。
TypeScript
Section titled “TypeScript”Bundle Target
Section titled “Bundle Target”Rolldownを呼び出すbundleターゲットを設定します。ts#projectから始める場合は、project.jsonに以下を追加します:
{ "targets": { "bundle": { "cache": true, "executor": "nx:run-commands", "outputs": ["{workspaceRoot}/dist/{projectRoot}/bundle"], "options": { "command": "rolldown -c rolldown.config.ts", "cwd": "{projectRoot}" }, "dependsOn": ["compile"] } }}そして、プロジェクトのルートにrolldown.config.tsを配置します:
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', },]);bundleターゲットを実行してdist/packages/my-project/bundle/index.jsを生成します:
pnpm nx bundle my-projectyarn nx bundle my-projectnpx nx bundle my-projectbunx nx bundle my-projectDockerfile
Section titled “Dockerfile”プロジェクトのソースディレクトリにDockerfileを作成します。このファイルは、バンドルをNodeベースイメージにCOPYし、バンドルできなかったexternalパッケージをnpm installするだけです。RUN npm installステップをCOPYの前に配置することで、Dockerがインストールされたnode_modulesレイヤーをキャッシュし、依存関係リストが実際に変更されたときにのみ再実行されるようにします:
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
Section titled “Docker Target”以下を実行するdockerターゲットを追加します:
Dockerfileをバンドル出力ディレクトリにコピー(ビルドコンテキストがバンドル +Dockerfileのみを含むようにするため)docker buildを実行(CDKの場合はオプション — 以下を参照)
{ "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"] } }}このターゲットを実行すると、dist/packages/my-project/bundle/の最小限のコンテキストからビルドされた、my-scope-my-project:latestというタグが付けられたローカルイメージが生成されます:
pnpm nx docker my-projectyarn nx docker my-projectnpx nx docker my-projectbunx nx docker my-projectPython
Section titled “Python”Bundle Target
Section titled “Bundle Target”ターゲットプラットフォーム用の依存関係をエクスポートしてインストールするためにuvを使用するbundleターゲットを設定します。py#projectジェネレーターとpy#lambda-functionジェネレーターの両方がこれを設定します。ターゲット設定は次のようになります:
{ "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"] } }}nx bundle my-projectを実行すると、プロジェクトのソース、その依存関係、およびrequirements.txtを含むdist/packages/my-project/bundle-arm/が生成されます — イメージがランタイムに必要とするすべてのものです。
Dockerfile
Section titled “Dockerfile”Dockerfileは単純にバンドルをPythonベースイメージにコピーします。uvがすでにすべての依存関係をバンドルディレクトリにインストールしているため、イメージ内でpip installを実行する必要はありません:
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
Section titled “Docker Target”Dockerfileをバンドル出力ディレクトリにコピーし、docker buildを実行するdockerターゲットを追加します:
{ "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"] } }}これにより、出力ディレクトリがクリアされ、バンドルコンテンツとDockerfileの両方がdist/.../dockerにコピーされ、これがDocker buildコンテキストになります。
pnpm nx docker my-projectyarn nx docker my-projectnpx nx docker my-projectbunx nx docker my-projectInfrastructure
Section titled “Infrastructure”結果のビルドコンテキストディレクトリをInfrastructure as Codeに配線することは、TypeScriptとPythonの両方で同じです — ビルドコンテキストディレクトリへのパスのみが異なります(TypeScriptの場合はdist/packages/my-project/bundle、Pythonの場合はdist/packages/my-project/docker)。
CDKのDockerImageAssetをビルドコンテキストディレクトリに向けて使用します。CDKはデプロイ時にイメージをビルドし、CDKアセットECRリポジトリに公開します:
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,});findWorkspaceRootヘルパーは、ts#infraジェネレーターによって生成され、:my-scope/common-constructsからエクスポートされます。共有コンストラクトを使用していない場合は、cdkが呼び出される場所(通常はワークスペースルート)から相対的なdistディレクトリへのパスをハードコードし、findWorkspaceRoot呼び出しを完全に省略できます。
DockerImageAssetは、コンテナイメージを受け入れる任意のAWSコンストラクトで使用できます。例えば、aws_ecs.ContainerImage.fromDockerImageAsset(image)などです。
TerraformのAWSプロバイダーには、ファーストクラスの「Dockerイメージをビルドしてプッシュする」リソースがありません。ジェネレーターが使用するパターンは次のとおりです:
- イメージを保持するための
aws_ecr_repository - ECRへの認証、ローカルでビルドされたイメージの再タグ付け、プッシュを行う
local-execプロビジョナーを持つnull_resource - ダウンストリームリソース(例:
aws_ecs_task_definition)が"${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 }}data.external.docker_digestブロックは、ローカルイメージハッシュが変更されるたびにnull_resourceが再実行されることを保証し、意味のあるコード変更ごとに新しいプッシュをトリガーします。
ts#strands-agentジェネレーター — Bedrock AgentCore RuntimeにデプロイされるTypeScriptエージェントのこのパターンの完全な例。py#strands-agentジェネレーター — Pythonの同等のもの。- Rolldownドキュメント — TypeScriptバンドラーの設定リファレンス。
uvドキュメント — Python依存関係のエクスポートとインストールのリファレンス。