关于 docker-ops
docker-ops 是面向容器化应用开发者的 Docker 运维技能,涵盖镜像构建、Compose、缓存与安全实践。
主要功能
- 01Dockerfile 最佳实践
- 02多阶段构建指南
- 03优化镜像层缓存
- 04Compose 服务模式
- 05容器安全建议
- 06排查常见问题
适用场景
- 01设计生产环境 Dockerfile
- 02缩减镜像体积与构建时间
- 03编排服务与网络
- 04检查容器安全设置
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ 0xdarkmatter/ claude-mods --skill docker-ops安装后在 Claude Code 中输入/docker-ops确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/.gitkeepreferences/compose-patterns.mdmulti-stage-builds.mdoptimization.mdscripts/.gitkeep详细介绍
Docker 操作
构建、运行和编排容器化应用的全面 Docker 模式。
Dockerfile 最佳实践
| 实践 | 应该做 | 不应该做 |
|---|---|---|
| 基础镜像 | FROM node:20-slim | FROM node:latest |
| 层缓存 | 先复制依赖文件,再复制源代码 | 在 RUN install 之前执行 COPY . . |
| 安装软件包 | apt-get update && apt-get install -y ... && rm -rf /var/lib/apt/lists/* | 为更新和安装分别使用独立的 RUN |
| 用户 | USER nonroot(如有需要则创建) | 在生产环境中以 root 身份运行 |
| 多阶段构建 | 将构建阶段与运行阶段分开 | 将编译器工具链打包进去 |
| 密钥 | --mount=type=secret(BuildKit) | COPY .env . 或 ARG PASSWORD |
| ENTRYPOINT 与 CMD | 固定二进制文件使用 ENTRYPOINT,默认值使用 CMD | 依赖 shell 形式处理信号 |
| WORKDIR | WORKDIR /app | RUN cd /app && ... |
| .dockerignore | 包含 .git、node_modules、__pycache__ | 完全不使用 .dockerignore |
| 标签 | LABEL org.opencontainers.image.* | 不添加元数据 |
多阶段构建决策树
按语言选择运行时基础镜像:
Go ──────────── CGO disabled? ──── Yes ──► scratch or distroless/static
No ───► distroless/base or alpine
Rust ─────────── Static musl? ──── Yes ──► scratch or distroless/static
No ───► distroless/cc or debian-slim
Node.js ──────── Need native? ──── Yes ──► node:20-slim
No ───► node:20-alpine (smaller)
Python ────────── Need C libs? ─── Yes ──► python:3.12-slim
No ───► python:3.12-slim (still slim)
Java ──────────── JRE only ──────────────► eclipse-temurin:21-jre-alpine
参见:
references/multi-stage-builds.md,了解各语言完整的注释示例。
层缓存规则
Docker 会缓存每一层。第 N 层缓存未命中时,之后的所有层都会失效。
哪些情况会使缓存失效
| 触发条件 | 效果 |
|---|---|
COPY/ADD 中的文件发生更改 | 使此层及之后的所有层失效 |
RUN 命令文本发生更改 | 使此层及之后的所有层失效 |
ARG 值发生更改 | 从 ARG 声明所在层开始使后续层失效 |
--no-cache 标志 | 使所有内容失效 |
| 基础镜像更新 | 使所有内容失效 |
最佳层顺序
# 1. Base image (changes rarely)
FROM python:3.12-slim
# 2. System dependencies (changes rarely)
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
# 3. Dependency files (changes occasionally)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 4. Application code (changes frequently)
COPY src/ ./src/
# 5. Runtime config (changes frequently)
CMD ["python", "-m", "app"]
经验法则: 按更改频率从低到高排列各层。
.dockerignore 要点
# Version control
.git
.gitignore
# Dependencies (rebuilt in container)
node_modules
__pycache__
*.pyc
.venv
vendor/
# Build artifacts
dist/
build/
target/
*.egg-info
# IDE and editor
.vscode
.idea
*.swp
*.swo
# Docker files (prevent recursive context)
Dockerfile*
docker-compose*
.dockerignore
# Environment and secrets
.env
.env.*
*.pem
*.key
# Documentation and tests (unless needed)
docs/
tests/
*.md
LICENSE
重要原因: 如果没有 .dockerignore,docker build 会将整个上下文目录发送给守护进程。仅 .git 文件夹就可能增加数百 MB。
Docker Compose 快速参考
服务定义
services:
web:
build:
context: .
dockerfile: Dockerfile
target: production # Multi-stage target
image: myapp:latest
ports:
- "8080:8000"
environment:
DATABASE_URL: postgres://db:5432/app
env_file:
- .env
volumes:
- ./src:/app/src # Bind mount (dev)
- app-data:/app/data # Named volume (persistent)
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
restart: unless-stopped
networks:
- backend
卷和网络
volumes:
app-data: # Named volume (Docker-managed)
db-data:
driver: local
networks:
backend:
driver: bridge
frontend:
driver: bridge
参见:
references/compose-patterns.md,了解包括配置文件、监视模式和覆盖文件在内的完整模式。
PHP/CMS 项目中有
.ddev/文件夹? DDEV 会生成自己的 compose 文件;不要 手动编写并行的技术栈。请使用ddev-ops(额外服务放在.ddev/docker-compose.<name>.yaml中,其中的 Compose 语法属于本技能)。
安全快速参考
| 领域 | 建议 |
|---|---|
| 用户 | 以非 root 身份运行:RUN adduser -D appuser && USER appuser |
| 基础镜像 | 固定摘要:FROM python:3.12-slim@sha256:abc123... |
| 文件系统 | 只读根文件系统:docker run --read-only --tmpfs /tmp |
| 功能 | 全部移除,仅添加所需项:--cap-drop=ALL --cap-add=NET_BIND_SERVICE |
| 密钥 | BuildKit 密钥:RUN --mount=type=secret,id=key cat /run/secrets/key |
| 扫描 | 扫描镜像:trivy image myapp:latest 或 grype myapp:latest |
| 不用 latest | 始终使用具体标签并固定版本 |
| 最小化镜像 | 尽可能使用 distroless 或 scratch |
| 不使用 SUID | RUN find / -perm /6000 -type f -exec chmod a-s {} + |
| 网络 | 后端服务使用内部网络 |
非 Root 用户模式
# Debian/Ubuntu-based
RUN groupadd -r appuser && useradd -r -g appuser -d /app -s /sbin/nologin appuser
COPY --chown=appuser:appuser . /app
USER appuser
# Alpine-based
RUN addgroup -S appuser && adduser -S -G appuser appuser
COPY --chown=appuser:appuser . /app
USER appuser
# Distroless (built-in nonroot user)
FROM gcr.io/distroless/static:nonroot
USER nonroot:nonroot
常见易错点
| 易错点 | 问题 | 解决方法 |
|---|---|---|
| 镜像过大 | 将构建工具、node_modules 打包进最终镜像 | 使用多阶段构建 |
| 缓存失效 | 在 RUN npm install 之前执行 COPY . . | 先复制锁定文件,安装依赖,再复制源代码 |
| 镜像层中包含密钥 | COPY .env . 或 ARG SECRET=... 会将密钥写入镜像历史记录 | 使用 --mount=type=secret 或运行时环境变量 |
| PID 1 问题 | 应用无法接收 SIGTERM,产生僵尸进程 | 使用 tini 作为 init,或使用 CMD 的 exec 形式 |
| 时区 | 容器使用 UTC | 设置 TZ 环境变量或安装 tzdata |
| DNS 缓存 | Alpine 的 musl DNS 问题 | 使用 RUN apk add --no-cache libc6-compat 或切换到 slim |
| apt 缓存 | apt-get update 使用了旧镜像层的缓存 | 始终在同一个 RUN 中组合执行 update && install |
| 信号丢失 | Shell 形式(CMD npm start)会通过 /bin/sh 包装命令 | 使用 exec 形式:CMD ["node", "server.js"] |
| 构建上下文过大 | 向守护进程发送数 GB 的数据 | 添加 .dockerignore,使用 docker build --progress=plain 检查 |
| 镜像层过多 | 每个 RUN 都会创建一个镜像层 | 使用 && 链接相关命令 |
PID 1 / 信号处理修复
# Option 1: Use tini as init process
RUN apt-get update && apt-get install -y --no-install-recommends tini \
&& rm -rf /var/lib/apt/lists/*
ENTRYPOINT ["tini", "--"]
CMD ["node", "server.js"]
# Option 2: Docker init flag (Docker 23.0+)
# docker run --init myapp
# Option 3: Node.js - handle signals in code
# process.on('SIGTERM', () => { server.close(); process.exit(0); });
必备 Docker 命令
# Build
docker build -t myapp:1.0 .
docker build -t myapp:1.0 --target production . # Multi-stage target
docker build --no-cache -t myapp:1.0 . # Force rebuild
# Run
docker run -d --name myapp -p 8080:8000 myapp:1.0
docker run --rm -it myapp:1.0 /bin/sh # Interactive debug
docker run --read-only --tmpfs /tmp myapp:1.0 # Read-only root
# Inspect
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
docker history myapp:1.0 # Layer breakdown
docker inspect myapp:1.0 | jq '.[0].Config' # Image config
# Debug running container
docker exec -it myapp /bin/sh
docker logs -f myapp
docker stats myapp
# Cleanup
docker system prune -a --volumes # Remove everything unused
docker image prune -a # Remove unused images
参考文件
| 文件 | 内容 |
|---|---|
references/multi-stage-builds.md | 各语言的多阶段构建模式(Go、Rust、Node、Python) |
references/compose-patterns.md | Compose 服务、网络、配置组、监视、覆盖配置 |
references/optimization.md | 镜像大小、BuildKit、安全扫描、调试 |
在 RUN 步骤中,只能根据锁定文件安装依赖:使用 npm ci(或包管理器的冻结安装命令)和 composer install --no-dev --optimize-autoloader。各包管理器需要使用什么命令,以及为什么在镜像中运行 npm install 是个错误,可参阅 package-manager-ops。
评论
支持 Markdown