Skip to content
On this page

Docker 最佳实践

概述

Docker 最佳实践涵盖了从镜像构建到容器部署的整个生命周期。遵循这些实践可以帮助您构建更安全、更高效、更可维护的容器化应用。

镜像构建最佳实践

1. 使用官方基础镜像

始终优先选择官方或可信的基础镜像,它们通常更安全、更优化:

docker
# 推荐:使用官方镜像
FROM node:16-alpine
FROM nginx:alpine
FROM python:3.9-slim

# 避免:使用不明确来源的镜像
FROM someuser/custom-image

2. 选择合适的镜像变体

根据需要选择最合适的镜像变体:

docker
# 使用 Alpine 镜像减小体积
FROM node:16-alpine  # 比标准镜像小约 50%

# 使用 slim 变体
FROM python:3.9-slim

# 避免使用 :latest 标签(除非明确需要)
FROM node:16-alpine  # 好的做法
FROM node:latest     # 避免这样做

3. 多阶段构建

使用多阶段构建减小最终镜像大小:

docker
# 构建阶段
FROM node:16-alpine AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci && npm cache clean --force
COPY . .
RUN npm run build

# 运行阶段
FROM node:16-alpine AS production

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

# 从构建阶段复制构建产物
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/public ./public

# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001 -G nodejs
COPY --chown=nodejs:nodejs . .

USER nodejs
EXPOSE 3000
CMD ["npm", "start"]

4. 优化 Dockerfile 指令顺序

合理安排指令顺序以充分利用构建缓存:

docker
FROM node:16-alpine

WORKDIR /app

# 先复制依赖文件(变化较少)
COPY package*.json ./
RUN npm ci --only=production

# 再复制应用代码(变化较频繁)
COPY . .

EXPOSE 3000
CMD ["npm", "start"]

5. 合并 RUN 指令

减少镜像层数以减小镜像大小:

docker
# 避免这样做(创建多个层)
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim

# 推荐这样做(合并为一个层)
RUN apt-get update && \
    apt-get install -y curl vim && \
    rm -rf /var/lib/apt/lists/*

6. 使用 .dockerignore

排除不必要的文件以减小构建上下文:

# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
*.log
.DS_Store
.vscode

安全最佳实践

1. 使用非 root 用户

始终以非 root 用户运行容器:

docker
FROM node:16-alpine

RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001 -G nodejs

WORKDIR /app
COPY --chown=nodejs:nodejs package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY --chown=nodejs:nodejs . .

USER nodejs
EXPOSE 3000
CMD ["npm", "start"]

2. 最小化镜像内容

只包含运行应用所需的必要文件:

docker
FROM node:16-alpine

WORKDIR /app

# 安装最小必要包
RUN apk add --no-cache \
    dumb-init \
    curl

COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY . .

# 使用 dumb-init 作为 PID 1
ENTRYPOINT ["/usr/bin/dumb-init", "--"]

EXPOSE 3000
CMD ["npm", "start"]

3. 避免在镜像中存储敏感信息

不要在镜像中存储密码、密钥等敏感信息:

docker
# 避免这样做
ENV API_KEY=secret_key

# 推荐:使用构建参数
ARG API_KEY
ENV API_KEY=$API_KEY

# 或使用运行时挂载
CMD ["node", "app.js"]

Docker Compose 最佳实践

1. 环境变量管理

合理管理环境变量:

yaml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "${PORT:-3000}:3000"
    environment:
      - NODE_ENV=${NODE_ENV:-production}
      - DATABASE_URL=${DATABASE_URL}
    env_file:
      - .env
    depends_on:
      - db

  db:
    image: postgres:13
    environment:
      POSTGRES_DB: ${DB_NAME}
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "${DB_PORT:-5432}:5432"

volumes:
  postgres_data:

2. 服务依赖管理

正确配置服务依赖关系:

yaml
version: '3.8'

services:
  web:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DB_HOST=db
      - REDIS_HOST=redis
    depends_on:
      - db
      - redis
    # 使用健康检查确保依赖服务已就绪
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  db:
    image: postgres:13
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3

volumes:
  postgres_data:

3. 资源限制配置

为服务设置适当的资源限制:

yaml
version: '3.8'

services:
  app:
    build: .
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s

存储最佳实践

1. 卷管理

使用命名卷管理持久数据:

yaml
version: '3.8'

services:
  app:
    image: my-app:latest
    volumes:
      - app-logs:/app/logs
      - app-data:/app/data
    tmpfs:
      - /tmp  # 使用 tmpfs 存储临时文件

volumes:
  app-logs:
  app-data:

2. 数据持久化

正确处理数据持久化需求:

yaml
version: '3.8'

services:
  db:
    image: postgres:13
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      # 使用命名卷持久化数据
      - postgres_data:/var/lib/postgresql/data
      # 使用绑定挂载(仅用于配置)
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    ports:
      - "5432:5432"

volumes:
  postgres_data:

网络最佳实践

1. 网络隔离

使用自定义网络实现适当的隔离:

yaml
version: '3.8'

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true  # 内部网络,无法访问外部

services:
  web:
    image: my-web-app:latest
    networks:
      - frontend
    ports:
      - "80:3000"

  app:
    image: my-app:latest
    networks:
      - frontend
      - backend
    environment:
      - DB_HOST=db

  db:
    image: postgres:13
    networks:
      - backend
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

监控和日志最佳实践

1. 健康检查

配置健康检查确保服务状态:

yaml
version: '3.8'

services:
  app:
    image: my-app:latest
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

2. 日志管理

配置适当的日志选项:

yaml
version: '3.8'

services:
  app:
    image: my-app:latest
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

性能优化最佳实践

1. 资源优化

合理配置容器资源:

bash
# 运行容器时设置资源限制
docker run \
  --memory=512m \
  --cpus="1.0" \
  --memory-swap=1g \
  my-app:latest

2. 构建缓存优化

充分利用 Docker 构建缓存:

docker
FROM node:16-alpine

WORKDIR /app

# 依赖文件变化较少,先复制以充分利用缓存
COPY package*.json ./
RUN npm ci --only=production

# 应用代码变化较频繁,后复制
COPY . .

EXPOSE 3000
CMD ["npm", "start"]

安全配置最佳实践

1. 安全选项

使用安全选项运行容器:

yaml
version: '3.8'

services:
  app:
    image: my-app:latest
    read_only: true  # 只读根文件系统
    user: "1001:1001"  # 非 root 用户
    cap_drop:
      - ALL  # 删除所有功能
    cap_add:
      - NET_BIND_SERVICE  # 添加必要功能
    security_opt:
      - no-new-privileges:true
    tmpfs:
      - /tmp

2. 秘钥管理

安全地管理敏感信息:

bash
# 使用 Docker secrets(Swarm 模式)
echo "mypassword" | docker secret create db_password -

# 使用环境变量文件(确保文件权限)
chmod 600 .env

CI/CD 集成最佳实践

1. 构建优化

优化 CI/CD 中的 Docker 构建:

bash
# 使用构建缓存
docker build --cache-from my-app:cache -t my-app:latest .

# 使用 BuildKit 加速构建
export DOCKER_BUILDKIT=1
docker build -t my-app:latest .

# 多平台构建
docker buildx build --platform linux/amd64,linux/arm64 -t my-app:latest --push .

2. 镜像标签策略

使用适当的镜像标签策略:

bash
# 使用版本号
docker build -t my-app:v1.2.3 .

# 使用 Git 提交哈希
docker build -t my-app:$(git rev-parse --short HEAD) .

# 使用分支名
docker build -t my-app:$(git rev-parse --abbrev-ref HEAD) .

故障排除最佳实践

1. 调试容器

调试运行中的容器:

bash
# 进入运行中的容器
docker exec -it container-name sh

# 查看容器日志
docker logs container-name

# 查看容器详细信息
docker inspect container-name

# 查看容器资源使用
docker stats container-name

2. 网络问题排查

排查网络问题:

bash
# 查看网络列表
docker network ls

# 检查网络详情
docker network inspect network-name

# 测试容器间连接
docker run --rm -it --network container-network appropriate/curl http://target-service:port

常见错误避免

1. 避免的实践

docker
# 避免:使用 root 用户
USER root  # 避免这样做

# 避免:在镜像中存储敏感信息
ENV API_KEY=secret  # 避免这样做

# 避免:使用 --privileged 标志
docker run --privileged container  # 避免这样做

# 避免:不设置资源限制
docker run my-app:latest  # 避免不设置资源限制

2. 正确的做法

docker
# 正确:使用非 root 用户
RUN addgroup -g 1001 -S appuser && \
    adduser -S appuser -u 1001 -G appuser
USER appuser

# 正确:使用构建参数处理敏感信息
ARG API_KEY
ENV API_KEY=$API_KEY

# 正确:设置资源限制
docker run -m 512m --cpus="1.0" my-app:latest

检查清单

Dockerfile 检查清单

  • [ ] 使用官方基础镜像
  • [ ] 选择合适的镜像变体(如 Alpine)
  • [ ] 使用非 root 用户
  • [ ] 实施多阶段构建
  • [ ] 合理安排指令顺序以利用缓存
  • [ ] 使用 .dockerignore
  • [ ] 合并 RUN 指令减少层数
  • [ ] 不在镜像中存储敏感信息

运行时检查清单

  • [ ] 设置适当的资源限制
  • [ ] 使用只读文件系统(如果可能)
  • [ ] 删除不必要的功能
  • [ ] 配置健康检查
  • [ ] 实施适当的日志配置
  • [ ] 使用安全选项运行容器

部署检查清单

  • [ ] 使用自定义网络
  • [ ] 实施适当的存储策略
  • [ ] 配置服务依赖关系
  • [ ] 使用环境变量管理配置
  • [ ] 实施监控和日志记录

小结

遵循 Docker 最佳实践可以帮助您构建更安全、更高效、更可维护的容器化应用。这些实践涵盖了从镜像构建到部署的整个生命周期,应该根据具体需求和场景进行适当调整。持续学习和应用新的最佳实践是保持容器化应用高质量的关键。