关于 docker
面向开发者的 Docker 专项 Skill,用于创建、审查和排查容器配置。内容涵盖 Dockerfile、Compose、镜像优化、安全加固及常见应用栈的运行诊断。
主要功能
- 01Dockerfile 设计与审查
- 02Docker Compose 配置
- 03镜像体积与构建优化
- 04容器安全加固
- 05网络与运行诊断
适用场景
- 01将应用容器化
- 02排查 Compose 网络或卷问题
- 03缩减镜像体积和构建时间
- 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
核心原则
- 层缓存 — 按变更频率从低到高排列各层
- 安全优先 — 使用非 root 用户、尽量缩小攻击面、不在镜像层中存放密钥
- 精简镜像 — 仅包含运行时所需内容
- 可复现性 — 固定版本,避免使用
latest标签,并使用锁定文件
基础镜像选择
推荐顺序(从优先到次选):
| 基础镜像 | 大小 | Shell | 适用场景 |
|---|---|---|---|
cgr.dev/chainguard/*(Wolfi) | ~10-30MB | 有 | 以零 CVE 为目标,包含 SBOM |
alpine:3.21 | ~7MB | 有 | 通用精简镜像 |
gcr.io/distroless/* | ~2-5MB | 无 | 加固生产环境,不用于调试 |
*-slim 变体 | ~70-100MB | 有 | Alpine 兼容性存在问题时 |
scratch | 0MB | 无 | 静态二进制文件(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
镜像优化
缩小镜像的技巧
- 多阶段构建 — 不要将构建工具打包进镜像
- 精简基础镜像 — Alpine 或 distroless
- 合并 RUN 命令 — 在同一层中完成清理
- 选择性复制 — 只复制所需的构件
# 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