英語版はこちらをご覧ください!
こんにちは。愛猫リンゴちゃんが大好きなローです。
今回は、AWS CDK L2 Construct を使って Amazon Bedrock AgentCore Runtime への AI エージェントデプロイを完全自動化した取り組みをご紹介します。
注意事項
本記事で使用している@aws-cdk/aws-bedrock-agentcore-alphaはアルファリリースのパッケージです。本番環境での利用は各自の責任において慎重にご検討ください。また、API が変更される可能性があります。
- はじめに
- ソリューションアーキテクチャ
- プロジェクト概要
- L2 Construct の威力:必要なリソース定義の比較
- fromAsset() と DockerImageAsset の内部動作
- イメージビルドの2つのアプローチ
- ARM64 ビルドの課題と解決
- CI/CD パイプライン
- ハマったポイント
- まとめ
- 参考リンク
- おまけ
はじめに
Amazon Bedrock AgentCore Runtime は、AI エージェントをサーバーレスで実行できるマネージドサービスです。しかし、従来のデプロイ方法では以下の課題がありました:
- ECR リポジトリ、CodeBuild、Lambda、カスタムリソースなど多数の AWS リソースを明示的に定義
- ビルドパイプラインの複雑な依存関係管理
- IAM ロールとポリシーの詳細な設定
これらを AWS CDK L2 Construct と GitHub Actions を活用することで、必要最小限のリソース定義だけで完全自動化を実現しました。
ソリューションアーキテクチャ
以下は、GitHub Actions から Bedrock AgentCore Runtime へのデプロイまでの全体的な流れを示したアーキテクチャ図です:

主要コンポーネント:
- GitHub Actions: OIDC 認証で AWS にアクセス、Docker イメージをビルド
- AWS CDK: インフラをコードで定義、CloudFormation スタックをデプロイ
- ECR: CDK ブートストラップで作成された共有リポジトリにイメージを保存
- IAM: AgentCore Runtime の実行ロールと権限を自動作成
- AgentCore Runtime: コンテナを実行し、Bedrock モデルを呼び出し
プロジェクト概要
構成
.
├── .github/
│ └── workflows/
│ └── deploy.yml # CI/CD パイプライン
├── agent/
│ ├── agent.py # Strands エージェント
│ ├── Dockerfile # ARM64 コンテナ
│ └── requirements.txt # Python 依存関係
├── lib/
│ └── stack.ts # CDK スタック(L2 construct 使用)
└── bin/
└── app.ts # CDK アプリ
技術スタック
- AWS CDK: L2 Construct (
@aws-cdk/aws-bedrock-agentcore-alpha) - GitHub Actions: CI/CD パイプライン
- Docker Buildx + QEMU: ARM64 クロスプラットフォームビルド
- OIDC 認証: 長期的な認証情報不要
L2 Construct の威力:必要なリソース定義の比較
Before: L1 Construct - 6つの AWS リソースを明示的に定義
従来の L1 Construct では、AgentCore Runtime をデプロイするために以下のリソースをすべて明示的に定義する必要がありました:
// 1. ECR リポジトリ const repository = new ecr.Repository(this, 'Repository', { repositoryName: 'simple-agent', removalPolicy: cdk.RemovalPolicy.DESTROY, }); // 2. CodeBuild プロジェクト(Docker ビルド用) const buildProject = new codebuild.Project(this, 'BuildProject', { environment: { buildImage: codebuild.LinuxArmBuildImage.AMAZON_LINUX_2_STANDARD_3_0, privileged: true, }, buildSpec: codebuild.BuildSpec.fromObject({ version: '0.2', phases: { pre_build: { commands: [ 'echo Logging in to Amazon ECR...', 'aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login ...', ], }, build: { commands: [ 'docker build --platform linux/arm64 -t $IMAGE_REPO_NAME:$IMAGE_TAG .', 'docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr...', ], }, post_build: { commands: ['docker push $AWS_ACCOUNT_ID.dkr.ecr...'], }, }, }), }); // 3. Lambda 関数(CodeBuild トリガー用) const buildTrigger = new lambda.Function(this, 'BuildTrigger', { runtime: lambda.Runtime.PYTHON_3_12, handler: 'index.handler', code: lambda.Code.fromInline(` import boto3 import cfnresponse def handler(event, context): codebuild = boto3.client('codebuild') # ビルド開始ロジック(50行以上) ... `), timeout: cdk.Duration.minutes(5), }); // 4. カスタムリソース(デプロイ時にビルド実行) const triggerBuild = new cdk.CustomResource(this, 'TriggerBuild', { serviceToken: buildTrigger.functionArn, properties: { ProjectName: buildProject.projectName, Timestamp: Date.now(), }, }); // 5. IAM ロール(AgentCore Runtime 用) const agentRole = new iam.Role(this, 'AgentRole', { assumedBy: new iam.ServicePrincipal('bedrock-agentcore.amazonaws.com'), managedPolicies: [ iam.ManagedPolicy.fromAwsManagedPolicyName('CloudWatchLogsFullAccess'), ], }); repository.grantPull(agentRole); // 6. AgentCore Runtime(L1 Construct) const agentRuntime = new cdk.CfnResource(this, 'AgentRuntime', { type: 'AWS::BedrockAgentCore::Runtime', properties: { AgentRuntimeName: 'simpleagent', RoleArn: agentRole.roleArn, AgentRuntimeArtifact: { ContainerConfiguration: { ContainerUri: `${repository.repositoryUri}:latest`, }, }, }, }); // 依存関係を明示的に設定 agentRuntime.node.addDependency(triggerBuild);
明示的に定義が必要なリソース:
- ECR リポジトリ
- CodeBuild プロジェクト
- Lambda 関数
- カスタムリソース
- IAM ロール
- AgentCore Runtime
After: L2 Construct - 1つのリソースだけ定義
L2 Construct を使うと、AgentCore Runtime だけを定義すれば完了です:
import * as agentcore from '@aws-cdk/aws-bedrock-agentcore-alpha'; const runtime = new agentcore.Runtime(this, 'AgentRuntime', { runtimeName: 'simpleagent2', agentRuntimeArtifact: agentcore.AgentRuntimeArtifact.fromAsset('./agent', { platform: cdk.aws_ecr_assets.Platform.LINUX_ARM64, }), networkConfiguration: agentcore.RuntimeNetworkConfiguration.usingPublicNetwork(), protocolConfiguration: agentcore.ProtocolType.HTTP, }); // Bedrock 呼び出し権限を追加 runtime.addToRolePolicy( new iam.PolicyStatement({ actions: ["bedrock:InvokeModel*"], resources: [`arn:aws:bedrock:*::foundation-model/*`], }) );
注意: L2 Construct では Docker コンテナのビルドは CodeBuild ではなく、
cdk deployを実行する環境(ローカルマシンや CI/CD ランナー)で行われます。
明示的に定義が必要なリソース: 1. AgentCore Runtime(のみ!)
自動的に作成・管理されるリソース:
- ECR リポジトリ(CDK ブートストラップで事前作成された共有リポジトリを使用)
- Docker イメージのビルドとプッシュ(DockerImageAsset が自動実行)
- IAM ロール(Runtime が自動作成)
- ECR pull 権限(自動付与)
- 依存関係の解決(自動)
何が嬉しいのか?
1. インフラの意図が明確
- L1: 「どうやってビルドするか」の実装詳細を書く
- L2: 「何をデプロイしたいか」だけを書く
2. メンテナンス負荷の削減
- L1: CodeBuild の buildspec、Lambda のビルドロジック、IAM ポリシーをすべて管理
- L2: AgentCore Runtime の設定だけを管理
3. ベストプラクティスの自動適用
- L1: IAM 権限、依存関係、エラーハンドリングを自分で実装
- L2: CDK チームが検証済みのパターンが自動適用
4. 変更への柔軟性
- L1: ビルド方法を変更すると複数リソースを修正
- L2:
fromAsset()のオプションを変更するだけ
fromAsset() と DockerImageAsset の内部動作
L2 Construct の fromAsset() は、以下のような処理フローで動作します:

1. コンストラクタ実行時(new Runtime())
// fromAsset() は AssetImage インスタンスを返すだけ agentRuntimeArtifact: agentcore.AgentRuntimeArtifact.fromAsset('./agent', { platform: cdk.aws_ecr_assets.Platform.LINUX_ARM64, })
この時点では DockerImageAsset は作成されません。
2. CloudFormation テンプレート生成時(cdk synth)
Runtime コンストラクタ内で Lazy.any() による遅延評価が登録されます:
// runtime.js の内部処理(コンストラクタ内) const cfnProps = { agentRuntimeName: this.agentRuntimeName, roleArn: this.role.roleArn, agentRuntimeArtifact: Lazy.any({ produce: () => this.renderAgentRuntimeArtifact() // 遅延評価を登録 }), // ... };
cdk synth 実行時、CloudFormation テンプレート生成のタイミングで produce() が呼ばれ、renderAgentRuntimeArtifact() が実行されます:
// renderAgentRuntimeArtifact() の実装 renderAgentRuntimeArtifact() { this.agentRuntimeArtifact.bind(this, this); // ここで bind() を呼び出し const config = this.agentRuntimeArtifact._render(); // ... }
3. bind() メソッドの詳細
bind() メソッドは、fromAsset() で作成された AssetImage インスタンスに対して呼ばれ、実際の DockerImageAsset を作成します:
// runtime-artifact.js の実装 class AssetImage extends AgentRuntimeArtifact { private asset?: assets.DockerImageAsset; private bound = false; public bind(scope: Construct, runtime: Runtime): void { // DockerImageAsset を作成(初回のみ) if (!this.asset) { const hash = md5hash(this.directory); // ディレクトリパスからハッシュ計算 this.asset = new assets.DockerImageAsset(scope, `AgentRuntimeArtifact${hash}`, { directory: this.directory, ...this.options, // platform: LINUX_ARM64 などのオプション }); } // ECR pull 権限を付与(初回のみ) if (!this.bound) { this.asset.repository.grantPull(runtime.role); this.bound = true; } } }
bind() メソッドが行うこと:
- ハッシュ計算:
md5hash(directory)でディレクトリパスからハッシュを生成 - DockerImageAsset 作成: ハッシュを ID に含めて一意なリソースを作成
- アセット情報の記録:
cdk.out/manifest.jsonにビルド情報を記録 - IAM 権限の付与: Runtime の IAM ロールに ECR pull 権限を自動付与
- 冪等性の保証:
boundフラグで複数回呼ばれても安全
重要: 遅延評価は Lazy.any() で登録され、実際の処理は bind() メソッド内で行われます。
4. デプロイ時(cdk deploy)
cdk-assets ツールが manifest.json を読み込み:
- Docker イメージをビルド
- ECR に認証
- イメージをプッシュ
- CloudFormation スタックをデプロイ
重要なポイント: Docker ビルドは cdk synth 時ではなく、cdk deploy 時に実行されます。ハッシュが変わらなければ再ビルドをスキップできます。
イメージビルドの2つのアプローチ
今回採用した方法は、CDK の fromAsset() を使い、cdk deploy 時に自動的にイメージをビルド・プッシュする方法です。しかし、別のアプローチも可能です:
アプローチ1: CDK 統合型(本記事で採用)
agentRuntimeArtifact: agentcore.AgentRuntimeArtifact.fromAsset('./agent', { platform: cdk.aws_ecr_assets.Platform.LINUX_ARM64, })
- メリット: シンプル、リソース定義が最小限
- デメリット: ビルド時間が
cdk deployに含まれる
アプローチ2: 事前ビルド型
ECR リポジトリを CDK で独自に定義し、GitHub Actions で CodeBuild を呼び出してイメージをビルド・プッシュしてから cdk deploy を実行:
const repository = new ecr.Repository(this, 'Repository'); const buildProject = new codebuild.Project(this, 'BuildProject', { /* ... */ }); agentRuntimeArtifact: agentcore.AgentRuntimeArtifact.fromEcrRepository( repository, 'latest' )
# GitHub Actions - name: Build and push image run: aws codebuild start-build --project-name $PROJECT_NAME - name: Deploy CDK stack run: npx cdk deploy --require-approval never
- メリット: ビルドとデプロイの分離、ビルドキャッシュの柔軟な制御
- デメリット: リソース定義が増える、パイプラインが複雑化
どちらのアプローチも有効ですが、シンプルさを優先する場合はアプローチ1、ビルドプロセスを細かく制御したい場合はアプローチ2を選択します。
ARM64 ビルドの課題と解決
AgentCore Runtime は ARM64 アーキテクチャ必須ですが、GitHub Actions(x86_64)でどうビルドするか?
解決策: QEMU + Docker Buildx
- name: Set up QEMU uses: docker/setup-qemu-action@v3 # ARM64 エミュレーション - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 # クロスプラットフォームビルド - name: Deploy CDK stack run: npx cdk deploy --require-approval never
Dockerfile でも明示的にプラットフォームを指定:
FROM --platform=linux/arm64 python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY agent.py . EXPOSE 8080 CMD ["uvicorn", "agent:app", "--host", "0.0.0.0", "--port", "8080"]
CI/CD パイプライン
OIDC 認証で安全に
従来の Access Key/Secret Key ではなく、OIDC 認証を採用:
- name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} aws-region: us-west-2
メリット:
- 長期的な認証情報の管理不要
- 一時的な認証情報を自動発行
- 特定のリポジトリ・ブランチからのみアクセス可能
デプロイフロー
コード変更 → PR 作成 → レビュー → main マージ → 自動デプロイ(5分)
パスフィルターで不要なビルドを回避:
on: push: branches: - main paths: - 'folder/subfolder/**'
ハマったポイント
1. Bedrock 権限が自動付与されない
L2 Construct が自動作成する IAM ロールには、Bedrock API を呼び出す権限が含まれていません。
// 手動で追加が必要 runtime.addToRolePolicy( new iam.PolicyStatement({ actions: ["bedrock:InvokeModel*"], resources: [`arn:aws:bedrock:*::foundation-model/*`], }) );
2. クロスリージョンアクセス
Runtime はオレゴン(us-west-2)にデプロイしても、バージニア北部(us-east-1)の Claude Sonnet 4 を呼び出す場合があります。リージョンをワイルドカード(*)にすることで対応:
resources: [`arn:aws:bedrock:*::foundation-model/*`]
3. CDK ブートストラップ必須
L2 Construct は CDK のアセット公開システムを使用するため、事前に cdk bootstrap が必要です:
cdk bootstrap aws://ACCOUNT_ID/us-west-2
まとめ
AWS CDK L2 Construct を活用することで、複雑だった AgentCore Runtime のデプロイを大幅に簡素化できました。
学んだこと:
- L2 Construct の抽象化レベルの高さ
fromAsset()とbind()メソッドによる遅延評価の仕組みLazy.any()を使った CloudFormation 生成時の処理フロー- ARM64 クロスプラットフォームビルドの実装
- OIDC 認証の実践的な使い方
参考リンク
おまけ
リンゴちゃんとは?と思う方向けに写真一枚を添えます。 クリスマスはあと2ヶ月ですが、気温も一気に下がってしまっているので風邪を引かないように気を付けましょう~

ロータッヘイ(執筆記事の一覧)
24卒入社の香港人です。
2025 Japan All AWS Certifications Engineers
リンゴちゃん(デボンレックス)にいつも癒されています。