Claude apps gatewayをAWSにデプロイする — ECS FargateとRDSの構成例
Claude apps gatewayをAmazon ECS Fargate/EKS・RDS for PostgreSQL・Secrets Manager・IAMロール認証でAWSに構築する実践例をまとめます。
この構成で何が組み上がるか
Claude apps gatewayを前提知識ゼロから構築する手順とプラットフォーム非依存の運用手順はすでに公開していますが、実際にAWSでどこまでのリソースを作ることになるかは、手を動かしてみないと感覚がつかめません。本記事はAmazon Bedrockをモデルの上流としたAWS上の構成例を、実際のawsコマンドで最初から最後まで組み立てます。
でき上がるのは、開発者のClaude Codeが内部ロードバランサー経由でゲートウェイ(ECS FargateまたはEKS)にHTTPS接続し、ゲートウェイがIAMロールでBedrockを呼び出す構成です。モデル認証情報は開発者端末に一切乗らず、ゲートウェイのIAMロールだけがBedrockを呼びます。
- Amazon ECS on AWS FargateサービスまたはAmazon EKS Deploymentでゲートウェイコンテナを稼働
- Amazon ECRリポジトリにゲートウェイイメージを保管
- Amazon RDS for PostgreSQLをプライベートサブネットに配置し、ゲートウェイのstoreとして使用
- AWS Secrets ManagerにJWT署名鍵・OIDCクライアントシークレット・Postgres接続文字列を保管
- IAMロールに
bedrock:InvokeModelとbedrock:InvokeModelWithResponseStreamを付与し、ECSタスクロールまたはEKSのIAM Roles for Service Accounts(IRSA)で束縛 - 内部Application Load BalancerでHTTPSを終端
なお上流はBedrockに限りません。Claude Platform on AWS(AWS認証・AWS Marketplace課金を使うAnthropic運営のClaude API)をBedrockの代わりに、あるいは併用する上流として選べます。認証情報とIAM権限だけがBedrockと異なり、それ以外の構成はこのページのままで通用します。
前提条件
構築は次を前提とします。
| 項目 | 内容 |
|---|---|
| AWSアカウント | 内容上記リソースを作成できる権限 |
| ツール | 内容AWS CLI v2(認証済み)とDockerがローカルにあること |
| VPC | 内容異なるアベイラビリティゾーンのプライベートサブネットを2つ以上持ち、NATゲートウェイ経由でアウトバウンド接続できること |
| IdP | 内容リダイレクトURIをhttps://<ゲートウェイのホスト>/oauth/callbackとしたOktaのOIDC WebアプリケーションまたはOIDC準拠の他IdP |
| TLS | 内容ロードバランサー用のホスト名(通常はRoute 53のプライベートホストゾーン)と、それに対するACM証明書 |
環境変数としてAWS_REGION(BedrockでClaude Codeが必要とするモデルを配信するUSリージョン)・ACCOUNT_ID・VPC_ID・PRIVATE_SUBNETSの4つを先に確定させておくと、以降のコマンドをそのまま実行できます。
export AWS_REGION=us-east-1
export ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
export VPC_ID=<your-vpc-id>
export PRIVATE_SUBNETS="<subnet-id-a> <subnet-id-b>"USリージョン以外を使う場合は、ゲートウェイ内蔵のモデルカタログがus.anthropic.*のinference profileに解決する前提が崩れるため、models:ブロックにそのリージョンのinference profile IDを追加し、IAMポリシーのARNプレフィックスも合わせて変更します。
手順1: セキュリティグループを作る
通信経路は3段階です。社内ネットワークからロードバランサーへ443番、ロードバランサーからゲートウェイへ8080番、ゲートウェイからPostgresへ5432番。それ以外は到達できません。EKSではAWS Load Balancer Controllerが独自のフロントエンドセキュリティグループを作るため、ALB用のグループは使わず、代わりにinbound-cidrsアノテーションで社内ネットワークに絞り込みます。
ALB_SG="$(aws ec2 create-security-group --group-name claude-gateway-alb \
--description "Claude gateway ALB" --vpc-id "$VPC_ID" \
--query GroupId --output text)"
GW_SG="$(aws ec2 create-security-group --group-name claude-gateway-svc \
--description "Claude gateway service" --vpc-id "$VPC_ID" \
--query GroupId --output text)"
DB_SG="$(aws ec2 create-security-group --group-name claude-gateway-db \
--description "Claude gateway Postgres" --vpc-id "$VPC_ID" \
--query GroupId --output text)"
aws ec2 authorize-security-group-ingress --group-id "$ALB_SG" \
--protocol tcp --port 443 --cidr <your-corporate-cidr>
aws ec2 authorize-security-group-ingress --group-id "$GW_SG" \
--protocol tcp --port 8080 --source-group "$ALB_SG"
aws ec2 authorize-security-group-ingress --group-id "$DB_SG" \
--protocol tcp --port 5432 --source-group "$GW_SG"手順2: IAMロールを作り、use caseフォームを提出する
ゲートウェイ専用のタスクロールには、Bedrockモデルを呼び出す権限だけを与えます。クロスリージョンのinference profile ARNと、その下にある基盤モデルARNの両方をカバーする必要があります。
cat > bedrock-invoke.json <<EOF
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"],
"Resource": [
"arn:aws:bedrock:${AWS_REGION}:${ACCOUNT_ID}:inference-profile/us.anthropic.*",
"arn:aws:bedrock:*::foundation-model/anthropic.*"
]
}]
}
EOF
aws iam create-role --role-name claude-gateway-task \
--assume-role-policy-document file://ecs-trust.json
aws iam put-role-policy --role-name claude-gateway-task \
--policy-name bedrock-invoke --policy-document file://bedrock-invoke.jsonECSにはこれとは別に実行ロールが要ります。ECSエージェント自身がECRからイメージを取得し、後述のSecrets Managerの値をコンテナへ注入するためのロールで、実行時にゲートウェイのAWS SDKが使うタスクロールとは別物です。実行ロールにはAmazonECSTaskExecutionRolePolicyを付け、Secrets Managerの3つのシークレットARNを名指しでsecretsmanager:GetSecretValueとDescribeSecretを許可します。ワイルドカードのgateway-*ではなく1シークレット1ARNで絞るのは、共有アカウントで無関係なシークレットまで拾わないためです。
IAMポリシーがBedrockの呼び出しを許可しても、商用リージョンでモデルアクセスが有効になっているとは限りません。アカウント内の誰もAnthropicのuse caseフォームを提出していない場合は、BedrockコンソールのModelカタログでAnthropicモデルを選び提出します。アクセスは提出直後に付与されます。EKSトラックでは、この2つのポリシードキュメントをIRSAロールに付け替えるだけで、ECS固有の信頼ポリシーは使いません。
手順3: Amazon RDS for PostgreSQLを用意する
インスタンスはプライベートサブネットに公開アドレスなしで作成し、ストレージ暗号化を有効にします。エンジンバージョンはPostgreSQL 16に固定します(ゲートウェイが求める14以上を満たし、パラメータグループのファミリーをインスタンスと確実に一致させるため)。
aws rds create-db-subnet-group --db-subnet-group-name claude-gateway-db \
--db-subnet-group-description "Claude gateway" --subnet-ids $PRIVATE_SUBNETS
aws rds create-db-parameter-group --db-parameter-group-name claude-gateway-db \
--db-parameter-group-family postgres16 \
--description "Claude gateway - require TLS on every connection"
aws rds modify-db-parameter-group --db-parameter-group-name claude-gateway-db \
--parameters "ParameterName=rds.force_ssl,ParameterValue=1,ApplyMethod=immediate"
PGPASS="$(openssl rand -hex 24)"
aws rds create-db-instance --db-instance-identifier claude-gateway-db \
--engine postgres --engine-version 16 --db-instance-class db.t4g.micro \
--allocated-storage 20 --db-name claude_gateway \
--master-username gateway --master-user-password "$PGPASS" \
--db-subnet-group-name claude-gateway-db \
--db-parameter-group-name claude-gateway-db \
--vpc-security-group-ids "$DB_SG" \
--no-publicly-accessible --storage-encrypted--master-user-passwordをコマンドライン引数で直接渡すと、実行中のプロセステーブルや監査/EDRログに平文で残ります。共有環境や監視対象のホストでは、手順5のシークレット作成と同じ理由で、0600権限のファイルを用意して--cli-input-jsonから渡してください。
インスタンスが起動するまで待ち、プライベートエンドポイントを取得して接続文字列を組み立てます。
aws rds wait db-instance-available --db-instance-identifier claude-gateway-db
DB_HOST="$(aws rds describe-db-instances --db-instance-identifier claude-gateway-db \
--query 'DBInstances[0].Endpoint.Address' --output text)"
GATEWAY_POSTGRES_URL="postgres://gateway:${PGPASS}@${DB_HOST}:5432/claude_gateway?sslmode=verify-full"sslmode=verify-fullは単なる暗号化ではなく、AWS RDS証明書バンドルを信頼アンカーにRDSサーバー証明書のチェーンとホスト名まで検証させる設定です。このバンドルはイメージビルド時に/etc/claude/rds-global-bundle.pemへコピーし、NODE_EXTRA_CA_CERTSで信頼させます。接続文字列にsslrootcert=を追記しないでください。ゲートウェイのドライバはクエリ文字列からsslmodeしか読まず、sslrootcertはPostgresへの起動時パラメータとして転送されてサーバーに拒否されます。手順5のSecrets Managerには、このGATEWAY_POSTGRES_URLをそのまま登録します。
手順4: gateway.yamlを書く
upstreamsはBedrockをauth: {}で指すだけで、ECSならタスクロール、EKSならIRSAロールのAWSデフォルト認証情報チェーンから認証します。listen.public_urlは外部から到達するhttps://オリジンで、ゲートウェイはIdPへのredirect_uriとディスカバリードキュメントをこの値だけから組み立て、X-Forwarded-*ヘッダーは一切使いません。listen.trusted_proxiesにはALBが属するサブネットのCIDRを指定します。ALBのノードはアタッチされたサブネットのアドレスを取るため、これでALB配下からのX-Forwarded-Forだけを信用し、開発者のIPを監査ログとサインインレート制限に正しく記録できます。ただしこの指定は、そのサブネット内の全ホストをプロキシとして信頼することでもあります。ALBのingress元である社内CIDRとサブネットを重ねないこと、X-Forwarded-Forを詐称しうる信頼できないワークロードとサブネットを共有しないことが前提です。
listen:
host: 0.0.0.0
port: 8080
public_url: https://claude-gateway.internal.example.com
trusted_proxies: [<your-alb-subnet-cidrs>]
oidc:
issuer: https://example.okta.com
client_id: 0oa1example2
client_secret: ${OIDC_CLIENT_SECRET}
allowed_email_domains: [example.com]
userinfo_fallback: true
scopes: [openid, profile, email, offline_access, groups]
session:
jwt_secret: ${GATEWAY_JWT_SECRET}
ttl_hours: 8
store:
postgres_url: ${GATEWAY_POSTGRES_URL}
upstreams:
- provider: bedrock
region: <your-region>
auth: {}Oktaの組織認可サーバーはid_tokenが薄いためuserinfo_fallback: trueが必須です。Microsoft Entra IDに切り替える場合は、issuerをhttps://login.microsoftonline.com/<tenant-id>/v2.0にし、userinfo_fallbackとgroupsスコープを外します。EntraはグループをオブジェクトIDで返すため、managed.policies側でGUIDに合わせるか、人が読める名前が必要ならApp Rolesを使います。
手順5: Secrets ManagerにシークレットをためてECRへイメージを積む
JWT署名鍵・OIDCクライアントシークレット・Postgres接続文字列の3つをSecrets Managerに作成します。作成コマンドの--secret-string引数はプロセステーブルや監査ログに平文で残るため、共有環境や監視対象のホストでは0600権限のファイルを作って--secret-string file://<path>で渡す方が安全です。
イメージはlinux-x64のglibcバイナリを./claudeとしてビルドコンテキストに置き、Dockerfileに前段のRDS証明書バンドルをコピーする2行を足します。ECRリポジトリは--image-tag-mutability IMMUTABLEで作成し、後段でデプロイ時に指定するバージョンタグが後から別イメージに差し替えられないようにします。
aws ecr create-repository --repository-name claude-gateway \
--image-tag-mutability IMMUTABLE --image-scanning-configuration scanOnPush=true
aws ecr get-login-password --region "$AWS_REGION" \
| docker login --username AWS --password-stdin \
"${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"
docker build --platform=linux/amd64 \
-t "${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/claude-gateway:<version>" .
docker push "${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/claude-gateway:<version>"Fargate on ARM64(Graviton)を使う場合はlinux/arm64バイナリとlinux/arm64ビルド、タスク定義側のcpuArchitectureをARM64に変更します。
手順6: デプロイする
ECS FargateとEKSでは前段の役割分担が変わります。どちらもBedrock呼び出しに使う認証情報の取り方が違うだけで、gateway.yaml自体は共通です。
| 観点 | ECS Fargate | EKS |
|---|---|---|
| Bedrock認証 | ECS Fargateタスクロール(手順2で作成) | EKSIRSA(eksctl create iamserviceaccountで新規作成) |
| ロードバランサー | ECS Fargate自前でALB+ターゲットグループを作成 | EKSAWS Load Balancer Controllerが内部ALBを自動プロビジョニング |
| 設定の受け渡し | ECS Fargateイメージにgateway.yamlを埋め込み | EKSConfigMapからマウント |
| ヘルスチェック | ECS FargateターゲットグループのGET /readyz | EKSIngressのreadinessプローブGET /readyz |
ECS Fargateの場合
クラスターと、ゲートウェイのログ(監査イベントを含むstderr)を受けるロググループを作ります。保持期間は別コマンドで、指定しないとCloudWatchは無期限に保持し続けます。
aws ecs create-cluster --cluster-name claude-gateway
aws logs create-log-group --log-group-name /ecs/claude-gateway
aws logs put-retention-policy --log-group-name /ecs/claude-gateway \
--retention-in-days 90タスク定義を書きます。executionRoleArnは手順2の実行ロール、taskRoleArnはBedrock権限を持つタスクロール、secretsは手順5で作った3つのシークレットARNです。
{
"family": "claude-gateway",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "1024",
"memory": "2048",
"runtimePlatform": { "cpuArchitecture": "X86_64", "operatingSystemFamily": "LINUX" },
"executionRoleArn": "arn:aws:iam::<account-id>:role/claude-gateway-execution",
"taskRoleArn": "arn:aws:iam::<account-id>:role/claude-gateway-task",
"containerDefinitions": [
{
"name": "gateway",
"image": "<account-id>.dkr.ecr.<region>.amazonaws.com/claude-gateway:<version>",
"portMappings": [{ "containerPort": 8080 }],
"secrets": [
{ "name": "GATEWAY_JWT_SECRET", "valueFrom": "<gateway-jwt-secret ARN>" },
{ "name": "OIDC_CLIENT_SECRET", "valueFrom": "<gateway-oidc-client-secret ARN>" },
{ "name": "GATEWAY_POSTGRES_URL", "valueFrom": "<gateway-postgres-url ARN>" }
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/claude-gateway",
"awslogs-region": "<region>",
"awslogs-stream-prefix": "gateway"
}
}
}
]
}aws ecs register-task-definition --cli-input-json file://claude-gateway-task.json内部ALBとターゲットグループを作ります。--ip-address-type ipv4が必須です。デュアルスタックの内部ALBは公開レンジのAAAAレコードを出し、/loginのプライベートネットワークチェックが拒否します。
ALB_ARN="$(aws elbv2 create-load-balancer --name claude-gateway \
--scheme internal --type application --ip-address-type ipv4 \
--subnets $PRIVATE_SUBNETS --security-groups "$ALB_SG" \
--query 'LoadBalancers[0].LoadBalancerArn' --output text)"
TG_ARN="$(aws elbv2 create-target-group --name claude-gateway \
--protocol HTTP --port 8080 --vpc-id "$VPC_ID" --target-type ip \
--health-check-path /readyz \
--query 'TargetGroups[0].TargetGroupArn' --output text)"HTTPSリスナーを追加します。--ssl-policyを明示しないと、TLS 1.0/1.1をまだ許すレガシーな既定ポリシーELBSecurityPolicy-2016-08にフォールバックします。ALBの既定アイドルタイムアウトは60秒で、ゲートウェイのkeepalive pingはその内側(約15秒間隔)でストリームを保つ設計なので、idle_timeout.timeout_secondsを3600へ引き上げてpingの間隔にさらに余裕を持たせます。
aws elbv2 create-listener --load-balancer-arn "$ALB_ARN" \
--protocol HTTPS --port 443 \
--ssl-policy ELBSecurityPolicy-TLS13-1-2-2021-06 \
--certificates CertificateArn=<your-acm-certificate-arn> \
--default-actions Type=forward,TargetGroupArn="$TG_ARN"
aws elbv2 modify-load-balancer-attributes --load-balancer-arn "$ALB_ARN" \
--attributes Key=idle_timeout.timeout_seconds,Value=3600サービスを作ります。デプロイメントサーキットブレーカーを有効にすると、壊れたイメージや起動できない設定でタスクが失敗し続けるデプロイを、直前の安定状態へ自動でロールバックします。ヘルスチェック猶予期間の60秒は、起動直後のタスクがイメージ取得・store接続・最初のヘルスチェック応答を終えるまでの時間で、これを過ぎるまでECSは失敗をデプロイの判定に数えません。
aws ecs create-service --cluster claude-gateway --service-name claude-gateway \
--task-definition claude-gateway --desired-count 1 --launch-type FARGATE \
--deployment-configuration "deploymentCircuitBreaker={enable=true,rollback=true}" \
--health-check-grace-period-seconds 60 \
--network-configuration "awsvpcConfiguration={subnets=[$(echo $PRIVATE_SUBNETS | tr ' ' ',')],securityGroups=[$GW_SG],assignPublicIp=DISABLED}" \
--load-balancers "targetGroupArn=$TG_ARN,containerName=gateway,containerPort=8080"EKSの場合
手順2で作った2つのIAMポリシードキュメントをマネージドポリシーにし、eksctl create iamserviceaccountでIRSAロールの作成・ポリシーのアタッチ・Kubernetesサービスアカウントへのロール注釈までを1コマンドで済ませます。
BEDROCK_POLICY_ARN="$(aws iam create-policy --policy-name claude-gateway-bedrock-invoke \
--policy-document file://bedrock-invoke.json --query Policy.Arn --output text)"
SECRETS_POLICY_ARN="$(aws iam create-policy --policy-name claude-gateway-secrets-read \
--policy-document file://secrets-read.json --query Policy.Arn --output text)"
kubectl create namespace claude-gateway
eksctl create iamserviceaccount --cluster <your-cluster> --region "$AWS_REGION" \
--namespace claude-gateway --name gateway --role-name claude-gateway \
--attach-policy-arn "$BEDROCK_POLICY_ARN" \
--attach-policy-arn "$SECRETS_POLICY_ARN" \
--approveDeploymentとService、Ingressをデプロイします。IngressにはAWS Load Balancer Controller向けのアノテーションを付け、scheme: internal・ip-address-type: ipv4・inbound-cidrsで社内ネットワークに絞り、証明書ARNとTLSポリシーを指定します。IRSAではAWS SDKがプロジェクトされたサービスアカウントトークンをSTSに交換するため、Podはインスタンスメタデータサービスに触れる必要がありません。
手順7: ゲートウェイのホスト名を確定し、URLを開発者マシンへ配信する
前提条件で挙げたRoute 53のプライベートホストゾーンをここで使います。ALB自身の*.elb.amazonaws.comという名前は内部ALBであればプライベートアドレスに解決しますが、そこにACM証明書を載せることはできません。そのためプライベートホストゾーンでゲートウェイ用の内部DNS名(たとえばclaude-gateway.internal.example.com)をALBへエイリアスし、listen.public_urlをその名前に設定します。EKSの場合もALBを直接作るかAWS Load Balancer Controllerが作るかの違いだけで、同じ扱いです。
ゲートウェイが起動し、public_urlのホスト名が解決するようになっても、開発者のマシンにゲートウェイのURLが届くまで/loginからは選べません。managed settingsの設定キー一覧にあるforceLoginMethodとforceLoginGatewayUrlをMDM経由で各デバイスへ配布します。ログイン画面のプロバイダー選択肢に、ゲートウェイを手動で選ぶ項目は用意されていません。
Terraformでコード化する
examples/gateway/awsは、ここまでの手順をコード化したバンドルです。setup.shはECS Fargateトラックを冪等にスクリプト化していて、既存リソースは検出してスキップするため再実行しても安全です。OktaのOIDCクライアントシークレットとACM証明書だけは自分で用意する必要があり、無い状態で実行するとECS/ALBのデプロイをスキップして不足入力を名指しし、create-secretコマンドを表示します。terraform/ディレクトリはセキュリティグループ・IAMロール・ECRリポジトリ・RDSインスタンス・Secrets Managerシークレット・内部ALB配下のECSサービスまでを宣言的に作成しますが、VPCとプライベートサブネットは前提として変数で渡す設計です。ECRリポジトリの作成とイメージのビルド・プッシュは別工程なので、applyは「リポジトリだけの部分apply→ビルド&プッシュ→全体apply」の2段階になります。
よくあるつまずき
| 症状 | 原因 | 対処 |
|---|---|---|
/loginが「ホストが組織のプライベートネットワーク上にない」と拒否する | 原因デュアルスタックの内部ALBが公開レンジのAAAAレコードを持つ | 対処ALBを--ip-address-type ipv4で作成するか、公開AAAAレコードの無い別のプライベートDNS名を用意する |
Bedrockリクエストが全件502で、ログにCould not load credentials from any providersと出る | 原因ECSのEC2起動タイプでタスクロールなしに動かしているか、EKSのノードがIRSA未対応で、IMDSv2の既定ホップ数1がコンテナ内で止まっている | 対処Fargateのタスクロール、EKSのIRSAを使う。インスタンス認証情報がどうしても必要ならhttp-put-response-hop-limitを2に上げる |
Bedrockが403 AccessDeniedExceptionを返す | 原因use caseフォーム未提出、AWS Marketplaceサブスクリプションの反映待ち、またはタスクロールのポリシーにARNが足りない | 対処Bedrockコンソールからuse caseフォームを提出し、数分待って再試行。両方のARNファミリーにbedrock:InvokeModel系の権限を付与する |
ECSタスクがResourceInitializationErrorでゲートウェイのログすら出ずに停止する | 原因実行ロールがSecrets Managerを読めないか、プライベートサブネットからSecrets ManagerとECRへの経路がない | 対処実行ロールに3つのgateway-シークレットへのsecretsmanager:GetSecretValueを付与し、NATゲートウェイまたはSecrets Manager/ECR/CloudWatch Logs用のインターフェースエンドポイントを用意する |
| Postgres接続タイムアウトで起動が失敗する | 原因データベースのセキュリティグループがゲートウェイ側の5432を許可していないか、サービスがDBと別のVPCで動いている | 対処claude-gateway-dbセキュリティグループにゲートウェイ側のセキュリティグループから5432を許可し、DBサブネットグループと同じVPCでサービスを動かす |
| ゲートウェイの起動がPostgres TLS証明書の検証エラーで落ちる | 原因接続文字列はsslmode=verify-fullだが、イメージがRDS CAバンドルを信頼していない(バンドルをイメージにコピーしていないか、NODE_EXTRA_CA_CERTSが指していない) | 対処手順3で示したDockerfileの2行(バンドルのコピーとNODE_EXTRA_CA_CERTSの設定)を追加し、イメージを新しいタグで再ビルド・再プッシュしてから再デプロイする |
BedrockがValidationException(on-demand throughputが非対応)を返す | 原因カスタムのmodels:エントリが、そのリージョンではinference profile経由でしか配信されない素の基盤モデルIDにマップされている | 対処モデルをクロスリージョンのinference profile ID(us.anthropic.*)にマップし直す。組み込みのモデルカタログは最初からこの形式になっている |
| ストリーミング応答が静かな時間の後に切れる | 原因v2.1.229より前のゲートウェイはBedrock/Claude Platform on AWS上流が無音の間(extended thinkingで出力が無い間等)何も送らず、ALBの既定60秒無通信タイムアウトに引っかかる | 対処v2.1.229以降へ更新するか、idle_timeout.timeout_secondsを3600に設定する |
テレメトリと支出の把握
Claude Codeはゲートウェイセッションで、各エクスポートにIdPの識別情報(user.id・user.email・user.groups)を刻むため、OTEL_RESOURCE_ATTRIBUTESを配線しなくても利用状況が開発者単位に自動で集計されます。telemetry.forward_toを設定すると、ゲートウェイは接続中の全クライアントへOTELエクスポーター設定を配布し、そのOTLPトラフィックを転送先へ中継します。AWSではADOT collector経由でAmazon CloudWatchやAmazon Managed Service for Prometheusへ流すのが典型です。ゲートウェイのログ自体はECS Fargateならawslogsドライバーが追加設定なしで届きますが、EKSではCloudWatch Observabilityアドオンかfluent-bitのDaemonSetを別途入れないと監査イベントを含むログが失われます。テレメトリは事後の可視化であり、リクエスト単位でライブに遮断するのは開発者ごとの支出上限を設定するAdmin APIの役割です。BedrockのClaude料金体系そのものはBedrock経由のClaude料金で扱っています。
まとめ
AWS上のClaude apps gatewayは、ECS FargateかEKSかという計算基盤の選択と、タスクロールかIRSAかという認証方式の選択の2軸で決まり、それ以外のgateway.yaml・RDS・Secrets Managerの構成はほぼ共通です。use caseフォームの提出とIAMポリシーの2ARNファミリー(inference profileと基盤モデル)を見落とすと403で止まりやすいので、デプロイ前に済ませておくと手戻りが減ります。Terraformバンドルはこの手順をそのまま宣言的に再現するので、複数環境へ展開する計画があるなら早い段階で切り替えるのも選択肢です。