Docker最佳实践

多阶段构建(Go示例)

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

# Stage 2: Minimal runtime image
FROM gcr.io/distroless/static-debian12
# Or: FROM alpine:3.19 (adds 5MB but has shell)
COPY --from=builder /app/server /server
EXPOSE 8080
ENTRYPOINT ["/server"]

层缓存优化

# BAD — invalidates cache on any source change
COPY . .
RUN npm install

# GOOD — package.json changes rarely
COPY package.json package-lock.json ./
RUN npm ci --only=production
COPY . .

# Rule: put things that change least first

安全加固

# Run as non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

# Read-only filesystem
docker run --read-only --tmpfs /tmp myapp

# Limit capabilities
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myapp

# Scan for vulnerabilities
docker scout cves myimage:latest
trivy image myimage:latest

docker-compose最佳实践

services:
  app:
    build:
      context: .
      target: production          # Use build target
    image: myapp:${VERSION:-latest}
    restart: unless-stopped
    environment:
      - NODE_ENV=production
    env_file: .env               # Never hardcode secrets
    ports:
      - "127.0.0.1:8080:8080"   # Bind to loopback only
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
    deploy:
      resources:
        limits:
          memory: 512m
          cpus: '0.5'

镜像大小对比

基础镜像大小Shell使用场景
ubuntu:22.04~77MB完整调试
debian:slim~74MB精简Debian
alpine:3.19~7MBsh小,musl libc
distroless/static~2MB静态编译二进制
scratch0MB最小,Go/Rust