Use Cases

Claude Code × AWS ECS/Fargate Komplettanleitung | Container-Deployments automatisieren

AWS ECS/Fargate-Deployments mit Claude Code automatisieren. Von Task-Definitionen über Service-Konfiguration bis Blue/Green-Deployment und CDK-Infrastruktur — aus Masas Praxiserfahrung.

„Ich möchte Container auf AWS betreiben, aber die ECS-Konfiguration ist viel zu komplex” — das ist die Hürde, auf die viele Entwickler zuerst stoßen. Task-Definitionen, Services, Cluster, Load Balancer, Auto Scaling… die Einstellungen sind so zahlreich, dass man nicht weiß, wo man anfangen soll.

Ich baue beruflich serverlose Container-Umgebungen mit ECS/Fargate, und seit ich Claude Code nutze, um einfach meine Architektur zu beschreiben und damit alles von Task-Definitionen bis CDK-Code generieren zu lassen, sind Deployments wesentlich einfacher geworden. Dieser Artikel erklärt die praktischen Schritte.


ECS/Fargate-Grundlagen in 3 Minuten

Cluster:          Die "Box", in der ECS-Container laufen
Task-Definition:  Container-Spezifikation (Image, CPU, Memory, Env-Variablen)
Service:          Definiert, wie viele Tasks laufen (inkl. Auto Scaling)
Fargate:          Serverloser Container-Ausführungsmotor — kein Server-Management nötig

Anders als bei EC2 sind bei Fargate kein Server-Provisioning und kein Patch-Management erforderlich. Man kann sich vollständig auf die Container-Konfiguration konzentrieren.


Schritt 1: Task-Definition automatisch generieren

claude -p "
Generiere eine ECS-Task-Definition als JSON mit folgenden Anforderungen:

- App: Node.js API (Port 3000)
- CPU: 512 (0.5 vCPU)
- Memory: 1024 MB
- Umgebungsvariablen: DATABASE_URL (aus Secrets Manager), NODE_ENV=production
- Logging: CloudWatch Logs (Log-Gruppe /ecs/myapp)
- Health-Check: /health-Endpunkt, 30-Sekunden-Intervall
- Container-Image: 123456789.dkr.ecr.ap-northeast-1.amazonaws.com/myapp:latest
- Execution Role: ecsTaskExecutionRole
- Task Role: myapp-task-role (Zugriff auf DynamoDB und S3)
"

Generierte Task-Definition:

{
  "family": "myapp-task",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "512",
  "memory": "1024",
  "executionRoleArn": "arn:aws:iam::123456789:role/ecsTaskExecutionRole",
  "taskRoleArn": "arn:aws:iam::123456789:role/myapp-task-role",
  "containerDefinitions": [
    {
      "name": "myapp",
      "image": "123456789.dkr.ecr.ap-northeast-1.amazonaws.com/myapp:latest",
      "portMappings": [{ "containerPort": 3000, "protocol": "tcp" }],
      "environment": [{ "name": "NODE_ENV", "value": "production" }],
      "secrets": [
        {
          "name": "DATABASE_URL",
          "valueFrom": "arn:aws:secretsmanager:ap-northeast-1:123456789:secret:prod/database-url"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/myapp",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "ecs"
        }
      },
      "healthCheck": {
        "command": ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"],
        "interval": 30,
        "timeout": 5,
        "retries": 3,
        "startPeriod": 60
      }
    }
  ]
}

Schritt 2: ECS/Fargate-Infrastruktur mit CDK aufbauen

Nicht nur die Task-Definition, sondern auch Cluster, Service und ALB werden mit CDK auf einmal generiert.

claude -p "
Implementiere folgende ECS/Fargate-Umgebung in CDK TypeScript in lib/ecs-stack.ts:

- VPC: Vorhandene VPC importieren (vpcId aus Umgebungsvariable)
- ECS-Cluster: Nur Fargate
- Task-Definition: Gleiche Spezifikation wie oben generiert
- ALB: HTTPS (ACM-Zertifikat-ARN aus Umgebungsvariable), HTTP leitet mit 301 um
- Service: Auto Scaling mit mindestens 2, maximal 10 Tasks
  - Skalierung bei 70% CPU-Auslastung
  - Target-Tracking-Skalierung
- Blue/Green-Deployment-Unterstützung (CodeDeploy-Integration)
- Service-Zustand im CloudWatch-Dashboard anzeigen
"
// lib/ecs-stack.ts
import * as cdk from "aws-cdk-lib";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as ecs from "aws-cdk-lib/aws-ecs";
import * as ecsp from "aws-cdk-lib/aws-ecs-patterns";
import * as acm from "aws-cdk-lib/aws-certificatemanager";

export class EcsStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Vorhandene VPC importieren
    const vpc = ec2.Vpc.fromLookup(this, "Vpc", {
      vpcId: process.env.VPC_ID!,
    });

    // ECS-Cluster
    const cluster = new ecs.Cluster(this, "Cluster", {
      vpc,
      clusterName: "myapp-cluster",
      containerInsights: true,  // CloudWatch Container Insights aktivieren
    });

    // ACM-Zertifikat
    const certificate = acm.Certificate.fromCertificateArn(
      this, "Certificate",
      process.env.CERTIFICATE_ARN!
    );

    // Task-Definition
    const taskDef = new ecs.FargateTaskDefinition(this, "TaskDef", {
      cpu: 512,
      memoryLimitMiB: 1024,
    });

    const container = taskDef.addContainer("app", {
      image: ecs.ContainerImage.fromEcrRepository(
        ecr.Repository.fromRepositoryName(this, "Repo", "myapp")
      ),
      environment: { NODE_ENV: "production" },
      logging: ecs.LogDrivers.awsLogs({ streamPrefix: "ecs" }),
      healthCheck: {
        command: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"],
        interval: cdk.Duration.seconds(30),
        timeout: cdk.Duration.seconds(5),
        retries: 3,
        startPeriod: cdk.Duration.seconds(60),
      },
    });
    container.addPortMappings({ containerPort: 3000 });

    // ALB + Fargate-Service (kompakt mit Patterns)
    const service = new ecsp.ApplicationLoadBalancedFargateService(
      this, "Service", {
        cluster,
        taskDefinition: taskDef,
        desiredCount: 2,
        certificate,
        redirectHTTP: true,
        publicLoadBalancer: true,
      }
    );

    // Auto-Scaling-Konfiguration
    const scaling = service.service.autoScaleTaskCount({
      minCapacity: 2,
      maxCapacity: 10,
    });
    scaling.scaleOnCpuUtilization("CpuScaling", {
      targetUtilizationPercent: 70,
      scaleInCooldown: cdk.Duration.seconds(60),
      scaleOutCooldown: cdk.Duration.seconds(60),
    });

    // Output
    new cdk.CfnOutput(this, "LoadBalancerDns", {
      value: service.loadBalancer.loadBalancerDnsName,
    });
  }
}

Schritt 3: CI/CD-Pipeline mit GitHub Actions aufbauen

claude -p "
Erstelle folgende CI/CD-Pipeline mit GitHub Actions:

1. Trigger bei Push auf den main-Branch
2. Docker-Image bauen und nach ECR pushen
3. Image-Tag in der ECS-Task-Definition auf den neuen SHA aktualisieren
4. Neue Task-Definition im ECS-Service deployen
5. Slack bei erfolgreichem Deployment benachrichtigen

Umgebung: ap-northeast-1, ECR-Repository-Name: myapp
"
name: Deploy to ECS

on:
  push:
    branches: [main]

env:
  AWS_REGION: ap-northeast-1
  ECR_REPOSITORY: myapp
  ECS_CLUSTER: myapp-cluster
  ECS_SERVICE: myapp-service
  CONTAINER_NAME: app

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Build, tag, and push image to ECR
        id: build-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT

      - name: Download task definition
        run: |
          aws ecs describe-task-definition --task-definition myapp-task \
            --query taskDefinition > task-definition.json

      - name: Update ECS task definition with new image
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: task-definition.json
          container-name: ${{ env.CONTAINER_NAME }}
          image: ${{ steps.build-image.outputs.image }}

      - name: Deploy to ECS
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          task-definition: ${{ steps.task-def.outputs.task-definition }}
          service: ${{ env.ECS_SERVICE }}
          cluster: ${{ env.ECS_CLUSTER }}
          wait-for-service-stability: true

      - name: Notify Slack on success
        if: success()
        uses: slackapi/slack-github-action@v1
        with:
          webhook: ${{ secrets.SLACK_WEBHOOK }}
          payload: |
            {"text": "✅ Deployed to ECS: ${{ github.sha }}"}

Schritt 4: Blue/Green-Deployment konfigurieren

claude -p "
Generiere CDK-Code und eine AppSpec-Datei für das Einrichten von Blue/Green-Deployment (CodeDeploy) für einen ECS-Service.
Rollback-Bedingung: Automatischer Rollback, wenn innerhalb von 5 Minuten nach dem Deployment ein CloudWatch-Alarm ausgelöst wird.
"
# appspec.yaml (für CodeDeploy)
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: <TASK_DEFINITION>
        LoadBalancerInfo:
          ContainerName: app
          ContainerPort: 3000
        PlatformVersion: LATEST
Hooks:
  - BeforeAllowTraffic: "arn:aws:lambda:ap-northeast-1:123456789:function:health-check"
  - AfterAllowTraffic: "arn:aws:lambda:ap-northeast-1:123456789:function:smoke-test"

Die 5 häufigsten Fallstricke

1. startPeriod im Health-Check vergessen

Fargate-Container führen sofort beim Start Health-Checks durch. Wenn die Anwendung langsam startet, wird sie sofort als UNHEALTHY markiert. Setzen Sie startPeriod: 60 (Sekunden), um der App Zeit zum Starten zu geben.

2. Task-Role und Execution-Role verwechseln

executionRole (ecsTaskExecutionRole): Berechtigungen zum Starten des Containers
  → Image aus ECR pullen, in CloudWatch Logs schreiben

taskRole (myapp-task-role): Berechtigungen, die die Anwendung nutzt
  → Zugriff auf DynamoDB, S3, SQS usw.

Es passiert sehr leicht, Berechtigungen der falschen Role zuzuordnen.

3. Security-Group-Konfiguration im awsvpc-Netzwerkmodus

Fargate erfordert zwingend den awsvpc-Netzwerkmodus. Wenn man vergisst, die Security Group des Containers so einzuschränken, dass nur Datenverkehr vom ALB erlaubt ist, wird der Container direkt dem Internet ausgesetzt.

4. Execution-Role für Secrets Manager erforderlich

Um Werte aus dem Secrets Manager über secrets abzurufen, muss die executionRole die Berechtigung secretsmanager:GetSecretValue haben. Das Fehlen dieser Berechtigung ist ein häufiger Grund, warum Container nicht starten.

5. Das desired_count: 0-Problem bei Deployments

Ein Service mit Auto-Scaling-Minimum 0 kann nachts alle Container herunterfahren, was am Morgen zu langsamen Starts führt. Setzen Sie das Minimum in der Produktion auf mindestens 2.


Zusammenfassung

AufgabeClaude Codes Beitrag
Task-Definition generierenVollständiges JSON aus Anforderungen
CDK-ImplementierungCluster, Service, ALB, Auto Scaling gemeinsam generiert
CI/CD-SetupGitHub-Actions-Workflow-Generierung
Blue/Green-DeploymentAppSpec und CodeDeploy-Config automatisch generiert
FehlerbehebungUrsache und Lösung aus Fehler-Logs ermittelt

ECS hat viele Konfigurationspunkte, aber indem man Claude Code sagt „Ich möchte diese Art von Setup”, erhält man eine vollständige, Best-Practice-konforme Konfiguration. Mit CDK anzufangen ist der einfachste und empfohlene Ansatz.

Verwandte Artikel

Referenzen

#claude-code #aws #ecs #fargate #container #devops

Bring deinen Claude-Code-Workflow aufs nächste Level

50 in der Praxis erprobte Prompt-Vorlagen zum direkten Copy-and-paste in Claude Code.

Kostenlos

Kostenloses PDF: Claude-Code-Spickzettel in 5 Minuten

Trag einfach deine E-Mail-Adresse ein – wir senden dir den A4-Spickzettel als PDF sofort zu.

Wir behandeln deine Daten sorgfältig und senden niemals Spam.

Masa

Über den Autor

Masa

Ingenieur, der Claude Code intensiv nutzt. Betreibt claudecode-lab.com, ein Tech-Medium in 10 Sprachen mit über 2.000 Seiten.