Docker: Phase 3综合实践

最后更新:2026-08-26

把 Phase 3 的知识串起来——编排一个完整的微服务电商后端。

1. 你将学到


2. 一个电商平台后端的故事

(1) 痛点:6 个服务手动启动

Charlie 需要为电商平台后端部署 6 个服务:React frontend、Nginx 反向代理、Go API、PostgreSQL、Redis cache、RabbitMQ 消息队列。手动用 docker run 需要记住 30+ parameter,启动顺序严格,出错率极高。

(2) Compose 一键编排的解法

Charlie 用一个 docker-compose.yml 全搞定。

BASH
docker compose up -d

(3) 收益:6 个服务一键启动

从手动 30 分钟降到 docker compose up -d 30 秒。新人入职 5 分钟跑起整个后端。


3. 微服务架构设计

(1) 架构总览

100%
graph TB
    USER["Browser"] --> NGX["Nginx<br/>:80 Reverse Proxy"]
    NGX -->|"api/"| API1["Go API #1<br/>:8080"]
    NGX -->|"api/"| API2["Go API #2<br/>:8080"]
    NGX -->|"api/"| API3["Go API #3<br/>:8080"]
    NGX -->|"/"| REACT["React SPA<br/>Static Files"]
    API1 --> PG["PostgreSQL<br/>:5432"]
    API2 --> PG
    API3 --> PG
    API1 --> REDIS["Redis<br/>:6379"]
    API2 --> REDIS
    API3 --> MQ["RabbitMQ<br/>:5672"]

(2) 服务清单

服务 镜像 端口 网络 依赖
Nginx nginx:1.25-alpine 80→80 frontend, backend api (healthy)
React 自建(多阶段) - frontend -
Go API 自建(多阶段) 8080 backend, db-net, cache-net postgres (healthy), redis
PostgreSQL postgres:15-alpine 5432 db-net -
Redis redis:7-alpine 6379 cache-net -
RabbitMQ rabbitmq:3-management 5672, 15672 backend -

4. Compose 文件编写

▶ 示例:编写完整 compose 文件(难度⭐⭐⭐)

YAML
# ============================================
# docker-compose.yml - E-commerce microservices
# ============================================

services:
  # ---------- Frontend ----------
  nginx:
    image: nginx:1.25-alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - react-build:/usr/share/nginx/html:ro
    depends_on:
      api:
        condition: service_healthy
    restart: unless-stopped
    networks:
      - frontend
      - backend
    healthcheck:
      test: ["CMD", "nginx", "-t"]
      interval: 30s
      timeout: 5s

  react:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    volumes:
      - react-build:/app/build
    networks:
      - frontend

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

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

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

  rabbitmq:
    image: rabbitmq:3-management-alpine
    ports:
      - "15672:15672"   # Management UI
    environment:
      RABBITMQ_DEFAULT_USER: guest
      RABBITMQ_DEFAULT_PASS: guest
    volumes:
      - mq-data:/var/lib/rabbitmq
    restart: unless-stopped
    networks:
      - backend

# ---------- Volumes ----------
volumes:
  pg-data:
  redis-data:
  mq-data:
  react-build:

# ---------- Networks ----------
networks:
  frontend:
  backend:
  db-net:
    internal: true   # No external access
  cache-net:
    internal: true

(1) Nginx 配置(负载均衡)

TEXT 📖 仅展示
# nginx.conf
upstream api_backend {
    server api:8080;   # Docker DNS round-robin to 3 replicas
}

server {
    listen 80;
    server_name localhost;

    # Static files (React SPA)
    location / {
        root /usr/share/nginx/html;
        try_files $uri $uri/ /index.html;
    }

    # API proxy
    location /api/ {
        proxy_pass http://api_backend/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

5. 多环境配置

▶ 示例:开发环境 override(难度⭐⭐)

YAML
# docker-compose.dev.yml
services:
  api:
    build:
      context: ./api
      dockerfile: Dockerfile.dev
    volumes:
      - ./api/src:/app/src   # Hot-reload
    environment:
      GO_ENV: development
    deploy:
      replicas: 1            # Single replica for debugging

  # Dev-only: database admin UI
  adminer:
    image: adminer
    ports:
      - "8081:8080"
    profiles: ["dev"]
    networks:
      - db-net

6. 验证部署

(1) 验证清单

检查项 命令 预期结果
所有服务运行 docker compose ps 6+ 服务 Up
Nginx 健康 curl http://localhost 200 OK
API 健康 curl http://localhost/api/health {"status":"ok"}
API 副本数 docker compose ps api 3 个容器
Redis 连接 docker compose exec redis redis-cli ping PONG
PostgreSQL 连接 docker compose exec postgres pg_isready accepting connections
数据库隔离 从 nginx 容器 ping postgres 失败(internal 网络)

7. 完整示例:一键部署电商平台

BASH
# ============================================
# Complete walkthrough: E-commerce microservices
# ============================================

# 1. Build and start all services
docker compose up -d --build

# 2. Verify all services are running
docker compose ps

# 3. Check health status
docker compose ps --format "table {{.Name}}\t{{.Status}}"

# 4. Test the API
curl -s http://localhost/api/health | python3 -m json.tool

# 5. Verify load balancing (multiple requests to different replicas)
for i in $(seq 1 6); do
  curl -s http://localhost/api/health | jq -r '.hostname'
done

# 6. Check database connectivity from API
docker compose exec api wget -qO- http://localhost:8080/health

# 7. Access RabbitMQ management UI
# Open http://localhost:15672 (guest/guest)

# 8. View logs
docker compose logs -f api

# 9. Scale API to 5 replicas
docker compose up -d --scale api=5

# 10. Clean up
docker compose down
docker compose down -v  # Also removes data volumes

❓ 常见问题

Q 服务间通信用网络别名还是 service 名?
A Compose 中推荐用 service 名(如 postgresredis)。Compose 自动为每个服务注册 DNS 记录,service 名就是主机名。网络别名用于同一服务在不同网络中需要不同名字的场景。
Q 依赖启动顺序怎么保证?
A depends_on + condition: service_healthy。API 依赖 postgres 的健康检查通过后才启动。注意:depends_on: [db](默认 condition: service_started)只等容器启动,不等服务就绪。
Q 多副本 API 如何做负载均衡?
A Docker 内置 DNS 轮询 + Nginx upstream。3 个 api 容器都注册 DNS 为 api,Nginx 的 upstream api_backend { server api:8080; } 会轮询分发。对于更复杂的负载均衡策略,可以用 HAProxy。
Q 数据库主从复制怎么配置?
A 单机 Compose 可以用多个 PostgreSQL 容器 + 主从复制配置。但生产级主从建议用 managed database(AWS RDS / 云服务商)或专用工具(Patroni/Stolon)。本课的 PostgreSQL 是单节点。
Q 配置热加载怎么实现?
A 开发环境用 Volume 挂载源码(如 -v ./api/src:/app/src)+ 热重载工具(air for Go, nodemon for Node, flask --debug for Python)。生产环境不挂载源码,配置变更通过环境变量 + docker compose up -d 重启。

📖 小节


📝 作业

  1. 基础题(难度⭐):设计一个博客系统的多服务架构(Nginx + API + DB + Cache),编写 docker-compose.yml 并启动。
  2. 进阶题(难度⭐⭐):配置 API 多副本(replicas: 3)+ Nginx 负载均衡,验证请求分发到不同副本。
  3. 挑战题(难度⭐⭐⭐):添加 dev/prod 两套 override 文件(dev 加 Adminer + 热重载,prod 加资源限制 + 日志轮转),分别启动验证差异。
Web-Tutorial.com

Web-Tutorial 技术团队

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

100%

🙏 帮我们做得更好

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

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