Docker i Kubernetes z Claude Code
ENClaude Code generuje Dockerfile, stacki Docker Compose, manifesty Kubernetes i charty Helm na podstawie repozytorium, w którym pracuje: lockfile’a, skryptów budowania, endpointu health i portów otwieranych przez kod. Wynik zasługuje na zaufanie dopiero po przejściu mechanicznych bramek: hadolinta, skanu Trivy, walidacji schematu kubeconform i próbnego wdrożenia na jednorazowy klaster kind.
Twój serwis działa na laptopie, a teraz ma działać w klastrze. Dockerfile z tutoriala często buduje obraz ważący ponad gigabajt, który działa jako root, a pierwszy manifest często nie ma limitów zasobów ani readiness probe, za to ma hasło do bazy w zmiennej środowiskowej. Chcesz, żeby Claude Code napisał to wszystko, ale nie przeczytasz 600 linii YAML-a przy każdej zmianie.
Ta strona jest dla developera, który konteneryzuje serwis, i dla tech leada, który odpowiada za standardy kontenerów w zespole. Opisuje pisanie i udowadnianie artefaktów kontenerowych z Claude Code. Ich wypuszczanie opisuje automatyzacja wdrożeń z Claude Code, pipeline, który je buduje, to konfiguracja pipeline’ów z Claude Code, a wersja tego workflow dla wszystkich narzędzi (także Cursora i Codexa) to konteneryzacja z Dockerem i Kubernetesem. Jeśli chcesz uruchomić samego Claude Code w kontenerze, zobacz konfigurację dev containerów.
Co zyskasz, konteneryzując z Claude Code
Dział zatytułowany „Co zyskasz, konteneryzując z Claude Code”- Sekcję
CLAUDE.md, która raz zapisuje twoje standardy kontenerów, więc każdy Dockerfile i manifest od Claude’a się do nich stosuje. - Reguły uprawnień i hook
PostToolUse, które lintują każdy edytowany plik kontenerowy i trzymają Claude’a z dala od współdzielonych klastrów i rejestrów. - Prompty do skopiowania: wieloetapowy Dockerfile, stack Compose do developmentu, utwardzone manifesty Kubernetes i chart Helm.
- Sekwencję pięciu bramek, która dowodzi, że artefakty działają, bez czytania linia po linii, i wskazuje, kto zatwierdza zmianę.
- Typowe awarie konfiguracji kontenerów pisanej przez agenta i sposób wyjścia z każdej.
Ustal zasady dla kontenerów przed pierwszym promptem
Dział zatytułowany „Ustal zasady dla kontenerów przed pierwszym promptem”Domyślne wybory Claude’a wyglądają sensownie, ale nie są twoje: pływający tag node:latest, użytkownik root i probe’y na ścieżkach, których serwis nie obsługuje. Zapisz zasady raz w projektowym CLAUDE.md. Każdy kolejny prompt je dziedziczy, a ostatnia linia zamienia „Claude napisał Dockerfile” w „Claude napisał Dockerfile i uruchomił na nim lintery i skanery”.
## Containers- Base images: pin by tag AND digest (`node:22-bookworm-slim@sha256:...`). Resolve digests with `docker buildx imagetools inspect IMAGE:TAG`; never write a digest from memory.- Every Dockerfile is multi-stage. The runtime stage runs as UID 10001, has no build tools, and declares a HEALTHCHECK that calls GET /healthz.- The service reads config from env vars only. Secrets come from Kubernetes Secrets (created by External Secrets), never from values.yaml, ConfigMaps or ENV lines.- Manifests live in deploy/k8s/, the Helm chart in deploy/chart/.- Every container sets resources.requests and limits, runAsNonRoot, readOnlyRootFilesystem, allowPrivilegeEscalation: false, and drops ALL capabilities.- Probes: /healthz = process alive (liveness), /readyz = dependencies reachable (readiness).- You may use the kube context `kind-dev` (kind cluster `dev`) only. Never push images or touch any other context.- After editing container files, run: hadolint, trivy config, kubeconform (see scripts/check-containers.sh).Trzymaj Claude’a z dala od współdzielonych klastrów i rejestrów
Dział zatytułowany „Trzymaj Claude’a z dala od współdzielonych klastrów i rejestrów”Praca z kontenerami jest o jedno polecenie od produkcji: docker push, helm upgrade i kubectl delete działają z tego samego terminala. Sesję ograniczają dwie warstwy.
Granicą jest poświadczenie. Uruchom Claude Code z kubeconfigiem, który zawiera wyłącznie lokalny klaster, więc nie ma żadnego produkcyjnego kontekstu do osiągnięcia:
# Terminal: utwórz lokalny klaster i kubeconfig, który nie zawiera niczego innegokind create cluster --name dev --kubeconfig "$HOME/.kube/kind-dev.yaml"KUBECONFIG="$HOME/.kube/kind-dev.yaml" claudeLista deny łapie typowe zapisy poleceń. Dodaj to do .claude/settings.json i zacommituj:
{ "permissions": { "deny": [ "Bash(docker push *)", "Bash(helm upgrade *)", "Bash(helm uninstall *)", "Bash(kubectl delete *)" ], "ask": [ "Bash(kubectl apply *)", "Bash(helm install *)" ] }}Lintuj każdy plik kontenerowy, który edytuje Claude
Dział zatytułowany „Lintuj każdy plik kontenerowy, który edytuje Claude”Hook PostToolUse uruchamia się po każdej edycji; przy kodzie wyjścia 2 Claude dostaje wynik lintera i poprawia błędy w następnym kroku. Zapisz skrypt jako .claude/hooks/lint-containers.sh, nadaj mu chmod +x i zarejestruj go w tym samym .claude/settings.json. Pełny opis zdarzeń i handlerów znajdziesz na stronie hooki Claude Code.
#!/usr/bin/env bash# .claude/hooks/lint-containers.sh (PostToolUse, matcher "Edit|Write"); needs jq, hadolint, kubeconformset -uo pipefailfile=$(jq -r '.tool_input.file_path // empty')[[ -f $file ]] || exit 0
case "$file" in *Dockerfile*) out=$(hadolint "$file" 2>&1) || { echo "hadolint failed on $file:" >&2; head -n 30 <<<"$out" >&2; exit 2; } ;; */deploy/k8s/*.yaml|*/deploy/k8s/*.yml) out=$(kubeconform -strict -summary -ignore-missing-schemas "$file" 2>&1) || { echo "kubeconform failed on $file:" >&2; head -n 30 <<<"$out" >&2; exit 2; } ;;esacexit 0{ "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/lint-containers.sh" } ] } ] }}Gdy hook PostToolUse się uruchamia, edycja już się odbyła, więc hook jej nie zablokuje; kod wyjścia 2 oddaje Claude’owi wynik lintera jako informację zwrotną. Przetestuj hook raz: poproś Claude’a o dopisanie FROM node:latest do testowego Dockerfile’a. W kolejnej odpowiedzi powinien przytoczyć ostrzeżenie DL3007 z hadolinta i przypiąć tag.
Wygeneruj produkcyjny Dockerfile na podstawie repozytorium
Dział zatytułowany „Wygeneruj produkcyjny Dockerfile na podstawie repozytorium”Formułuj prośbę o Dockerfile, opierając się na faktach z repozytorium. Claude czyta lockfile, żeby wybrać menedżer pakietów, skrypt budowania, żeby znaleźć katalog wyjściowy, i kod serwera, żeby znaleźć port i ścieżkę health. Poproś, żeby skończył tabelą: to ją recenzujesz, a nie Dockerfile.
Dla serwisu w Go spodziewaj się statycznej binarki skopiowanej do etapu scratch albo distroless. Dla Pythona: środowiska wirtualnego zbudowanego w jednym etapie i skopiowanego do etapu runtime. Jeśli tabela pokazuje duży obraz, w kolejnej iteracji podaj konkretną liczbę zamiast ogólnej prośby:
The runtime image is 480 MB. List the five largest layers with `docker history myapp:test`,explain what each contains, and remove everything the runtime stage does not execute.Zbuduj stack Compose do lokalnego developmentu
Dział zatytułowany „Zbuduj stack Compose do lokalnego developmentu”W Compose developerzy uruchamiają serwis razem z zależnościami. Najważniejsza linia to depends_on z condition: service_healthy. Bez niej aplikacja startuje, zanim PostgreSQL przyjmuje połączenia, i pada przy starcie.
docker compose up --wait czeka, aż każdy serwis z health checkiem zgłosi stan healthy, więc prompt kończy się dowodem, a nie deklaracją.
Wygeneruj utwardzone manifesty Kubernetes
Dział zatytułowany „Wygeneruj utwardzone manifesty Kubernetes”W YAML-u Kubernetesa chowają się braki: manifest bez readinessProbe aplikuje się bez błędu, a potem kieruje ruch do aplikacji, zanim jest gotowa. Wpisz każde wymaganie do promptu i poproś o tabelę podsumowania, którą porównasz ze standardami platformy.
Limit CPU jest pominięty celowo: limit CPU dławi poda nawet wtedy, gdy węzeł ma wolne rdzenie, co objawia się skokami opóźnień, a nie błędami. Limit pamięci zostaje, bo pamięci nie da się zdławić, można tylko zabić proces.
Zamień manifesty w chart Helm
Dział zatytułowany „Zamień manifesty w chart Helm”Gdy surowe manifesty przechodzą na kind, chart parametryzuje je per środowisko. Konwertuj działające manifesty zamiast generować chart od zera: wtedy recenzujesz tylko różnicę między wyrenderowanym chartem a sprawdzonymi manifestami.
Zweryfikuj artefakty kontenerowe bez czytania każdej linii
Dział zatytułowany „Zweryfikuj artefakty kontenerowe bez czytania każdej linii”Każda bramka poniżej działa mechanicznie, w sesji i ponownie w CI. Razem zamieniają „YAML wygląda dobrze” w dowód.
-
Zlintuj Dockerfile. hadolint wyłapuje nieprzypięte obrazy bazowe,
apt-getbez sprzątania iCMDw formie shellowej, która połyka sygnały. Hook opisany wyżej uruchamia go przy każdej edycji. -
Przeskanuj obraz i konfigurację. Trivy działa dwa razy:
trivy image --severity HIGH,CRITICAL --exit-code 1 myapp:testszuka podatnych pakietów, atrivy config deploy/błędów konfiguracji, takich jak użytkownik root czy brakreadOnlyRootFilesystem. Jeśli zespół już używa Docker Scout,docker scout cve myapp:testzastąpi skan obrazu. -
Zwaliduj każdy manifest względem schematu Kubernetesa. kubeconform z
-strictodrzuca nieznane pola, dzięki czemu literówkareadinessprobezostaje wyłapana, a nie po cichu zignorowana. Zasoby niestandardowe, takie jakExternalSecret, nie mają wbudowanego schematu, więc-ignore-missing-schemasje pomija; wskaż-schema-locationna katalog schematów CRD, jeśli chcesz sprawdzać także je. Dla chartu przekaż mu na wejście wynikhelm template. -
Zrób próbne wdrożenie na jednorazowy klaster. Świeży klaster kind nie ma tego, co zapewnia produkcja, więc najpierw go przygotuj, bo inaczej rollout nigdy się nie zakończy:
- Załaduj obraz. Węzeł kind nie pobierze obrazu, który istnieje tylko w twoim lokalnym Dockerze:
kind load docker-image myapp:test --name dev. - Wyłącz External Secrets. Klaster nie ma CRD-ów External Secrets, dlatego
values-kind.yamlustawiaexternalSecret.enabled: false,image: myapp:testipullPolicy: IfNotPresent. - Utwórz zastępczy Secret.
kubectl create secret generic myapp-db --from-literal=DATABASE_URL='postgres://app:app@postgres:5432/app'wskazuje na jednorazowego Postgresa opisanego niżej. - Uruchom zależności.
/readyzsprawdza bazę i Redisa, więc w tej samej przestrzeni nazw uruchom jednorazowe Postgresa i Redisa zdeploy/kind/.
Potem zainstaluj chart, poczekaj na rollout i uruchom test chartu:
Okno terminala # Terminal: w kontekście kind-dev, po przygotowaniu opisanym wyżejhelm install myapp deploy/chart -f deploy/chart/values-staging.yaml \-f deploy/chart/values-kind.yaml --waitkubectl rollout status deploy/myapphelm test myappZakończony rollout dowodzi, że probe’y, security context i porty zgadzają się z kodem. Tego sprawdzenia nie zastąpi żaden linter.
- Załaduj obraz. Węzeł kind nie pobierze obrazu, który istnieje tylko w twoim lokalnym Dockerze:
-
Wskaż, kto zatwierdza. Właściciel serwisu zatwierdza zmianę na podstawie dowodów z kroków 1–4 i tabel od Claude’a. Zmiana security contextu, NetworkPolicy, RBAC albo podpięcia sekretów wymaga też właściciela platformy, czyli drugiego recenzenta, którego twój pakiet dowodów wskazuje dla wrażliwych ścieżek.
Umieść kroki 1–3 w scripts/check-containers.sh, żeby hook, sesja i job w CI uruchamiały te same polecenia. Skan obrazu wymaga zbudowanego obrazu, więc CI uruchamia trivy image po kroku budowania:
#!/usr/bin/env bash# scripts/check-containers.sh: jedna bramka dla hooka, sesji i CIset -euo pipefail
find . -name 'Dockerfile*' -not -path './node_modules/*' -print0 | xargs -0 -r hadolinttrivy config --exit-code 1 deploy/kubeconform -strict -summary -ignore-missing-schemas deploy/k8s/for values in deploy/chart/values-*.yaml; do helm template myapp deploy/chart -f "$values" | kubeconform -strict -summary -ignore-missing-schemasdoneCzęść dotyczącą pipeline’u opisuje strona konfiguracja pipeline’ów z Claude Code.
Debuguj działający klaster przez serwer MCP dla Kubernetesa
Dział zatytułowany „Debuguj działający klaster przez serwer MCP dla Kubernetesa”Przy pytaniach typu „dlaczego ten pod wpada w crash-loop” Kubernetes MCP server (projekt społecznościowy z organizacji containers na GitHubie; npm kubernetes-mcp-server 0.0.67 i około 2,1 tys. gwiazdek na GitHubie, oba odczytane 2026-09-26) daje Claude’owi ustrukturyzowany dostęp do podów, logów i zdarzeń zamiast parsowania wyjścia kubectl. Ustawienia uruchomieniowe trzyma w pliku TOML, a na start właściwe jest read_only = true:
# Terminal: ~/.config/k8s-mcp.toml zawiera linię read_only = trueclaude mcp add -s user kubernetes -- npx -y kubernetes-mcp-server@0.0.67 --config ~/.config/k8s-mcp.tomlDodaj go w zakresie użytkownika (-s user), a nie projektu, bo kubeconfig, który czyta, jest twój. Po podłączeniu prompt w rodzaju „Why is the checkout pod in namespace staging in CrashLoopBackOff? Read its logs and events, then propose a manifest fix as a diff; do not apply it.” zwraca diagnozę opartą na zdarzeniach z samego klastra. Inne serwery infrastrukturalne porównuje strona serwery MCP do infrastruktury.
Co się psuje, gdy Claude pisze konfigurację kontenerów?
Dział zatytułowany „Co się psuje, gdy Claude pisze konfigurację kontenerów?”Tag albo digest obrazu bazowego nie istnieje. Claude wpisał wiarygodnie wyglądający node:22.9-alpine3.21 albo digest z pamięci, a build pada przy pobieraniu. Wyjście: każ Claude’owi rozwiązać każde FROM przez docker buildx imagetools inspect i wkleić wynik do odpowiedzi. Reguła w CLAUDE.md zapobiega powtórce.
Każda zmiana kodu przebudowuje wszystkie zależności. COPY . . stoi przed instalacją zależności, więc każda edycja źródeł unieważnia cache. Wyjście: „Reorder the Dockerfile so the dependency layer depends only on the lockfile, then prove it: change a source file, rebuild, and show that the install step reports CACHED.”
Próbne wdrożenie wisi w ImagePullBackOff. Węzeł kind szukał myapp:test w rejestrze, bo obraz nigdy nie trafił do klastra albo polityka pobierania to Always. Wyjście: uruchom kind load docker-image myapp:test --name dev, ustaw pullPolicy: IfNotPresent i nigdy nie pozwól Claude’owi „naprawić” tego wypchnięciem obrazu do współdzielonego rejestru: zabraniają tego zarówno lista deny, jak i reguła w CLAUDE.md.
Rollout próbnego wdrożenia nigdy się nie kończy. helm install pada z no matches for kind "ExternalSecret" albo pody tkwią w CreateContainerConfigError lub nigdy nie stają się gotowe. W klastrze kind brakuje CRD-a, Secretu albo zależności, którą sprawdza /readyz. Wyjście: uruchom kubectl get events --sort-by=.lastTimestamp i kubectl describe pod, a potem dodaj brakujący element do przygotowania klastra kind (flaga w values-kind.yaml, zastępczy Secret albo jednorazowe Postgres i Redis), a nie do produkcyjnych wartości domyślnych chartu.
Pody restartują się z OOMKilled. Limit pamięci jest niższy niż faktyczny working set. Wyjście: odczytaj ostatni stan z kubectl describe pod, a potem poproś Claude’a o dobranie requests i limits na podstawie metryki container_memory_working_set_bytes, którą klaster już eksportuje, a nie na oko. Stałe dashboardy opisuje monitoring z Claude Code.
Claude naprawia padający probe, luzując go. Poproszony o naprawę readiness probe agent podnosi failureThreshold do 60 albo przepina probe na /healthz. Pod robi się zielony, a i tak dostaje ruch, zanim połączy się z bazą. Wyjście: napisz w prompcie, że ścieżki i progi probe’ów są stałe, a poprawka musi być w aplikacji; każdy diff dotykający probe’a recenzuj jako zmianę wrażliwą.
Readiness probe zwraca 200, choć aplikacja nie obsłuży ruchu. /readyz, który tylko zwraca 200, dowodzi jedynie, że proces żyje. Wyjście: „Make /readyz check that the database pool has a free connection and Redis answers PING, each with a 500 ms timeout, and return 503 with the failing dependency named in the body.”
Chart działa na stagingu, a pada na produkcji. Szablon odwołuje się do klucza, który definiuje tylko values-staging.yaml. Wyjście: „Render the chart with each values file, list every .Values key the templates use, and show which values file leaves each one undefined.” Potem dodaj render produkcyjny do bramki kubeconform.
Wolumeny Compose należą na hoście do roota. UID w kontenerze różni się od twojego, więc zapisane przez niego pliki są dla ciebie tylko do odczytu. Wyjście: „Add a USER_UID build argument defaulting to 1000 for the development stage only; keep UID 10001 in the runtime stage.”
Obraz pada z exec format error na innej maszynie. Został zbudowany dla arm64 na laptopie z Apple silicon, a działa na węzłach amd64. Wyjście: buduj przez docker buildx build --platform linux/amd64,linux/arm64 i publikuj manifest wieloarchitekturowy z CI, a nie z czyjegoś laptopa.