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."Codex domyślnie działa na GPT-5.5 w App, CLI, IDE i Cloud. 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 (CI lub worktree) użyj codex exec z presetem o niskim tarciu:
codex exec --full-auto "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:
codex exec --full-auto "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.