🟡 Intermediate

Docker Image Optimization — Optimalizácia kontajnerových obrazov

Veľkosť Docker obrazu priamo ovplyvňuje čas nasadenia, spotrebu úložiska a bezpečnostnú plochu útoku. Menší obraz = rýchlejší deploy, menej zraniteľností a nižšie náklady. V tomto článku si ukážeme praktické techniky na zmenšenie obrazov z stoviek megabajtov na desiatky.


Prečo optimalizovať?

  • Rýchlejšie buildy a deploymenty — menej dát na prenos
  • Menšia attack surface — menej balíkov = menej zraniteľností
  • Nižšie náklady — úložisko v registri a sieťový prenos nie sú zadarmo
  • Rýchlejší cold start — dôležité pre serverless a auto-scaling

Multi-stage Builds

Multi-stage build je najúčinnejšia technika. Oddelíte build prostredie od runtime prostredia.

❌ Pred optimalizáciou (~900 MB)

FROM node:20
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/server.js"]

✅ Po optimalizácii (~150 MB)

# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
RUN npm prune --production

# Runtime stage
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]

Go aplikácia — ešte dramatickejší rozdiel

# Build stage
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o server .

# Runtime stage (~12 MB namiesto ~800 MB)
FROM scratch
COPY --from=builder /app/server /server
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/server"]

Výber base image

Voľba base image má obrovský vplyv na výslednú veľkosť:

Base Image Veľkosť Použitie
ubuntu:24.04 ~78 MB Vývoj, debugging
debian:bookworm-slim ~74 MB Stabilné prostredie
alpine:3.20 ~7 MB Minimálna veľkosť
distroless/static ~2 MB Statické binárky (Go, Rust)
distroless/cc ~20 MB C/C++ aplikácie
distroless/java21 ~190 MB Java aplikácie
scratch 0 MB Len statická binárka

Alpine vs Debian

Alpine používa musl libc namiesto glibc. To môže spôsobiť problémy s niektorými aplikáciami:

# Alpine — menší, ale pozor na kompatibilitu
FROM python:3.12-alpine
RUN apk add --no-cache gcc musl-dev  # Niekedy treba kompilovať C extensions

# Debian slim — väčší, ale kompatibilnejší
FROM python:3.12-slim

Kedy Alpine: Node.js, Go, jednoduché Python aplikácie Kedy Debian slim: Python s C extensions, ML knižnice, legacy aplikácie

Distroless images

Google distroless obrazy neobsahujú shell, package manager ani žiadne utility. Obsahujú len runtime a CA certifikáty:

FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o server .

FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/server /
CMD ["/server"]

.dockerignore

Bez .dockerignore sa do build kontextu kopíruje všetko, vrátane node_modules, .git a testov:

.git
.gitignore
node_modules
npm-debug.log
Dockerfile
docker-compose.yml
.env
.env.*
*.md
tests/
coverage/
.vscode/
.idea/
dist/

Rozdiel môže byť dramatický — build kontext z 500 MB na 5 MB.


Layer Caching

Docker cachuje každú vrstvu. Poradie inštrukcií v Dockerfile je kľúčové:

❌ Zlé poradie — cache sa invaliduje pri každej zmene kódu

FROM node:20-alpine
WORKDIR /app
COPY . .                    # Zmena akéhokoľvek súboru invaliduje cache
RUN npm ci                  # Musí sa spustiť znovu

✅ Správne poradie — dependencies sa cachujú

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./       # Zmení sa len ak sa zmenia dependencies
RUN npm ci                  # Cachované ak package.json nezmenený
COPY . .                    # Len kód aplikácie

Ďalšie tipy pre caching

# Používajte --mount=type=cache pre build cache
RUN --mount=type=cache,target=/root/.npm npm ci

# Kombinujte RUN príkazy na redukciu vrstiev
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl ca-certificates && \
    rm -rf /var/lib/apt/lists/*

Ďalšie optimalizačné techniky

Odstránenie nepotrebných súborov

RUN apt-get update && \
    apt-get install -y --no-install-recommends build-essential && \
    make build && \
    apt-get purge -y build-essential && \
    apt-get autoremove -y && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

Používajte konkrétne tagy

# ❌ Nepredvídateľné
FROM node:latest

# ✅ Reprodukovateľné
FROM node:20.11.1-alpine3.19

Komprimácia s docker-slim

# Automatická optimalizácia obrazu
docker-slim build --target myapp:latest --tag myapp:slim
# Typicky 5-30x zmenšenie

Security Scanning

Optimalizovaný obraz by mal byť aj bezpečný. Skenujte obrazy v CI/CD:

# Trivy — rýchly a komplexný skener
trivy image myapp:latest

# Grype — alternatíva od Anchore
grype myapp:latest

# Docker Scout — natívny v Docker Desktop
docker scout cves myapp:latest

Integrácia do CI pipeline (GitHub Actions)

- name: Build image
  run: docker build -t myapp:${{ github.sha }} .

- name: Scan for vulnerabilities
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: myapp:${{ github.sha }}
    exit-code: 1
    severity: HIGH,CRITICAL

Porovnanie — pred a po

Metrika Pred Po
Veľkosť obrazu 950 MB 85 MB
Build čas (cold) 120s 45s
Build čas (cached) 90s 8s
CVE (critical) 12 0
CVE (high) 34 3
Deploy čas 45s 12s

Záver

Optimalizácia Docker obrazov nie je luxus — je to best practice. Začnite multi-stage buildmi, zvoľte správny base image, nastavte .dockerignore a dbajte na poradie vrstiev. Výsledkom budú rýchlejšie deploymenty, bezpečnejšie aplikácie a nižšie prevádzkové náklady.