Przejdź do głównej zawartości

Wzorce orkiestracji Kubernetes dla agentów AI to prompty i bramki, dzięki którym Claude Code, Codex lub Cursor piszą Deploymenty i charty Helm z realnymi ograniczeniami operacyjnymi: czas sond, zapas pamięci, budżety zakłóceń i ruch DNS. Manifest przechodzący kubectl apply wciąż może wywołać awarię, więc każda zmiana przechodzi przed merge’em walidację schematu, dry-run po stronie serwera i diff.

Twój rollout właśnie położył API. Sonda readiness zrobiła się zielona, zanim pula połączeń do Postgresa się rozgrzała, więc ruch trafił na pody zwracające 500, a manifest napisał agent, który nie wiedział, że pula potrzebuje ośmiu sekund. Ta strona jest dla developera lub tech leada, który odpowiada za taki manifest i chce, żeby następny udowodnił skrypt, a nie człowiek czytający 400 linii YAML-a.

  • Blok reguł klastra do CLAUDE.md, AGENTS.md albo reguły Cursora, żeby agent startował od twoich ograniczeń, a nie od swoich domyślnych ustawień.
  • scripts/verify-k8s.sh — bramkę, która renderuje chart i sprawdza schemat, niezmienniki, admission po stronie serwera oraz diff względem klastra.
  • Prompty do skopiowania: Deployment bezpieczny dla sond, chart Helm bez zależności od Bitnami, diagnoza CrashLoopBackOff i strojenie limitów zasobów na podstawie realnego zużycia.
  • Konfigurację serwera MCP dla Kubernetes w trybie tylko do odczytu dla wszystkich trzech narzędzi, żeby diagnoza opierała się na stanie żywego klastra.
  • Sposoby wyjścia z sześciu typowych awarii, które powodują manifesty pisane przez agenta.

Agent wypełnia każdą lukę w prompcie domyślną wartością z danych treningowych: initialDelaySeconds zamiast startupProbe, tag obrazu latest, NetworkPolicy typu default-deny bez reguły dla DNS. Zapisz reguły raz, w pliku, który każde narzędzie czyta w każdej sesji: CLAUDE.md dla Claude Code, AGENTS.md dla Codeksa i reguła w .cursor/rules/ dla Cursora.

## Kubernetes rules (k8s/ and charts/)
- Every container: requests and limits for CPU and memory; memory limit >= 1.25x observed P99.
- Slow-starting apps get a startupProbe; never gate readiness with initialDelaySeconds.
- Probes use named ports. Liveness never checks downstream dependencies.
- Pods run as non-root, readOnlyRootFilesystem: true, drop ALL capabilities, seccomp RuntimeDefault.
- Images are pinned by version tag or digest. Never `latest`.
- A PodDisruptionBudget sets exactly one of minAvailable / maxUnavailable.
- A default-deny NetworkPolicy always allows egress to kube-dns on UDP and TCP 53.
- No Bitnami dependencies (charts.bitnami.com or oci://registry-1.docker.io/bitnamicharts). Postgres comes from the CloudNativePG operator.
- If an HPA manages a Deployment, do not set spec.replicas in the Deployment.
- Done means: scripts/verify-k8s.sh exits 0. Paste its summary into the PR.

To właśnie ostatnia reguła zmienia code review. Zamienia „wygląda OK” w kod wyjścia, który agent musi uzyskać, zanim odda pracę.

Czytanie wygenerowanego YAML-a linia po linii się nie skaluje i przepuszcza awarie, które mają znaczenie, bo brakująca reguła DNS po prostu nie istnieje na ekranie. Zamiast tego wyrenderuj to, co faktycznie trafi do klastra, i pozwól sprawdzić to czterem warstwom. Uruchamiaj skrypt w terminalu i w CI.

#!/usr/bin/env bash
# scripts/verify-k8s.sh (terminal or CI; needs helm, kubeconform, kubectl)
set -euo pipefail
CHART=charts/orders-api
VALUES=$CHART/values-production.yaml
OUT=.rendered; rm -rf "$OUT"; mkdir -p "$OUT"
# 1. Render exactly what will be applied
helm lint "$CHART" -f "$VALUES"
helm template orders-api "$CHART" -n production -f "$VALUES" > "$OUT/all.yaml"
# 2. Schema: built-in kinds plus CRDs (such as the CNPG Cluster)
kubeconform -strict -summary \
-schema-location default \
-schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' \
"$OUT/all.yaml"
# 3. Invariants a schema cannot express
fail() { echo "FAIL: $1"; exit 1; }
grep -Eq 'image: .*:latest' "$OUT/all.yaml" && fail "image pinned to latest"
# Assumes no registry port: registry.internal:5000/orders-api (no tag) passes.
grep -Eq '^\s*image: "?[^:@"]+"?\s*$' "$OUT/all.yaml" && fail "image with no tag or digest"
# orders-api warms a Postgres pool, so its Deployment must have a startupProbe.
# Drop this check for charts whose apps start fast.
if grep -q 'kind: Deployment' "$OUT/all.yaml"; then
grep -q 'startupProbe:' "$OUT/all.yaml" || fail "Deployment without startupProbe"
fi
if grep -q 'kind: NetworkPolicy' "$OUT/all.yaml"; then
grep -q 'k8s-app: kube-dns' "$OUT/all.yaml" || fail "NetworkPolicy without DNS egress"
fi
grep -Eq 'charts\.bitnami\.com|bitnamicharts' "$CHART/Chart.yaml" && fail "Bitnami dependency"
grep -Eq 'image: .*bitnami' "$OUT/all.yaml" && fail "Bitnami image"
# 4. Admission and drift against a real API server (staging or kind)
kubectl apply --dry-run=server -f "$OUT/all.yaml"
set +e; kubectl diff -f "$OUT/all.yaml" > "$OUT/cluster.diff"; rc=$?; set -e
[ "$rc" -le 1 ] || fail "kubectl diff errored (rc=$rc)"
echo "OK: $(grep -c '^kind:' "$OUT/all.yaml") objects; diff in $OUT/cluster.diff"

Każda warstwa łapie inną klasę błędów:

WarstwaŁapieNie łapie
helm lint i helm templateBłędy szablonów, własne kontrole fail chartuNiczego, co jest poprawne, ale złe
kubeconform -strictZły apiVersion, nieznane pola lub pola z literówkamiZachowania: czasu sond, DNS
Linie grep z niezmiennikamiReguły zespołu, np. przypięty tag obrazu i obecność ruchu do DNSReguł, których nie zapisałeś
kubectl apply --dry-run=serverAdmission: Pod Security Standards, webhooki polityk, walidację PDB, brakujące CRDZachowania w czasie działania
kubectl diffTo, co faktycznie zmieni się w klastrzeNiczego; ten wynik czyta człowiek

kubectl diff zwraca 1, gdy są różnice, a więcej niż 1 przy błędzie, dlatego skrypt toleruje dokładnie 1. cluster.diff to jedyny artefakt, który czyta człowiek: jest krótki i pokazuje zmianę stanu klastra, a nie szablon, który ją wygenerował. Dołącz go do PR-a razem z podsumowaniem skryptu, tak jak opisuje pakiet dowodów. Właściciel serwisu zatwierdza diff, a nie YAML.

Po merge’u bramka przenosi się na rollout. kubectl rollout status deployment/orders-api -n production --timeout=5m zatrzymuje pipeline, jeśli nowe pody nigdy nie osiągną stanu Ready. Analizę canary i automatyczny rollback opisuje strona o progressive delivery.

Poniższe prompty działają we wszystkich trzech narzędziach. Różni się sposób uruchomienia ich na katalogu manifestów i to, jak trzymasz agenta z dala od klastra, gdy pisze.

Uruchom claude w katalogu głównym repozytorium i wklej prompt. Claude Code czyta CLAUDE.md oraz istniejący kustomization.yaml albo chart, więc nowe obiekty dostają te same etykiety i nakładki (overlays).

Do audytu tylko do odczytu w CI albo w skrypcie użyj trybu print z uprawnieniami planu — agent czyta pliki, ale ich nie edytuje:

Okno terminala
claude -p --permission-mode plan --output-format json \
"Audit charts/orders-api against the Kubernetes rules in CLAUDE.md. List violations by severity with file and line."

Sama forma claude "query" otwiera sesję interaktywną z tym promptem. To nie jest polecenie wsadowe. Od v2.1.283 (oba kanały wydań, stan na 2026-10-12) sesje interaktywne startują w trybie auto, w którym klasyfikator zatwierdza większość akcji. Żeby agent nie zmienił działającego klastra, dodaj reguły deny do .claude/settings.json, np. "deny": ["Bash(kubectl apply *)", "Bash(kubectl delete *)"], i uruchamiaj sesję z kontekstem kubeconfig powiązanym z rolą RBAC tylko do odczytu. Reguła prefiksowa łapie tylko zwykłą pisownię polecenia, więc realną ochroną jest rola RBAC.

Jak wygenerować Deployment, który nie ściga się z własnymi sondami

Dział zatytułowany „Jak wygenerować Deployment, który nie ściga się z własnymi sondami”

Częsty błąd w Deploymencie napisanym przez agenta to sonda readiness, która przechodzi, zanim aplikacja jest gotowa. Wpisz czas rozgrzewania do promptu, a startupProbe poprawnie wstrzyma pozostałe sondy.

W wyniku szukaj bloku sond:

# k8s/base/deployment.yaml (container excerpt)
startupProbe: # gates liveness and readiness while the pool warms
httpGet: { path: /health/startup, port: http }
periodSeconds: 2
failureThreshold: 30 # up to 60 s
readinessProbe:
httpGet: { path: /health/ready, port: http }
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet: { path: /health/live, port: http }
periodSeconds: 10
failureThreshold: 3

Jeśli agent zamiast tego zwróci initialDelaySeconds w sondzie readiness, odrzuć to. initialDelaySeconds to stałe zgadywanie — za krótkie na zimnym węźle albo stracony czas na rozgrzanym. startupProbe czeka na prawdziwy sygnał i nie pozwala sondzie liveness zabić poda w trakcie rozgrzewania.

Poproś o Service, Ingress i NetworkPolicy w jednym prompcie, żeby workload był jednocześnie wystawiony i ograniczony. Reguła, którą agenci często pomijają, to DNS:

# k8s/base/networkpolicy.yaml (egress excerpt)
egress:
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- { protocol: UDP, port: 53 }
- { protocol: TCP, port: 53 }

Bez niej pody startują, nie rozwiązują żadnej nazwy hosta i logują błędy no such host, które wyglądają jak błąd aplikacji. Linia z niezmiennikiem w verify-k8s.sh zamiast tego przerywa build. Sprawdź etykietę k8s-app: kube-dns w swoim klastrze poleceniem kubectl get pods -n kube-system --show-labels, bo niektóre dystrybucje etykietują CoreDNS inaczej.

Gdy te same manifesty trafiają na dev, staging i produkcję, każ agentowi wyodrębnić chart z walidacją wartości. Najpierw popraw jedną domyślną wartość z danych treningowych: agenci wciąż sięgają po zależności Bitnami. Od 28 sierpnia 2025 r. Bitnami przeniosło wersjonowane obrazy kontenerów z docker.io/bitnami do nieutrzymywanego repozytorium bitnamilegacy, a spakowane charty przestały dostawać aktualizacje (ogłoszenie Bitnami). Pin skopiowany ze starego przykładu może wskazywać obraz, którego już nie ma.

Dla Postgresa zainstaluj operator CloudNativePG raz na klaster, a chart aplikacji niech renderuje tylko zasób Cluster. Nie dołączaj operatora jako subchartu aplikacji, bo wtedy jego CRD i klastrowy RBAC dzielą cykl życia z wydaniem aplikacji.

Okno terminala
# Platform team, once per cluster (terminal)
helm repo add cnpg https://cloudnative-pg.github.io/charts
helm upgrade --install cnpg --namespace cnpg-system --create-namespace cnpg/cloudnative-pg

Helper walidacyjny sprawia, że zły plik wartości kończy się błędem już na helm template, czyli na pierwszej warstwie bramki:

# charts/orders-api/templates/_helpers.tpl (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.enabled is true" }}
{{- end }}
{{- end }}

Jak bezpiecznie skalować i drenować węzły z HPA i PDB

Dział zatytułowany „Jak bezpiecznie skalować i drenować węzły z HPA i PDB”

Generuj HorizontalPodAutoscaler i PodDisruptionBudget razem. PDB ma jedną pułapkę: przyjmuje minAvailable albo maxUnavailable, nigdy oba, a serwer API odrzuca PDB, który ustawia oba pola. maxUnavailable: 1 jest zwykle lepszym wyborem przy co najmniej dwóch replikach, bo działa dalej, gdy HPA zmienia liczbę replik. Przy jednej replice każdy PDB albo blokuje drenowanie, albo dopuszcza przestój.

# pdb.yaml — exactly one of minAvailable / maxUnavailable
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: { name: orders-api, namespace: production }
spec:
maxUnavailable: 1
selector:
matchLabels: { app: orders-api }

W GitOps HPA i Argo CD walczą o spec.replicas, dopóki nie każesz Argo CD tego pola ignorować. Dodaj do Application:

spec:
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers: [/spec/replicas]
syncPolicy:
syncOptions: [RespectIgnoreDifferences=true]

Samo ignoreDifferences tylko ukrywa różnicę: jeśli spec.replicas nadal jest w Gicie, każda synchronizacja (i każdy self-heal) zapisuje je z powrotem ponad HPA. Dlatego usuń spec.replicas z Deploymentu w Gicie, jak mówi plik z regułami, albo zostaw pokazaną wyżej opcję RespectIgnoreDifferences=true, żeby synchronizacja nie ruszała liczby replik.

Podział ruchu i wdrożenia canary, wraz z analizą decydującą o promocji, opisuje strona o progressive delivery. Gdy generujesz routing Istio, sprawdź, które wersje API obsługuje twój mesh (kubectl api-resources --api-group=networking.istio.io), zanim zaakceptujesz apiVersion wybrany przez agenta.

Jak zdiagnozować padający pod na podstawie stanu klastra

Dział zatytułowany „Jak zdiagnozować padający pod na podstawie stanu klastra”

Bez dostępu do klastra agent odpowiada na „dlaczego ten pod się restartuje?” ogólną checklistą. Ze społecznościowym serwerem MCP dla Kubernetes (npm kubernetes-mcp-server, wersja 0.0.67 na 2026-09-26) sam listuje pody, czyta logi i pobiera zdarzenia. Ustawienia tego serwera leżą w pliku TOML; CLI zachowało tylko --config, --config-dir i --version. Uruchom go w trybie tylko do odczytu:

~/.config/k8s-mcp.toml
read_only = true
toolsets = ["core", "config", "helm"]

Wskaż serwerowi także kontekst kubeconfig powiązany z rolą RBAC tylko do odczytu. read_only = true ogranicza narzędzia serwera; RBAC ogranicza, co da się zrobić wyciekłym poświadczeniem. W toolsets zostaw tylko to, czego potrzebuje diagnoza, bo definicja każdego włączonego narzędzia zajmuje kontekst w każdej sesji. claude mcp add domyślnie zapisuje w zakresie local (Claude Code 2.1.283); dodaj --scope project, żeby zapisać .mcp.json, który commitujesz, a cały zespół dostaje ten sam serwer.

Okno terminala
claude mcp add kubernetes -- npx -y kubernetes-mcp-server@0.0.67 --config "$HOME/.config/k8s-mcp.toml"

Agent nie ma dostępu do klastra? Uruchom sam kubectl describe pod POD -n production i kubectl logs POD -n production --previous, a oba wyniki wklej nad tym samym promptem. Więcej serwerów infrastrukturalnych oraz to, kiedy skill pasuje lepiej niż serwer, znajdziesz w serwerach MCP dla infrastruktury.

Jak dostroić limity zasobów na podstawie realnego zużycia

Dział zatytułowany „Jak dostroić limity zasobów na podstawie realnego zużycia”

Limity wybrane przez agenta to zgadywanie. Zastąp je liczbami z własnych metryk.

Skąd brać te metryki i jakie alerty mówią, że limit jest za ciasny, opisują wzorce monitoringu i logowania.

Co się psuje, gdy agent pisze konfigurację Kubernetes?

Dział zatytułowany „Co się psuje, gdy agent pisze konfigurację Kubernetes?”
  1. CrashLoopBackOff tuż po rolloucie. Sonda liveness zabija poda, zanim ten skończy start. Potwierdź przez kubectl describe pod POD -n production: zdarzenia pokazują Liveness probe failed, a po nim restart. kubectl logs POD -n production --previous pokazuje przerwaną sekwencję startu. Wyjście: przenieś ochronę startu do startupProbe i dodaj kontrolę startupProbe do verify-k8s.sh, jeśli jej jeszcze nie ma.

  2. OOMKilled pod obciążeniem. kubectl describe pod pokazuje Last State: Terminated, Reason: OOMKilled. Wyjście: podnieś limit pamięci powyżej zaobserwowanego P99 promptem do zasobów powyżej i na realnych danych. Nie podwajaj liczby tylko po to, żeby alert ucichł.

  3. Drenowanie węzła wisi. PDB nie zostawia budżetu zakłóceń, np. minAvailable: 3 przy trzech replikach. Wyjście: przejdź na maxUnavailable: 1. PDB z oboma polami nigdy nie trafi do klastra, bo odrzuca go dry-run po stronie serwera w bramce.

  4. ImagePullBackOff. Tag nie istnieje, rejestr wymaga imagePullSecrets albo chart nadal wskazuje usunięty obraz Bitnami. Wyjście: potwierdź tag w rejestrze, dołącz sekret do pobierania obrazów i zastąp zależność Bitnami tak, jak pokazano wyżej.

  5. Argo CD utknęło w OutOfSync lub Progressing. OutOfSync, które wraca po każdej synchronizacji, to zwykle HPA i Git walczące o spec.replicas: dodaj wpis ignoreDifferences i albo usuń spec.replicas z Deploymentu w Gicie, albo ustaw RespectIgnoreDifferences=true, żeby synchronizacja nie resetowała liczby replik. Progressing bez końca oznacza, że Deployment nigdy nie osiąga stanu Ready: wróć do punktów 1 i 2.

  6. Awaryjne kubectl rollout undo samo się cofa. Przy selfHeal: true Argo CD w ciągu kilku minut przywraca to, co jest w Gicie. Wyjście: zrób revert commita w repozytorium konfiguracji. To jest właściwy rollback — nie kubectl. Przećwicz tę ścieżkę według strony o przećwiczonym rollbacku z decyzją człowieka.

Zielona bramka przy zepsutej produkcji to jedyna cicha awaria w tym procesie. Zdarza się, gdy klaster stagingowy używany do dry-runu po stronie serwera nie ma CRD, polityki albo etykiety Pod Security, które ma produkcja. Utrzymuj oba w zgodzie, a każde nowe odrzucenie przez admission na produkcji traktuj jako lukę w stagingu.

Czy ten artykuł Ci pomógł?

Jedno kliknięcie, bez logowania.

wcale bardzo