Appearance
Quelle
Diese Seite ist abgeleitet. Bitte Änderungen in der kanonischen SSOT-Fassung vornehmen: source-of-truth/handbooks/operator/tcloud-lifecycle.md. Diagramm-Quellen (PlantUML) liegen unter source-of-truth/diagrams/operator/.
Operator-Handbuch — T-Cloud Lifecycle
Was dieses Buch ist. Eine vollständige, von Hand abarbeitbare Anleitung, um die Officina-del-Caffè-Appliance auf der T-Cloud (Open Telekom Cloud, OTC) komplett hochzufahren und wieder vollständig herunterzufahren — von der nackten Infrastruktur bis zur erreichbaren Live-URL und zurück auf null.
Qualitätsziel. Eine menschliche Operator:in ist nach diesem Buch vollständig handlungsfähig — ohne maschinellen AI-Agenten. Jeder Schritt nennt das Arbeitsverzeichnis, die nötigen Umgebungsvariablen, das genaue Kommando, einen Beispiel-Output und — wo relevant — was in der OTC-Console zu tun ist.
Inhalt
- Wie man dieses Buch liest
- Architektur auf einen Blick
- Werkzeuge & Voraussetzungen
- Credentials laden (age-Bundle + OS_-Mapping)
- Kontext-Konventionen (cwd, ENV, KUBECONFIG)
- HOCHFAHREN — Schritt für Schritt
- HERUNTERFAHREN — geordneter Abriss
- Statussequenzen
- Bekannte Probleme & Lösungen
- OTC-Console-Cheatsheet
- Health- & Smoke-Checks
- Was bewusst bestehen bleibt
1. Wie man dieses Buch liest
- Jeder Befehlsblock ist mit einem Kontext-Header versehen. Vor jedem Block steht, in welchem Verzeichnis Sie stehen müssen (
cwd:) und welche ENV-Variablen gesetzt sein müssen (env:). Wenn Sie unsicher sind, prüfen Sie zuerst Kapitel 5. - Platzhalter stehen in spitzen Klammern, z. B.
<EIP>,<rev>. Ersetzen Sie sie durch echte Werte aus den jeweils vorherigen Outputs. - OTC-Console meint das Web-Portal unter
https://console.otc.t-systems.com(Browser, Login mit Ihrem OTC-Account). Schritte, die zwingend dort passieren, sind mit dem Symbol 🖱️ Console markiert. - Gefahrenstellen sind mit ⚠️ markiert. Lesen Sie diese, bevor Sie das Kommando absenden — mehrere davon haben uns in früheren Sessions Stunden gekostet.
- Reihenfolge ist nicht optional. Beim Teardown insbesondere ist die Lösch-Reihenfolge der OTC-Ressourcen eine harte Dependency-Kette.
Der schmalere, reine Bootstrap-Runbook lebt unter
deploy/sw-auctions/runbooks/demo-bootstrap.md. Dieses Handbuch ist die breitere Operator-Sicht inkl. Teardown, Troubleshooting und Diagrammen.
2. Architektur auf einen Blick
Komponentenarchitektur
| Komponente | Rolle | Host (Demo) |
|---|---|---|
| storefront (Nuxt 3 SSR) | öffentliche Auktions-UI | shop.officina-del-caffe.uprix.de |
| shopware (PHP-FPM + Nginx) | Hauptanwendung, Store-API + Admin | admin.officina-del-caffe.uprix.de |
| docs-server (VitePress) | dieses Handbuch | docs.officina-del-caffe.uprix.de |
| erp-emulator (Nuxt 3) | simuliert das ERP | erp.officina-del-caffe.uprix.de |
| erp-integration (Go) | Sync-Brücke ERP ↔ Shopware | clusterintern |
| minio (S3) | Object-Store (Akten/Medien) | clusterintern ⚠️ emptyDir |
| mailpit | Mailcatcher (DOI-/Outbid-Mails) | mail.officina-del-caffe.uprix.de |
| RDS-MySQL 8 | Datenbank (OTC managed) | privat |
| DCS-Redis 7 | Cache/Session (OTC managed) | privat |
| SWR | Container-Registry | swr.eu-de.otc.t-systems.com/officina-del-caffe |
Verteilungsarchitektur
Die öffentliche IP gehört dem ELB (Load Balancer) vor dem CCE-Cluster — nicht einem Worker-Knoten. DNS zeigt per A-Record auf die ELB-EIP. Die Eltern-Domain uprix.de liegt bei united-domains; die OTC-Zone officina-del-caffe.uprix.de bekommt nach tofu apply per NS-Delegation ihre Nameserver zugewiesen.
3. Werkzeuge & Voraussetzungen
Lokal installiert (Tool-Boundary: nichts global nachinstallieren ohne Freigabe — bevorzugt Container/ddev):
| Tool | Mindestens | Prüfung |
|---|---|---|
tofu (OpenTofu) | 1.8 | tofu version |
kubectl | 1.29 | kubectl version --client |
helm | 3.14 | helm version --short |
jq | 1.6 | jq --version |
python3 + PyYAML/Boto3 | 3.11 | python3 -c 'import yaml,boto3' |
curl, openssl | system | curl --version |
age | 1.1 | age --version |
docker | 24 | docker version |
Ein gebündelter Preflight liegt im Taskfile:
cwd: Repo-Root
~/git/mms/odc/odc-tcloudenv: keine
bash
task ci:doctorBeispiel-Output:
text
ci:doctor — tooling preflight
✅ docker Docker version 24.0.7, build afdd53b
✅ helm v3.14.2+gc309b6f
✅ python3 Python 3.11.9
✅ pyyaml PyYAML 6.0.1
✅ tofu OpenTofu v1.8.2
✅ task Task version: v3.37.2
✅ kubectl Client Version: v1.29.3
ci:doctor ok.Zugänge, die Sie out-of-band brauchen:
- Die age-Identity-Datei (privater Schlüssel) unter
~/.config/sw-auctions/age-identity.txt(oder viaAGE_IDENTITY_FILEüberschrieben). Sie liegt niemals im Repo. - Ein OTC-Account mit Web-Login für die Console.
- Bitbucket-PATs (read + write) für den Image-Build-Pfad (nur beim Hochfahren nötig).
- Zugang zum united-domains-Kundencenter für die NS-Delegation (nur beim ersten Mal bzw. nach einem Zonen-Neuaufbau).
4. Credentials laden (age-Bundle + OS_-Mapping)
Alle OTC-Credentials liegen age-verschlüsselt unter infra/connectivity/cred-bundles/<env>.env.age und werden mit secrets-load.sh in die aktuelle Shell geladen (Vertrag: ADR-008).
cwd: Repo-Root env:
AGE_IDENTITY_FILE(nur falls Identity nicht am Default-Pfad liegt)
bash
# Bundle prüfen, OHNE etwas zu laden:
source infra/connectivity/secrets-load.sh --checkBeispiel-Output:
text
identity: /home/stl/.config/sw-auctions/age-identity.txt (readable)
bundle-dir: /home/stl/git/mms/odc/odc-tcloud/infra/connectivity/cred-bundles
found: dev.env.age
managed vars: OS_REGION_NAME OS_PROJECT_NAME ... OTC_TF_USERNAME OTC_TF_PASSWORD ...Dann laden:
bash
source infra/connectivity/secrets-load.sh dev⚠️ Fallstrick: OS_* vs. OTC_TF_* (Provider-Auth)
Der OpenTofu-opentelekomcloud-Provider liest OS_ACCESS_KEY / OS_SECRET_KEY und OS_USERNAME / OS_PASSWORD — nicht die OTC_TF_*-Namen und nicht die AWS_*-Variablen (die sind nur für das S3/OBS-State-Backend). Je nach Bundle-Stand exportiert secrets-load.sh teils nur OTC_TF_*. Verifizieren Sie nach dem Laden:
bash
env | grep -E '^OS_(ACCESS_KEY|SECRET_KEY|USERNAME|PASSWORD|REGION_NAME)=' \
| sed 's/=.*/=<set>/'Erwartet (Werte ausgeblendet):
text
OS_REGION_NAME=<set>
OS_ACCESS_KEY=<set>
OS_SECRET_KEY=<set>
OS_USERNAME=<set>
OS_PASSWORD=<set>Fehlen OS_ACCESS_KEY/OS_SECRET_KEY, mappen Sie aus den OTC_TF_*:
bash
export OS_ACCESS_KEY="$OTC_TF_ACCESS_KEY"
export OS_SECRET_KEY="$OTC_TF_SECRET_KEY"
export OS_USERNAME="$OTC_TF_USERNAME"
export OS_PASSWORD="$OTC_TF_PASSWORD"Wiedervorlage (bekannt):
secrets-load.shsoll dieses Mapping künftig selbst erledigen. Bis dahin ist der Handgriff oben Pflicht — sonst scheiterttofu/Teardown mit Auth-Fehlern.
Aufräumen am Ende der Session:
bash
source infra/connectivity/secrets-load.sh --unset5. Kontext-Konventionen (cwd, ENV, KUBECONFIG)
Damit immer klar ist, wo Sie agieren, gewöhnen Sie sich diesen Prompt-Banner an:
bash
echo "cwd=$(pwd)"; echo "KUBECONFIG=${KUBECONFIG:-<unset>}"; \
kubectl config current-context 2>/dev/null || echo "context=<none>"Beispiel-Output (gesunder Boot-Kontext):
text
cwd=/home/stl/git/mms/odc/odc-tcloud/infra/cce-appliance
KUBECONFIG=/home/stl/git/mms/odc/odc-tcloud/infra/cce-appliance/kubeconfig
context=swa-applianceDie zentrale Variable ist KUBECONFIG. Sie zeigt nach dem Infra-Apply auf die vom Cluster exportierte kubeconfig:
bash
export KUBECONFIG="$(tofu -chdir=infra/cce-appliance output -raw kubeconfig_path)"⚠️ Prüfen Sie vor jedem kubectl/helm-Kommando, dass current-contextswa-appliance ist — nicht versehentlich cce-nginx oder ein lokaler ddev-Kontext.
6. HOCHFAHREN — Schritt für Schritt
Gesamtmodell (ADR-025). Schritt 1 + 1b sind ein Bootstrap außerhalb des Clusters (Terraform + Argo-Install). Schritt 2–5 (Images → Deploy → Provisionieren → Smoke) laufen danach im Cluster. Für die manuelle Operator-Strecke fahren wir alle Schritte hier von Hand; die Pipeline-Automatisierung ist die spätere Ausbaustufe.
Schritt 0 — Credentials & Preflight
Siehe Kapitel 3 und 4. Ende dieses Schritts: task ci:doctor grün, OS_* gesetzt.
Schritt 1 — Infrastruktur (OTC) provisionieren
cwd:
infra/cce-applianceenv:OS_*gesetzt (Schritt 0)
bash
cd infra/cce-appliance
tofu init
tofu plan -out plan.tfplan
tofu apply plan.tfplan # CCE-Cluster, RDS-MySQL, DCS-Redis, OBS, DNS-Zone, VPCDas legt an: CCE-Cluster swa-appliance (v1.29), RDS-MySQL 8, DCS-Redis 7, VPC/Subnet, DNS-Zone. Letzter gemessener Drill am 2026-06-10: 8m34s für den OpenTofu-Apply; für Operator-Planung weiter 10–15 min einrechnen.
⚠️ Replace-Falle: Zeigt tofu plan ein -/+ destroy and then create replacement für CCE-Worker (Tag-/Metadata-Konflikt), nicht apply. Stattdessen die Tags in der 🖱️ Console angleichen (siehe Kapitel 9, P-14) und neu planen.
kubeconfig abgreifen und Cluster prüfen:
bash
export KUBECONFIG="$(tofu output -raw kubeconfig_path)"
kubectl get nodesBeispiel-Output:
text
NAME STATUS ROLES AGE VERSION
192.168.0.123 Ready <none> 3m v1.29.3
192.168.0.214 Ready <none> 3m v1.29.3Schritt 1.1 — DNS-Delegation (nur erstmalig / nach Zonen-Neuaufbau)
bash
tofu output dns_zone_nameserversBeispiel-Output:
text
dns_zone_nameservers = [
"ns1.open-telekom-cloud.com.",
"ns2.open-telekom-cloud.com.",
]🖱️ Console / Registrar: Diese Nameserver im united-domains-Kundencenter als NS-Records für die Subdomain officina-del-caffe unter uprix.de eintragen.
⚠️ Bekannt (R6/R9): Die DNS-Zone hängt aktuell am cce-appliance-Workspace und wird beim tofu destroy mitgelöscht. Nach jedem Wiederaufbau zeigen die alten NS ins Leere — Delegation neu setzen. Mittelfristig wandert die Zone in einen eigenen, langlebigen infra/dns/-Workspace.
Schritt 1.2 — Plattform-Secrets in den Cluster
cwd: Repo-Root env:
KUBECONFIG(Schritt 1),OS_*
bash
bash deploy/method-kit/scripts/render-platform-secrets.sh demoDas liest die tofu-Outputs (mysql_*, redis_*) und schreibt idempotent die Secrets platform-mysql und platform-redis in den Namespace officina-demo.
Schritt 1.3 — Shopware-App- & JWT-Secrets (out-of-band)
⚠️ Diese Werte gehören NIE ins Repo. Der §6-Init bricht fail-fast ab, wenn sie fehlen.
bash
kubectl create namespace officina-demo 2>/dev/null || true
kubectl -n officina-demo create secret generic shopware-app \
--from-literal=APP_SECRET="$(openssl rand -hex 16)" \
--from-literal=JWT_PASSPHRASE=""
TMP="$(mktemp -d)"
openssl genrsa -out "$TMP/private.pem" 2048
openssl rsa -in "$TMP/private.pem" -pubout -out "$TMP/public.pem"
kubectl -n officina-demo create secret generic shopware-jwt \
--from-file=private.pem="$TMP/private.pem" \
--from-file=public.pem="$TMP/public.pem"
rm -rf "$TMP"Frisch erzeugte JWT-Keys invalidieren alte Admin-Tokens — bei frischem Stage (leere RDS) unkritisch.
Schritt 1a — Ingress + cert-manager (Edge)
cwd: Repo-Root env:
KUBECONFIG
bash
helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
-n ingress-nginx --create-namespace \
--set controller.service.type=LoadBalancer \
--set controller.service.annotations."kubernetes\.io/elb\.class"=union \
--set-string controller.service.annotations."kubernetes\.io/elb\.autocreate"='{"type":"public","bandwidth_name":"swa-appliance-ingress","bandwidth_chargemode":"traffic","bandwidth_size":5,"bandwidth_sharetype":"PER","eip_type":"5_bgp"}'OTC erzeugt dadurch automatisch einen ELB + EIP. EIP auslesen (kann 1–2 min dauern):
bash
INGRESS_EIP=$(kubectl -n ingress-nginx get svc ingress-nginx-controller \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo "INGRESS_EIP=$INGRESS_EIP"Beispiel-Output:
text
INGRESS_EIP=80.158.52.123A-Record auf die EIP setzen (per Tofu-Var nachgereicht):
cwd:
infra/cce-appliance
bash
tofu apply -var "ingress_eip=$INGRESS_EIP"cert-manager + Let's-Encrypt-Issuer:
cwd: Repo-Root
bash
helm upgrade --install cert-manager cert-manager \
--repo https://charts.jetstack.io \
-n cert-manager --create-namespace \
--set crds.enabled=true
kubectl apply -f deploy/method-kit/argo/bootstrap/letsencrypt-prod-issuer.yaml⚠️ Let's-Encrypt-Ratelimit: Bei wiederholten Auf-/Abbauten am selben Tag riskieren Sie das LE-Production-Ratelimit (5 Zertifikate/Domain/Woche). Nutzen Sie für Drills den Staging-Issuer; wechseln Sie erst für die echte Demo auf letsencrypt-prod.
Schritt 1b — Argo (Workflows + ArgoCD)
cwd: Repo-Root env:
KUBECONFIG,BITBUCKET_CI_READ_TOKEN,BITBUCKET_CI_WRITE_TOKEN,ARGO_ARTIFACT_BUCKET
bash
export ARGO_ARTIFACT_BUCKET='sw-auctions-argo-artifacts-dev'
task argo:bootstrapdeploy/sw-auctions/argo/bootstrap.sh legt den OBS-Artifact-Bucket bei einem From-scratch-Boot idempotent an, falls er noch fehlt. Nach dem Bootstrap sind die Tool-UIs direkt über die Demo-Domain erreichbar:
| Tool | URL | Hinweis |
|---|---|---|
| Argo Workflows | https://argo.officina-del-caffe.uprix.de | authMode: server, kein separates Demo-Passwort im Runbook. |
| ArgoCD | https://argocd.officina-del-caffe.uprix.de | User admin; Passwort aus dem Cluster-Secret abrufen, nicht dokumentieren. |
ArgoCD-Initialpasswort sicher im Terminal abrufen:
bash
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath="{.data.password}" | base64 -d; echoSchritt 2 — Images bauen (Argo-Workflow → SWR)
cwd: Repo-Root env:
KUBECONFIG,BITBUCKET_CI_READ_TOKEN
Aktuellen main-HEAD ermitteln und Build-Workflow starten:
bash
REV=$(curl -sS -H "Authorization: Bearer $BITBUCKET_CI_READ_TOKEN" \
"https://bitbucket.telekom-mms.com/rest/api/1.0/projects/BUDCCTA/repos/dccta-shopware-auctions/commits?until=refs/heads/main&limit=1" \
| jq -r .values[0].id)
echo "REV=$REV"
cat <<YAML | kubectl create -f -
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata: {generateName: bootstrap-build-, namespace: officina-cicd}
spec:
workflowTemplateRef: {name: service-build}
arguments:
parameters:
- {name: repoUrl, value: "https://bitbucket.telekom-mms.com/scm/budccta/dccta-shopware-auctions.git"}
- {name: revision, value: "$REV"}
- {name: services, value: "[\"storefront\",\"shopware\"]"}
YAMLWorkflow beobachten, bis Succeeded:
bash
kubectl -n officina-cicd get wf -wDer Build pusht die Images nach SWR (
...:demo-sha.<rev>) und bumptdeploy/sw-auctions/values/demo.yamlviagit-commit-push.
Schritt 3 — Appliance per ArgoCD/ApplicationSet ausrollen
cwd: Repo-Root env:
KUBECONFIG
Der Standardpfad ist GitOps: task argo:bootstrap appliziert das ApplicationSet officina-appliance; daraus entsteht die ArgoCD-Application officina-appliance-demo. Diese Application verwaltet den Helm-Release aus deploy/method-kit/charts/appliance mit deploy/sw-auctions/values/demo.yaml.
⚠️ Nicht zusätzlich manuell helm upgrade --install ausführen. Nach dem Argo-Bootstrap ist die App GitOps-owned; ein paralleler manueller Helm-Install kollidiert mit Ressourcen, die Argo bereits erzeugt hat.
Wenn der Build-Workflow neue Images gebaut und values/demo.yaml gepusht hat, Argo hart refreshen und auf den Remote-Commit warten:
bash
git fetch origin main
TARGET="$(git rev-parse origin/main)"
kubectl -n argocd annotate application officina-appliance-demo \
argocd.argoproj.io/refresh=hard --overwrite
for i in $(seq 1 60); do
STATUS="$(kubectl -n argocd get application officina-appliance-demo \
-o jsonpath='sync={.status.sync.status} health={.status.health.status} rev={.status.sync.revision}{"\n"}')"
echo "$STATUS"
echo "$STATUS" | grep -q "sync=Synced health=Healthy rev=${TARGET}" && break
sleep 10
done
kubectl -n argocd get application officina-appliance-demo -o wideErwartet: SYNC STATUS=Synced, HEALTH STATUS=Healthy, REVISION=$TARGET.
Debug-Fallback ohne Argo: Nur wenn argocd/ApplicationSet bewusst nicht installiert ist, darf der alte direkte Helm-Pfad genutzt werden:
bash
task render TAG="demo-sha.${REV:0:7}"
helm upgrade --install officina-appliance-demo \
deploy/method-kit/charts/appliance \
-n officina-demo -f deploy/sw-auctions/values/demo.yaml \
--wait --timeout 20mDieser Fallback darf nicht parallel zur ArgoCD-Application officina-appliance-demo laufen.
Schritt 3.1 — Demo-Runtime-Artefakte (MinIO, Mailpit)
Diese Snapshots ergänzen die Helm-Chart um Dinge, die noch nicht im Chart sind:
bash
kubectl apply -f deploy/sw-auctions/demo-runtime/minio-deployment.yaml
kubectl apply -f deploy/sw-auctions/demo-runtime/minio-service.yaml
kubectl apply -f deploy/sw-auctions/demo-runtime/minio-init-job.yaml
kubectl apply -f deploy/sw-auctions/demo-runtime/mailpit-deployment.yaml
kubectl apply -f deploy/sw-auctions/demo-runtime/mailpit-service.yaml
kubectl apply -f deploy/sw-auctions/demo-runtime/mailpit-ingress.yamlShopware darf hier nicht gepatcht werden: Das Prod-Image startet supervisord mit nginx + php-fpm. Ein php -S-Command-Override ist eine SPEC-023-Regression und führt zu falschem Media-/Admin-Routing.
⚠️ ArgoCD-Self-Heal: Solange Sie Live-Patches fahren, überschreibt ArgoCD sie sofort wieder. Im Demo-Modus die Controller nur fuer die Dauer eines bewusst dokumentierten Live-Fix abschalten:
bash
kubectl -n argocd scale statefulset argocd-application-controller --replicas=0
kubectl -n argocd scale deploy argocd-applicationset-controller --replicas=0Nach Commit/Push des Desired-State-Fix die Controller sofort wieder hochfahren und auf den gepushten Commit reconciliieren:
bash
TARGET=$(git rev-parse HEAD)
kubectl -n argocd scale statefulset argocd-application-controller --replicas=1
kubectl -n argocd scale deploy argocd-applicationset-controller --replicas=1
kubectl -n argocd rollout status statefulset/argocd-application-controller --timeout=180s
kubectl -n argocd rollout status deploy/argocd-applicationset-controller --timeout=180s
kubectl -n argocd annotate application officina-appliance-demo \
argocd.argoproj.io/refresh=hard --overwrite
kubectl -n argocd get application officina-appliance-demo -o wideErwartet: SYNC STATUS=Synced, HEALTH STATUS=Healthy, REVISION=$TARGET.
Der minio-init-Job legt bvg-records mit Object-Lock COMPLIANCE/1d an. Falls ein leerer Demo-Bucket ohne Object-Lock existiert, wird er neu angelegt; bei nicht-leerem Bucket bricht der Job bewusst ab.
Schritt 4 — Shopware provisionieren (wie ddev)
cwd: Repo-Root env:
KUBECONFIG
bash
SW=$(kubectl -n officina-demo get pods -l app.kubernetes.io/name=shopware \
-o jsonpath='{.items[?(@.status.phase=="Running")].metadata.name}')
echo "SW=$SW"
kubectl -n officina-demo exec $SW -c shopware -- bash -lc '
cd /var/www/html
php bin/console system:install --basic-setup --force --create-database --no-interaction
touch install.lock
php bin/console plugin:refresh
for p in SwAuctionsServiceNotification SwAuctionsServiceCustomer \
SwAuctionsServiceCatalogue SwAuctionsServiceRecords \
SwAuctionsServiceAuction SwAuctionsServiceBidding; do
php bin/console plugin:install --activate --no-interaction $p || true
done
php bin/console assets:install
php bin/console theme:compile
php bin/console dal:refresh:index
php bin/console system:config:set SwAuctionsServiceRecords.config.s3Endpoint "http://minio:9000"
php bin/console system:config:set SwAuctionsServiceRecords.config.s3Region "us-east-1"
php bin/console system:config:set SwAuctionsServiceRecords.config.s3AccessKey "minioadmin"
php bin/console system:config:set SwAuctionsServiceRecords.config.s3SecretKey "minioadmin"
php bin/console system:config:set SwAuctionsServiceRecords.config.s3Bucket "bvg-records"
php bin/console system:config:set SwAuctionsServiceRecords.config.s3UsePathStyle "1"
php bin/console sw-auctions:catalogue:seed-coffee-lots
php bin/console sw-auctions:auction:seed-demo-fanout
'Schritt 4.1 — Sales-Channel-Domains setzen
⚠️ Bekannt: Shopware braucht je Sales-Channel mindestens einen Domain-Eintrag. Fehlt er, zeigt der Browser „Sales Channel Not Found". system:install --basic-setup legt nur http://localhost an; das muss auf shop.* (und für den Headless/Admin-Channel auf admin.*) gemappt werden.
bash
kubectl -n officina-demo exec $SW -c shopware -- php -r "
preg_match('#mysql://([^:]+):([^@]+)@([^:]+):(\d+)/(.+)#', getenv('DATABASE_URL'), \$m);
\$p = new PDO(\"mysql:host={\$m[3]};port={\$m[4]};dbname={\$m[5]}\", \$m[1], \$m[2]);
\$p->exec('UPDATE sales_channel_domain SET url=\"https://shop.officina-del-caffe.uprix.de\" WHERE url LIKE \"http://localhost%\"');
echo 'shop-domain rows: ' . \$p->query('SELECT ROW_COUNT()')->fetchColumn() . PHP_EOL;
"Schritte 5–8 — Smoke-Tests / Live-Bestätigung
Siehe Kapitel 11. Ende: alle App- und Tool-Domains liefern HTTP 200, die Store-API liefert total=7, und sw-auctions:auction:list zeigt 7 Auktionen. Die Appliance ist LIVE.
7. HERUNTERFAHREN — geordneter Abriss
Grundregel (teuer gelernt). Die Tofu-State-Liste ist KEINE verlässliche Quelle dafür, „was noch lebt". Verwaiste Ressourcen (RDS-Instanz, Security-Group-v1, EVS-Disks) überleben ein
tofu destroyund müssen über direkte OTC-APIs plus Console-RMS-Inventur gegengeprüft und in der richtigen Reihenfolge gelöscht werden.
Phase A — Workload abräumen (zuerst, im Cluster!)
cwd: Repo-Root env:
KUBECONFIG
bash
export KUBECONFIG="$PWD/infra/cce-appliance/kubeconfig"
# ArgoCD-Application zuerst (sonst re-synct sie alles zurück)
kubectl -n argocd delete application officina-appliance-demo \
--cascade=foreground --wait=true --ignore-not-found=true
# Helm-Releases
helm -n officina-demo uninstall officina-appliance-demo || true
helm -n officina-demo uninstall officina || true
# Namespace (löst Pods, Services, PVCs)
kubectl delete namespace officina-demo --wait=true⚠️ LoadBalancer zuerst freigeben: Jeder Service: type=LoadBalancer hält einen ELB + EIP in OTC. Werden diese nicht über das Löschen der Services freigegeben, hängen ELB/EIP später beim tofu destroy. Das Löschen des Namespace erledigt das für die App-Services; den ingress-nginx-Controller separat:
bash
helm -n ingress-nginx uninstall ingress-nginx || true
kubectl delete namespace ingress-nginx --wait=truePVCs der CI-Workspaces (Argo) lösen die zugehörigen EVS-Volumes:
bash
kubectl -n officina-cicd delete pvc --all --wait=true || trueOptional, wenn Argo selbst auch weg soll:
bash
kubectl delete namespace officina-cicd --wait=true --ignore-not-found=true --timeout=10m
kubectl delete namespace argocd --wait=true --ignore-not-found=true --timeout=10mWenn argocd dabei in Terminating hängen bleibt und die Namespace-Conditions resources-finalizer.argocd.argoproj.io auf einer Application zeigen, ist der Application-Controller wahrscheinlich im Demo-Modus auf 0 skaliert und kann den Finalizer nicht selbst entfernen. Dann gezielt nur diesen ArgoCD-Finalizer löschen:
bash
kubectl -n argocd patch application officina-appliance-demo \
--type=merge -p '{"metadata":{"finalizers":null}}' || true
kubectl wait --for=delete namespace/argocd --timeout=5m || truePhase B — Infrastruktur per Tofu zerstören
cwd:
infra/cce-applianceenv:OS_*gesetzt und verifiziert (Kapitel 4!)
bash
cd infra/cce-appliance
tofu destroyDas zerstört regulär ~20 Ressourcen: CCE-Cluster, NodePool, NAT, beide EIPs, VPC+Subnet, RDS, DCS, DNS-Zone + Wildcard, SecGroups, Keypair.
⚠️ Kein endloses Retry. Zeigt der Lauf minutenlang Still destroying... (typisch für Subnet + RDS-SecGroup), maximal einen Retry — danach in Phase C mit einer OTC-API-Inventur weitermachen. Endloses Warten löst nichts.
Phase C — Verwaiste Reste manuell abräumen (Dependency-Kette)
🖱️ Console: Öffnen Sie das RMS (Resource Management Service) und machen Sie einen Ressourcen-Screenshot/-Export. Das zeigt Ressourcen, die außerhalb des Tofu-State leben. Achtung: Der Console-RMS-Index kann stale sein (zeigt z. B. einen CCE-Cluster, der per Direkt-API schon 404 ist) — die Direkt-API ist die Wahrheit.
Lösch-Reihenfolge (hart): RDS → SecurityGroup(v1) → EVS-Disks → Subnet → VPC. Jede andere Reihenfolge endet mit 409 („Subnet still in use" / „Router contains subnets").
- Verwaiste RDS-Instanz (z. B.
swa-appliance-mysql) über die RDS-v3-API löschen. 🖱️ Console-Alternative: Relational Database Service → Instanzen → Löschen. - Security-Group des RDS löschen. ⚠️ API-Quirk: Die Neutron-v2.0-API meldet die SG fälschlich als
404, während sie real noch existiert und der echte Router-Blocker ist. Löschung MUSS über die v1-native VPC-API erfolgen:DELETE /v1/{project}/security-groups/{id}(→204→404). 🖱️ Console-Alternative: VPC → Security Groups (nicht Network Console → Security Groups, das ist die Neutron-Sicht). - Verwaiste EVS-Disks (
pvc-*, Statusavailable, 0 Attachments) löschen. 🖱️ Console: Elastic Volume Service → Disks → Status „Available" → Löschen. - Subnet löschen (jetzt ohne
409). 🖱️ VPC → Subnets. - VPC löschen. 🖱️ VPC → Virtual Private Cloud.
Tofu-State nachziehen (entfernt die nicht mehr existierenden Objekte aus dem State):
bash
tofu state list
tofu state rm 'opentelekomcloud_vpc_subnet_v1.this' 'opentelekomcloud_vpc_v1.this'
# nur die Objekte entfernen, die per API bereits 404 sind!Phase D — Verifikation (unabhängiger Scan)
🖱️ Optional per Console-RMS, verlässlicher per IAM-Token + REST-APIs. Ziel ist ein Gesamt-Scan = 0 über alle App-Ressourcenklassen:
| Ressource | Soll |
|---|---|
| CCE-Cluster | 0 |
| RDS | 0 |
| DCS/Redis | 0 |
| VPC | 0 |
| Subnet | 0 |
| SecurityGroup | 0 |
| EIP | 0 |
| ELB | 0 |
| NAT-GW | 0 |
| EVS-Volumes | 0 |
| DNS-Zonen | 0 |
| SWR-Namespaces | 0 (falls auch SWR abgerissen wird — meist behalten) |
⚠️ DNS-Konsequenz: Nach dem Abriss existiert die Zone officina-del-caffe.uprix.de. nicht mehr; die NS-Delegation bei united-domains zeigt ins Leere, bis ein Re-Apply neue OTC-Nameserver liefert und Sie die Delegation neu setzen (Schritt 1.1).
8. Statussequenzen
Appliance-Lifecycle (Operator-Sicht)
Der grüne Zustand Live ist das Ziel für die SCD-Demo. Von dort führen drei Pfade zurück: ein Pod-Recreate (Medien gehen verloren → Reseed), ein Image-Bump (ArgoCD-Sync) und der Teardown (Workload weg → Abriss → Leer).
OTC-Ressourcen-Abriss (Dependency-Kette)
Siehe das Diagramm in Phase C. Es zeigt die harte Reihenfolge RDS → SG(v1) → EVS → Subnet → VPC und die typischen 409-Blocker dazwischen.
9. Bekannte Probleme & Lösungen
Diese Tabelle ist die destillierte Erfahrung aus den bisherigen Boot- und Teardown-Sessions. Quelle steht je Zeile dabei.
| # | Symptom | Ursache | Lösung | Quelle |
|---|---|---|---|---|
| P-1 | tofu destroy hängt minutenlang auf Still destroying... (Subnet, RDS-SG) | OTC-Delete-Latenz / verwaiste Abhängigkeit | Max. 1 Retry, dann API-Inventur (Phase C). Kein endloses Warten. | SESSION-2026-05-27_18-40 |
| P-2 | Nach tofu destroy leben RDS/SG/EVS weiter | Nicht alles im Tofu-State; Reste überleben Destroy | Direkt per OTC-API + RMS prüfen und in Reihenfolge RDS→SG(v1)→EVS→Subnet→VPC löschen | SESSION-2026-05-30_18-20 |
| P-3 | 409 "Router contains subnets" / "Subnet still in use" | Falsche Lösch-Reihenfolge | Erst RDS, dann SG(v1), dann EVS-Disks, dann Subnet, dann VPC | SESSION-2026-05-30_18-20 |
| P-4 | Security-Group meldet via API 404, blockiert aber trotzdem | Neutron-v2-API lügt; SG existiert in v1-VPC-API | Löschen über DELETE /v1/{project}/security-groups/{id} (Console: VPC → Security Groups) | SESSION-2026-05-30_18-20 |
| P-5 | DNS-Zone nach Destroy weg, Domain tot | Zone hängt am cce-appliance-Workspace | NS-Delegation nach Re-Apply neu setzen; mittelfristig eigener infra/dns/-Workspace | SESSION-2026-05-28_18-14 |
| P-6 | Ingress nicht erreichbar, DNS zeigt auf Worker-IP | Public IP gehört dem ELB, nicht dem Worker | A-Record auf ELB-EIP; Service type=LoadBalancer erzeugt ELB+EIP | SESSION-2026-05-28_00-59 |
| P-7 | Root-Domain lässt sich nicht per CNAME auf ELB legen | CNAME auf Root ist DNS-technisch unzulässig | A-Record direkt auf die ELB-EIP | SESSION-2026-05-28_00-59 |
| P-8 | TLS/HTTPS fehlt am Ingress | cert-manager + Issuer nicht installiert | ingress-nginx und cert-manager + LE-Issuer per Helm ausrollen (Schritt 1a) | SESSION-2026-05-28_00-59 |
| P-9 | Manuelle Patches verschwinden nach Sekunden | ArgoCD-Self-Heal reconciled zurück | Controller nur kurz auf replicas=0; Fix in Chart/Manifest/Values ziehen, committen/pushen, Controller wieder auf 1 und Synced/Healthy prüfen | SESSION-2026-05-27_18-40, SESSION-2026-06-10_12-43 |
| P-10 | Medien/Bilder weg nach Pod-Recreate | MinIO nutzt emptyDir statt PVC | Reseed: sw-auctions:catalogue:seed-coffee-lots --force-media-reimport; produktiv PVC | SESSION-2026-05-28_18-14 |
| P-11 | Storefront-Bilder 404 (/products/<hersteller>/...) | Altes Storefront-Image oder stale Daten referenzieren Bildpfade, die im aktuellen Build nicht vorhanden sind | Storefront-Image neu bauen/pushen und Demo-Daten reseeden; das aktuelle Image enthält Routen und Assets ohne Nitro-Live-Patch | SESSION-2026-05-28_18-14, SESSION-2026-06-02_18-08 |
| P-12 | Browser zeigt „Sales Channel Not Found" | Sales-Channel ohne Domain-Eintrag | Domains auf shop.*/admin.* mappen (Schritt 4.1) | demo-bootstrap.md |
| P-13 | Images nicht push-/pullbar | SWR-Org/Repos fehlen oder Pull-Secret fehlt | SWR-Org officina-del-caffe + Repos anlegen; swr-pull-Secret im Namespace | SESSION-2026-05-28_18-14 |
| P-14 | tofu plan will CCE-Worker replacen (-/+) | Tag-/Metadata-Konflikt | Nicht applyen; in der Console re-taggen, dann neu planen | SESSION-2026-05-27_15-48 |
| P-15 | ELB-Tagging/Ownership unklar | ELB entsteht out-of-Tofu via Service: LoadBalancer | kubernetes.io/elb.tags-Annotation am Service setzen | SESSION-2026-05-27_15-48 |
| P-16 | tofu/Teardown scheitert mit Auth-Fehler | Provider braucht OS_*, nicht OTC_TF_*/AWS_* | OS_ACCESS_KEY/OS_SECRET_KEY/OS_USERNAME/OS_PASSWORD mappen (Kapitel 4) | SESSION-2026-05-28_18-14 |
| P-17 | aws/aws-sdk-php fehlt im Pod (vendor/aws) | Lag früher in require-dev statt require | Prod-Image/Composer-Lock korrigieren; kein Runtime-composer require per Bootstrap-CM | SESSION-2026-05-28_18-14, SPEC-023 |
| P-18 | Shopware bootet nicht / Web-Installer erscheint | install.lock fehlt | install.lock ins Prod-Image backen; kein Bootstrap-Skript als Webserver-Ersatz | SESSION-2026-05-28_18-14, SPEC-023 |
| P-19 | RollingUpdate erzeugt 2. Pod, sprengt knappes Cluster | Surge-Strategie bei 1-Replica | strategy.type=Recreate (im Manifest gesetzt) | SESSION-2026-05-30_18-20 |
| P-20 | Admin-Host-Root landet in Storefront-Channel-Auflösung | / nicht nach /admin geführt | Ingress-Annotation nginx.ingress.kubernetes.io/app-root=/admin | SESSION-2026-05-30_18-20 |
| P-21 | Shopware-Init endet mit There are no commands defined in the "sw-auctions:storefront" namespace | Gepinntes Shopware-Image ist älter als der Init-Vertrag und enthält sw-auctions:storefront:provision-cms noch nicht | Init guardiert den Command per bin/console list; langfristig neues Shopware-Image bauen/pinnen | SESSION-2026-06-05_00-06 |
| P-22 | Argo-Bootstrap bricht lokal mit yq (v4) required ab | Operator-Maschine hat kein lokales yq; Tool-Boundary verbietet Nachinstallation | deploy/sw-auctions/argo/bootstrap.sh nutzt Python/PyYAML für Template-Patches | SESSION-2026-06-05_00-06 |
| P-23 | infra/swr-Plan will erp-emulator/erp-integration zerstören | SWR-IaC-Liste war älter als die Demo-Values | Repositories in infra/swr/variables.tf dauerhaft aufnehmen; SWR-Plan muss 0-Destroy sein | SESSION-2026-06-05_00-06 |
| P-24 | Runbook nennt demo.env.age, Preflight findet nur dev.env.age | Realer Credential-Bundle-Name ist aktuell dev | source infra/connectivity/secrets-load.sh dev verwenden; kein Klartext-Bundle anlegen | SESSION-2026-06-05_00-06 |
| P-25 | argocd-Namespace bleibt beim Teardown in Terminating | Demo-Modus hat den ArgoCD-Application-Controller auf 0 skaliert; Application-Finalizer kann nicht abgeräumt werden | Nur den betroffenen resources-finalizer.argocd.argoproj.io auf der Demo-Application patchen, dann Namespace-Wait fortsetzen | SESSION-2026-06-05_00-06 |
| P-26 | Admin lädt nicht sauber oder Storefront-Bilder liefern Sales Channel Not Found/400 auf admin.* | Shopware-Deployment wurde per Runtime-Patch auf php -S ... public/index.php umgebogen; vorhandene Media-/Asset-Pfade laufen dann durch Shopware statt nginx | Command-/Bootstrap-Patch entfernen; Pod muss Image-CMD supervisord mit nginx + php-fpm nutzen; aktuelle Media-URLs nach Pod-Recreate neu prüfen | SESSION-2026-06-10_12-43 |
Jeder geschlossene Root-Cause (
R1…R9im Runbook) verkleinert den manuellen Aufwand. Den aktuellen Stand der offenen Punkte führtdeploy/sw-auctions/runbooks/demo-bootstrap.mdim Abschnitt „Root-cause TODOs".
10. OTC-Console-Cheatsheet
Login: https://console.otc.t-systems.com → Region eu-de wählen (oben).
| Ich will… | In der Console… |
|---|---|
| Cluster-Status sehen | Cloud Container Engine (CCE) → Cluster |
| RDS-Instanz löschen | Relational Database Service → Instanzen |
| Redis löschen | Distributed Cache Service (DCS) |
| Security-Group löschen (echte Quelle) | Virtual Private Cloud → Security Groups ⚠️ nicht die Network-Console-Sicht |
| Verwaiste Disks finden | Elastic Volume Service (EVS) → Disks → Filter „Available" |
| EIP/ELB prüfen | Elastic IP bzw. Elastic Load Balance |
| NAT-Gateway prüfen | NAT Gateway |
| DNS-Zone & Records | Domain Name Service → Public Zones |
| Nameserver für Delegation | DNS → Zone öffnen → NS-Record (gehört zu united-domains) |
| Registry/Images | SoftWare Repository for Container (SWR) |
| State-/Artefakt-Buckets | Object Storage Service (OBS) |
| Gesamt-Inventur (kann stale sein) | Resource Management Service (RMS) → Resources |
⚠️ Console-Disziplin (ADR-008): Jede Console-Änderung gehört in die jeweilige SESSION-…-Datei vermerkt und in infra/-Tofu nachgezogen. Keine Console-Klicks ohne IaC-Nachzug.
11. Health- & Smoke-Checks
cwd: Repo-Root env:
KUBECONFIG
HTTP-Smoke (Live-URLs):
bash
for u in https://shop.officina-del-caffe.uprix.de/ \
https://shop.officina-del-caffe.uprix.de/auctions \
https://admin.officina-del-caffe.uprix.de/admin \
https://docs.officina-del-caffe.uprix.de/ \
https://erp.officina-del-caffe.uprix.de/ \
https://mail.officina-del-caffe.uprix.de/ \
https://argo.officina-del-caffe.uprix.de/ \
https://argocd.officina-del-caffe.uprix.de/; do
curl -sS -o /dev/null -w "$u → %{http_code}\n" "$u"
doneBeispiel-Output (gesund):
text
https://shop.officina-del-caffe.uprix.de/ → 200
https://shop.officina-del-caffe.uprix.de/auctions → 200
https://admin.officina-del-caffe.uprix.de/admin → 200
https://docs.officina-del-caffe.uprix.de/ → 200
https://erp.officina-del-caffe.uprix.de/ → 200
https://mail.officina-del-caffe.uprix.de/ → 200
https://argo.officina-del-caffe.uprix.de/ → 200
https://argocd.officina-del-caffe.uprix.de/ → 200Store-API-Smoke:
bash
curl -sS \
-H 'sw-access-key: SWSCBFFRZZM3TFLUT3PFQWR1ZW' \
https://admin.officina-del-caffe.uprix.de/store-api/sw-auctions/auctionsErwartet: HTTP 200 mit total=7 und 7 elements.
Fachlicher Check (Shopware-CLI):
bash
SW=$(kubectl -n officina-demo get pods -l app.kubernetes.io/name=shopware \
-o jsonpath='{.items[?(@.status.phase=="Running")].metadata.name}')
kubectl -n officina-demo exec $SW -c shopware -- php bin/console sw-auctions:auction:listErwartet: 7 Auktionen mit 40 Listings.
Pod-Übersicht:
bash
kubectl -n officina-demo get podsBeispiel-Output (gesund):
text
NAME READY STATUS RESTARTS AGE
officina-appliance-demo-shopware-... 1/1 Running 0 12m
officina-appliance-demo-storefront-... 1/1 Running 0 12m
officina-appliance-demo-erp-emulator-... 1/1 Running 0 12m
officina-appliance-demo-erp-integration-... 1/1 Running 0 12m
minio-... 1/1 Running 0 14m
mailpit-... 1/1 Running 0 11m12. Was bewusst bestehen bleibt
Beim regulären Appliance-Teardown werden diese Dinge absichtlich NICHT mitgelöscht:
- OBS-Bucket
sw-auctions-tfstate-dev— das gemeinsame Tofu-Remote-State-Backend. Ohne ihn verlieren alle Workspaces ihren State. - SWR-Org
officina-del-caffe(Reposstorefront,shopware,docs-server,erp-emulator,erp-integration) — Image-Cache für schnelles Re-Apply. Liegt im getrennten Workspaceinfra/swr/. - OBS-Bucket
sw-auctions-argo-artifacts-dev— Argo-Workflow-Artefakte. Wird vom Argo-Bootstrap bei Bedarf angelegt und gehört nicht zum Appliance-Teardown. - Workspaces
infra/connectivity/,infra/cce-nginx/,infra/mcp-server/— nicht Teil des Appliance-Abrisses; ihr State liegt ebenfalls insw-auctions-tfstate-dev.
Wer auch SWR abreißen will (selten), nutzt cd infra/swr && tofu destroy separat — und nimmt den 15-min-Layer-Re-Push beim nächsten Aufbau in Kauf.
Verwandte Spezifikationen & Quellen
- Runbook
deploy/sw-auctions/runbooks/demo-bootstrap.md— schmaler Bootstrap-Pfad. - ADR-025 — In-Cluster-Bootstrap-Pipeline (Schritt 2–5 als Argo-DAG).
- ADR-010 — OpenTofu/OTC-Stack & OBS-State.
- ADR-013 — Argo-Family (CD-Trigger-Vertrag).
- ADR-008 — Connectivity / age-Bundle / Console-Disziplin.
- ADR-015 — Resource-Tagging-Convention.
infra/cce-appliance/README.md— Workspace-Scope & Variablen.infra/connectivity/README.md— Cred-Bootstrap.- Sessions:
SESSION-2026-05-28_18-14-tcloud-live.md,SESSION-2026-05-30_18-20-tcloud-provisioning-patterns.md,SESSION-2026-05-27_18-40-argo-family-poc-otc-and-anti-retry-runbook.md,SESSION-2026-05-28_00-59-recherche-cce-ingress-domains-otc.md,SESSION-2026-06-05_00-06-tcloud-bringup-after-provisioning-changes.md.
Changelog
| Version | Datum | Autor/Rolle | Änderung | Begründung / Trigger |
|---|---|---|---|---|
| 0.1.0 | 2026-05-31 | infra-engineer | Erstfassung des Operator-Handbuchs T-Cloud-Lifecycle: Hoch-/Herunterfahren, 6 PlantUML/SVG-Diagramme (Komponenten-, Verteilungsarchitektur, Boot-/Teardown-Sequenz, 2 Statussequenzen), bekannte Probleme P-1…P-20, OTC-Console-Cheatsheet, Smoke-Checks. | Orchestrator-Auftrag: vollständige, agentenfreie Operator-Anleitung in den Handbüchern, als SSOT-Derivat und im Docs-Server. |
| 0.1.1 | 2026-06-02 | infra-engineer | minio-init-job.yaml in die Demo-Runtime-Artefakte aufgenommen; manueller Bucket-Befehl durch Job-Vertrag ersetzt. | T-Cloud-Re-Bootstrap zeigte, dass der Records-Bucket ohne Object-Lock erzeugt wurde und seed-coffee-lots deshalb beim S3-Upload scheiterte. |
| 0.1.2 | 2026-06-02 | infra-engineer | Obsoleten Storefront-Nitro-Live-Patch aus dem Runtime-Apply-Set entfernt. | Neues Storefront-Image enthält die Routes/Assets selbst; der alte Patch scheiterte unter USER node an root-owned .output. |
| 0.1.3 | 2026-06-05 | infra-engineer | Realen From-scratch-Bootpfad nachgezogen: dev-Credential-Bundle, OTC-ELB-Autocreate, Argo-Tool-Domains, Argo-Artifact-Bucket, yq-freier Bootstrap, SWR-Repos, Shopware-CMS-Init-Guard, Smoke-Erwartung 7 Auktionen. | Boot-Drill nach Provisionierungsänderungen zeigte mehrere reproduzierbarkeitsrelevante Lücken. |
| 0.1.4 | 2026-06-05 | infra-engineer | Teardown-Pfad um Argo-Namespace-Abriss und ArgoCD-Finalizer-Recovery ergänzt. | Session-Ende mit explizitem Teardown zeigte einen argocd-Namespace in Terminating, weil der Demo-Self-Heal-Controller auf 0 skaliert war. |
| 0.1.5 | 2026-06-10 | infra-engineer | Shopware-Bootstrap-CM und Runtime-Patch aus dem Hochfahrpfad entfernt; Known-Issue P-26 für php -S-Regression ergänzt. | T-Cloud-Debug: Admin-/Media-400 durch Rückfall auf den verbotenen Built-in-Server-Pfad. |
| 0.1.6 | 2026-06-10 | infra-engineer | ArgoCD-Re-enable und Synced/Healthy-Abschlusscheck ergänzt; P-9 präzisiert. | Nach Commit 1175a9b reconciliierte die Demo-App stabil auf den Remote-Desired-State. |
| 0.1.7 | 2026-06-10 | infra-engineer | Bootpfad auf GitOps-Standard korrigiert: ApplicationSet/ArgoCD ist führend; direkter Helm-Install ist nur noch Debug-Fallback ohne Argo. Gemessene Provisioning-Zeit und Python/Boto3-Voraussetzung ergänzt. | Doku-Drift-Check nach stabilem Boot/Teardown: manueller Helm-Install kollidiert nach task argo:bootstrap mit GitOps-owned Ressourcen. |
| 0.1.8 | 2026-06-10 | spec-steward | Known-Issue P-11 vom gelöschten Storefront-Nitro-Live-Patch auf aktuellen Image-/Reseed-Vertrag korrigiert. | Doku-Drift-Check: storefront-patches-ConfigMap existiert nicht mehr; aktuelle Storefront-Images enthalten Routen und Assets selbst. |