Claude Code 与 Docker 实战:Compose、Volume、Network 和 CI 构建
用 Claude Code 安全整理 Dockerfile、Compose、本地环境和 CI 镜像构建。
先决定要让 Claude Code 负责什么
只对 Claude Code 说“帮我 Docker 化”,很容易得到一个能跑一次、但难以审查的 Dockerfile。Docker 的核心是把应用运行环境打包起来:image 是模板,container 是运行中的实例,volume 把数据保存在容器外,network 让多个容器可以用服务名互相访问。
实际项目里,Dockerfile 只是其中一部分。还要一起看 docker-compose.yml、.dockerignore、环境变量、health check 和 CI build。建议打开官方文档一起核对:Dockerfile reference、Compose file reference、Docker build best practices、Docker Build GitHub Actions 以及 Claude Code overview。
如果你还要整理 CI,可以参考 Claude Code CI/CD setup。权限边界和安全审查可以接着看 Claude Code approval and sandbox guide 与 Claude Code security best practices。
4 个常见使用场景
Docker 集成的价值不是“看起来专业”,而是让团队少猜环境。先写清楚要解决的问题,再让 Claude Code 改文件。
| 场景 | Docker 固定的内容 | 可以交给 Claude Code 的任务 |
|---|---|---|
| 新成员入门 | Node.js、PostgreSQL、Redis、端口、启动命令 | 让 docker compose up 启动完整 stack |
| 接近生产的调试 | 环境变量、服务名、health check、启动顺序 | 列出本地和生产差异 |
| Pull Request 验证 | Dockerfile、lockfile、build cache、CI 日志 | 增加只 build image 的 workflow |
| 培训和交接 | 命令、失败模式、恢复步骤 | 把规则写入 CLAUDE.md 和 README |
对有收入目标的 SaaS 或内容站来说,这很重要。结账、咨询表单、管理画面、产品页如果只在某台电脑上可用,收入就依赖不可复现的本地状态。个人学习可以从 ClaudeCodeLab 产品 开始;团队想把规则、权限、CI 和 review 流程落到真实仓库,可以看 Claude Code 培训与咨询。
给 Claude Code 的安全提示词
第一次指示要包括目标、要生成的文件、禁止事项和验证方法。这样可以减少“看起来像 Docker 化了,但其实很危险”的输出。
Dockerize this Node.js + TypeScript API.
Context:
- pnpm is the package manager.
- The app listens on port 3000.
- GET /health returns 200.
- Local development needs PostgreSQL 16 and Redis 7.
Create:
- Production multi-stage Dockerfile
- docker-compose.yml for local development
- .dockerignore
- Docker-related package.json scripts
- GitHub Actions workflow that only verifies docker build
Constraints:
- Do not run the production container as root.
- Do not COPY .env files or secrets into the image.
- Explain the volume and network choices for the README.
- Include verification commands and how to inspect failures.
multi-stage build 的意思是把构建环境和运行环境分开。TypeScript 编译工具留在 build stage,最终 image 只带生产依赖和编译后的 dist。这会降低体积,也减少生产环境里的攻击面。
可复制的 Dockerfile
下面假设 API 把 src/index.ts 构建为 dist/index.js。dev stage 给本地 Compose 用,runner stage 给生产镜像用。
# syntax=docker/dockerfile:1
FROM node:22-bookworm-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
WORKDIR /app
RUN corepack enable
FROM base AS dev
ENV NODE_ENV=development
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
CMD ["pnpm", "dev"]
FROM base AS deps
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
FROM deps AS build
COPY tsconfig.json ./
COPY src ./src
RUN pnpm build
FROM base AS prod-deps
ENV NODE_ENV=production
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile --prod && pnpm store prune
FROM base AS runner
ENV NODE_ENV=production
RUN groupadd --system --gid 1001 nodejs \
&& useradd --system --uid 1001 --gid nodejs appuser
COPY --from=prod-deps --chown=appuser:nodejs /app/node_modules ./node_modules
COPY --from=build --chown=appuser:nodejs /app/dist ./dist
COPY --chown=appuser:nodejs package.json ./
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --retries=3 CMD node -e "fetch('http://127.0.0.1:3000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
CMD ["node", "dist/index.js"]
让 Claude Code review 时,重点问三件事:secret 是否可能进入 image、COPY 顺序是否破坏 cache、runtime image 是否还残留 devDependencies。
Compose 中的 Volume 和 Network
Compose 可以一次启动多个容器。这里 API、PostgreSQL、Redis 在同一个 network 内,API 可以通过 postgres 和 redis 这两个服务名连接。Volume 则让数据离开一次性容器:pgdata 保存数据库文件,api_node_modules 避免宿主机的 node_modules 覆盖 Linux 容器里的依赖。
services:
api:
build:
context: .
target: dev
command: pnpm dev
ports:
- "3000:3000"
environment:
NODE_ENV: development
DATABASE_URL: postgres://app:app@postgres:5432/app
REDIS_URL: redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- .:/app
- api_node_modules:/app/node_modules
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
interval: 10s
timeout: 3s
retries: 5
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: app
POSTGRES_DB: app
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
timeout: 3s
retries: 10
redis:
image: redis:7-alpine
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
volumes:
pgdata:
api_node_modules:
depends_on 主要控制启动顺序,不等于数据库已经可以连接。配合 healthcheck 和 condition: service_healthy,可以减少 API 抢跑导致的连接失败。
防止 secret 进入 build context
先写 .dockerignore。它可以减少 build context,也能挡住最危险的 .env。
.git
node_modules
dist
coverage
.env
.env.*
!.env.example
npm-debug.log*
pnpm-debug.log*
Dockerfile*
docker-compose*.yml
再把团队常用命令放进 package.json。这样每个人都执行同一套命令,Claude Code 也能引用同一个检查入口。
{
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc -p tsconfig.json",
"start": "node dist/index.js",
"docker:dev": "docker compose up --build",
"docker:check": "bash scripts/docker-check.sh"
},
"dependencies": {
"@fastify/redis": "latest",
"fastify": "latest",
"pg": "latest"
},
"devDependencies": {
"tsx": "latest",
"typescript": "latest"
}
}
可重复的检查脚本
手动检查容易漏。把步骤放进 scripts/docker-check.sh,以后服务增加时再让 Claude Code 更新脚本。
#!/usr/bin/env bash
set -euo pipefail
docker compose build api
docker compose up -d postgres redis api
cleanup() {
docker compose down
}
trap cleanup EXIT
for attempt in {1..30}; do
if curl -fsS http://127.0.0.1:3000/health >/dev/null; then
echo "healthcheck ok"
exit 0
fi
echo "waiting for api... ${attempt}/30"
sleep 2
done
docker compose logs api
exit 1
这段脚本会 build、启动 stack、等待 /health,失败时输出 API 日志。它比 README 里一串手动命令更可靠。
CI 先 build,不急着 push
第一步只验证 image 能不能 build,不向 registry 发布。等基础稳定后,再加入 push、签名、漏洞扫描和 SBOM。
name: docker-build
on:
pull_request:
push:
branches:
- main
jobs:
image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v7
with:
context: .
target: runner
push: false
tags: claude-code-docker-sample:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
让 Claude Code 解释这个 workflow 是否会 push、权限是否过大、cache 为空时是否仍能 build。它的回答应该能指向具体 YAML 行。
常见失败
第一类是把 .env 复制进 image。secret 一旦进入 registry,处理成本会很高。.dockerignore、CI secrets、build log 要一起看。
第二类是把宿主机的 node_modules 挂进 Linux 容器。macOS、Windows、Linux 的 native module 可能不同。命名 volume 可以隔离这件事。
第三类是 API 比数据库先启动。depends_on 不是万能保险,数据库 health check 和应用重试都要有。
第四类是生产容器使用 root。demo 里看不出问题,但真实服务会扩大风险。让 Claude Code 指出证明 non-root 的 Dockerfile 行,会比“看起来安全”更可审查。
安全工作流
推荐顺序是:读仓库、列 runtime 依赖、先写 .dockerignore、再写 Compose、再写生产 Dockerfile、再放验证脚本、最后加 CI。每一步结束后读 git diff,让 Claude Code 输出 changed files、reason、verification command、remaining risk。
这也适合 session handoff template。Docker 集成不该只是一次性生成文件,而应该变成团队共享的开发习惯。
初学者最容易误解的是“放进 Docker 就等于和生产一致”。本地 Compose 只能帮助统一启动顺序、环境变量、端口、Volume 和基础依赖,不能自动复现真实云环境的 CPU、内存、网络延迟、托管数据库、TLS、日志管道和权限策略。让 Claude Code 在 PR 里明确写出“本地已验证什么”和“本地无法证明什么”,可以避免把 demo 当成生产保证。
如果服务会影响付款、预约、邮件、广告计量或咨询表单,还要把业务路径加入 Docker 检查。容器能启动但转化事件丢失,依然是事故。先用假数据验证 .env.example、webhook mock、健康检查和日志字段,再接真实 secret,会比一次性连上所有外部服务安全得多。
实测后的结论
实际使用时,把任务拆小会明显提高 Claude Code 的输出质量。先 .dockerignore,再 Compose,再 health check,最后 CI,比一次生成所有内容更容易审查。命名 node_modules volume 和数据库 health gate 解决了最常见的两个新手问题:只在自己电脑能跑,以及 API 启动了但连不上 Postgres。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
从Obsidian到CLAUDE.md的Claude Code流程:不再反复解释上下文
把 Obsidian 工作笔记整理成 CLAUDE.md 运行说明,让 Claude Code 每次都带着正确上下文开始。
Claude Code 收入 CTA 路由:从文章分流到 PDF、Gumroad 与咨询
用 Claude Code 按读者意图把文章流量分到免费 PDF、Gumroad 教材或咨询入口。
Claude Code 团队交接规则: 把审查证据、权限、回滚和收入路径一起交付
面向团队的 Claude Code 交接格式: 证据、权限、回滚、免费 PDF、Gumroad 与咨询路径都要可审查。