AgentCore Runtime のデプロイを Github Actions で自動化してみた

記事タイトルとURLをコピーする

英語版はこちらをご覧ください!

blog.serverworks.co.jp

こんにちは。愛猫リンゴちゃんが大好きなローです。

今回は、AWS CDK L2 Construct を使って Amazon Bedrock AgentCore Runtime への AI エージェントデプロイを完全自動化した取り組みをご紹介します。

注意事項
本記事で使用している @aws-cdk/aws-bedrock-agentcore-alpha はアルファリリースのパッケージです。本番環境での利用は各自の責任において慎重にご検討ください。また、API が変更される可能性があります。

はじめに

Amazon Bedrock AgentCore Runtime は、AI エージェントをサーバーレスで実行できるマネージドサービスです。しかし、従来のデプロイ方法では以下の課題がありました:

  • ECR リポジトリ、CodeBuild、Lambda、カスタムリソースなど多数の AWS リソースを明示的に定義
  • ビルドパイプラインの複雑な依存関係管理
  • IAM ロールとポリシーの詳細な設定

これらを AWS CDK L2 ConstructGitHub Actions を活用することで、必要最小限のリソース定義だけで完全自動化を実現しました。

ソリューションアーキテクチャ

以下は、GitHub Actions から Bedrock AgentCore Runtime へのデプロイまでの全体的な流れを示したアーキテクチャ図です:

アーキテクチャ図

主要コンポーネント:

  1. GitHub Actions: OIDC 認証で AWS にアクセス、Docker イメージをビルド
  2. AWS CDK: インフラをコードで定義、CloudFormation スタックをデプロイ
  3. ECR: CDK ブートストラップで作成された共有リポジトリにイメージを保存
  4. IAM: AgentCore Runtime の実行ロールと権限を自動作成
  5. 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);

明示的に定義が必要なリソース:

  1. ECR リポジトリ
  2. CodeBuild プロジェクト
  3. Lambda 関数
  4. カスタムリソース
  5. IAM ロール
  6. 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ヶ月ですが、気温も一気に下がってしまっているので風邪を引かないように気を付けましょう~

リンゴちゃん(クリスマス ver.)

ロータッヘイ(執筆記事の一覧)

24卒入社の香港人です。
2025 Japan All AWS Certifications Engineers
リンゴちゃん(デボンレックス)にいつも癒されています。