Docker 容器化最佳实践:从开发到生产
Docker 彻底改变了我们构建、交付和运行应用程序的方式。但要写出高效的 Dockerfile,让镜像既小又安全,需要遵循一系列最佳实践。
1. 选择正确的基础镜像
基础镜像的选择直接影响最终镜像的大小和安全性。
使用官方镜像
# ✅ 推荐:官方镜像,定期更新
FROM node:20-alpine
# ❌ 避免:来源不明的镜像
FROM some-random-user/node:latest
Alpine vs Slim vs Full
| 镜像类型 | 大小 | 适用场景 |
|---|---|---|
node:20 |
~1GB | 不需要优化 |
node:20-slim |
~250MB | 通用推荐 |
node:20-alpine |
~120MB | 追求极致体积 |
Alpine 使用 musl libc 而非 glibc,某些原生模块可能不兼容。
2. 多阶段构建
多阶段构建是减小镜像体积的最强武器:
# 阶段 1:构建
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# 阶段 2:运行
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
EXPOSE 3000
CMD ["node", "dist/index.js"]
3. 利用构建缓存
Docker 按层缓存,将不常变动的指令放在前面:
# ✅ 正确顺序:依赖安装在前
COPY package*.json ./
RUN npm ci
COPY . .
# ❌ 错误顺序:源码变化导致依赖重新安装
COPY . .
RUN npm ci
.dockerignore 不可忽视
node_modules
.git
dist
.env
*.md
.DS_Store
coverage
4. 以非 root 用户运行
FROM node:20-alpine
# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
USER nodejs
COPY --chown=nodejs:nodejs . /app
WORKDIR /app
CMD ["node", "index.js"]
5. 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
6. 使用 docker-compose 管理多服务应用
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
target: production
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:pass@db:5432/mydb
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--spider", "http://localhost:3000/health"]
interval: 30s
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 10s
volumes:
pgdata:
7. 日志管理
应用日志应输出到 stdout/stderr,由 Docker 或容器编排平台统一处理:
// ✅ 好的做法
console.log(JSON.stringify({
level: 'info',
message: 'User logged in',
userId: 123,
timestamp: new Date().toISOString()
}));
// ❌ 避免
fs.appendFileSync('/var/log/app.log', 'User logged in\n');
8. 安全扫描
# 使用 Docker Scout 扫描漏洞
docker scout quickview myapp:latest
# 使用 Trivy
trivy image myapp:latest
# 使用 Hadolint 检查 Dockerfile
hadolint Dockerfile
总结
高效的 Docker 实践可以总结为:
- 使用官方、轻量级基础镜像
- 多阶段构建减小体积
- 合理安排 COPY 顺序利用缓存
- 以非 root 用户运行
- 添加健康检查
- 将日志输出到 stdout/stderr
- 定期安全扫描
记住,一个好的 Dockerfile 不仅是能构建出镜像,更要让镜像小、安全、可维护。