Docker最佳实践

一、镜像构建最佳实践

1.1 Dockerfile最佳实践

1.1.1 基础镜像选择

选择合适的基础镜像:

# ❌ 不推荐: 使用大镜像
FROM ubuntu:latest

# ✅ 推荐: 使用Alpine镜像
FROM alpine:3.18

# ✅ 推荐: 使用特定版本
FROM node:18-alpine

# ✅ 推荐: 使用官方镜像
FROM nginx:1.25-alpine

# ✅ 推荐: 使用多阶段构建
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
CMD ["npm", "start"]

基础镜像对比:

基础镜像大小对比:
┌──────────────────┬──────────────┬──────────────┐
│     镜像         │   大小       │   适用场景   │
├──────────────────┼──────────────┼──────────────┤
│  alpine         │ 5MB          │ 最小化镜像   │
│  debian:slim    │ 80MB         │ 需要glibc    │
│  ubuntu         │ 77MB         │ 完整环境     │
│  centos         │ 231MB        │ 企业环境     │
│  busybox        │ 1.2MB        │ 极简环境     │
└──────────────────┴──────────────┴──────────────┘

选择原则:
✅ 优先选择Alpine镜像
✅ 使用特定版本标签
✅ 使用官方维护的镜像
✅ 定期更新基础镜像
✅ 考虑安全性和大小

1.1.2 指令优化

优化Dockerfile指令:

# ❌ 不推荐: 每个RUN指令一层
FROM alpine:3.18
RUN apk add --no-cache nodejs
RUN apk add --no-cache npm
RUN apk add --no-cache git

# ✅ 推荐: 合并RUN指令
FROM alpine:3.18
RUN apk add --no-cache \
    nodejs \
    npm \
    git

# ❌ 不推荐: 在前面复制文件
FROM alpine:3.18
COPY . /app
RUN apk add --no-cache nodejs

# ✅ 推荐: 先安装依赖,后复制文件
FROM alpine:3.18
RUN apk add --no-cache nodejs
COPY . /app

# ❌ 不推荐: 不清理缓存
FROM alpine:3.18
RUN apk add nodejs

# ✅ 推荐: 清理缓存
FROM alpine:3.18
RUN apk add --no-cache nodejs && \
    rm -rf /var/cache/apk/*

# ❌ 不推荐: 使用ADD下载文件
FROM alpine:3.18
ADD https://example.com/file.tar.gz /tmp/

# ✅ 推荐: 使用curl下载并清理
FROM alpine:3.18
RUN apk add --no-cache curl && \
    curl -fsSL https://example.com/file.tar.gz | tar xzf - -C /tmp/ && \
    apk del curl

指令优化原则:

Dockerfile优化原则:
┌──────────────────┬──────────────┬──────────────┐
│     原则         │   说明       │   效果       │
├──────────────────┼──────────────┼──────────────┤
│  合并指令       │ 减少镜像层    │ 减小镜像大小 │
│  顺序优化       │ 变化少的在前  │ 利用缓存     │
│  清理缓存       │ 删除临时文件  │ 减小镜像大小 │
│  使用COPY       │ 不用ADD      │ 明确意图     │
│  多阶段构建     │ 分离构建环境  │ 减小镜像大小 │
└──────────────────┴──────────────┴──────────────┘

指令顺序建议:
1. FROM: 基础镜像
2. LABEL: 元数据标签
3. ENV: 环境变量
4. RUN: 安装依赖
5. COPY: 复制文件
6. EXPOSE: 暴露端口
7. VOLUME: 数据卷
8. CMD/ENTRYPOINT: 启动命令

1.1.3 多阶段构建

使用多阶段构建:

# 构建阶段
FROM golang:1.21-alpine AS builder

WORKDIR /build

# 安装依赖
RUN apk add --no-cache git

# 复制go.mod和go.sum
COPY go.mod go.sum ./
RUN go mod download

# 复制源代码
COPY . .

# 构建应用
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

# 运行阶段
FROM alpine:3.18

WORKDIR /app

# 安装ca证书
RUN apk --no-cache add ca-certificates

# 从构建阶段复制二进制文件
COPY --from=builder /build/main .

# 暴露端口
EXPOSE 8080

# 运行应用
CMD ["./main"]

多阶段构建优势:

多阶段构建优势:
✅ 减小最终镜像大小
✅ 分离构建和运行环境
✅ 提高安全性
✅ 简化构建过程

镜像大小对比:
单阶段构建: 800MB
多阶段构建: 15MB
减少: 98%

1.2 镜像标签管理

1.2.1 标签命名规范

标签命名最佳实践:

# ❌ 不推荐: 使用latest标签
docker build -t myapp:latest .

# ✅ 推荐: 使用具体版本号
docker build -t myapp:1.0.0 .

# ✅ 推荐: 使用多个标签
docker build -t myapp:1.0.0 -t myapp:1.0 -t myapp:latest .

# ✅ 推荐: 包含构建信息
docker build -t myapp:1.0.0-20240101 .

# ✅ 推荐: 包含Git提交信息
docker build -t myapp:1.0.0-$(git rev-parse --short HEAD) .

标签命名规范:

标签命名规范:
┌──────────────────┬──────────────┬──────────────┐
│     标签类型     │   示例       │   说明       │
├──────────────────┼──────────────┼──────────────┤
│  版本号         │ 1.0.0        │ 语义化版本   │
│  主版本号       │ 1            │ 主版本       │
│  次版本号       │ 1.0          │ 次版本       │
│  构建号         │ 1.0.0-123    │ 包含构建号   │
│  Git提交        │ 1.0.0-a1b2c3d│ 包含提交ID   │
│  环境标识       │ 1.0.0-prod   │ 环境标识     │
│  latest         │ latest       │ 最新版本     │
└──────────────────┴──────────────┴──────────────┘

标签使用建议:
✅ 避免在生产环境使用latest
✅ 使用语义化版本号
✅ 记录镜像构建信息
✅ 定期更新基础镜像
✅ 删除旧版本镜像

1.2.2 镜像仓库管理

镜像仓库最佳实践:

# 推送镜像到仓库
docker tag myapp:1.0.0 registry.example.com/myapp:1.0.0
docker push registry.example.com/myapp:1.0.0

# 推送多个标签
docker push registry.example.com/myapp:1.0
docker push registry.example.com/myapp:latest

# 使用镜像签名
docker trust sign registry.example.com/myapp:1.0.0

# 验证镜像签名
docker trust inspect registry.example.com/myapp:1.0.0

# 扫描镜像漏洞
docker scan registry.example.com/myapp:1.0.0

二、容器运行最佳实践

2.1 容器配置最佳实践

2.1.1 资源限制

设置合理的资源限制:

# ✅ 推荐: 设置CPU和内存限制
docker run -d \
  --name myapp \
  --cpus="1.5" \
  --memory="512m" \
  --memory-swap="1g" \
  --memory-reservation="256m" \
  myapp:1.0.0

# ✅ 推荐: 设置IO限制
docker run -d \
  --name myapp \
  --device-read-bps=/dev/sda:10mb \
  --device-write-bps=/dev/sda:10mb \
  myapp:1.0.0

# ✅ 推荐: 使用Docker Compose
# docker-compose.yml
version: '3.8'
services:
  myapp:
    image: myapp:1.0.0
    deploy:
      resources:
        limits:
          cpus: '1.5'
          memory: 512M
        reservations:
          cpus: '0.5'
          memory: 256M

资源限制建议:

资源限制建议:
┌──────────────────┬──────────────┬──────────────┐
│     资源类型     │   建议配置   │   说明       │
├──────────────────┼──────────────┼──────────────┤
│  CPU限制        │ 根据应用需求  │ 避免CPU竞争 │
│  内存限制       │ 应用内存+50%  │ 避免OOM     │
│  Swap限制       │ 内存限制2倍   │ 控制Swap使用│
│  IO限制         │ 根据磁盘性能  │ 避免IO竞争  │
│  进程数限制     │ 根据应用需求  │ 防止fork炸弹│
└──────────────────┴──────────────┴──────────────┘

资源监控:
✅ 监控容器资源使用
✅ 设置资源告警
✅ 定期调整资源限制
✅ 记录资源使用趋势

2.1.2 重启策略

配置合理的重启策略:

# ✅ 推荐: 生产环境使用always
docker run -d \
  --name myapp \
  --restart=always \
  myapp:1.0.0

# ✅ 推荐: 失败时重启
docker run -d \
  --name myapp \
  --restart=on-failure:5 \
  myapp:1.0.0

# ✅ 推荐: 除非手动停止
docker run -d \
  --name myapp \
  --restart=unless-stopped \
  myapp:1.0.0

# ✅ 推荐: 使用Docker Compose
# docker-compose.yml
version: '3.8'
services:
  myapp:
    image: myapp:1.0.0
    restart: unless-stopped

重启策略对比:

重启策略对比:
┌──────────────────┬──────────────┬──────────────┐
│     策略         │   行为       │   适用场景   │
├──────────────────┼──────────────┼──────────────┤
│  no             │ 不重启        │ 开发环境     │
│  on-failure     │ 失败时重启    │ 测试环境     │
│  always         │ 总是重启      │ 生产环境     │
│  unless-stopped │ 除非手动停止  │ 生产环境     │
└──────────────────┴──────────────┴──────────────┘

重启策略建议:
✅ 生产环境使用unless-stopped或always
✅ 设置重启延迟
✅ 配置健康检查
✅ 监控重启次数

2.1.3 健康检查

配置健康检查:

# Dockerfile中配置健康检查
FROM nginx:1.25-alpine

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost/ || exit 1

CMD ["nginx", "-g", "daemon off;"]
# 运行时配置健康检查
docker run -d \
  --name myapp \
  --health-cmd="curl -f http://localhost/ || exit 1" \
  --health-interval=30s \
  --health-timeout=3s \
  --health-start-period=5s \
  --health-retries=3 \
  myapp:1.0.0

# 查看健康状态
docker inspect --format='{{.State.Health.Status}}' myapp

# 查看健康检查历史
docker inspect --format='{{json .State.Health}}' myapp | jq

健康检查建议:

健康检查配置建议:
┌──────────────────┬──────────────┬──────────────┐
│     参数         │   建议值     │   说明       │
├──────────────────┼──────────────┼──────────────┤
│  interval       │ 30s          │ 检查间隔     │
│  timeout        │ 3s           │ 超时时间     │
│  start-period   │ 5-60s        │ 启动时间     │
│  retries        │ 3            │ 重试次数     │
└──────────────────┴──────────────┴──────────────┘

健康检查建议:
✅ 检查应用核心功能
✅ 设置合理的超时时间
✅ 考虑应用启动时间
✅ 监控健康检查结果

2.2 日志管理最佳实践

2.2.1 日志配置

配置日志驱动:

# ✅ 推荐: 使用json-file驱动并限制大小
docker run -d \
  --name myapp \
  --log-driver json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  myapp:1.0.0

# ✅ 推荐: 使用syslog驱动
docker run -d \
  --name myapp \
  --log-driver syslog \
  --log-opt syslog-address=tcp://192.168.1.100:514 \
  myapp:1.0.0

# ✅ 推荐: 使用fluentd驱动
docker run -d \
  --name myapp \
  --log-driver fluentd \
  --log-opt fluentd-address=localhost:24224 \
  --log-opt tag="docker.{{.Name}}" \
  myapp:1.0.0

# ✅ 推荐: 全局配置
# /etc/docker/daemon.json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

日志配置建议:

日志配置建议:
┌──────────────────┬──────────────┬──────────────┐
│     配置项       │   建议值     │   说明       │
├──────────────────┼──────────────┼──────────────┤
│  max-size       │ 10m          │ 单文件大小   │
│  max-file       │ 3            │ 文件数量     │
│  日志格式       │ JSON         │ 结构化日志   │
│  日志级别       │ INFO         │ 生产环境     │
└──────────────────┴──────────────┴──────────────┘

日志管理建议:
✅ 限制日志文件大小
✅ 使用结构化日志
✅ 集中收集日志
✅ 定期归档日志
✅ 监控日志异常

2.2.2 日志最佳实践

应用日志最佳实践:

# Python应用日志示例
import logging
import json
import sys

class JSONFormatter(logging.Formatter):
    def format(self, record):
        log_entry = {
            'timestamp': self.formatTime(record),
            'level': record.levelname,
            'message': record.getMessage(),
            'logger': record.name,
            'module': record.module,
            'line': record.lineno
        }
        if record.exc_info:
            log_entry['exception'] = self.formatException(record.exc_info)
        return json.dumps(log_entry)

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    stream=sys.stdout,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

logger = logging.getLogger(__name__)

# 使用日志
logger.info("Application started", extra={'version': '1.0.0'})
logger.error("Failed to connect database", exc_info=True)

三、安全最佳实践

3.1 镜像安全

3.1.1 镜像安全扫描

使用镜像扫描工具:

# 使用Docker Scan扫描镜像
docker scan myapp:1.0.0

# 输出
✗ Low severity vulnerability found in openssl/libssl1.1
  In openssl: CVE-2023-XXXX
  More info: https://avd.aquasec.com/nvd/cve-2023-xxxx

✗ Medium severity vulnerability found in curl/libcurl4
  In curl: CVE-2023-YYYY
  More info: https://avd.aquasec.com/nvd/cve-2023-yyyy

# 使用Trivy扫描镜像
trivy image myapp:1.0.0

# 使用Clair扫描镜像
clair-scanner myapp:1.0.0

# 使用Anchore扫描镜像
anchore-cli image add myapp:1.0.0
anchore-cli image vuln myapp:1.0.0 all

镜像安全建议:

镜像安全建议:
✅ 使用可信的基础镜像
✅ 定期扫描镜像漏洞
✅ 及时修复漏洞
✅ 使用镜像签名
✅ 最小化镜像内容
✅ 不在镜像中存储敏感信息
✅ 使用多阶段构建
✅ 定期更新基础镜像

3.1.2 最小权限原则

使用非root用户运行:

# ✅ 推荐: 使用非root用户
FROM alpine:3.18

# 创建用户和组
RUN addgroup -g 1000 appgroup && \
    adduser -u 1000 -G appgroup -D appuser

# 设置工作目录
WORKDIR /app

# 复制文件并设置权限
COPY --chown=appuser:appgroup . .

# 切换用户
USER appuser

# 启动应用
CMD ["./app"]
# 运行时指定用户
docker run -d \
  --name myapp \
  --user 1000:1000 \
  myapp:1.0.0

# 只读文件系统
docker run -d \
  --name myapp \
  --read-only \
  --tmpfs /tmp \
  myapp:1.0.0

权限控制建议:

权限控制建议:
┌──────────────────┬──────────────┬──────────────┐
│     措施         │   说明       │   效果       │
├──────────────────┼──────────────┼──────────────┤
│  非root用户     │ 使用普通用户  │ 减少权限     │
│  只读文件系统   │ 只读挂载      │ 防止篡改     │
│  删除能力       │ 删除不必要能力│ 最小权限     │
│  禁用特权       │ 不使用特权模式│ 提高安全     │
│  只读根文件系统 │ --read-only   │ 防止修改     │
└──────────────────┴──────────────┴──────────────┘

能力控制:
# 删除所有能力
docker run -d --name myapp --cap-drop=ALL myapp:1.0.0

# 只添加需要的能力
docker run -d --name myapp --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp:1.0.0

3.2 网络安全

3.2.1 网络隔离

网络隔离最佳实践:

# ✅ 推荐: 使用自定义网络
docker network create --driver bridge mynetwork

# ✅ 推荐: 限制容器间通信
docker network create --driver bridge --internal internal-network

# ✅ 推荐: 使用网络策略
# 在Kubernetes中使用NetworkPolicy

# ✅ 推荐: 不暴露不必要的端口
docker run -d \
  --name myapp \
  --network mynetwork \
  myapp:1.0.0

# ✅ 推荐: 使用反向代理
docker run -d \
  --name nginx \
  -p 80:80 \
  --network mynetwork \
  nginx:latest

网络安全建议:

网络安全建议:
✅ 使用自定义网络
✅ 限制容器间通信
✅ 不暴露不必要的端口
✅ 使用反向代理
✅ 配置防火墙规则
✅ 使用TLS加密通信
✅ 定期审计网络配置

3.2.2 敏感信息管理

管理敏感信息:

# ❌ 不推荐: 使用环境变量传递敏感信息
docker run -d \
  --name myapp \
  -e DB_PASSWORD=secret123 \
  myapp:1.0.0

# ✅ 推荐: 使用Docker Secrets
echo "secret123" | docker secret create db_password -

docker service create \
  --name myapp \
  --secret db_password \
  myapp:1.0.0

# ✅ 推荐: 使用文件挂载
docker run -d \
  --name myapp \
  -v /host/secrets:/run/secrets:ro \
  myapp:1.0.0

# ✅ 推荐: 使用Kubernetes Secrets
kubectl create secret generic db-password --from-literal=password=secret123

敏感信息管理建议:

敏感信息管理建议:
✅ 不在镜像中存储敏感信息
✅ 不使用环境变量传递敏感信息
✅ 使用Secrets管理敏感信息
✅ 加密存储敏感信息
✅ 定期轮换密钥
✅ 审计敏感信息访问

四、性能优化最佳实践

4.1 镜像优化

4.1.1 减小镜像大小

镜像优化技巧:

# ✅ 推荐: 使用Alpine基础镜像
FROM alpine:3.18

# ✅ 推荐: 合并RUN指令
RUN apk add --no-cache \
    nodejs \
    npm && \
    rm -rf /var/cache/apk/*

# ✅ 推荐: 使用多阶段构建
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
CMD ["npm", "start"]

# ✅ 推荐: 使用.dockerignore
# .dockerignore
node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
README.md
.env

镜像优化建议:

镜像优化建议:
┌──────────────────┬──────────────┬──────────────┐
│     优化方法     │   效果       │   说明       │
├──────────────────┼──────────────┼──────────────┤
│  使用Alpine     │ 减小80%      │ 最小化基础   │
│  多阶段构建     │ 减小50-90%   │ 分离构建环境 │
│  合并指令       │ 减小10-20%   │ 减少镜像层   │
│  清理缓存       │ 减小5-10%    │ 删除临时文件 │
│  .dockerignore  │ 减小10-30%   │ 排除无用文件 │
└──────────────────┴──────────────┴──────────────┘

镜像大小对比:
Ubuntu基础镜像: 77MB
Alpine基础镜像: 5MB
减少: 93%

4.1.2 构建缓存优化

优化构建缓存:

# ❌ 不推荐: 频繁变化的文件在前
FROM alpine:3.18
COPY . /app
RUN apk add --no-cache nodejs

# ✅ 推荐: 变化少的在前
FROM alpine:3.18
RUN apk add --no-cache nodejs
COPY package*.json ./
RUN npm install
COPY . .

# ✅ 推荐: 使用BuildKit
# DOCKER_BUILDKIT=1 docker build -t myapp:1.0.0 .

# ✅ 推荐: 使用缓存挂载
# syntax=docker/dockerfile:1
FROM alpine:3.18
RUN --mount=type=cache,target=/var/cache/apk \
    apk add --no-cache nodejs

4.2 运行时优化

4.2.1 资源优化

资源优化配置:

# ✅ 推荐: 设置资源限制
docker run -d \
  --name myapp \
  --cpus="1.5" \
  --memory="512m" \
  --memory-swap="1g" \
  --memory-swappiness=0 \
  --oom-kill-disable=false \
  myapp:1.0.0

# ✅ 推荐: 使用CPU亲和性
docker run -d \
  --name myapp \
  --cpuset-cpus="0,1" \
  myapp:1.0.0

# ✅ 推荐: 使用IO限制
docker run -d \
  --name myapp \
  --device-read-bps=/dev/sda:10mb \
  --device-write-bps=/dev/sda:10mb \
  myapp:1.0.0

# ✅ 推荐: 使用tmpfs
docker run -d \
  --name myapp \
  --mount type=tmpfs,destination=/tmp,tmpfs-size=100m \
  myapp:1.0.0

4.2.2 网络优化

网络优化配置:

# ✅ 推荐: 使用host网络模式(高性能)
docker run -d \
  --name myapp \
  --network host \
  myapp:1.0.0

# ✅ 推荐: 优化MTU
docker network create \
  --driver bridge \
  --opt com.docker.network.driver.mtu=1500 \
  mynetwork

# ✅ 推荐: 优化DNS
docker run -d \
  --name myapp \
  --dns 8.8.8.8 \
  --dns 8.8.4.4 \
  --dns-opt ndots:1 \
  myapp:1.0.0

# ✅ 推荐: 优化TCP参数
docker run -d \
  --name myapp \
  --sysctl net.core.somaxconn=65535 \
  --sysctl net.ipv4.tcp_max_syn_backlog=65535 \
  myapp:1.0.0

五、本章小结

5.1 核心概念总结

5.1.1 最佳实践要点

镜像构建最佳实践:
├─ 使用Alpine基础镜像
├─ 合并RUN指令
├─ 使用多阶段构建
├─ 优化构建缓存
├─ 使用.dockerignore
└─ 清理临时文件

容器运行最佳实践:
├─ 设置资源限制
├─ 配置重启策略
├─ 配置健康检查
├─ 管理日志
└─ 使用非root用户

安全最佳实践:
├─ 镜像安全扫描
├─ 使用非root用户
├─ 最小权限原则
├─ 网络隔离
└─ 敏感信息管理

性能优化最佳实践:
├─ 减小镜像大小
├─ 优化构建缓存
├─ 资源优化配置
└─ 网络优化配置

5.1.2 最佳实践检查清单

镜像构建检查清单:
✅ 使用Alpine或最小化基础镜像
✅ 使用特定版本标签
✅ 合并RUN指令
✅ 清理缓存和临时文件
✅ 使用多阶段构建
✅ 配置.dockerignore
✅ 设置非root用户
✅ 扫描镜像漏洞

容器运行检查清单:
✅ 设置CPU和内存限制
✅ 配置重启策略
✅ 配置健康检查
✅ 限制日志大小
✅ 使用自定义网络
✅ 不暴露不必要的端口
✅ 使用只读文件系统
✅ 删除不必要的能力

5.2 下一章预告

下一章: Docker CI/CD

将学习:

  • 🔄 CI/CD概念
  • 🛠️ Jenkins集成
  • 📦 GitLab CI/CD
  • 🚀 自动化部署流程

📝 练习题

基础题

  1. 镜像优化: 优化以下Dockerfile,减小镜像大小:
FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -y nodejs
RUN apt-get install -y npm
COPY . /app
CMD ["npm", "start"]
  1. 安全配置: 为一个Nginx容器配置安全最佳实践。

  2. 资源限制: 为一个应用容器设置合理的资源限制。

进阶题

  1. 多阶段构建: 使用多阶段构建优化一个Go应用的镜像。

  2. 健康检查: 为一个Web应用配置健康检查。

  3. 日志管理: 配置容器的日志管理策略。

实践题

  1. 完整最佳实践: 对一个完整的Docker应用应用最佳实践,要求:

    • 镜像优化
    • 安全配置
    • 资源限制
    • 健康检查
    • 日志管理
  2. 最佳实践文档: 编写一份Docker最佳实践文档,包含:

    • 镜像构建规范
    • 容器运行规范
    • 安全规范
    • 性能优化规范