发布于

Docker 容器化实践:从入门到生产部署

作者

Docker 容器化实践:从入门到生产部署

Docker 已经成为现代应用部署的标准工具。本文将深入探讨 Docker 的核心概念、最佳实践和生产环境部署策略。

Docker 基础概念

什么是 Docker?

Docker 是一个开源的容器化平台,它允许开发者将应用程序及其依赖项打包到轻量级、可移植的容器中。

# 基础 Dockerfile 示例
FROM node:16-alpine

# 设置工作目录
WORKDIR /app

# 复制 package.json 和 package-lock.json
COPY package*.json ./

# 安装依赖
RUN npm ci --only=production

# 复制应用代码
COPY . .

# 暴露端口
EXPOSE 3000

# 启动应用
CMD ["npm", "start"]

核心概念解释

# 镜像(Image)- 只读模板
docker images

# 容器(Container)- 镜像的运行实例
docker ps

# Dockerfile - 构建镜像的指令文件
docker build -t myapp:latest .

# 仓库(Registry)- 存储镜像的地方
docker push myapp:latest
docker pull nginx:alpine

Dockerfile 最佳实践

1. 多阶段构建

# 多阶段构建 - 前端应用
FROM node:16-alpine AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

# 生产阶段
FROM nginx:alpine

# 复制构建产物
COPY --from=builder /app/dist /usr/share/nginx/html

# 复制 nginx 配置
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

2. 优化镜像大小

# Node.js 应用优化
FROM node:16-alpine

# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

WORKDIR /app

# 只复制必要的文件
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

# 复制应用代码
COPY --chown=nextjs:nodejs . .

# 切换到非 root 用户
USER nextjs

EXPOSE 3000

# 使用 exec 形式的 CMD
CMD ["node", "server.js"]

3. 缓存优化

# 利用 Docker 缓存层
FROM python:3.9-slim

WORKDIR /app

# 先复制依赖文件(变化较少)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

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

# 设置环境变量
ENV PYTHONPATH=/app
ENV FLASK_APP=app.py

EXPOSE 5000

CMD ["flask", "run", "--host=0.0.0.0"]

Docker Compose 编排

1. 基础服务编排

# docker-compose.yml
version: '3.8'

services:
  # Web 应用
  web:
    build: .
    ports:
      - '3000:3000'
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://user:password@db:5432/myapp
    depends_on:
      - db
      - redis
    volumes:
      - ./uploads:/app/uploads
    restart: unless-stopped

  # 数据库
  db:
    image: postgres:13-alpine
    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
    restart: unless-stopped

  # Redis 缓存
  redis:
    image: redis:6-alpine
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data
    restart: unless-stopped

  # Nginx 反向代理
  nginx:
    image: nginx:alpine
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - web
    restart: unless-stopped

volumes:
  postgres_data:
  redis_data:

2. 开发环境配置

# docker-compose.dev.yml
version: '3.8'

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - '3000:3000'
    environment:
      - NODE_ENV=development
    volumes:
      - .:/app
      - /app/node_modules
    command: npm run dev

  db:
    ports:
      - '5432:5432'
    environment:
      POSTGRES_DB: myapp_dev

  # 开发工具
  adminer:
    image: adminer
    ports:
      - '8080:8080'
    depends_on:
      - db

3. 生产环境配置

# docker-compose.prod.yml
version: '3.8'

services:
  web:
    image: myapp:latest
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
    environment:
      - NODE_ENV=production
    secrets:
      - db_password
      - jwt_secret

  db:
    image: postgres:13-alpine
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == manager
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password

secrets:
  db_password:
    external: true
  jwt_secret:
    external: true

容器网络和存储

1. 网络配置

# 自定义网络
version: '3.8'

services:
  web:
    networks:
      - frontend
      - backend

  db:
    networks:
      - backend

  nginx:
    networks:
      - frontend

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true # 内部网络,不能访问外网
# Docker 网络命令
docker network create --driver bridge mynetwork
docker network ls
docker network inspect mynetwork

# 连接容器到网络
docker network connect mynetwork mycontainer

2. 数据持久化

# 数据卷配置
version: '3.8'

services:
  db:
    image: postgres:13
    volumes:
      # 命名卷
      - postgres_data:/var/lib/postgresql/data
      # 绑定挂载
      - ./backups:/backups
      # 临时文件系统
      - type: tmpfs
        target: /tmp
        tmpfs:
          size: 100M

volumes:
  postgres_data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /data/postgres

监控和日志

1. 健康检查

# Dockerfile 中的健康检查
FROM node:16-alpine

WORKDIR /app
COPY . .
RUN npm install

EXPOSE 3000

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

CMD ["npm", "start"]
# docker-compose.yml 中的健康检查
version: '3.8'

services:
  web:
    build: .
    healthcheck:
      test: ['CMD', 'curl', '-f', 'http://localhost:3000/health']
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

2. 日志管理

# 日志配置
version: '3.8'

services:
  web:
    image: myapp:latest
    logging:
      driver: 'json-file'
      options:
        max-size: '10m'
        max-file: '3'

  # 使用 ELK 栈
  elasticsearch:
    image: elasticsearch:7.14.0
    environment:
      - discovery.type=single-node
    volumes:
      - elasticsearch_data:/usr/share/elasticsearch/data

  logstash:
    image: logstash:7.14.0
    volumes:
      - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf

  kibana:
    image: kibana:7.14.0
    ports:
      - '5601:5601'
    depends_on:
      - elasticsearch

volumes:
  elasticsearch_data:

3. 监控配置

# Prometheus + Grafana 监控
version: '3.8'

services:
  prometheus:
    image: prom/prometheus
    ports:
      - '9090:9090'
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus

  grafana:
    image: grafana/grafana
    ports:
      - '3001:3000'
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana_data:/var/lib/grafana

  node-exporter:
    image: prom/node-exporter
    ports:
      - '9100:9100'

volumes:
  prometheus_data:
  grafana_data:

安全最佳实践

1. 镜像安全

# 安全的 Dockerfile
FROM node:16-alpine

# 更新系统包
RUN apk update && apk upgrade

# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nextjs -u 1001 -G nodejs

WORKDIR /app

# 设置正确的文件权限
COPY --chown=nextjs:nodejs package*.json ./
RUN npm ci --only=production

COPY --chown=nextjs:nodejs . .

# 切换到非 root 用户
USER nextjs

# 只暴露必要的端口
EXPOSE 3000

CMD ["node", "server.js"]

2. 运行时安全

# 安全的 docker-compose 配置
version: '3.8'

services:
  web:
    image: myapp:latest
    # 只读根文件系统
    read_only: true
    # 临时文件系统
    tmpfs:
      - /tmp
      - /var/run
    # 安全选项
    security_opt:
      - no-new-privileges:true
    # 资源限制
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
    # 用户命名空间
    user: '1001:1001'

3. 密钥管理

# Docker Secrets
echo "mysecretpassword" | docker secret create db_password -

# 在 swarm 模式下使用
docker service create \
  --name myapp \
  --secret db_password \
  myapp:latest

生产部署策略

1. Docker Swarm 部署

# 初始化 Swarm
docker swarm init

# 部署服务栈
docker stack deploy -c docker-compose.prod.yml myapp

# 扩展服务
docker service scale myapp_web=5

# 滚动更新
docker service update --image myapp:v2 myapp_web

2. CI/CD 集成

# GitHub Actions 示例
name: Build and Deploy

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2

      - name: Build Docker image
        run: |
          docker build -t myapp:${{ github.sha }} .
          docker tag myapp:${{ github.sha }} myapp:latest

      - name: Push to registry
        run: |
          echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
          docker push myapp:${{ github.sha }}
          docker push myapp:latest

      - name: Deploy to production
        run: |
          docker stack deploy -c docker-compose.prod.yml myapp

3. 蓝绿部署

#!/bin/bash
# 蓝绿部署脚本

CURRENT_COLOR=$(docker service ls --filter name=myapp --format "{{.Name}}" | grep -o "blue\|green")
NEW_COLOR="blue"

if [ "$CURRENT_COLOR" = "blue" ]; then
    NEW_COLOR="green"
fi

echo "当前环境: $CURRENT_COLOR"
echo "部署到: $NEW_COLOR"

# 部署新版本
docker service create \
  --name myapp-$NEW_COLOR \
  --replicas 3 \
  --network myapp-network \
  myapp:latest

# 等待服务就绪
sleep 30

# 健康检查
if curl -f http://myapp-$NEW_COLOR:3000/health; then
    echo "新版本健康检查通过"

    # 切换流量
    docker service update --label-add version=$NEW_COLOR nginx

    # 移除旧版本
    docker service rm myapp-$CURRENT_COLOR

    echo "部署完成"
else
    echo "健康检查失败,回滚"
    docker service rm myapp-$NEW_COLOR
    exit 1
fi

性能优化

1. 镜像优化

# 优化的多阶段构建
FROM node:16-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:16-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:16-alpine AS runner
WORKDIR /app

ENV NODE_ENV production

RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

COPY --from=deps --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nextjs:nodejs /app/dist ./dist

USER nextjs

EXPOSE 3000

CMD ["node", "dist/server.js"]

2. 资源限制

# 资源限制和预留
version: '3.8'

services:
  web:
    image: myapp:latest
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 512M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s

故障排查

1. 常用调试命令

# 查看容器日志
docker logs -f container_name

# 进入容器
docker exec -it container_name /bin/sh

# 查看容器资源使用
docker stats

# 检查容器详细信息
docker inspect container_name

# 查看容器进程
docker top container_name

2. 性能分析

# 容器性能监控
docker run --rm -it \
  --pid container:myapp \
  --cap-add SYS_PTRACE \
  nicolaka/netshoot

# 网络调试
docker run --rm -it \
  --net container:myapp \
  nicolaka/netshoot

总结

Docker 容器化为现代应用部署提供了强大的能力:

  1. 环境一致性:开发、测试、生产环境完全一致
  2. 快速部署:秒级启动和扩展
  3. 资源隔离:安全的多租户环境
  4. 微服务架构:完美支持微服务部署
  5. DevOps 集成:与 CI/CD 流水线无缝集成

掌握 Docker,你就能构建出高效、可靠的现代应用部署方案!


Docker 是现代应用部署的基础设施,值得深入学习和实践。