Claude Code × AWS ECS/Fargate完全ガイド|安全なコンテナデプロイを自動化
Claude CodeでECS/Fargateを安全に自動化。Docker、ECR、タスク定義、ログ、IAMの実装手順を解説。
AWSでコンテナを動かすならECS/Fargateは有力ですが、最初の壁は「設定項目が多すぎる」ことです。Dockerfile、ECR、タスク定義、IAMロール、Secrets Manager、ALB、CloudWatch Logs、ヘルスチェックまで、一つでも抜けるとデプロイは成功してもサービスが安定しません。
この記事では、Claude Codeに丸投げするのではなく、人間が決めるべき設計と、Claude Codeに生成させると効率が良い実装を分けて整理します。初心者向けに言うと、ECSのタスク定義は「コンテナの起動指示書」、Fargateは「サーバー管理をAWSに任せる実行環境」、task roleは「アプリがAWSを触る権限」です。
Masaが小さなNode.js APIをFargateへ移したとき、最初の失敗はIAMではなくヘルスチェックでした。アプリ起動に40秒かかるのにstartPeriodを入れず、デプロイ直後に何度も置き換えが走りました。Claude Codeは設定を速く書けますが、運用で落ちる条件まで伝えないと、きれいなだけの危ない構成になります。
この記事で作る構成
Developer
|
| docker build / push
v
Amazon ECR ----> Amazon ECS Service on AWS Fargate
|
| pulls secrets / writes logs
v
Secrets Manager CloudWatch Logs
^
|
Application Load Balancer -> /health -> Node.js container
ここではap-northeast-1の例で、既存VPCとALBのターゲットグループがある前提にします。新規VPCやALBまでCDKで作りたい場合は、内部リンクのClaude Code × AWS CloudFormation/CDK完全ガイドと合わせて読むと流れがつながります。IAM設計が不安なら先にClaude Code × AWS IAM完全ガイドを確認してください。
実例3つ
| ユースケース | Fargateが向く理由 | 注意点 |
|---|---|---|
| SaaSのREST API | 常時2台以上で動かし、ALB配下でローリング更新できる | DB接続数とヘルスチェックを先に設計する |
| 管理画面のバックエンド | EC2管理なしで小さく始められる | 夜間にdesiredCountを0へ落とすと初回応答が遅い |
| バッチ兼APIの軽量サービス | 同じイメージをserviceとrun-taskで使える | task roleに最小権限だけを付ける |
Lambdaでも動く処理ならClaude Code × AWS Lambda完全ガイドが軽い選択肢です。一方で、常時起動、長めのHTTP処理、既存Docker資産、言語ランタイムの自由度が必要ならFargateが扱いやすいです。
1. ヘルスチェック付きの最小API
まずローカルで動くアプリを作ります。/healthはALBとECSの両方から見られるため、DBが一時的に遅いだけで全コンテナを落とさないよう、最初はプロセス生存と基本依存だけを見る設計にします。
{
"scripts": {
"start": "node src/server.js"
},
"dependencies": {
"express": "^4.19.2"
}
}
// src/server.js
const express = require("express");
const app = express();
const port = Number(process.env.PORT || 3000);
app.get("/health", (_req, res) => {
res.status(200).json({
ok: true,
service: "myapp",
time: new Date().toISOString(),
});
});
app.get("/", (_req, res) => {
res.json({ message: "Hello from ECS Fargate" });
});
app.listen(port, "0.0.0.0", () => {
console.log(`myapp listening on ${port}`);
});
# Dockerfile
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY src ./src
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
CMD ["node", "src/server.js"]
curl -f http://localhost:3000/healthが通ることをローカルで確認してからECSへ進めます。Claude Codeには「health check endpointを追加して」だけでなく、「ALBとECSが同じ/healthを見る」「DB障害で即座にunhealthyにしない」まで伝えると事故が減ります。
2. ECRへビルドしてpushする
次のスクリプトは、ECRリポジトリ作成、ログイン、ビルド、pushまでをまとめたものです。latestだけで運用するとロールバックしにくいので、Git SHAや日時タグを使います。
set -euo pipefail
export AWS_REGION="ap-northeast-1"
export AWS_ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
export ECR_REPOSITORY="myapp"
export IMAGE_TAG="$(git rev-parse --short HEAD 2>/dev/null || date +%Y%m%d%H%M%S)"
export IMAGE_URI="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${ECR_REPOSITORY}:${IMAGE_TAG}"
aws ecr describe-repositories \
--repository-names "${ECR_REPOSITORY}" \
--region "${AWS_REGION}" >/dev/null 2>&1 || \
aws ecr create-repository \
--repository-name "${ECR_REPOSITORY}" \
--image-scanning-configuration scanOnPush=true \
--region "${AWS_REGION}"
aws ecr get-login-password --region "${AWS_REGION}" | \
docker login --username AWS --password-stdin \
"${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"
docker build -t "${IMAGE_URI}" .
docker push "${IMAGE_URI}"
echo "Pushed ${IMAGE_URI}"
公式のAmazon ECR手順でもget-login-passwordを使う流れが案内されています。チーム運用ではGitHub ActionsのOIDCを使い、長期のAWSアクセスキーをGitHub Secretsへ置かない構成に寄せるのが安全です。
3. ECSタスク定義を登録する
Fargateではawsvpcネットワークモードが基本です。Secrets Managerから値を注入する場合、アプリのtask roleではなく、コンテナ起動を担当するexecution roleにsecretsmanager:GetSecretValueが必要です。カスタマー管理KMSキーで暗号化しているならkms:Decryptも必要になります。
set -euo pipefail
export AWS_REGION="ap-northeast-1"
export AWS_ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
export IMAGE_URI="${IMAGE_URI:?Run the ECR push script first}"
export EXECUTION_ROLE_ARN="arn:aws:iam::${AWS_ACCOUNT_ID}:role/ecsTaskExecutionRole"
export TASK_ROLE_ARN="arn:aws:iam::${AWS_ACCOUNT_ID}:role/myapp-task-role"
export SECRET_ARN="arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:prod/myapp/DATABASE_URL"
aws logs create-log-group --log-group-name /ecs/myapp --region "${AWS_REGION}" 2>/dev/null || true
aws logs put-retention-policy --log-group-name /ecs/myapp --retention-in-days 30 --region "${AWS_REGION}"
cat > ecs-task-definition.json <<EOF
{
"family": "myapp-task",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"executionRoleArn": "${EXECUTION_ROLE_ARN}",
"taskRoleArn": "${TASK_ROLE_ARN}",
"runtimePlatform": {
"cpuArchitecture": "X86_64",
"operatingSystemFamily": "LINUX"
},
"containerDefinitions": [
{
"name": "app",
"image": "${IMAGE_URI}",
"essential": true,
"portMappings": [
{ "containerPort": 3000, "hostPort": 3000, "protocol": "tcp" }
],
"environment": [
{ "name": "NODE_ENV", "value": "production" },
{ "name": "PORT", "value": "3000" }
],
"secrets": [
{ "name": "DATABASE_URL", "valueFrom": "${SECRET_ARN}" }
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/myapp",
"awslogs-region": "${AWS_REGION}",
"awslogs-stream-prefix": "ecs"
}
},
"healthCheck": {
"command": ["CMD-SHELL", "wget -qO- http://localhost:3000/health || exit 1"],
"interval": 30,
"timeout": 5,
"retries": 3,
"startPeriod": 60
}
}
]
}
EOF
aws ecs register-task-definition \
--cli-input-json file://ecs-task-definition.json \
--region "${AWS_REGION}"
startPeriodは地味ですが重要です。Node.js、Rails、Next.jsなど起動が遅いアプリでは、これがないだけで新しいタスクが安定する前に落とされます。
4. Fargateサービスを作成する
次は既存のprivate subnet、タスク用security group、ALB target groupを使ってserviceを作ります。タスクのsecurity groupはALBのsecurity groupから3000番だけ許可し、インターネットから直接入れない構成にします。
set -euo pipefail
export AWS_REGION="ap-northeast-1"
export CLUSTER_NAME="myapp-cluster"
export SERVICE_NAME="myapp-service"
export TASK_FAMILY="myapp-task"
export SUBNET_1="subnet-xxxxxxxx"
export SUBNET_2="subnet-yyyyyyyy"
export TASK_SECURITY_GROUP="sg-xxxxxxxx"
export TARGET_GROUP_ARN="arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/myapp/abc123"
aws ecs create-cluster \
--cluster-name "${CLUSTER_NAME}" \
--region "${AWS_REGION}" >/dev/null
aws ecs create-service \
--cluster "${CLUSTER_NAME}" \
--service-name "${SERVICE_NAME}" \
--task-definition "${TASK_FAMILY}" \
--desired-count 2 \
--launch-type FARGATE \
--platform-version LATEST \
--health-check-grace-period-seconds 90 \
--network-configuration "awsvpcConfiguration={subnets=[${SUBNET_1},${SUBNET_2}],securityGroups=[${TASK_SECURITY_GROUP}],assignPublicIp=DISABLED}" \
--load-balancers "targetGroupArn=${TARGET_GROUP_ARN},containerName=app,containerPort=3000" \
--region "${AWS_REGION}"
aws ecs wait services-stable \
--cluster "${CLUSTER_NAME}" \
--services "${SERVICE_NAME}" \
--region "${AWS_REGION}"
private subnetでassignPublicIp=DISABLEDにする場合、ECR、CloudWatch Logs、Secrets Managerへ出る経路が必要です。NAT Gatewayを使うか、VPCエンドポイントを用意します。NAT Gatewayは便利ですが固定費が乗るため、小規模検証では料金を見落としやすいです。
5. CloudWatch Logsとデプロイ確認
ECSは「タスクが起動しない理由」をイベント、タスク停止理由、CloudWatch Logsに分散して出します。Claude Codeへ調査を頼むときも、この3点をセットで渡すと精度が上がります。
export AWS_REGION="ap-northeast-1"
export CLUSTER_NAME="myapp-cluster"
export SERVICE_NAME="myapp-service"
aws ecs describe-services \
--cluster "${CLUSTER_NAME}" \
--services "${SERVICE_NAME}" \
--query "services[0].events[0:5].[createdAt,message]" \
--output table \
--region "${AWS_REGION}"
aws ecs list-tasks \
--cluster "${CLUSTER_NAME}" \
--service-name "${SERVICE_NAME}" \
--desired-status STOPPED \
--region "${AWS_REGION}"
aws logs tail /ecs/myapp \
--follow \
--since 10m \
--region "${AWS_REGION}"
よくある停止理由は、ECRからpullできない、secretを読めない、security groupでALBから届かない、アプリが0.0.0.0ではなくlocalhostだけでlistenしている、の4つです。
Claude Codeへの実装依頼テンプレ
次のテンプレを使うと、Claude Codeが作るコードの粒度が安定します。
AWS ECS/FargateへNode.js APIをデプロイする実装を作ってください。
前提:
- リージョン: ap-northeast-1
- ECR repository: myapp
- コンテナポート: 3000
- health endpoint: /health
- ECS launch type: FARGATE
- network mode: awsvpc
- desired count: 2
- task CPU/memory: 512 / 1024
- secrets: DATABASE_URLをSecrets Managerから注入
- logs: CloudWatch Logs /ecs/myapp, retention 30 days
作ってほしいもの:
1. production用Dockerfile
2. ECR push用bash script
3. ECS task definition登録script
4. Fargate service作成script
5. CloudWatch Logs確認script
6. IAMでexecution roleとtask roleを分ける説明
制約:
- 疑似コードは禁止。aws cliで実行できる形にする
- secret値を環境変数へ直書きしない
- public subnetへ直接公開しない
- 公式AWSドキュメントと照合すべき注意点を最後に列挙する
公式AWSドキュメントで確認した注意点
- AWS Fargateの公式ガイドでは、Fargateタスクの制約やプラットフォームの考え方を確認できます。EC2起動タイプの設定をそのまま持ち込まないこと。
- タスク定義パラメータでは、
awsvpc、CPU/メモリ、health checkの範囲を確認できます。 - タスク実行IAMロールでは、ECR pull、CloudWatch Logs、Secrets Manager連携に必要な権限を確認できます。
- ECSのログ送信では、
awslogsドライバーとロググループ設定を確認できます。 - AWS Fargate料金はリージョン、vCPU、メモリ、追加ephemeral storage、稼働秒数で変わります。東京リージョンの検証を常時2タスクで放置すると、学習用途でも月額が積み上がります。
- AWSリージョン別サービスで、使うリージョンのECS、Fargate、Secrets Manager、ALB、CloudWatch対応を確認してください。記事の例は東京リージョンですが、チームのデータ所在地やレイテンシ要件で変わります。
落とし穴
1つ目はexecution roleとtask roleの混同です。execution roleはコンテナを起動するECSエージェント側の権限、task roleはアプリコードがAWS APIを呼ぶ権限です。DynamoDBアクセスをexecution roleへ足しても、アプリからは使えません。
2つ目はsecretのリージョン違いです。ECSタスク、Secrets Manager、KMSキーのリージョンがずれると起動時に失敗します。マルチリージョン構成ならsecretの複製とARNの切り替えをデプロイ変数に入れます。
3つ目は料金です。Fargate自体は使った分だけですが、ALB、NAT Gateway、CloudWatch Logs、ECR保存容量も一緒に発生します。検証環境はdesiredCount=1、ログ保持30日、不要なNAT削除まで運用メモに入れておくと無駄が減ります。
4つ目はヘルスチェックの過剰判定です。DBが一瞬遅いだけで/healthが500を返すと、正常なコンテナまで置き換え対象になります。深い依存確認は/readyへ分け、ALBは軽い/healthを見る設計が扱いやすいです。
5つ目はイメージアーキテクチャです。Apple SiliconでARM64イメージを作り、タスク定義がX86_64のままだと起動しません。docker buildx build --platform linux/amd64を使うか、タスク定義のruntimePlatformを合わせます。
収益導線と次の一手
個人で練習するなら、このページのコードを小さなサンプルAPIで試し、無料チートシートでClaude Codeへの依頼文を増やしてください。実案件でECS、IAM、CI/CD、監視、ロールバックを一度に整える必要がある場合は、Claude Code導入相談でリポジトリの前提に合わせた手順へ落とし込むのが早いです。再利用できるプロンプトやレビュー観点が欲しい場合はプロダクト一覧も役に立ちます。
まとめ
ECS/Fargateは「Dockerを置けば終わり」ではなく、IAM、ネットワーク、secret、ログ、health check、料金を一緒に設計するサービスです。Claude Codeを使う価値は、長いJSONやCLIを速く書くことだけではありません。人間が落とし穴を先に言語化し、Claude Codeに実行可能なコードへ落としてもらうことで、初回デプロイと運用の両方が安定します。
この記事で紹介した内容を実際に試した結果、最も効果があったのはDockerfileではなく、Claude Codeへの依頼テンプレに「execution roleとtask roleを分ける」「CloudWatch Logs確認コマンドも出す」「health check grace periodを入れる」と明記したことでした。初回はsecret権限漏れでタスクが止まりましたが、ECSイベントとログを一緒にClaude Codeへ渡すと、修正すべきIAMポリシーと再デプロイ手順まで一度で整理できました。
無料PDF: Claude Code はじめてのチートシート
まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。
スパムは送りません。登録情報は厳重に管理します。
Claude Codeを仕事で使える形にしませんか?
無料PDFで基礎を固めたあと、すぐ使えるテンプレート集で試し、必要なら業務自動化や導入相談まで進められます。
この記事を書いた人
Masa
Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。
関連書籍・参考図書
この記事のテーマに関連する書籍を楽天ブックスで探せます。
※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。
関連記事
ObsidianメモをCLAUDE.mdに変えるClaude Code運用: 文脈を毎回説明しない仕組み
Obsidianの作業メモからCLAUDE.md用の運用ノートを作り、Claude Codeに安定した文脈を渡す方法。
Claude Code Revenue CTA Routing: 記事からPDF、Gumroad、相談へ送る設計
PVだけで終わらせず、読者の状態に合わせて無料PDF、Gumroad教材、導入相談へ分岐するCTA設計です。
Claude Codeチーム引き継ぎルール: レビュー、権限、収益導線まで渡す実務手順
Claude Codeの作業をチームで渡すための証拠、権限、ロールバック、無料PDF/Gumroad/相談導線の実務ルール。