cdk-local (cdkl) で Bedrock AgentCore Runtime のローカル開発体験を試してみた
はじめに
go-to-k/cdk-local (以下 cdkl) に Bedrock AgentCore Runtime のローカル実行サブコマンド invoke-agentcore が追加されました。あわせて AWS CDK の --hotswap も AgentCore Runtime に対応したため、開発サイクルを高速化する選択肢が増えています。
本記事では cdkl・hotswap・通常デプロイの3手法でデプロイ速度を実測し、cdkl 独自機能(--from-cfn-stack、--assume-role)の動作も確認しました。
cdkl や hotswap は開発用です。本番反映には通常 cdk deploy を使ってください。
以前、同じ作者の cdkd について以下の記事を書きました。
hotswap の AgentCore Runtime 対応については以下で解説されています。
検証環境と準備
環境
検証時点(2026年6月)のバージョンです。
| 項目 | 値 |
|---|---|
| EC2 | t4g.medium (ARM64), Amazon Linux 2023 |
| リージョン | us-east-1 |
| Node.js | v22.22.3 |
| CDK CLI | 2.1125.0 |
| cdk-local | 0.105.0 |
| Docker | 25.0.14 |
| IAM ロール | AdministratorAccess + AmazonSSMManagedInstanceCore(検証用) |
本記事では検証簡略化のため AdministratorAccess を使用しています。実運用・共有環境では必要最小権限に絞ってください。
検証環境の構築には CloudFormation テンプレートを使用しました(付録に全文を掲載)。
アプリコード (agent-code/main.py)
AgentCore Runtime アプリはポート 8080 で /ping (GET) と /invocations (POST) を実装する必要があります。環境変数 GREETING の値をレスポンスで返すシンプルなアプリです。
from http.server import HTTPServer, BaseHTTPRequestHandler
import json, os
VERSION = "AAA"
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/ping':
self.send_response(200)
self.end_headers()
return
self.send_response(404)
self.end_headers()
def do_POST(self):
if self.path == '/invocations':
greeting = os.environ.get('GREETING', 'NOT_SET')
resp = json.dumps({"greeting": greeting, "version": VERSION})
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
self.wfile.write(resp.encode())
return
self.send_response(404)
self.end_headers()
if __name__ == '__main__':
server = HTTPServer(('0.0.0.0', 8080), Handler)
server.serve_forever()
CDK スタック (lib/agentcore-app-stack.ts)
SSM パラメータから環境変数を取得し、AgentCore Runtime に渡します。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as agentcore from 'aws-cdk-lib/aws-bedrockagentcore';
import * as ssm from 'aws-cdk-lib/aws-ssm';
import * as path from 'path';
export class AgentcoreAppStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const greeting = ssm.StringParameter.valueForStringParameter(this, '/cdkl-test/greeting');
const artifact = agentcore.AgentRuntimeArtifact.fromCodeAsset({
path: path.join(__dirname, '..', 'agent-code'),
runtime: agentcore.AgentCoreRuntime.PYTHON_3_12,
entrypoint: ['main.py'],
});
new agentcore.Runtime(this, 'HelloAgent', {
runtimeName: 'helloAgent',
agentRuntimeArtifact: artifact,
environmentVariables: {
GREETING: greeting,
},
});
}
}
CDK プロジェクトの初期化
mkdir agentcore-app && cd agentcore-app
cdk init app --language typescript
npm install aws-cdk-lib constructs
SSM パラメータの作成とデプロイ
aws ssm put-parameter --name /cdkl-test/greeting --type String --value "Hello from SSM!" --overwrite --region us-east-1
cdk deploy --region us-east-1
デプロイ速度比較
3手法で4回ずつ計測し平均を取りました。全て warm 実行(Docker キャッシュあり、既存スタック更新)で、各試行では Python コード内の VERSION 定数のみを "AAA" → "BBB" → "CCC" ... と変更しています(Docker ビルドはレイヤーキャッシュが効く前提)。SSM パラメータや CDK 定義は変更していません。cdkl はローカルでレスポンス取得まで、hotswap / 通常 deploy は CLI 完了までを計測範囲としました。
計測結果
| 手法 | 平均 | 通常比 | ドリフト(※) |
|---|---|---|---|
cdkl invoke-agentcore (ローカル) |
16.8秒 | 2.1倍速い | なし |
cdk deploy --hotswap |
15.2秒 | 2.3倍速い | あり |
cdk deploy (通常) |
35.1秒 | 基準 | なし |
※ドリフト = CloudFormation の管理状態と実リソースの状態がずれること
個別測定値
cdkl: 17262ms, 16669ms, 16766ms, 16566ms → 平均 16,816ms (min 16,566 / max 17,262)
hotswap: 15658ms, 15408ms, 15014ms, 14881ms → 平均 15,240ms (min 14,881 / max 15,658)
normal: 35017ms, 34980ms, 35097ms, 35136ms → 平均 35,058ms (min 34,980 / max 35,136)
cdkl の計測スクリプト
cdkl はサーバーとして起動するため、バックグラウンド起動 → ヘルスチェック待ち → リクエスト送信 → 停止の流れで計測しました。
#!/bin/bash
START=$(date +%s%3N)
# バックグラウンドで起動
cdkl invoke-agentcore --path ./agent-code --runtime python3.12 --entrypoint main.py &
CDKL_PID=$!
# /ping が応答するまで待機
until curl -s http://localhost:8080/ping > /dev/null 2>&1; do
sleep 0.5
done
# /invocations にリクエスト
RESPONSE=$(curl -s -X POST http://localhost:8080/invocations)
END=$(date +%s%3N)
echo "Response: $RESPONSE"
echo "Time: $((END - START))ms"
# 停止
kill $CDKL_PID 2>/dev/null
クラウド伝播時間
速度比較とは別の通常 deploy 試行で、3秒間隔のポーリングによりクラウド側の応答切り替わりを観測しました。
deploy_start = 0秒
最後に旧版応答 = +35秒
新版に切替 = +40秒
CFn完了 = +44秒
CloudFormation が Runtime 更新を発行した後、CFn 完了前に新版へ切り替わりました。3秒間隔のポーリングのため±3秒程度の誤差を含みます。
hotswap のドリフト確認
hotswap でデプロイすると、CloudFormation スタック更新を行わずに実リソースを直接更新します。
cdk deploy --hotswap --region us-east-1
✨ AWS::BedrockAgentCore::Runtime 'helloAgent-fksMu1GelV' hotswapped!
✨ hotswap-deployment time: 0.69s
✨ Total time: 11.93s
このログは hotswap が適用されたことを確認するための別試行です。速度比較には上記4回の測定値(平均 15.2秒)を使っています。
hotswap ログから、CloudFormation UpdateStack ではなく実リソースが直接更新されたことがわかります。一方、CloudFormation スタック更新は実行されていないため、CloudFormation の管理状態は更新されません。
実際のエンドポイントにリクエストすると、新しいコードで動作していました。
curl -s -X POST https://<runtime-endpoint>/invocations
{"version": "BBB"}
なお、cdk diff では差分として表示されませんでした。
cdk diff --region us-east-1
Stack AgentcoreAppStack
There were no differences
hotswap ログと実リソースの応答から、CloudFormation を経由せずに実リソースだけが更新され、CloudFormation 管理状態と実リソースの状態がずれていることを確認しました。
cdkl の独自機能
cdkl invoke-agentcore は Docker を使ってローカルに AgentCore Runtime を起動します。本記事で確認した範囲では AWS リソースを更新しないため、CloudFormation ドリフトは発生しません。
--from-cfn-stack(デプロイ済みスタックの環境変数を注入)
--from-cfn-stack を指定すると、デプロイ済みの CloudFormation スタックから環境変数の値を取得してローカルコンテナに渡します。cdkl はデフォルトではクラウド上の SSM パラメータを読み込まないため、このオプションを使うことで、デプロイ済みスタック上で解決された環境変数の値がローカルに反映されます。
オプションなしで起動した場合:
cdkl invoke-agentcore --path ./agent-code --runtime python3.12 --entrypoint main.py
curl -s -X POST http://localhost:8080/invocations | jq .
{"greeting": "NOT_SET", "version": "AAA"}
--from-cfn-stack を付けた場合:
cdkl invoke-agentcore --path ./agent-code --runtime python3.12 --entrypoint main.py \
--from-cfn-stack AgentcoreAppStack
curl -s -X POST http://localhost:8080/invocations | jq .
{"greeting": "Hello from SSM!", "version": "AAA"}
| モード | GREETING の値 |
|---|---|
| オプションなし | NOT_SET |
--from-cfn-stack |
Hello from SSM! |
SSM パラメータに設定した値がローカルコンテナに注入されました。このオプションは CloudFormation / SSM への読み取りアクセスが発生するため、AWS 認証情報が必要です。
--assume-role(実行ロールの切り替え)
--assume-role を使うと、Runtime 実行ロールに切り替えた状態でローカルコンテナを起動できます。アプリが AWS SDK を呼ぶ場合に、本番と同じ権限で動作確認ができます。
--assume-role の確認では、アプリに sts:GetCallerIdentity を呼ぶ処理を追加しました。
# --assume-role 確認用の追加コード(main.py に追記)
import boto3
# do_POST 内:
caller_arn = boto3.client("sts").get_caller_identity()["Arn"]
resp = json.dumps({"greeting": greeting, "version": VERSION, "callerArn": caller_arn})
boto3 を依存に追加します。
echo "boto3" > agent-code/requirements.txt
オプションなしの場合:
cdkl invoke-agentcore --path ./agent-code --runtime python3.12 --entrypoint main.py
コンテナ内で sts:GetCallerIdentity を確認すると EC2 ロールが使われていました。
arn:aws:sts::123456789012:assumed-role/cdkl-test-ec2-role/...
--assume-role を付けた場合:
cdkl invoke-agentcore --path ./agent-code --runtime python3.12 --entrypoint main.py \
--assume-role arn:aws:iam::123456789012:role/AgentcoreAppStack-HelloAgentExecutionRoleB1EEC5C6-xxxxx
arn:aws:sts::123456789012:assumed-role/AgentcoreAppStack-HelloAgentExecutionRoleB1EEC5C6-xxxxx/...
| モード | 実行ロール |
|---|---|
| オプションなし | cdkl-test-ec2-role (EC2 ロール) |
--assume-role |
AgentcoreAppStack-HelloAgentExecutionRoleB1EEC5C6-* |
--assume-role を使うには、指定するロールの信頼ポリシーに開発者ロールからの AssumeRole を許可する設定が必要です。検証では、既存の信頼ポリシーを確認した上で開発用 EC2 ロールからの AssumeRole 許可を追加しました。
以下は検証環境専用の例です。本番用ロールには適用しないでください。
aws iam update-assume-role-policy --role-name AgentcoreAppStack-HelloAgentExecutionRoleB1EEC5C6-xxxxx \
--policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"Service": "bedrock.amazonaws.com"},
"Action": "sts:AssumeRole"
},
{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::123456789012:role/cdkl-test-ec2-role"},
"Action": "sts:AssumeRole"
}
]
}'
使い分け
| フェーズ | 推奨手法 | 理由 |
|---|---|---|
| ハンドラー実装中 | cdkl invoke-agentcore |
ローカル完結で高速。AWS リソースを更新せず、失敗してもクラウド環境に変更を加えない |
| 環境変数や実行ロール込みで確認 | cdkl + --from-cfn-stack / --assume-role |
クラウド定義に近い条件でローカルテストできる |
| 開発環境への反映 | cdk deploy --hotswap |
通常 deploy の半分以下の時間。ドリフトが発生する |
| 共有環境・本番 | cdk deploy |
CloudFormation 管理状態を維持 |
ローカル開発では cdkl でイテレーションを回し、動作が固まったら hotswap で開発環境に反映、本番には通常デプロイという流れが開発サイクルとして自然です。
まとめ
cdkl によるローカル実行と hotswap による開発環境への高速反映を使い分けることで、AgentCore Runtime の開発サイクルを短縮できることを確認しました。
今回は AgentCore Runtime のみで試しましたが、cdkl は活発にアップデートが続いており、Lambda / API Gateway / ECS / ALB / CloudFront などもサポートされています。サーバーレスを中心とした構成のローカル開発が捗りそうなので、他のリソースでも有効に活用できるか今後試してみたいと思います。クラウド環境へのデプロイ時間や、ローカル環境との環境差が課題になっている方は、選択肢として試してみてください。
付録: 検証環境の CloudFormation テンプレート
このテンプレートは検証用です。AdministratorAccess を付与しているため、共有環境・本番環境ではそのまま使用しないでください。
cfn-ec2-cdkl-agentcore.yaml(クリックで展開)
AWSTemplateFormatVersion: '2010-09-09'
Description: EC2 (ARM64) for cdkl + AgentCore development (SSM only, no SSH)
Resources:
Role:
Type: AWS::IAM::Role
Properties:
RoleName: cdkl-test-ec2-role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AdministratorAccess
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: cdkl-test-ec2-profile
Roles:
- !Ref Role
Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: !Sub '{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64}}'
InstanceType: t4g.medium
IamInstanceProfile: !Ref InstanceProfile
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeSize: 30
VolumeType: gp3
Tags:
- Key: Name
Value: cdkl-agentcore-dev
UserData:
Fn::Base64: |
#!/bin/bash
set -ex
# Node.js 22
curl -fsSL https://rpm.nodesource.com/setup_22.x | bash -
dnf install -y nodejs docker git tmux unzip
# Docker
systemctl enable docker && systemctl start docker
usermod -aG docker ec2-user
# CDK + cdk-local (バージョン固定)
npm install -g aws-cdk@2.1125.0 cdk-local@0.105.0
Outputs:
InstanceId:
Value: !Ref Instance
SsmCommand:
Value: !Sub 'aws ssm start-session --target ${Instance} --region ${AWS::Region}'









