AMAZON ECS → STACKIT KUBERNETES ENGINE · TUTORIAL
Amazon ECS zu STACKIT Kubernetes: ein durchgespieltes Beispiel
ECS hat auf STACKIT kein Eins-zu-eins-Gegenstück: Die Container bleiben, Task Definition und Service werden zu Kubernetes-Objekten auf der STACKIT Kubernetes Engine (SKE). Dieses Tutorial führt StackToIT auf einer kleinen Fargate-Umgebung aus und zeigt, was es schreibt, was es zurückhält und was Sie entscheiden, bevor der erste Pod läuft.
Die Eingabe: eine Task Definition, ein Service
Eine für diese Seite geschriebene Demo-Umgebung: ein Fargate-Service mit drei Tasks zu je 0,5 vCPU und 1 GiB hinter einem Application Load Balancer, ein Secret, ein einfacher Umgebungswert und eine awslogs-Log-Gruppe. Links Task Definition und Service, rechts das Deployment, das StackToIT dafür entworfen hat (der lange Kommentarkopf der Datei ist hier weggelassen; alles Gezeigte ist unverändert).
resource "aws_ecs_task_definition" "web" {
family = "shop-web"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = "512"
memory = "1024"
execution_role_arn = aws_iam_role.task_execution.arn
container_definitions = jsonencode([
{
name = "web"
image = "registry.example.com/shop/web:1.8.2"
essential = true
portMappings = [{ containerPort = 8080, protocol = "tcp" }]
environment = [{ name = "APP_ENV", value = "production" }]
secrets = [{ name = "DATABASE_URL", valueFrom = aws_secretsmanager_secret.db_url.arn }]
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = "/ecs/shop-web"
"awslogs-region" = "eu-central-1"
"awslogs-stream-prefix" = "web"
}
}
}
])
}resource "aws_ecs_service" "web" {
name = "web"
cluster = aws_ecs_cluster.shop.id
task_definition = aws_ecs_task_definition.web.arn
desired_count = 3
launch_type = "FARGATE"
network_configuration {
subnets = [aws_subnet.app_a.id, aws_subnet.app_b.id]
security_groups = [aws_security_group.web.id]
}
load_balancer {
target_group_arn = aws_lb_target_group.web.arn
container_name = "web"
container_port = 8080
}
}apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app.kubernetes.io/name: web
spec:
replicas: 0 # held back until the start step of the plan scales to the number the header names
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 3
selector:
matchLabels:
app.kubernetes.io/name: web
template:
metadata:
labels:
app.kubernetes.io/name: web
spec:
containers:
- name: web
image: "registry.example.com/shop/web:1.8.2"
ports:
- containerPort: 8080
protocol: TCP
env:
- name: APP_ENV
valueFrom:
configMapKeyRef:
name: web-env
key: APP_ENV
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: web-secrets
key: DATABASE_URL
resources:
requests:
cpu: "500m"
memory: "1024Mi"
limits:
cpu: "500m"
memory: "1024Mi"
readinessProbe:
httpGet:
path: "/healthz"
port: 8080
periodSeconds: 30
timeoutSeconds: 6
successThreshold: 3
failureThreshold: 3Der Ablauf
Fünf Schritte von den Terraform-Dateien bis zur laufenden Workload. Die ersten beiden erledigt StackToIT, alles Weitere ist Arbeit Ihres Teams mit den Dateien, die StackToIT geschrieben hat.
1 · Bewertung ausführen
Führen Sie StackToIT in dem Ordner aus, der das Terraform enthält. Es liest die Dateien, verbindet sich nie mit AWS und schreibt einen Ausgabeordner mit Entwurf, Entscheidungen und Ausführungsplan.
2 · Klassifizierung lesen
Cluster und Service werden als REPLATFORM eingestuft, das umgebende Netzwerk, Secret, Rolle, Log-Gruppe und Load Balancer als RECONFIGURE. Task Definition und Target Group haben keine eigene Einheit: Ihr Inhalt fließt in den Service ein.
3 · Entscheidungen beantworten
Beantworten Sie die offenen Entscheidungen in review/decisions.yaml (wie der Load Balancer die Pods erreicht, ob die Pods segmentiert werden, in welchem Netzwerk der Cluster und seine Konsumenten liegen, das Opt-in für die API-Aufrufe) und setzen Sie die Variablen des Node-Pools (Maschinentyp, Minimum, Maximum) in der Datei mit den Zielwerten.
4 · Erst den Cluster anwenden, dann die Manifeste
Zuerst das Terraform für den SKE-Cluster anwenden. Die Manifeste werden mit replicas auf 0 geschrieben, damit nichts läuft und keine Verbindung zu einem Datenspeicher aufbaut, bevor dessen Daten da sind.
5 · Hochskalieren und Traffic umschalten
Nach dem Datenumzug auf die im Quellsystem genannte Zahl der Tasks skalieren, den Smoke-Test der Checkliste ausführen, Load Balancer oder DNS-Eintrag umstellen und ECS weiterlaufen lassen, bis Sie das Ergebnis abgenommen haben.
Was der Lauf meldet
Die Zusammenfassung, die der Befehl für die Demo-Umgebung ausgibt (console.txt im Ausgabeordner). Aufgerufen wurde im Eingabeordner stacktoit assess . <Ausgabeordner> --offline --no-validate; --offline ist der dokumentierte Weg, ohne Netzwerkanfragen zu arbeiten, und --no-validate hat die OpenTofu-Prüfung der Quelle übersprungen; deshalb meldet die Zusammenfassung sie als nicht gelaufen.
Source: input
Source files: 1
Resources scanned: 13
support resources: 1 (+0 Terraform helpers, out of scope; 1 + 0 = the 1 support node of summary.support.nodes in assessment.json)
Migration units: 10 (10 instances)
Unit dependencies: 14
Source checks: Built-in source checks passed — OpenTofu validation of the source was not run
STACKIT mapping
────────────────────────────────────────────
DIRECT 0
RECONFIGURE 8 ████████████████████████ 80%
REPLATFORM 2 ██████ 20%
CONTAINERIZE 0
REDESIGN 0
MANUAL_REVIEW 0
BLOCKED 0 (the mapping class BLOCKED, not the execution blockers, which Migration readiness counts)Wie die ECS-Einstellungen auf Kubernetes landen
| ECS-Einstellung in der Demo | Kubernetes-Ergebnis im Entwurf | Was Sie prüfen |
|---|---|---|
| desired_count = 3 | replicas: 0 in der Datei, im Startschritt auf 3 skaliert | Der Startschritt schreibt die Zahl auch in die Datei; erneutes Anwenden mit 0 würde die Pods wieder zurückhalten |
| cpu 512, memory 1024 am Task | Requests und Limits von 500m und 1024Mi am Container | Requests und Limits sind hier gleich; mit realer Auslastung neu bewerten |
| secrets valueFrom aus einem Secrets-Manager-Secret | secretKeyRef auf ein Secret web-secrets, Schlüssel DATABASE_URL | Das Secret legen Sie an; der Wert steht nie im Entwurf |
| environment APP_ENV | eine ConfigMap web-env mit leerem data-Block | Werte werden absichtlich nicht kopiert, weil ein Wert unter jedem Namen ein Geheimnis sein kann |
| Health Check /healthz der Target Group | readinessProbe httpGet auf /healthz, Port 8080 | Die Quelle setzt nur den Pfad, daher schreibt der Entwurf die dokumentierten Standardwerte der Target Group (Intervall 30, Schwellwerte 3, Timeout je nach Protokoll); das kubelet akzeptiert jeden Status von 200 bis 399 |
| Deployment minimum 100 %, maximum 200 % (Standardwerte, nicht gesetzt) | maxUnavailable 0, maxSurge 3 | StackToIT leitet beides für drei Replikate nach den Rundungsregeln von ECS ab |
| awslogs-Log-Treiber | nicht übernommen | Der Container schreibt nach stdout; wohin der Cluster die Logs liefert, entscheiden Sie, zum Beispiel STACKIT Logs |
| launch_type FARGATE, Netzwerkmodus awsvpc | nicht übernommen | Pods laufen im Netzwerk des Node-Pools; es gibt keine Netzwerkschnittstelle und keine Security Group pro Task |
Cluster und Load Balancer
Fargate kennt keine Nodes, deshalb ist die Größe des Node-Pools eine Variable, die Sie aus den Task-Größen ableiten, die der Bericht summiert (für diesen Service 1,5 vCPU und 3 GiB an Untergrenze und Obergrenze). Der Load Balancer erreicht die Pods über einen NodePort-Service auf Port 30000, mit den Node-Adressen als Pool-Zielen.
resource "stackit_ske_cluster" "shop" {
project_id = var.project_id
name = "shop"
node_pools = [{
name = "workloads"
machine_type = var.shop_machine_type
minimum = var.shop_nodes_min
maximum = var.shop_nodes_max
availability_zones = var.availability_zones
}]
} target_pools = [{
name = "web"
target_port = 30000
targets = [for ip in var.web_target_ips : { ip = ip }]
active_health_check = {
healthy_threshold = var.web_health_healthy_threshold
interval = var.web_health_interval
interval_jitter = var.alb_interval_jitter
timeout = var.web_health_timeout
unhealthy_threshold = var.web_health_unhealthy_threshold
http_health_checks = {
ok_status = var.web_health_ok_status
path = "/healthz"
}
}
}]apiVersion: v1
kind: Service
metadata:
name: web-nodeport
labels:
app.kubernetes.io/name: web
spec:
type: NodePort
selector:
app.kubernetes.io/name: web
ports:
- name: p8080-tcp
port: 8080
targetPort: 8080
protocol: TCP
nodePort: 30000
Entscheiden, wie der Traffic die Pods erreicht
| Option laut Plan | Bedeutung | Was es Sie kostet |
|---|---|---|
| NodePort hinter dem erzeugten Load Balancer (der Entwurf) | Ein NodePort-Service und ein Target Pool aus den Node-IPs | Sie halten die Node-Adressen aktuell, wenn der Pool skaliert; Load Balancer und Nodes müssen im selben Netzwerk liegen |
| Ein Ingress-Controller hinter dem Load Balancer | Ein Controller wie ingress-nginx routet per Ingress-Regeln zu ClusterIP-Services | Sie installieren und betreiben den Controller; der Entwurf schreibt weder ihn noch die Ingress-Regeln |
| Die SKE-Erweiterung ALB Ingress | Wird über die SKE-API am Cluster aktiviert; das Anlegen einer IngressClass stellt einen verwalteten Application Load Balancer bereit, und die Ingress-Objekte dieser Klasse legen seine Routen fest | Stand November 2026 nur auf Anfrage über den STACKIT Support erhältlich, als Opt-in-Funktion, die sich ohne Ankündigung ändern kann und die STACKIT nicht für den Produktivbetrieb empfiehlt; der erzeugte Load-Balancer-Block wird nicht angewendet |
Die Checkliste, die den Workload zurückhält
applications/web.json meldet den Workload als nicht bereit und führt drei blockierende Punkte (Umgebungswert, Secret, Smoke-Test). Die Workload gilt erst als bereit, wenn Ihr Team sie in der Entscheidungsdatei abhakt; StackToIT deployt nichts und führt keinen Test aus.
"readiness": "NOT_READY",
"readiness_words": "Checklist items are open: StackToIT lists the work and counts an item done only when the customer marks it in the decisions file (application_checklists); StackToIT never deploys the application.",Was Sie vor dem Anwenden ändern
Der Entwurf ist ein Ausgangspunkt. Diese Änderungen und Antworten verlangt der Demo-Lauf.
Dem Cluster eine Größe geben
Maschinentyp sowie minimale und maximale Node-Anzahl setzen. Das Maximum muss mindestens der Zahl der Zonen entsprechen; standardmäßig sind drei Zonen gewählt.
Den Load Balancer platzieren
Der Quell-Load-Balancer überspannt zwei Subnetze, die auf STACKIT getrennte Netzwerke sind. Seine Ziele sind die Cluster-Nodes, deshalb platziert ihn der Entwurf in dem Netzwerk, das SKE für den Cluster anlegt. Der Provider exportiert weder dieses Netzwerk noch die Node-Adressen: Sie wenden zuerst den Cluster an und setzen danach Netzwerk-ID und Node-IPs.
Das Zertifikat hochladen
Der HTTPS-Listener nennt eine Zertifikats-ARN. Die ARN wird nicht umgesetzt: Zertifikat bei STACKIT hochladen und die ID in der Listener-Variable übergeben.
Zugriff einschränken
Die Quelle lässt jede IPv4-Adresse auf Port 443 zu, der erzeugte Load Balancer ebenso. Entscheiden Sie, ob ein erlaubter Quellbereich geschrieben wird.
Die Netzwerkrichtlinie schreiben
Die Security Group der Tasks wird nicht auf Pods übertragen. Der Plan nennt als Optionen eine NetworkPolicy, eine Security Group an den Nodes oder den Verzicht auf Segmentierung.
ConfigMap und Secret füllen
APP_ENV in die ConfigMap eintragen, web-secrets mit DATABASE_URL anlegen und prüfen, ob die Nodes die Image-Registry erreichen.
Fragen aus den Teams
Kann ich Fargate-artige Task-Größen auf SKE beibehalten?
Fargate gibt es auf STACKIT nicht. Die Pods laufen im Node-Pool; aus den Task-Größen werden Requests und Limits der Container, und der Node-Pool wird aus deren Summe plus System-Pods und Surge beim Rolling Update dimensioniert.
Wendet StackToIT die Manifeste auf meinen Cluster an?
Nein. Es schreibt Manifeste, Terraform und Ausführungsplan. Ihr Team wendet sie nach den beantworteten Entscheidungen mit der Kubeconfig des Clusters an.
Was passiert mit der Task-Rolle meiner Anwendung?
Die Demo-Umgebung hat nur eine Execution-Rolle. Der Entwurf macht daraus einen STACKIT-Service-Account ohne Berechtigungen, denn für die Rolle des Plattform-Agenten, der Images zieht und Secrets einspielt, findet der Lauf kein STACKIT-Gegenstück. Weder diese Rolle noch eine Task-Rolle kommt in den Pods an: Es wird kein ServiceAccount und keine Workload-Identität erzeugt. Wie Images gezogen werden und wie sich die Anwendung authentifiziert, entscheiden Sie.
Wie mache ich nach dem Cutover einen Rollback?
Der Kopf des Manifests beschreibt es: Der ECS-Service bleibt in Betrieb, Sie leiten den Traffic dorthin zurück. Die Pods können angewendet bleiben; ECS wird erst herunterskaliert und gelöscht, nachdem die Pods die Last über den von Ihnen gewählten Zeitraum getragen haben.
Quellen
Ihr nächster Schritt: STACKIT.
Erzählen Sie uns von Ihrer Umgebung. Wir zeigen Ihnen, was StackToIT dafür liefert: Zielarchitektur, Terraform-Artefakte und einen strukturierten Migrationsplan.