Skip to content

AWS Bedrock AgentCore'u CDK ile deploy etmek: hızlı başlangıç

AgentCore Runtime üzerinde minimal bir Strands agent'ı CDK ile deploy etme rehberi — parametrize stack, arm64 build, deploy ve invoke akışı, ve ilk çağrıdan önce gereken IAM ve Marketplace ön koşulları.

Çıkış noktası

Önceki post AgentCore Runtime'ı mimari düzeyde anlatıyor: container, identity, memory, gateway. Bu yazı tam orada bırakıyor ve deployment sorusunu soruyor: AgentCore artık @aws-cdk/aws-bedrock-agentcore-alpha üzerinden CDK'dan erişilebilir, peki uçtan uca çalışan bir trial neye benziyor? Pratikte kod kısa ve ağırlığı alpha L2 taşıyor; ilk invoke sonuç dönmeden önce birkaç IAM ve Marketplace ön koşulunun yerine oturması gerekiyor.

Ne kuruyoruz

eu-central-1'de AgentCore Runtime üzerinde minimal bir Strands agent'ı deploy eden tek bir CDK stack'i; build script'i ve boto3 invoke yardımcısı dahil. Tam dosya yapısı:

text
agent-core-cdk/├── bin/app.ts                    # CDK app, cdk.json'dan context okuyor├── lib/agent-runtime-stack.ts    # Runtime construct + 2 IAM statement├── cdk.json                      # region, modelId, runtimeName, vb.├── agent/│   ├── main.py                   # Strands BedrockModel + @app.entrypoint│   └── requirements.txt          # bedrock-agentcore + strands-agents├── scripts/│   ├── build-agent.sh            # uv pip --python-platform aarch64-manylinux2014│   └── invoke.py                 # boto3 bedrock-agentcore client└── package.json

Toplam yaklaşık 150 satır TypeScript ve Python. Region, model, ve runtime config cdk.json'da; Stack sınıfının kendisi parametre-sürücülü kalıyor ve tek bir konfigürasyon değişimi seni başka region veya modele taşıyor. İlginç olan kısım hacim değil; hangi satırları sen yazıyorsun, hangilerini alpha L2 senin için yazıyor. Tüm kaynak kod: github.com/ayhansipahi/agent-core-cdk.

Uygulama girişi ve yapılandırma

cdk.json runtime parametrelerini CDK context olarak taşıyor; bu sayede Stack sınıfı saf construct kalıyor ve değerler kod düzenlemeden değişiyor:

json
{  "app": "npx ts-node --prefer-ts-exts bin/app.ts",  "context": {    "agentCore:region": "eu-central-1",    "agentCore:modelId": "anthropic.claude-sonnet-4-5-20250929-v1:0",    "agentCore:runtimeName": "helloAgent",    "agentCore:inferenceProfilePrefix": "eu",    "agentCore:description": "Minimal Strands agent on AgentCore Runtime"  }}

bin/app.ts bunları okuyor, doğruluyor, ve stack'e pas geçiyor:

typescript
const region = app.node.tryGetContext('agentCore:region') as string;const modelId = app.node.tryGetContext('agentCore:modelId') as string;const runtimeName = app.node.tryGetContext('agentCore:runtimeName') as string;const inferenceProfilePrefix = app.node.tryGetContext(  'agentCore:inferenceProfilePrefix',) as string;const runtimeDescription = app.node.tryGetContext('agentCore:description') as string;
if (!region || !modelId || !runtimeName || !inferenceProfilePrefix || !runtimeDescription) {  throw new Error(    'Missing required cdk.json context: agentCore:{region, modelId, runtimeName, inferenceProfilePrefix, description}',  );}
new AgentRuntimeStack(app, 'AgentCoreCdkStack', {  env: { region, account: process.env.CDK_DEFAULT_ACCOUNT },  modelId,  runtimeName,  inferenceProfilePrefix,  runtimeDescription,});

Throw deploy-time bir koruma — bir key eksikse synth undefined ARN'lerle bir stack üretmek yerine erken patlıyor. Region veya modeli değiştirmek için cdk.json'ı düzenleyip yeniden deploy et; Stack sınıfının kendisi bunlar için hiçbir zaman string literal tutmuyor.

CDK stack

lib/agent-runtime-stack.ts dört config prop'u alıp tüm altyapı yüzeyini üretiyor — iki manuel PolicyStatement bloğu, bir Runtime construct'ı, bir CfnOutput, iki stack-level tag:

typescript
import * as cdk from 'aws-cdk-lib';import { Construct } from 'constructs';import * as iam from 'aws-cdk-lib/aws-iam';import * as agentcore from '@aws-cdk/aws-bedrock-agentcore-alpha';import * as path from 'path';
export interface AgentRuntimeStackProps extends cdk.StackProps {  readonly modelId: string;  readonly inferenceProfilePrefix: string;  readonly runtimeName: string;  readonly runtimeDescription: string;}
export class AgentRuntimeStack extends cdk.Stack {  constructor(scope: Construct, id: string, props: AgentRuntimeStackProps) {    super(scope, id, props);
    const inferenceProfileId = `${props.inferenceProfilePrefix}.${props.modelId}`;
    const artifact = agentcore.AgentRuntimeArtifact.fromCodeAsset({      path: path.join(__dirname, '..', 'agent', 'dist'),      runtime: agentcore.AgentCoreRuntime.PYTHON_3_13,      entrypoint: ['main.py'],    });
    const runtime = new agentcore.Runtime(this, 'AgentRuntime', {      runtimeName: props.runtimeName,      agentRuntimeArtifact: artifact,      description: props.runtimeDescription,    });
    runtime.role.addToPrincipalPolicy(      new iam.PolicyStatement({        sid: 'BedrockInvokeModel',        actions: ['bedrock:InvokeModel', 'bedrock:InvokeModelWithResponseStream'],        resources: [          `arn:aws:bedrock:${this.region}::foundation-model/${props.modelId}`,          `arn:aws:bedrock:*::foundation-model/${props.modelId}`,          `arn:aws:bedrock:${this.region}:${this.account}:inference-profile/${inferenceProfileId}`,        ],      }),    );
    runtime.role.addToPrincipalPolicy(      new iam.PolicyStatement({        sid: 'BedrockMarketplaceSubscriptionCheck',        actions: [          'aws-marketplace:Subscribe',          'aws-marketplace:Unsubscribe',          'aws-marketplace:ViewSubscriptions',        ],        resources: ['*'],      }),    );
    new cdk.CfnOutput(this, 'AgentRuntimeArn', {      value: runtime.agentRuntimeArn,    });
    cdk.Tags.of(this).add('Project', 'agent-core-cdk');    cdk.Tags.of(this).add('ManagedBy', 'cdk');  }}

Üzerinde durulması gereken üç ayrıntı var. Bedrock invoke statement'ı tek değil üç ARN listeliyor: regional foundation model, wildcard region, ve inference profile (çalışma anında props.inferenceProfilePrefix + props.modelId'den kuruluyor). EU cross-region inference eu. prefix'li profili kullanıyor; wildcard region şart çünkü inference profile içeride başka region'lara dağıtım yapıyor. Marketplace statement'ı * resource kullanıyor; Marketplace API action'ları resource-level IAM desteklemiyor. Runtime adı camelCase olmak zorunda çünkü alpha L2 alttaki CFN naming kısıtlarını miras alıyor — tire içeren bir isim synth aşamasında değil deploy aşamasında patlıyor.

Agent kodu

agent/main.py model ID ve region'ı environment'tan okuyor. AWS_REGION varsayılanı cdk.json ile aynı; MODEL_ID varsayılanı ise eu. prefix'li inference profile değeri, çünkü agent doğrudan Bedrock'a çağrı atıyor ve prefix'li ID gerekiyor — stack ise aynı değeri synth aşamasında inferenceProfilePrefix + modelId'den kuruyor. Böylece aynı dosya hem lokal agentcore dev ile hem de deploy edilmiş runtime'da çalışıyor:

python
import os
from bedrock_agentcore.runtime import BedrockAgentCoreAppfrom strands import Agentfrom strands.models import BedrockModel
MODEL_ID = os.environ.get("MODEL_ID", "eu.anthropic.claude-sonnet-4-5-20250929-v1:0")REGION = os.environ.get("AWS_REGION", "eu-central-1")
app = BedrockAgentCoreApp()
model = BedrockModel(model_id=MODEL_ID, region_name=REGION)agent = Agent(model=model)

@app.entrypointdef invoke(payload, context):    user_message = payload.get("prompt", "Hello!")    response = agent(user_message)    return {"result": str(response)}

BedrockAgentCoreApp runtime kontratı; AgentCore container'ı içinde 8080 portunda HTTP server'ı ayağa kaldırıyor ve gelen her invoke_agent_runtime çağrısını @app.entrypoint ile dekorlanmış fonksiyona route ediyor. Default MODEL_ID eu. regional prefix'iyle, çünkü runtime eu-central-1'de çalışıyor ve Bedrock cross-region inference için prefix'li profili zorunlu kılıyor. AgentCore AWS_REGION'ı otomatik enjekte ediyor; MODEL_ID'i cdk.json değerinden farklı yapmak istersen Runtime construct'ının environmentVariables prop'undan override edebilirsin.

ARM64 build adımı

AgentCore Runtime sadece arm64 üzerinde çalışıyor. macOS veya x86_64 host üzerinde pip install -r requirements.txt --target=dist çalıştırırsan x86_64 wheel üretiyor ve container ilk invoke'da exec format hatasıyla düşüyor. Build script'i platformu zorla doğru sabitliyor:

bash
uv pip install \  --python-platform aarch64-manylinux2014 \  --python-version 3.13 \  --target="$DIST_DIR" \  --only-binary=:all: \  -r agent/requirements.txt
cp agent/main.py "$DIST_DIR/"

--only-binary=:all: güvenlik ağı. Bir bağımlılığın arm64 wheel'ı yoksa ve pip source'tan derlemeye düşerse, source build host arch'ında çalışır ve sonuç .so dosyaları container'da bozuk gelir. :all: pip'i sessiz başarısızlık yerine sesli hataya zorluyor. pip yerine uv bu bağımlılık setinde ~4-6 saniye ile ~30 saniye farkı, ama düz pip --platform da işe yarar. Çıktı agent/dist/ altında; AgentRuntimeArtifact.fromCodeAsset orayı zip'leyip yüklüyor.

İlk deploy: 66 saniye

Sıfırdan cdk deploy bu trial'da 66.13s sürdü: CFN bootstrap reuse, Runtime kaynağı oluşturma, IAM rolü oluşturma, ve CDK staging bucket'ına S3 asset yükleme. Sonraki deploy'lar daha hızlı çünkü Runtime kaynağı zaten yerinde: yalnızca IAM diff'i içeren bir update 30.48s, yeni Runtime version'ı zorlayan bir description bump'ı 24.9s, hiçbir diff'i olmayan idempotent re-deploy 21.25s.

Kolay tarafı buydu. İlk başarılı invoke ile arasında üç IAM/billing ön koşulu var.

Dokümanlarda görmediğin ön koşullar

Üç katman ilk invoke'u kapatıyor. Yukarıdaki CDK stack ilk ikisini zaten kapsıyor; üçüncüsü hesap seviyesinde, ilk deploy'dan önce kontrol etmeye değer.

1. Bedrock model invoke (stack içinde). Runtime rolünün foundation model ve inference profile için bedrock:InvokeModel iznine ihtiyacı var. Olmazsa invoke şunu döndürüyor:

text
not authorized to perform bedrock:InvokeModel

Stack'teki ilk PolicyStatement bunu kapsıyor — üç ARN formu (regional, wildcard, inference profile).

2. Marketplace subscription validation (stack içinde). Bedrock üzerindeki yeni Anthropic modelleri her ConverseStream çağrısında bir AWS Marketplace subscription'ı doğruluyor (Sonnet 4.5 model kartı Marketplace product ID: prod-mxcfnwvpd6kb4 listeliyor). Runtime rolünde aws-marketplace:Subscribe, Unsubscribe, ve ViewSubscriptions yoksa invoke şunu döndürüyor:

text
Model access is denied due to IAM user or service role is not authorized to perform therequired AWS Marketplace actions (aws-marketplace:ViewSubscriptions, aws-marketplace:Subscribe).

AWS AgentCore dokümanlarındaki minimal IAM örneği Anthropic modeli varsayıyor ama bu action'ları içermiyor; stack'teki ikinci PolicyStatement bunları kapsıyor.

3. Hesap ödeme yöntemi (hesap seviyesi, IaC değil). İki IAM bloğu da doğruyken Marketplace subscription'ının kendisi billing katmanında hâlâ başarısız olabilir:

text
Model access is denied due to INVALID_PAYMENT_INSTRUMENT: A valid payment instrumentmust be provided. Your AWS Marketplace subscription for this model cannot be completedat this time.

Çözüm AWS Billing console'da (Marketplace değil): Account → Payment preferences → Add card. Yaklaşık iki dakika propagasyon, redeploy yok.

L2 alpha bonusu

Deploy sonrası aws iam get-role-policy ile runtime rolüne bakınca L2 construct'ın kendi başına eklediği yedi policy statement görünüyor. Bunlar stack kodunda yok; agentcore.Runtime'ın senin yerine yazdıkları:

SidServisAmaç
LogGroupAccesslogsRuntime'ın kendi log group'u için DescribeLogStreams, CreateLogGroup
DescribeLogGroupslogsDaha geniş kapsamlı DescribeLogGroups, console görünürlüğü
LogStreamAccesslogsInvoke logları için CreateLogStream, PutLogEvents
XRayAccessxrayPutTraceSegments, PutTelemetryRecords, GetSamplingRules, GetSamplingTargets
CloudWatchMetricscloudwatchbedrock-agentcore namespace'iyle sınırlı PutMetricData
GetAgentAccessTokenbedrock-agentcoreGetWorkloadAccessToken, GetWorkloadAccessTokenForJWT, GetWorkloadAccessTokenForUserId
S3AssetReads3arn:aws:s3:::cdk-hnb659fds-assets-<account>-<region>/* üzerinde GetObject, GetObjectVersion

Yukarıdaki iki manuel statement (Bedrock invoke + Marketplace) ile birlikte runtime rolünde toplamda dokuz statement oluyor. Bunu L1 CfnRuntime üzerine elle yazsaydın yaklaşık 80 satır JSON bakımı ekstradan boynunda olurdu. Version pin uyarısına rağmen alpha L2'de kalmanın gerekçesi tam olarak bu.

Sayılar

Hepsi aynı POC oturumundan, eu-central-1'de, her invoke için fresh runtimeSessionId:

OperasyonServer tarafıClient tarafıNotlar
Deploy 1 (ilk yaratma)66.13sCFN bootstrap reuse, Runtime + IAM + S3 asset
Deploy 2 (yalnız IAM update)30.48sMarketplace bloğu eklendi
Deploy 3 (description bump)24.9sYeni Runtime version'ı zorladı, fresh container
Deploy 4 (idempotent re-deploy)21.25sNo-diff tabanı
Invoke 1 (cold container)6.244s10.16sİlk çağrı, container boot dahil
Invoke 2 (warm)2.476s5.79sİkinci çağrı, aynı client süreci
Invoke 33.387s7.41sContainer fresh session'lar arasında reuse görünüyor

Üç invoke'ta da client tarafı overhead yaklaşık 3.5-4 saniye: boto3 import, archive resolve, AgentCore session create, network round-trip. SLO ölçümünde server tarafı timing'i temel al; client tarafı sayısı Python ve SDK startup ile domine ediliyor.

Kapanış

AgentCore-on-CDK küçük bir yüzey — yaklaşık 150 satır TypeScript ve Python, yanında cdk.json. AWS dokümanlarının minimal örneğinin işaretlemediği iki nokta yanında getirmen gerekiyor: yeni Anthropic modelleri için runtime rolüne Marketplace IAM bloğu, ve ilk invoke'tan önce hesabında geçerli bir ödeme yöntemi. Tavsiye: alpha L2'de kal (@aws-cdk/aws-bedrock-agentcore-alpha); aksi halde L1 CfnRuntime üzerine elle yazacağın yedi policy statement'ı senin için ekliyor. Sürümü pin'le çünkü alpha qualifier semver-stable değil; bu trial 2.252.0-alpha.0 üzerinde koştu. Yalnızca alpha qualifier sert bir compliance blocker'ıysa L1'e in.

Bu stack'ten doğal sonraki adım aynı alpha modülün açtığı AgentCore Memory ve Gateway construct'ları; ikisi de bu trial'ın kurduğu Runtime rolünün üzerine ekleniyor.

Kaynaklar

İlgili Yazılar