Wzorce orkiestracji Kubernetes
Twój rollout właśnie położył API. Sonda readiness zrobiła się zielona, zanim pula połączeń do bazy danych skończyła się rozgrzewać, więc Kubernetes przekierował ruch produkcyjny na pody, które natychmiast zaczęły zwracać 500-tki. Dashboard jest czerwony, PM pyta o ETA, a manifest, który to spowodował, wygenerowało narzędzie AI, które nie wiedziało, że Twoja pula potrzebuje ośmiu sekund na zapełnienie.
To jest prawdziwe ryzyko związane z konfiguracją Kubernetes generowaną przez AI: YAML aplikuje się bez błędów, a mimo to wdraża awarię. Rozwiązaniem nie jest rezygnacja z AI — jest sterowanie nim za pomocą promptów, które kodują Twoje rzeczywiste ograniczenia (timing sond, zapas zasobów, budżety zakłóceń), oraz przeglądanie wyniku pod kątem trybów awarii, które gryzą na produkcji.
Co z tego wyniesiesz
Dział zatytułowany „Co z tego wyniesiesz”- Prompt do skopiowania, który generuje Deployment zero-downtime z sondami dostrojonymi do prawdziwego okna rozgrzewki — a nie do domyślnego dla AI
initialDelaySeconds: 0. - Prompt do debugowania, który zamienia wynik
kubectl describe+kubectl logsw uszeregowaną listę przyczyn źródłowych dlaCrashLoopBackOff,OOMKillediImagePullBackOff. - Prompt, który przekształca luźne manifesty w sparametryzowany wykres Helm z walidacją wartości — wykorzystując utrzymywane zależności wykresów, a nie wycofany katalog Bitnami.
- Playbook „Gdy to się zepsuje” dla pięciu awarii, które manifesty generowane przez AI powodują najczęściej: wyścigi sond, OOMKille, PDB blokujące drenowanie węzłów, błędy pobierania obrazów i zacięte synchronizacje ArgoCD.
Przepływ pracy
Dział zatytułowany „Przepływ pracy”Wzorzec jest taki sam we wszystkich trzech narzędziach: opisz workload i jego ograniczenia operacyjne, pozwól narzędziu wygenerować kod, a następnie porównaj wynik z tym, co naprawdę by zawiodło. Różni się to, jak wywołujesz każde z narzędzi — a ta różnica ma znaczenie w pracy z Kubernetes, gdzie często iterujesz nad katalogiem manifestów.
Otwórz czat agenta (Cmd/Ctrl+I), wskaż katalog przez @k8s/ i pozwól agentowi pisać pliki bezpośrednio. Checkpointy Cursora pozwalają zaakceptować Deployment, a potem cofnąć samo HPA, jeśli wygenerował błędny cel metryki. Trzymaj otwarty @k8s/values.yaml, aby edycje inline pozostały ograniczone do jednego pliku.
Mocną stroną jest tu przegląd wizualny: Cursor pokazuje diff dla każdego manifestu, którego dotyka, więc wyłapiesz maxUnavailable: 0, które zablokowałoby Deployment z jedną repliką, zanim go zaaplikujesz.
Uruchom claude w katalogu głównym repozytorium, aby otworzyć REPL, a następnie wklej prompt. Ponieważ Claude Code czyta drzewo robocze, widzi Twój istniejący kustomization.yaml i nakładki oraz utrzymuje nowe manifesty spójne z nimi.
Do skryptowego, nieinteraktywnego generowania (np. regenerowania manifestów w CI) użyj trybu print: claude -p "Regenerate the staging HPA from k8s/base/hpa.yaml with maxReplicas 30" --output-format text. Nie traktuj samego claude "query" jak komendy wsadowej — otwiera ona interaktywny REPL zasilony tym promptem.
Codex obejmuje App, CLI, IDE i Cloud. W terminalu uruchom codex, aby wystartować TUI, albo przekaż prompt pozycyjnie: codex "Generate a zero-downtime Deployment for the orders service". Przy wieloplikowych refaktoryzacjach, które nie mają dotykać Twojego drzewa roboczego, uruchom go w git worktree, aby zmiany trafiły na izolowaną gałąź.
Ustaw bramkę zatwierdzeń jawnie przez --ask-for-approval on-request (wartości: untrusted, on-failure, on-request, never), a sandbox przez --sandbox workspace-write. Dla przebiegu bez nadzoru, który mimo to nie może dotknąć sieci, połącz --full-auto z --sandbox workspace-write. Codex Cloud może otworzyć powstałe zmiany manifestów jako PR na GitHubie do przeglądu.
Konfiguracja MCP jest identyczna we wszystkich trzech narzędziach — wszystkie mówią protokołem Model Context Protocol. Serwer MCP dla Kubernetes pozwala narzędziu odpytywać stan klastra na żywo (co faktycznie działa, ostatnie zdarzenia) zamiast zgadywać:
# Claude Codeclaude mcp add k8s -- npx -y kubernetes-mcp-server
# Cursor / Codex: add the same server to .mcp.json# (Cursor reads .cursor/mcp.json; Codex reads ~/.codex/config.toml or .mcp.json){ "mcpServers": { "k8s": { "command": "npx", "args": ["-y", "kubernetes-mcp-server"] } }}Z podłączonym klastrem „dlaczego pod orders się restartuje?” dostaje odpowiedź na podstawie prawdziwego wyniku kubectl get events zamiast ogólnej listy kontrolnej.
Krok 1 — Wygeneruj Deployment, który nie ściga się z własnymi sondami
Dział zatytułowany „Krok 1 — Wygeneruj Deployment, który nie ściga się z własnymi sondami”Najczęstszą wadą manifestów generowanych przez AI jest sonda readiness, która przechodzi, zanim aplikacja jest naprawdę gotowa. Zakoduj okno rozgrzewki w prompcie, a wygenerowany startupProbe poprawnie zablokuje pozostałe sondy.
Oto kształt, jakiego powinieneś się spodziewać — zwróć uwagę, jak startupProbe chroni powolny rozruch, a maxUnavailable: 0 gwarantuje brak spadków pojemności podczas rolloutu:
apiVersion: apps/v1kind: Deploymentmetadata: name: orders-api namespace: production labels: app: orders-api tier: apispec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 selector: matchLabels: app: orders-api template: metadata: labels: app: orders-api annotations: prometheus.io/scrape: "true" prometheus.io/port: "9090" prometheus.io/path: "/metrics" spec: securityContext: runAsNonRoot: true runAsUser: 1000 fsGroup: 1000 seccompProfile: type: RuntimeDefault affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchLabels: app: orders-api topologyKey: kubernetes.io/hostname containers: - name: orders-api image: myregistry/orders-api:v1.4.0 ports: - name: http containerPort: 8080 - name: metrics containerPort: 9090 resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" startupProbe: # gates liveness/readiness until the pool warms httpGet: path: /health/startup port: http periodSeconds: 2 failureThreshold: 30 # up to 60s before the app is declared ready readinessProbe: httpGet: path: /health/ready port: http periodSeconds: 5 failureThreshold: 3 livenessProbe: httpGet: path: /health/live port: http periodSeconds: 10 failureThreshold: 3 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: ["ALL"]Co sprawdzić przed zaaplikowaniem: jeśli AI wypisze initialDelaySeconds na sondzie readiness zamiast startupProbe, odrzuć to — initialDelaySeconds to sztywne zgadywanie, podczas gdy startupProbe dostosowuje się do powolnego rozruchu i powstrzymuje liveness przed zabiciem poda w trakcie rozgrzewki.
Krok 2 — Postaw otaczającą sieć
Dział zatytułowany „Krok 2 — Postaw otaczającą sieć”Sam Deployment jest nieosiągalny. Wygeneruj Service, Ingress i domyślnie odrzucającą (default-deny) NetworkPolicy razem, aby workload był jednocześnie udostępniony i zablokowany. Poniższe manifesty mają kształt produkcyjny — TLS przez cert-manager, ograniczanie rate na Ingressie i egress ograniczony do Postgresa, Redisa i DNS:
apiVersion: v1kind: Servicemetadata: name: orders-api namespace: productionspec: type: ClusterIP ports: - name: http port: 80 targetPort: http selector: app: orders-api---# ingress.yamlapiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: orders-api namespace: production annotations: nginx.ingress.kubernetes.io/rate-limit: "100" nginx.ingress.kubernetes.io/force-ssl-redirect: "true" cert-manager.io/cluster-issuer: "letsencrypt-prod"spec: ingressClassName: nginx tls: - hosts: [api.example.com] secretName: api-tls-secret rules: - host: api.example.com http: paths: - path: / pathType: Prefix backend: service: name: orders-api port: number: 80---# networkpolicy.yaml — default-deny ingress, scoped egressapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: orders-api namespace: productionspec: podSelector: matchLabels: app: orders-api policyTypes: [Ingress, Egress] ingress: - from: - podSelector: matchLabels: app: nginx-ingress ports: - protocol: TCP port: 8080 egress: - to: - namespaceSelector: matchLabels: name: production ports: - protocol: TCP port: 5432 # PostgreSQL - protocol: TCP port: 6379 # Redis - to: # DNS must be allowed explicitly - namespaceSelector: {} podSelector: matchLabels: k8s-app: kube-dns ports: - protocol: UDP port: 53Krok 3 — Sparametryzuj do wykresu Helm
Dział zatytułowany „Krok 3 — Sparametryzuj do wykresu Helm”Gdy te same manifesty muszą trafić na dev, staging i prod, poproś narzędzie o wyodrębnienie ich do wykresu z walidacją wartości. Ważna korekta w 2026: nie pozwól, by AI domyślnie sięgnęło po zależności wykresów Bitnami. Od 28 sierpnia 2025 publiczny katalog Bitnami został okrojony do podzbioru społecznościowego dostępnego tylko jako „latest”, a starsze tagi obrazów przeniesiono do bitnamilegacy, więc piny wykresów postgresql/redis, których narzędzia AI nauczyły się z danych treningowych, prowadzą teraz do usuniętych lub zepsutych obrazów. Zamiast tego przypnij utrzymywane alternatywy.
Chart.yaml i helper walidacji powinny wrócić wyglądając tak — zwróć uwagę, że repozytoria zależności to CloudNativePG i wykresy prometheus-community, oba aktywnie utrzymywane:
apiVersion: v2name: orders-apidescription: Helm chart for the orders-api servicetype: applicationversion: 1.0.0appVersion: "1.4.0"dependencies: - name: cloudnative-pg version: "0.x.x" # CloudNativePG operator — maintained repository: https://cloudnative-pg.github.io/charts condition: postgresql.enabled - name: kube-prometheus-stack version: "70.x.x" repository: https://prometheus-community.github.io/helm-charts condition: monitoring.enabled# templates/_helpers.tpl (validation excerpt){{- define "orders-api.validateValues" -}}{{- if not .Values.image.repository }} {{- fail "image.repository is required" }}{{- end }}{{- if and .Values.ingress.enabled (not .Values.ingress.hosts) }} {{- fail "ingress.hosts must be set when ingress is enabled" }}{{- end }}{{- end }}Krok 4 — Podłącz GitOps, Istio i autoskalowanie
Dział zatytułowany „Krok 4 — Podłącz GitOps, Istio i autoskalowanie”Dla ciągłego dostarczania wygeneruj Application ArgoCD, który wskazuje na nakładkę wykresu i sam się naprawia. Dla kształtowania ruchu wygeneruj konfigurację Istio — i upewnij się, że apiVersion to networking.istio.io/v1 (awansowane do stabilnego w Istio 1.22 i rekomendowane dla nowych konfiguracji), a nie starsze v1beta1, które narzędzia AI nadal domyślnie wypisują.
apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata: name: orders-api-production namespace: argocd finalizers: [resources-finalizer.argocd.argoproj.io]spec: project: default source: repoURL: https://github.com/myorg/k8s-configs targetRevision: main path: applications/orders-api/overlays/production destination: server: https://kubernetes.default.svc namespace: production syncPolicy: automated: prune: true selfHeal: true syncOptions: - CreateNamespace=true---# virtualservice.yaml — note apiVersion v1 (Istio 1.22+), not v1beta1apiVersion: networking.istio.io/v1kind: VirtualServicemetadata: name: orders-api namespace: productionspec: hosts: [orders-api] http: - route: # 90/10 canary split - destination: host: orders-api subset: v1 weight: 90 - destination: host: orders-api subset: v2 weight: 10 retries: attempts: 3 perTryTimeout: 10s retryOn: 5xx timeout: 30sDla autoskalowania wygeneruj HPA oraz — co kluczowe — PodDisruptionBudget. PDB ma ostrą krawędź, którą narzędzia AI mylą: możesz ustawić minAvailable ALBO maxUnavailable, nigdy oba. Manifest z obydwoma jest odrzucany przez kubectl apply. Wybierz jedno (maxUnavailable jest zwykle lepsze, bo śledzi liczbę replik):
apiVersion: autoscaling/v2kind: HorizontalPodAutoscalermetadata: name: orders-api namespace: productionspec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: orders-api minReplicas: 3 maxReplicas: 20 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 behavior: scaleDown: stabilizationWindowSeconds: 300 # avoid flapping---# pdb.yaml — exactly one of minAvailable / maxUnavailableapiVersion: policy/v1kind: PodDisruptionBudgetmetadata: name: orders-api namespace: productionspec: maxUnavailable: 1 selector: matchLabels: app: orders-apiKrok 5 — CI/CD z aktualnymi wersjami akcji
Dział zatytułowany „Krok 5 — CI/CD z aktualnymi wersjami akcji”Wygeneruj workflow GitHub Actions do budowania, pushowania i podbijania tagu obrazu. Narzędzia AI mają tendencję do wypisywania przestarzałych majorów akcji ze swoich danych treningowych — przypnij aktualne (actions/checkout@v6, docker/build-push-action@v7, peter-evans/create-pull-request@v8):
# .github/workflows/deploy.yml (excerpt)jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: docker/setup-buildx-action@v4 - uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository }} - uses: docker/build-push-action@v7 with: context: . push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max bump-prod: needs: build if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: repository: myorg/k8s-configs token: ${{ secrets.CONFIG_REPO_TOKEN }} - run: kustomize edit set image orders-api=ghcr.io/${{ github.repository }}:${{ github.sha }} working-directory: applications/orders-api/overlays/production - uses: peter-evans/create-pull-request@v8 with: token: ${{ secrets.CONFIG_REPO_TOKEN }} commit-message: "Deploy ${{ github.sha }} to production" title: "Deploy ${{ github.sha }} to production" branch: deploy-${{ github.sha }}Prompty do skopiowania
Dział zatytułowany „Prompty do skopiowania”Gdy to się zepsuje
Dział zatytułowany „Gdy to się zepsuje”-
CrashLoopBackOff tuż po rollout. Zwykle sonda liveness zabija poda, zanim ten skończy się uruchamiać. Sprawdź
kubectl logs <pod> --previouspod kątem ostatnich słów przed zabiciem. Naprawa: przenieś zabezpieczenie rozruchu dostartupProbe(zobacz Krok 1), aby liveness ruszało dopiero, gdy aplikacja zgłosi gotowość. -
OOMKilled pod obciążeniem.
kubectl describe podpokazujeLast State: Terminated, Reason: OOMKilled. Limit pamięci jest zbyt ciasny dla szczytowego zużycia. Pamięć jest nieściśliwa — jądro zabija kontener, zamiast go dławić. Naprawa: podnieś limit pamięci powyżej zaobserwowanego P99 (użyj promptu „zaciśnij limity zasobów” z prawdziwymi danymikubectl top), a nie na podstawie zgadywania. -
Drenowanie węzła wisi w nieskończoność.
kubectl drainzacina się, bo PodDisruptionBudget nie pozwala na kolejną eksmisję. Częsta przyczyna:minAvailablerówne liczbie replik (np.minAvailable: 3przy 3 replikach nie zostawia żadnego budżetu zakłóceń) albo manifest nielegalnie ustawił jednocześnieminAvailableimaxUnavailable. Naprawa: ustaw dokładnie jedno pole i zostaw realny zapas (maxUnavailable: 1). -
ImagePullBackOff.
kubectl describe podpokazuje błąd pobierania. Albo tag nie istnieje (podbicie w CI wypchnęło inny SHA niż ten, do którego odwołuje się manifest), albo rejestr potrzebujeimagePullSecrets, albo — coraz częściej w 2026 — wykres wskazuje na usunięty obraz Bitnami. Naprawa: potwierdź tag przezcrane ls/ UI rejestru, dołącz pull secret i zastąp zależności Bitnami utrzymywanymi wykresami. -
ArgoCD zacięte na
OutOfSynclubProgressingw nieskończoność. Często pole, które klaster mutuje (jakreplicas, zarządzane przez HPA), walczy z Gitem. Dodaj wpisignoreDifferencesdla/spec/replicas, aby ArgoCD przestało próbować cofać HPA. Jeśli toProgressing, leżący u podstaw Deployment nigdy nie staje się Ready — wróć do trybu awarii 1 lub 2.