Wzorce Docker i kontenerów
Twój obraz waży 1,2 GB, build trwa osiem minut przy każdym przebiegu CI, a skan docker scout właśnie zgłosił 40 podatności CVE w warstwie bazowej. Tymczasem kontener nie wyłącza się czysto, bo PID 1 połyka sygnał SIGTERM, a twój „produkcyjny” plik Compose to ten sam, którego używasz do lokalnego hot-reloadu. Nic z tego nie jest egzotyczne — to domyślny stan Dockerfile’a, który rozrósł się organicznie. Ten przepis prowadzi AI przez naprawę każdego z tych problemów, z promptami, które możesz wkleić bezpośrednio.
Co z tego wyniesiesz
Dział zatytułowany „Co z tego wyniesiesz”- Wzorzec wieloetapowego Dockerfile’a, który oddziela zależności buildowe od runtime i wypuszcza obraz non-root poniżej 150 MB.
- Prompty do skopiowania i wklejenia, które zmniejszają obraz, utwardzają go przeciw CVE oraz dodają poprawne healthchecki i obsługę sygnałów.
- Wzorzec orkiestracji Compose z warunkami zdrowotnymi
depends_onzamiast hacków zsleep. - Pipeline GitHub Actions, który buduje wieloarchitekturowo, skanuje za pomocą Trivy i wdraża — z każdym krokiem faktycznie nadającym się do sparsowania.
- Tryby awarii (unieważnianie cache, debugowanie distroless, fałszywie negatywne healthchecki, zepsute buildy wieloarchitekturowe) i jak się z nich wycofać.
Szybka konfiguracja
Dział zatytułowany „Szybka konfiguracja”Skieruj swoje narzędzie na repozytorium i pozwól mu przeczytać istniejący Dockerfile, package.json/pyproject.toml oraz dowolny docker-compose.yml, zanim zaproponuje zmiany. Konfiguracja jest identyczna we wszystkich trzech narzędziach; różni się tylko sposób wywołania.
Otwórz repozytorium, włącz tryb Agent i odwołaj się do plików jawnie, aby zakotwiczyć go na twoim realnym buildzie:
@Dockerfile @package.json — audit this image and tell me the three biggestwins for size and security before changing anything.Uruchom z katalogu głównego projektu, aby miał kontekst systemu plików:
claude "Audit @Dockerfile and @package.json. List the three biggest wins forimage size and security, with the line numbers, before you change anything."ChatGPT Codex oferuje GPT-5.6 Sol, Terra i Luna zależnie od planu i zadania. Rozpocznij sesję z katalogu głównego repozytorium, używając pozycyjnego promptu:
codex "Audit the Dockerfile and package.json in this repo. List the threebiggest wins for image size and security with line numbers, then wait."Do bezobsługowego, skryptowanego audytu używaj tego wyłącznie w zaufanym CI lub izolowanym checkoutcie, z timeoutem i obowiązkowym przeglądem wyniku:
codex exec --sandbox workspace-write -c approval_policy=never "Audit the Dockerfile, report size and CVE wins as a checklist"Zanim pozwolisz agentowi dotknąć twojego Dockerfile’a, daj mu zasady gry. Umieść je w .cursor/rules/docker.mdc (aktualny format reguł projektowych Cursora; stary jednoplikowy .cursorrules jest już przestarzały) albo w CLAUDE.md:
- Przypinaj obrazy bazowe do digestu lub jawnej wersji — nigdy
:latest. - Buildy wieloetapowe: zależności buildowe nigdy nie trafiają do etapu runtime.
- Uruchamiaj jako użytkownik non-root; usuwaj domyślne capabilities.
- Zawsze dostarczaj
.dockerignoreorazHEALTHCHECK. - Łącz warstwy
RUNi czyść cache menedżera pakietów w tej samej warstwie.
Buildy wieloetapowe
Dział zatytułowany „Buildy wieloetapowe”Wzorzec o najwyższej dźwigni: kompiluj lub instaluj w grubym etapie buildera, a następnie kopiuj do szczupłego etapu runtime tylko artefakty. Błąd, który widzimy najczęściej, to przeinstalowywanie zależności w finalnym etapie (albo taniec cp -R node_modules && npm ci), co zarówno rozdyma obraz, jak i walczy z nawykiem npm ci do wycierania node_modules przy każdym przebiegu. Czysty podział to: pełna instalacja do buildu, instalacja --omit=dev do runtime.
Wklej powyższy prompt do trybu Agent z dołączonymi @Dockerfile i @package.json. Przejrzyj wygenerowane etapy w widoku diffa, a potem poproś o uzasadnienie każdej warstwy, której nie rozpoznajesz.
# Build stageFROM node:20-alpine AS builderRUN apk add --no-cache python3 make g++WORKDIR /appCOPY package*.json ./RUN npm ciCOPY . .RUN npm run build
# Production stageFROM node:20-alpine AS productionRUN apk add --no-cache dumb-initRUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001WORKDIR /appCOPY package*.json ./RUN npm ci --omit=dev && npm cache clean --forceCOPY --from=builder --chown=nodejs:nodejs /app/dist ./distUSER nodejsEXPOSE 3000HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD node healthcheck.jsENTRYPOINT ["dumb-init", "--"]CMD ["node", "dist/index.js"]Claude Code edytuje plik w miejscu i pokazuje ci diff. Przydatne, gdy chcesz, żeby przepisał istniejący Dockerfile, zamiast generować nowy od zera:
claude "Rewrite @Dockerfile as a multi-stage build: full npm ci in the builder,npm ci --omit=dev in the runtime stage, non-root user, dumb-init as PID 1,HEALTHCHECK on /health. Show me the diff and the expected final image size."Dla Pythona obowiązuje ten sam podział — pełna instalacja w builderze, kopiowanie site-packages do szczupłego runtime. Codex dobrze radzi sobie z repozytoriami poliglotycznymi na wszystkich swoich powierzchniach; w IDE lub App skieruj go na katalog serwisu:
codex "Generate a multi-stage Dockerfile for this Python 3.12 app using Poetry.Builder installs deps with 'poetry install --no-interaction --only main' into avenv; runtime stage is python:3.12-slim, copies the venv, runs as a non-rootuser, adds a HEALTHCHECK, and starts gunicorn with 4 workers."Wsparcie Codeksa dla worktree przydaje się tutaj: postaw worktree na każdy serwis, aby mógł generować Dockerfile’e dla kilku serwisów równolegle, bez kolizji.
Optymalizacja kontenerów
Dział zatytułowany „Optymalizacja kontenerów”Gdy już się buduje, zmniejsz go. Obrazy distroless i scratch całkowicie usuwają powłokę i menedżer pakietów; binarki Go z CGO_ENABLED=0 i -ldflags="-w -s" mogą zmieścić się w pojedynczych cyfrach megabajtów. Sztuczka polega na poproszeniu AI, by wyjaśniło każde cięcie, żebyś nie wypuścił „mniejszego” obrazu, w którym brakuje certyfikatów CA albo bazy stref czasowych.
Najpierw uruchom docker history --human image:tag, wklej wynik do czatu i pozwól Cursorowi celować konkretnie w najgrubsze warstwy, zamiast zgadywać.
# Go service, distroless final stageFROM golang:1.24-alpine AS builderRUN apk add --no-cache git ca-certificatesWORKDIR /appCOPY go.mod go.sum ./RUN go mod downloadCOPY . .RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o server ./cmd/server
FROM gcr.io/distroless/static:nonrootCOPY --from=builder /app/server /serverCOPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/USER nonroot:nonrootEXPOSE 8080ENTRYPOINT ["/server"]Niech Claude Code doda montowania cache BuildKit, dzięki czemu powtarzane buildy pomijają pobieranie zależności — to pojedyncza największa wygrana w czasie buildu CI:
claude "Add BuildKit cache mounts to @Dockerfile so npm/go dependency downloadsare cached across builds. Use 'RUN --mount=type=cache' and add the'# syntax=docker/dockerfile:1' directive at the top. Explain the cache key."Codex potrafi uruchomić build i odczytać faktyczne rozmiary warstw, a potem iterować — poproś go, by mierzył, a nie szacował:
codex "Build this image, run 'docker history' to find the three largest layers,then rewrite the Dockerfile to shrink them. Re-build and confirm the new size."Orkiestracja Docker Compose
Dział zatytułowany „Orkiestracja Docker Compose”Compose to miejsce, gdzie zderzają się wygoda lokalnego developmentu i produkcyjna poprawność. Dwie reguły, które AI zwykle myli, dopóki nie naciśniesz: używaj depends_on z condition: service_healthy (nie samego depends_on, które czeka tylko, aż kontener wystartuje, a nie aż będzie gotowy) oraz usuń pole version: na najwyższym poziomie — jest przestarzałe w Compose V2 i jedynie generuje ostrzeżenie.
services: api: build: ./api ports: - "8080:8080" environment: - DATABASE_URL=postgresql://postgres:${DB_PASSWORD}@postgres:5432/app - REDIS_URL=redis://redis:6379 depends_on: postgres: condition: service_healthy redis: condition: service_healthy networks: [app-network]
postgres: image: postgres:16-alpine environment: - POSTGRES_PASSWORD=${DB_PASSWORD:?set DB_PASSWORD} volumes: - postgres-data:/var/lib/postgresql/data networks: [app-network] healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s timeout: 5s retries: 5
redis: image: redis:7-alpine networks: [app-network] healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5
networks: app-network: driver: bridge
volumes: postgres-data:Do lokalnego developmentu trzymaj osobny docker-compose.override.yml, który dodaje montowania hot-reload i porty debugowania — Compose scala go automatycznie. Poproś Claude Code o wygenerowanie nadpisania, a nie o zmutowanie twojego pliku produkcyjnego:
claude "Create a docker-compose.override.yml that adds to my existing services:bind mounts for hot reload, NODE_ENV=development, the 9229 debugger port, and amailhog container for email testing. Don't touch the base compose file."Codex potrafi zwalidować plik, zanim go zacommitujesz — docker compose config rozwiązuje zmienne i wykrywa błędy schematu:
codex "Generate the compose file per my spec, then run 'docker compose config'to validate it parses with no warnings, and fix anything it flags."Hardening bezpieczeństwa
Dział zatytułowany „Hardening bezpieczeństwa”Użytkownik non-root to absolutne minimum. Kolejna warstwa to system plików root tylko do odczytu, usunięte capabilities Linuksa oraz skan w pętli. Niech AI naprawi zarówno Dockerfile, jak i ograniczenia runtime, bo część z nich (FS tylko do odczytu, usunięte capabilities) żyje w komendzie uruchomieniowej albo w security_opt Compose’a, a nie w obrazie.
Agent Cursora potrafi uruchomić docker scout cves image:tag w trybie inline, a następnie załatać konkretne znalezione advisories — wklej wynik skanu, żeby celował w realne podatności, a nie hipotetyczne.
FROM node:20-alpine AS productionRUN apk update && apk upgrade && rm -rf /var/cache/apk/*RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001WORKDIR /appCOPY --chown=nodejs:nodejs package*.json ./RUN npm ci --omit=dev && npm cache clean --forceCOPY --chown=nodejs:nodejs . .USER nodejsHEALTHCHECK --interval=30s --timeout=3s --retries=3 CMD node healthcheck.js || exit 1CMD ["node", "--max-old-space-size=512", "index.js"]Ograniczenia runtime (Compose):
services: app: read_only: true tmpfs: - /tmp cap_drop: - ALL security_opt: - no-new-privileges:trueWepnij skanowanie w hook pre-push, aby podatny obraz nigdy nie dotarł do rejestru. Claude Code jest tu naturalnym wyborem, bo hooki to jego specjalność:
claude "Write a git pre-push hook that runs 'docker scout cves --exit-code--only-severity critical,high' against the image we build, and blocks the pushif any critical or high CVE is found. Put it in .git/hooks/pre-push."W przypadku sekretów Dockera nigdy nie wpiekaj ich w warstwy. Niech Codex podepnie sekrety oparte na plikach oraz entrypoint, który je ładuje:
codex "Set up Docker Compose secrets for db_password and jwt_secret sourced fromfiles, expose them as *_FILE env vars, and write a POSIX docker-entrypoint.shthat reads each file into an env var then execs the main command. No secrets inthe image or in 'docker history'."Integracja CI/CD
Dział zatytułowany „Integracja CI/CD”Nagrodą jest pipeline, który buduje wieloarchitekturowo, skanuje i wdraża bez udziału człowieka. Najczęstszy błąd kopiuj-wklej to tutaj krok GitHub Actions, który definiuje jednocześnie uses: i run: — Actions odrzuca to komunikatem „a step cannot have both the uses and run keys”. Narzędzia do buildu/uwierzytelniania trafiają do jednego kroku (uses:), a komendy, które je konsumują, do następnego (run:).
name: Docker CI/CDon: push: branches: [main]env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }}jobs: build-and-test: runs-on: ubuntu-latest permissions: contents: read packages: write security-events: write steps: - uses: actions/checkout@v4 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=sha type=ref,event=branch - uses: docker/build-push-action@v5 with: context: . platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max - name: Trivy scan uses: aquasecurity/trivy-action@master with: image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ github.sha }} format: sarif output: trivy-results.sarif - uses: github/codeql-action/upload-sarif@v3 with: sarif_file: trivy-results.sarif
deploy-staging: needs: build-and-test runs-on: ubuntu-latest steps: - name: Install doctl uses: digitalocean/action-doctl@v2 with: token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} - name: Deploy run: | doctl kubernetes cluster kubeconfig save staging-cluster kubectl set image deployment/app app=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ github.sha }} kubectl rollout status deployment/appClaude Code błyszczy w bezgłowym CI — możesz uruchomić go wewnątrz pipeline’u, aby przeprowadzić triage nieudanego skanu:
claude -p "Read trivy-results.sarif. Summarize the critical and high findings,map each to the Dockerfile line that introduced it, and propose the minimalbase-image bump or package pin to clear them." --output-format jsonIntegracja Codeksa z GitHubem potrafi otworzyć poprawkę jako PR bezpośrednio z zadania Cloud, albo możesz sterować nim z CLI. Dla skryptowanej bramki SBOM-i-skan w zaufanym CI użyj izolowanego checkoutu, ustaw timeout i wymagaj przeglądu diffu oraz testów:
codex exec --sandbox workspace-write -c approval_policy=never "Add an SBOM generation step (docker buildx with--sbom=true) and a build provenance attestation to .github/workflows/docker-ci.yml,keeping the existing scan job intact."Kiedy to się psuje
Dział zatytułowany „Kiedy to się psuje”Kontenery psują się w sposób, który wygląda jak bugi aplikacji. Oto cztery przypadki, które pochłaniają najwięcej czasu, oraz ruch wybawiający z każdego z nich.