Docker: 综合实战

最后更新:2026-08-26

这是本课程的毕业项目——把 23 课的知识串起来,从零部署一个全栈微服务到生产环境。

1. 你将学到


2. 一个 3 天交付的故事

(1) 痛点:3 天内从本地到生产

Charlie 接到一个任务:在 3 天内将电商平台从本地开发部署到生产环境。架构包括 React frontend、Go API、PostgreSQL、Redis、RabbitMQ——5 个服务,0 个自动化流程。

(2) Docker 全栈工具链的解法

Charlie 用 Docker 全栈工具链,2 天完成交付:Dockerfile 编写 → Compose 编排 → CI/CD 自动部署 → 监控告警。

(3) 收益:2 天完成交付

从手动部署到全自动化,2 天内完成了架构设计、容器化、编排、CI/CD、监控——这就是掌握 Docker 全栈技能的价值。


3. 全栈架构设计

(1) 架构总览

100%
graph TB
    USER["Browser"] --> ING["Nginx<br/>:80<br/>React SPA + API Proxy"]
    ING -->|"api/"| API1["Go API #1<br/>:8080"]
    ING -->|"api/"| API2["Go API #2<br/>:8080"]
    ING -->|"api/"| API3["Go API #3<br/>:8080"]
    API1 --> PG["PostgreSQL<br/>:5432<br/>Primary DB"]
    API2 --> PG
    API3 --> PG
    API1 --> REDIS["Redis<br/>:6379<br/>Cache"]
    API2 --> REDIS
    API3 --> MQ["RabbitMQ<br/>:5672<br/>Message Queue"]
    MQ --> WRK["Worker<br/>Background Jobs"]
    PROM["Prometheus<br/>:9090<br/>Metrics"] --> GRAF["Grafana<br/>:3000<br/>Dashboards"]
    PROM --> API1
    PROM --> PG
    PROM --> REDIS

(2) 服务清单

服务 技术栈 镜像 端口
Nginx 反向代理 + 静态文件 自建(多阶段) 80
Go API REST API 自建(多阶段) 8080
PostgreSQL 关系数据库 postgres:15-alpine 5432
Redis 缓存 + 会话 redis:7-alpine 6379
RabbitMQ 消息队列 rabbitmq:3-management 5672/15672
Worker 后台任务处理 自建(同 API 镜像) -
Prometheus 指标采集 prom/prometheus 9090
Grafana 可视化面板 grafana/grafana 3000

4. 前端 Dockerfile(React + Nginx)

▶ 示例:前端 Dockerfile 多阶段构建(难度⭐⭐⭐)

DOCKERFILE
# ============================================
# Stage 1: Build React application
# ============================================
FROM node:20-alpine AS builder

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

# ============================================
# Stage 2: Serve with Nginx
# ============================================
FROM nginx:1.25-alpine

# Copy built assets
COPY --from=builder /app/dist /usr/share/nginx/html

# Copy Nginx config for SPA routing
COPY nginx.conf /etc/nginx/conf.d/default.conf

# Security: non-root user
RUN chown -R nginx:nginx /usr/share/nginx/html && \
    chown -R nginx:nginx /var/cache/nginx && \
    chown -R nginx:nginx /var/log/nginx

EXPOSE 80
HEALTHCHECK --interval=30s --timeout=5s \
  CMD wget -qO- http://localhost/ || exit 1

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

5. 后端 Dockerfile(Go 多阶段)

▶ 示例:后端 Dockerfile 多阶段构建(难度⭐⭐⭐)

DOCKERFILE
# ============================================
# Stage 1: Build Go binary
# ============================================
FROM golang:1.22-alpine AS builder

RUN apk add --no-cache git

WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o /app/server

# ============================================
# Stage 2: Minimal runtime
# ============================================
FROM alpine:3.19

RUN apk --no-cache add ca-certificates tzdata curl && \
    adduser -D -u 1000 appuser

WORKDIR /app
COPY --from=builder /app/server .

RUN chown -R appuser:appuser /app
USER appuser

EXPOSE 8080

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

CMD ["/app/server"]

6. Docker Compose 编排

▶ 示例:完整 docker-compose.prod.yml(难度⭐⭐⭐)

YAML
# ============================================
# docker-compose.prod.yml - Full stack
# ============================================
services:
  nginx:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - "80:80"
    depends_on:
      api:
        condition: service_healthy
    restart: unless-stopped
    networks:
      - frontend
      - backend
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "5"

  api:
    build:
      context: ./api
      dockerfile: Dockerfile
    environment:
      DATABASE_URL: postgresql://appuser:${DB_PASSWORD}@postgres:5432/${DB_NAME}
      REDIS_URL: redis://redis:6379
      RABBITMQ_URL: amqp://guest:${MQ_PASSWORD}@rabbitmq:5672
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    restart: unless-stopped
    networks:
      - backend
      - db-net
      - cache-net
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 15s
      timeout: 5s
      retries: 3
      start_period: 10s
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "5"

  worker:
    build:
      context: ./api
      dockerfile: Dockerfile
    command: ["/app/server", "worker"]
    environment:
      DATABASE_URL: postgresql://appuser:${DB_PASSWORD}@postgres:5432/${DB_NAME}
      RABBITMQ_URL: amqp://guest:${MQ_PASSWORD}@rabbitmq:5672
    depends_on:
      postgres:
        condition: service_healthy
      rabbitmq:
        condition: service_healthy
    restart: unless-stopped
    networks:
      - backend
      - db-net

  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: ${DB_NAME}
    volumes:
      - pg-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U appuser"]
      interval: 5s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    networks:
      - db-net

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
    restart: unless-stopped
    networks:
      - cache-net

  rabbitmq:
    image: rabbitmq:3-management-alpine
    environment:
      RABBITMQ_DEFAULT_PASS: ${MQ_PASSWORD}
    ports:
      - "15672:15672"
    volumes:
      - mq-data:/var/lib/rabbitmq
    healthcheck:
      test: ["CMD", "rabbitmq-diagnostics", "check_port_connectivity"]
      interval: 15s
      timeout: 10s
    restart: unless-stopped
    networks:
      - backend

  prometheus:
    image: prom/prometheus:latest
    volumes:
      - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus-data:/prometheus
    ports:
      - "9090:9090"
    restart: unless-stopped
    networks:
      - backend
      - db-net
      - cache-net

  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    environment:
      GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD}
    volumes:
      - grafana-data:/var/lib/grafana
    depends_on:
      - prometheus
    restart: unless-stopped
    networks:
      - backend

volumes:
  pg-data:
  redis-data:
  mq-data:
  prometheus-data:
  grafana-data:

networks:
  frontend:
  backend:
  db-net:
    internal: true
  cache-net:
    internal: true

7. CI/CD 配置

(1) GitHub Actions 全流程

YAML
# .github/workflows/deploy.yml
name: Build and Deploy

on:
  push:
    branches: [main]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: docker/setup-buildx-action@v3

      - uses: docker/login-action@v3
        with:
          registry: ${{ secrets.REGISTRY }}
          username: ${{ secrets.REGISTRY_USER }}
          password: ${{ secrets.REGISTRY_PASS }}

      - name: Build and push API
        uses: docker/build-push-action@v5
        with:
          context: ./api
          push: true
          tags: ${{ secrets.REGISTRY }}/api:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Build and push Frontend
        uses: docker/build-push-action@v5
        with:
          context: ./frontend
          push: true
          tags: ${{ secrets.REGISTRY }}/frontend:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Deploy to production
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            cd /opt/ecommerce
            export API_TAG=${{ github.sha }}
            export FRONTEND_TAG=${{ github.sha }}
            docker compose -f docker-compose.prod.yml pull
            docker compose -f docker-compose.prod.yml up -d --remove-orphans
            docker image prune -f

8. 监控配置

(1) Prometheus 配置

YAML
# monitoring/prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'api'
    static_configs:
      - targets: ['api:8080']
    metrics_path: /metrics

  - job_name: 'postgres'
    static_configs:
      - targets: ['postgres-exporter:9187']

  - job_name: 'redis'
    static_configs:
      - targets: ['redis-exporter:9121']

  - job_name: 'rabbitmq'
    static_configs:
      - targets: ['rabbitmq:15692']

9. 部署与验证

(1) 部署清单

步骤 操作 验证
1 docker compose -f docker-compose.prod.yml up -d --build docker compose ps 全部 Up
2 访问 http://localhost React 页面正常显示
3 curl http://localhost/api/health {"status":"ok"}
4 访问 http://localhost:3000 Grafana 登录页
5 访问 http://localhost:15672 RabbitMQ 管理页
6 docker compose logs api API 日志无错误

(2) 对比表:裸机 vs Docker vs K8s

维度 裸机部署 Docker Compose Kubernetes
部署时间 1-2 天 30 分钟 1-2 天(初始搭建)
可重复性
自动扩缩 ✅ HPA
自愈 restart 策略 ✅ 自动重建
零停机更新 困难 Compose 重启 ✅ 滚动更新
监控 手动 Prometheus+Grafana 内置 + Prometheus
复杂度

10. 完整示例:一键部署全栈

BASH
# ============================================
# Complete walkthrough: Full-stack deployment
# ============================================

# 1. Create .env file (NEVER commit this)
cat > .env << 'EOF'
DB_PASSWORD=secure_db_pass_2024
DB_NAME=ecommerce
REDIS_PASSWORD=secure_redis_pass
MQ_PASSWORD=secure_mq_pass
GRAFANA_PASSWORD=admin123
EOF

# 2. Build and start all services
docker compose -f docker-compose.prod.yml up -d --build

# 3. Wait for services to initialize
sleep 30

# 4. Verify all services
docker compose -f docker-compose.prod.yml ps
echo "=== Health Checks ==="
docker compose -f docker-compose.prod.yml ps --format "table {{.Name}}\t{{.Status}}"

# 5. Test the API
curl -s http://localhost/api/health

# 6. Check database connectivity
docker compose -f docker-compose.prod.yml exec postgres pg_isready -U appuser

# 7. Access monitoring
echo "Grafana: http://localhost:3000 (admin/${GRAFANA_PASSWORD})"
echo "RabbitMQ: http://localhost:15672 (guest/${MQ_PASSWORD})"
echo "Prometheus: http://localhost:9090"

# 8. View aggregated logs
docker compose -f docker-compose.prod.yml logs --tail 50 api

# 9. Scale API if needed
docker compose -f docker-compose.prod.yml up -d --scale api=5

# 10. Clean up
docker compose -f docker-compose.prod.yml down

❓ 常见问题

Q 微服务比单体复杂多少?
A 运维复杂度显著增加——网络配置、服务发现、日志聚合、分布式追踪都是新挑战。但收益是:独立部署、独立扩缩、故障隔离。建议:小团队(<5 人)用模块化单体,大团队用微服务。不要为了微服务而微服务。
Q CI/CD 中怎么做数据库迁移?
A 在部署步骤前添加迁移步骤:① 构建迁移镜像;② docker run --rm migrate:latest alembic upgrade head;③ 部署新版本。关键:迁移必须向后兼容——新代码必须同时支持旧表和新表结构。
Q 多服务如何统一日志?
A 三种方案:① ELK Stack(Elasticsearch + Logstash + Kibana)——企业标准;② Loki + Grafana(轻量,云原生推荐);③ 云服务商日志服务(AWS CloudWatch / 阿里云 SLS)。所有容器 stdout → 采集器 → 集中存储 → 搜索面板。
Q 生产环境怎么配置 HTTPS?
A 在 Nginx 容器中配置 TLS:① Let's Encrypt 免费证书 + certbot 自动续期;② 反向代理模式:Nginx 处理 TLS,内部通信 HTTP;③ 云服务商的负载均衡器处理 TLS(ALB/SLB),容器不需要证书。
Q 怎么监控整个微服务栈的健康状态?
A Prometheus + Grafana 黄金组合:① Prometheus 采集各服务的 /metrics 端点;② Grafana 展示仪表盘 + 告警规则;③ 关键指标:API 延迟(P50/P95/P99)、错误率、CPU/内存、数据库连接数。先监控后优化。

📖 小节


📝 作业

  1. 基础题(难度⭐):设计一个博客系统的微服务架构图(Nginx + API + DB + Cache),列出每个服务的镜像和端口。
  2. 进阶题(难度⭐⭐):用 Docker 全栈技术部署该架构,编写 docker-compose.prod.yml 并成功启动。
  3. 挑战题(难度⭐⭐⭐):配置 Prometheus + Grafana 监控面板,添加 API 延迟和错误率的仪表盘,设置邮件告警规则。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏