cdk-local (cdkl) で Bedrock AgentCore Runtime のローカル開発体験を試してみた

cdk-local (cdkl) で Bedrock AgentCore Runtime のローカル開発体験を試してみた

Bedrock AgentCore Runtime をローカルで素早く実行できる cdk-local (cdkl) の invoke-agentcore を試しました。通常デプロイ・hotswap との速度比較に加え、--from-cfn-stack と --assume-role の動作も検証しています。
2026.06.06

はじめに

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 について以下の記事を書きました。

https://dev.classmethod.jp/articles/cdkd-deploy-6x-faster-than-cloudformation/

hotswap の AgentCore Runtime 対応については以下で解説されています。

https://go-to-k.hatenablog.com/entry/cdk-hotswap-bedrock-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}'

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事