mcpField
D
docker-containerization

docker-containerization

github.com/curiositech云与 DevOps257131 次安装1 天前更新
代码仓库

关于 docker-containerization

面向构建和维护容器的开发者,提供编写与改进生产级 Dockerfile 的方法,涵盖多阶段构建、缓存、安全、镜像体积及 Docker Compose 开发模式。

主要功能

  1. 01多阶段 Dockerfile 模式
  2. 02镜像层缓存策略
  3. 03Docker Compose 开发环境
  4. 04镜像体积优化
  5. 05安全加固与健康检查

适用场景

  1. 01为 Go、Rust、TypeScript 或 Python 编写 Dockerfile
  2. 02通过依赖层缓存缩短重建时间
  3. 03使用非 root 用户和健康检查加固容器

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/curiositech/some_claude_skills --skill docker-containerization

安装后在 Claude Code 中输入/docker-containerization确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

.claude-plugin/
plugin.json
references/
compose-patterns.md
multi-stage-patterns.md

详细介绍

Docker 容器化

编写可用于生产环境的 Dockerfile,采用多阶段构建、强化安全性并优化镜像大小。涵盖用于本地开发的 docker-compose、镜像层缓存、健康检查,以及将 2GB 镜像精简到 50MB 的各种模式。

适用场景

适用于:

  • 从头编写 Dockerfile,或改进现有 Dockerfile
  • 为编译型语言(Go、Rust、TypeScript)进行多阶段构建
  • 使用 Docker Compose 配置本地开发环境
  • 优化镜像大小(选择基础镜像、镜像层缓存)
  • Docker 安全扫描与加固
  • 开发环境与生产环境的 Dockerfile 模式
  • 调试容器构建失败问题
  • 优化 .dockerignore

不适用于:

  • Kubernetes 部署/编排(属于不同领域)
  • 云厂商特定的容器服务(ECS、Cloud Run、App Runner)
  • CI/CD 流水线配置(请使用 github-actions-pipeline-builder)
  • 超出 docker-compose 范围的容器网络配置
  • Docker Swarm

Dockerfile 决策树

flowchart TD
    Start[What are you building?] --> Lang{Language/runtime?}
    Lang -->|Node.js/TypeScript| Node[Node pattern]
    Lang -->|Python| Python[Python pattern]
    Lang -->|Go| Go[Go pattern]
    Lang -->|Rust| Rust[Rust pattern]
    Lang -->|Static site| Static[Static pattern]

    Node --> NQ{Need build step?}
    NQ -->|Yes, TypeScript/bundler| MultiNode[Multi-stage: build + runtime]
    NQ -->|No, plain JS| SingleNode[Single stage with slim base]

    Python --> PQ{Package manager?}
    PQ -->|pip| PipPattern[pip + venv pattern]
    PQ -->|uv| UvPattern[uv pattern — fastest]
    PQ -->|poetry| PoetryPattern[poetry export pattern]

    Go --> GoMulti[Multi-stage: build + scratch/distroless]
    Rust --> RustMulti[Multi-stage: build + debian-slim]
    Static --> StaticMulti[Multi-stage: build + nginx/caddy]

各语言的生产环境模式

Node.js / TypeScript(多阶段)
# Stage 1: Dependencies
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production

# Stage 2: Build (TypeScript/bundler)
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 3: Production
FROM node:22-alpine AS production
WORKDIR /app
ENV NODE_ENV=production

# Security: non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nextjs -u 1001

COPY --from=deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY package.json ./

USER nextjs
EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
  CMD wget -qO- http://localhost:3000/health || exit 1

CMD ["node", "dist/index.js"]
Python(uv — 速度最快)
FROM python:3.12-slim AS base

# Install uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

WORKDIR /app

# Install dependencies (cached layer)
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-editable

# Copy application code
COPY . .

# Non-root user
RUN useradd -r -s /bin/false appuser
USER appuser

EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=3s \
  CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"

CMD ["uv", "run", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
Go(多阶段 → Distroless)
# Build stage
FROM golang:1.22-alpine AS build
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 ./cmd/server

# Production: distroless (no shell, no package manager, minimal attack surface)
FROM gcr.io/distroless/static-debian12
COPY --from=build /server /server
EXPOSE 8080
USER nonroot:nonroot
ENTRYPOINT ["/server"]

镜像层缓存策略

flowchart TD
    subgraph "Slow to change (cache hit)"
        A[Base image] --> B[System packages]
        B --> C[Language runtime deps]
    end
    subgraph "Medium change frequency"
        C --> D[Application dependencies]
    end
    subgraph "Fast changing (cache miss OK)"
        D --> E[Application code]
        E --> F[Build step]
    end

规则:按指令变更频率从低到高排列 Dockerfile 指令。每条指令都会创建一个镜像层。某个镜像层发生变化时,其后的所有镜像层都会重新构建。

反面模式:在依赖之前 COPY

新手:

COPY . .                    # ← Busts cache on ANY file change
RUN npm install             # ← Reinstalls everything every build

专家:

COPY package.json package-lock.json ./  # ← Only busts on dependency changes
RUN npm ci                              # ← Cached when deps unchanged
COPY . .                                # ← Only app code changes trigger rebuild

说明:自 Docker 引入镜像层缓存以来,这一直是最佳实践,但使用旧教程训练的大模型仍会生成错误的顺序。


用于开发的 Docker Compose

# docker-compose.yml
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: development          # Use a dev-specific stage
    ports:
      - "${PORT:-3000}:3000"
    volumes:
      - .:/app                     # Hot reload via bind mount
      - /app/node_modules          # Anonymous volume: don't override node_modules
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/myapp
    depends_on:
      db:
        condition: service_healthy
    develop:
      watch:                       # Docker Compose Watch (2024+)
        - action: sync
          path: ./src
          target: /app/src
        - action: rebuild
          path: package.json

  db:
    image: postgres:16-alpine
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: myapp
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
    ports:
      - "5432:5432"

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s

volumes:
  pgdata:
反面模式:不设置健康检查

新手:只依赖 depends_on——但它只会等待容器启动,不会等待服务就绪。 专家:始终为数据库/缓存服务添加 healthcheck,并在 depends_on 中使用 condition: service_healthy。Postgres 容器虽然已经启动,但如果 WAL 恢复尚未完成,就会导致应用崩溃。


优化镜像大小

基础镜像大小适用场景
node:22~1.1 GB生产环境中绝不使用
node:22-slim~200 MB需要 apt 软件包时
node:22-alpine~130 MB默认选择
distroless~20 MBGo/Rust 编译后的二进制文件
scratch0 MB完全静态的二进制文件
chainguard/*~10-30 MB强化安全性的替代方案
快速优化
# 1. Use --no-cache for apk/apt
RUN apk add --no-cache curl

# 2. Combine RUN commands to reduce layers
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl && \
    rm -rf /var/lib/apt/lists/*

# 3. Use .dockerignore aggressively
# .dockerignore:
node_modules
.git
*.md
.env*
dist
coverage
.next

安全加固

# 1. Non-root user (MANDATORY)
RUN addgroup -g 1001 -S appgroup && \
    adduser -S appuser -u 1001 -G appgroup
USER appuser

# 2. Read-only filesystem (in compose)
# docker-compose.yml:
#   read_only: true
#   tmpfs:
#     - /tmp

# 3. No new privileges
# docker run --security-opt no-new-privileges ...

# 4. Pin image digests for reproducibility
FROM node:22-alpine@sha256:abc123...

# 5. Scan for vulnerabilities
# docker scout quickview myimage:latest
# trivy image myimage:latest
反面模式:以 Root 身份运行

新手:跳过 USER 指令。所有进程都以 root 身份运行。 专家:在容器内以 root 身份运行意味着容器逃逸会让攻击者获得主机上的 root 权限。始终创建并切换到非 root 用户。仅在构建阶段安装软件包时使用 root。 检测方式:docker inspect --format='{{.Config.User}}' image:tag——如果为空,表示以 root 身份运行。


按服务类型选择健康检查策略

原则:检查存活状态,而非就绪状态

Docker HEALTHCHECK 要回答的是一个问题:“此进程是否存活且具备基本功能?”它不回答“所有依赖项是否都可访问?”——后者属于就绪状态检查(Kubernetes 的概念)。混淆两者会导致连锁重启:数据库宕机 → 每个 API 容器的健康检查都“失败” → 编排器重启所有容器 → 数据库恢复时出现惊群效应。

API 服务
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  CMD wget -qO- http://localhost:${PORT}/health || exit 1

/health 端点应:

  • 如果进程可以处理 HTTP 请求,则返回 200
  • 不检查数据库连接(这属于就绪状态检查)
  • 不运行开销大的查询或计算
  • 在 <100ms 内响应——每 30 秒运行一次
// Minimal /health endpoint
app.get('/health', (req, res) => res.status(200).json({ status: 'ok' }));

如果需要为监控仪表板提供更全面的健康检查(数据库状态、队列深度、缓存命中率),请在 /health/detailed 上提供,并且不要将其接入 Docker HEALTHCHECK。

Compose 对应配置:

healthcheck:
  test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
  interval: 30s
  timeout: 3s
  start_period: 10s
  retries: 3
Worker / 后台作业服务

Worker 不提供 HTTP 服务。使用心跳文件模式:

HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
  CMD test $(find /tmp/worker-heartbeat -mmin -1 2>/dev/null | wc -l) -gt 0 || exit 1

Worker 在每次成功完成作业循环迭代时写入一个时间戳文件:

// Inside your worker loop
await processJob();
fs.writeFileSync('/tmp/worker-heartbeat', Date.now().toString());

如果心跳文件的时间超过 1 分钟,说明 Worker 卡住了。检查项:进程是否存活、事件循环是否未被阻塞、作业是否正在出队。

静态文件服务器(nginx、Caddy)
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget -qO- http://localhost:80/ || exit 1

启动时间短——静态服务器启动很快。只需检查它能否提供页面。无需 /health 端点。

数据库容器

使用数据库的原生客户端进行健康检查,而不是 HTTP:

# PostgreSQL
healthcheck:
  test: ["CMD-SHELL", "pg_isready -U postgres"]
  interval: 10s
  timeout: 5s
  start_period: 30s    # DBs are slow to start — generous grace period
  retries: 5

# Redis
healthcheck:
  test: ["CMD", "redis-cli", "ping"]
  interval: 10s
  timeout: 3s
  retries: 5

# MySQL
healthcheck:
  test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
  interval: 10s
  timeout: 5s
  start_period: 30s
  retries: 5
调优参数
参数建议
interval应用为 30s,数据库为 10s。数值越小,CPU 开销和日志噪声越大。
timeout3-5s。如果健康检查耗时更长,开销就太大了。
start_period首次检查前的等待时间。静态服务器为 5s,API 为 10s,数据库为 30s,JVM 应用为 60s 以上。
retries应用为 3,数据库为 5。数值过低会因短暂波动而重启。

参考资料

  • references/multi-stage-patterns.md — 复杂的多阶段构建请参阅此文档:使用 BuildKit 进行构建缓存、交叉编译、单体仓库 Dockerfile、Bun/Deno 模式
  • references/compose-patterns.md — 高级 docker-compose 用法请参阅此文档:配置文件、extends、覆盖文件、网络、机密管理、GPU 直通

评论

支持 Markdown