
Docker 开发工作流
多阶段 Dockerfile
# 阶段 1:依赖
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
# 阶段 2:构建
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# 阶段 3:生产(最小镜像)
FROM node:20-alpine AS production
WORKDIR /app
# 非 root 用户
RUN addgroup -S app && adduser -S -G app app
USER app
COPY --chown=app:app --from=builder /app/dist ./dist
COPY --chown=app:app --from=deps /app/node_modules ./node_modules
COPY --chown=app:app package.json ./
EXPOSE 3000
CMD ["node", "dist/server.js"]
# 阶段 4:开发(包含 devDependencies 和热重载)
FROM node:20-alpine AS development
WORKDIR /app
# 安装所有依赖,包括 devDependencies
COPY package*.json ./
RUN npm install # 不是 npm ci - 允许更新
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]

用于开发的 Docker Compose
# docker-compose.yml
version: '3.9'
services:
app:
build:
context: .
target: development # 使用开发阶段
ports:
- "3000:3000"
- "9229:9229" # 调试端口
volumes:
- .:/app # 绑定挂载用于热重载
- /app/node_modules # 匿名卷(防止主机覆盖)
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://user:pass@postgres:5432/devdb
- REDIS_URL=redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
command: npm run dev
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: devdb
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d devdb"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
adminer:
image: adminer
ports:
- "8080:8080"
depends_on:
- postgres
volumes:
postgres_data:
redis_data:
用于生产的 Docker Compose 覆盖
# docker-compose.prod.yml
version: '3.9'
services:
app:
build:
target: production
restart: unless-stopped
environment:
- NODE_ENV=production
volumes: [] # 生产环境无绑定挂载
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
deploy:
replicas: 3
resources:
limits:
cpus: '0.5'
memory: 512M
# 使用覆盖文件
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

在 Docker 中调试
# 进入运行中的容器
docker exec -it myapp-app-1 sh
# 在容器中调试 Node.js
# 在 docker-compose.yml 中:
# command: node --inspect=0.0.0.0:9229 dist/server.js
# 然后连接 VS Code 调试器到端口 9229
# 查看带时间戳的日志
docker compose logs -f --timestamps app
# 检查容器状态
docker inspect myapp-app-1 | jq '.[0].State'
docker stats --no-stream
# 从容器复制文件
docker cp myapp-app-1:/app/logs ./local-logs
BuildKit 高级功能
# 启用 BuildKit 缓存挂载
# syntax=docker/dockerfile:1
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
# 在构建之间缓存 npm 缓存
RUN --mount=type=cache,target=/root/.npm \
npm ci --prefer-offline
COPY . .
RUN npm run build
# 使用 BuildKit 构建
DOCKER_BUILDKIT=1 docker build .
# 或在 daemon.json 中设置
{
"features": { "buildkit": true }
}
# 构建多平台镜像
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest --push .
# 层检查
docker buildx imagetools inspect myapp:latest
docker history myapp:latest

Docker 网络
# 创建自定义网络
docker network create myapp-network --driver bridge --subnet 172.20.0.0/16
# 连接容器
docker run --network myapp-network --name backend myapp-backend
docker run --network myapp-network --name frontend myapp-frontend
# DNS 解析:容器可以通过名称互相访问
# 从 frontend 容器中 http://backend:3000 可访问
# 主机网络(仅 Linux,用于性能)
docker run --network host myapp
# 端口检查
docker port myapp-app-1
有用的 Docker 命令
# 清理
docker system prune -af --volumes # 移除所有未使用的内容
docker image prune -a # 移除所有未使用的镜像
# 检查构建
docker buildx build --progress=plain . 2>&1 | head -50
# 大小分析
dive myapp:latest # 交互式层浏览器
# 资源使用
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"