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