mcpField
D
docker

docker

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

关于 docker

面向开发者的 Docker 专项 Skill,用于创建、审查和排查容器配置。内容涵盖 Dockerfile、Compose、镜像优化、安全加固及常见应用栈的运行诊断。

主要功能

  1. 01Dockerfile 设计与审查
  2. 02Docker Compose 配置
  3. 03镜像体积与构建优化
  4. 04容器安全加固
  5. 05网络与运行诊断

适用场景

  1. 01将应用容器化
  2. 02排查 Compose 网络或卷问题
  3. 03缩减镜像体积和构建时间
  4. 04审查容器安全配置

安装

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

$npx skills add https://github.com/marcioaltoe/skills --skill docker

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

附带文件agents/openai.yaml

详细介绍

Docker 专家

适用场景

  • 创建或审查 Dockerfile
  • 优化镜像大小或构建性能
  • 加固容器安全
  • 为多服务应用设置 Docker Compose
  • 调试容器网络、卷或运行时问题
  • 将应用容器化(Node.js、Python、Go、Java、Rust)

不在适用范围内(建议使用专门的技能):

  • Kubernetes 编排 → kubernetes expert
  • CI/CD 流水线 → github-actions expert
  • 特定云平台的容器服务(ECS、Cloud Run)→ devops expert

核心原则

  1. 层缓存 — 按变更频率从低到高排列各层
  2. 安全优先 — 使用非 root 用户、尽量缩小攻击面、不在镜像层中存放密钥
  3. 精简镜像 — 仅包含运行时所需内容
  4. 可复现性 — 固定版本,避免使用 latest 标签,并使用锁定文件

基础镜像选择

推荐顺序(从优先到次选):

基础镜像大小Shell适用场景
cgr.dev/chainguard/*(Wolfi)~10-30MB有以零 CVE 为目标,包含 SBOM
alpine:3.21~7MB有通用精简镜像
gcr.io/distroless/*~2-5MB无加固生产环境,不用于调试
*-slim 变体~70-100MB有Alpine 兼容性存在问题时
scratch0MB无静态二进制文件(Go、Rust)

规则:

  • 始终固定确切版本:使用 node:22.14.0-alpine3.21,而非 node:alpine
  • 切勿使用 latest — 这会破坏可复现性
  • 根据实际运行时需求选择基础镜像

Dockerfile 最佳实践

层排序
# 1. Base image + system deps (rarely change)
FROM node:22-alpine
RUN apk add --no-cache dumb-init

# 2. Dependencies (change occasionally)
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production

# 3. Application code (changes frequently)
COPY . .

# 4. Metadata + runtime config
USER node
EXPOSE 3000
CMD ["dumb-init", "node", "server.js"]
.dockerignore

始终创建 .dockerignore 文件,以缩小构建上下文:

.git
node_modules
__pycache__
*.pyc
.vscode
.idea
.DS_Store
*.log
coverage/
.env
.env.local
dist/
build/
README.md
docs/
BuildKit 缓存挂载

加快依赖安装速度:

# Node.js
RUN --mount=type=cache,target=/root/.npm \
    npm ci

# Python
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt

# Go
RUN --mount=type=cache,target=/go/pkg/mod \
    go build -o /app .

使用 DOCKER_BUILDKIT=1 或 Docker Desktop 启用(默认已启用)。

多阶段构建模式

Node.js
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

FROM node:22-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:22-alpine AS runtime
RUN addgroup -g 1001 -S app && adduser -S app -u 1001
WORKDIR /app
COPY --from=build --chown=app:app /app/dist ./dist
COPY --from=deps --chown=app:app /app/node_modules ./node_modules
COPY --chown=app:app package.json ./
USER app
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/index.js"]
Python
FROM python:3.13-slim AS build
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

FROM python:3.13-slim AS runtime
RUN useradd -r -u 1001 app
WORKDIR /app
COPY --from=build /install /usr/local
COPY --chown=app:app . .
USER app
EXPOSE 8000
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Go
FROM golang:1.24-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /app .

FROM scratch
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=build /app /app
USER 65534:65534
EXPOSE 8080
ENTRYPOINT ["/app"]
Java
FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /app
COPY . .
RUN ./gradlew build --no-daemon

FROM eclipse-temurin:21-jre-alpine AS runtime
RUN addgroup -g 1001 -S app && adduser -S app -u 1001
WORKDIR /app
COPY --from=build --chown=app:app /app/build/libs/*.jar app.jar
USER app
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
  CMD wget -qO- http://localhost:8080/actuator/health || exit 1
CMD ["java", "-jar", "app.jar"]
Rust
FROM rust:1.84-alpine AS build
RUN apk add --no-cache musl-dev
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo "fn main() {}" > src/main.rs && cargo build --release && rm -rf src
COPY src ./src
RUN cargo build --release

FROM scratch
COPY --from=build /app/target/release/app /app
USER 65534:65534
EXPOSE 8080
ENTRYPOINT ["/app"]

安全加固

非 root 用户
# Alpine
RUN addgroup -g 1001 -S app && adduser -S app -u 1001 -G app
USER app

# Debian/Ubuntu
RUN groupadd -g 1001 app && useradd -r -u 1001 -g app app
USER app

# Distroless/scratch (numeric only)
USER 65534:65534
密钥管理
# BuildKit secrets (never stored in layers)
RUN --mount=type=secret,id=api_key \
    API_KEY=$(cat /run/secrets/api_key) && \
    ./configure --api-key="$API_KEY"
# Build with secret
docker build --secret id=api_key,src=./api_key.txt .

切勿这样做:

ENV API_KEY=secret123          # Visible in image history
COPY .env /app/.env            # Baked into layer
ARG PASSWORD=hunter2           # Visible in build history
运行时加固
docker run \
  --user 1001:1001 \
  --cap-drop=ALL \
  --cap-add=NET_BIND_SERVICE \
  --read-only \
  --tmpfs /tmp:noexec,nosuid \
  --security-opt="no-new-privileges:true" \
  --memory="512m" \
  --cpus="1.0" \
  my-image
漏洞扫描
# Docker Scout
docker scout quickview my-image
docker scout cves my-image

# Trivy
trivy image my-image

# Grype
grype my-image

Docker Compose

可用于生产环境的模式
services:
  app:
    build:
      context: .
      target: runtime
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    networks:
      - frontend
      - backend
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
        reservations:
          cpus: "0.5"
          memory: 256M
    restart: unless-stopped
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  db:
    image: postgres:17-alpine
    environment:
      POSTGRES_DB_FILE: /run/secrets/db_name
      POSTGRES_USER_FILE: /run/secrets/db_user
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_name
      - db_user
      - db_password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - backend
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    command: redis-server --maxmemory 128mb --maxmemory-policy allkeys-lru
    volumes:
      - redis_data:/data
    networks:
      - backend

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true  # No external access

volumes:
  postgres_data:
  redis_data:

secrets:
  db_name:
    file: ./secrets/db_name.txt
  db_user:
    file: ./secrets/db_user.txt
  db_password:
    file: ./secrets/db_password.txt
网络隔离
networks:
  frontend:        # Web-facing services
  backend:
    internal: true # Database, cache — no external access

services:
  proxy:
    networks: [frontend]
  api:
    networks: [frontend, backend]
  db:
    networks: [backend]  # Only reachable from api
开发与生产环境覆盖配置
# compose.yml (base)
services:
  app:
    build: .

# compose.override.yml (dev, loaded automatically)
services:
  app:
    build:
      target: development
    volumes:
      - .:/app
      - /app/node_modules
    ports:
      - "9229:9229"  # Debug
    environment:
      - NODE_ENV=development
    command: npm run dev

# compose.prod.yml
services:
  app:
    build:
      target: runtime
    environment:
      - NODE_ENV=production
    restart: unless-stopped
# Dev (auto-loads compose.override.yml)
docker compose up

# Prod
docker compose -f compose.yml -f compose.prod.yml up -d

镜像优化

缩小镜像的技巧
  1. 多阶段构建 — 不要将构建工具打包进镜像
  2. 精简基础镜像 — Alpine 或 distroless
  3. 合并 RUN 命令 — 在同一层中完成清理
  4. 选择性复制 — 只复制所需的构件
# Bad — 3 layers, cleanup doesn't reduce size
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*

# Good — 1 layer, cleanup is effective
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl && \
    rm -rf /var/lib/apt/lists/*
CMD:exec 形式与 shell 形式
CMD ["node", "server.js"]   # Exec form — PID 1, receives signals directly
CMD node server.js           # Shell form — spawns /bin/sh, signal issues

始终优先使用 exec 形式。

开发工作流

热重载设置
services:
  app:
    build:
      target: development
    volumes:
      - .:/app              # Source code
      - /app/node_modules   # Prevent overwrite
    ports:
      - "3000:3000"
      - "9229:9229"         # Debug port
    environment:
      - NODE_ENV=development
      - DEBUG=app:*
    command: npm run dev
调试
services:
  app:
    # Node.js inspect
    command: node --inspect=0.0.0.0:9229 server.js
    ports:
      - "9229:9229"

    # Python debugpy
    # command: python -m debugpy --listen 0.0.0.0:5678 main.py
    # ports:
    #   - "5678:5678"

特定平台说明

Linux
// /etc/docker/daemon.json
{
  "userns-remap": "default",
  "storage-driver": "overlay2",
  "live-restore": true,
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}
  • 使用用户命名空间重映射以提高安全性
  • 在生产环境中配置 SELinux/AppArmor 配置文件
  • 使用 overlay2 存储驱动程序
macOS
volumes:
  - ./src:/app/src:delegated  # Better write performance
  - ./build:/app/build:cached  # Container writes cached
  • 在 Docker Desktop 中分配足够的资源
  • 使用 :delegated/:cached 提高绑定挂载的性能
  • 考虑使用 mutagen 或 docker compose watch 同步文件
  • 针对 ARM(Apple Silicon)构建多平台镜像
Windows
  • 优先使用 WSL2 后端以获得最佳性能
  • 处理行尾:添加 .gitattributes,内容为 * text=auto eol=lf
  • 在 Dockerfile 和 compose 文件中使用正斜杠
  • 确保在 Docker Desktop 设置中共享驱动器
  • 注意文件权限差异
# .gitattributes — prevent CRLF issues in containers
* text=auto eol=lf
*.sh text eol=lf
Dockerfile text eol=lf

生产环境检查清单

  • 固定基础镜像版本(不使用 latest)
  • 使用多阶段构建分离构建环境与运行环境
  • 以非 root 用户运行
  • 镜像层或 ENV 中不包含密钥
  • 已配置 .dockerignore
  • 已实现健康检查
  • 已设置资源限制(CPU、内存)
  • 已配置日志轮转
  • 已通过漏洞扫描
  • 正确处理信号(使用 exec 形式的 CMD,或 dumb-init/tini)
  • 尽可能使用只读文件系统
  • 已移除 capabilities(--cap-drop=ALL)

反模式

不要这样做应改为
以 root 身份运行使用 USER 1001 或具名用户
使用 latest 标签固定确切版本
使用 --privileged 标志仅使用必需的 --cap-add
挂载 Docker 套接字使用 Docker-in-Docker 或其他替代方案
在 ENV/ARG 中硬编码机密使用 BuildKit 机密或运行时挂载
跳过健康检查在 Dockerfile 或 Compose 中添加 HEALTHCHECK
忽略资源限制设置 memory 和 cpus 限制
复制整个上下文使用 .dockerignore
安装不必要的软件包使用 --no-install-recommends
使用 shell 格式的 CMD使用 exec 格式 ["cmd", "arg"]

诊断

构建缓慢
  • 检查层的顺序(先依赖项,再源代码)
  • 启用 BuildKit(DOCKER_BUILDKIT=1)
  • 为软件包管理器使用缓存挂载
  • 检查 .dockerignore(构建上下文是否过大?)
镜像过大
  • 使用多阶段构建
  • 切换到 Alpine 或 distroless
  • 合并 RUN 命令并清理
  • 使用 docker history <image> 查找较大的层
网络问题
  • 确认服务名称可用于 DNS 解析
  • 检查 Compose 中的网络分配
  • 使用 docker network inspect 进行调试
  • 确保健康检查通过后,依赖服务再启动
容器崩溃
  • 检查日志:docker logs <container>
  • 确认信号处理正常(PID 1 必须处理 SIGTERM)
  • 检查资源限制(OOM 终止)
  • 确认健康检查端点存在

评论

支持 Markdown