第16章:企业级实战综合项目
本章目标:通过一个完整的电商项目,综合运用所学 Docker 知识,搭建生产级容器化应用。
16.1 项目概述
16.1.1 项目架构
┌─────────────────────────────────────────────────────────────┐
│ 电商系统容器化架构 │
│ │
│ 用户 → Nginx → API Gateway → 微服务 → 数据库/缓存 │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 接入层 │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Nginx LB │ │ Nginx LB │ (主备) │ │
│ │ └──────┬───────┘ └──────┬───────┘ │ │
│ └─────────┼─────────────────┼────────────────────────┘ │
│ │ │ │
│ ┌─────────┼─────────────────┼────────────────────────┐ │
│ │ ▼ ▼ 应用服务层 │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Gateway │ │ Gateway │ (API 网关) │ │
│ │ └──────┬───────┘ └──────┬───────┘ │ │
│ │ │ │ │ │
│ │ ┌──────┴─────┬───────────┴──────┬──────────┐ │ │
│ │ ▼ ▼ ▼ ▼ │ │
│ │ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │ │
│ │ │用户│ │商品│ │订单│ │支付│ │ │
│ │ │服务│ │服务│ │服务│ │服务│ │ │
│ │ └────┘ └────┘ └────┘ └────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 数据层 │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ MySQL │ │ Redis │ │ MongoDB │ │ │
│ │ │ (主从) │ │ (集群) │ │ (文档库) │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
16.1.2 技术栈
| 组件 | 技术选型 | 说明 |
|---|---|---|
| 反向代理 | Nginx | 负载均衡、SSL 终止 |
| API 网关 | Kong / Traefik | 路由、限流、认证 |
| 用户服务 | Python/Go | 用户注册、登录、权限 |
| 商品服务 | Python/Go | 商品管理、搜索 |
| 订单服务 | Python/Go | 订单创建、状态管理 |
| 支付服务 | Python/Go | 支付对接、回调处理 |
| 数据库 | MySQL 8.0 | 业务数据存储 |
| 缓存 | Redis 7 | 热点数据缓存 |
| 消息队列 | RabbitMQ | 异步消息处理 |
| 搜索引擎 | Elasticsearch | 商品搜索 |
| 监控 | Prometheus + Grafana | 指标监控 |
| 日志 | ELK Stack | 日志收集分析 |
16.2 项目结构
ecommerce-docker/
├── docker-compose.yml # 主配置
├── docker-compose.prod.yml # 生产环境配置
├── .env # 环境变量
├── .env.prod # 生产环境变量
│
├── nginx/ # Nginx 配置
│ ├── nginx.conf
│ └── conf.d/
│ ├── api.conf
│ └── frontend.conf
│
├── services/ # 微服务
│ ├── gateway/ # API 网关
│ │ ├── Dockerfile
│ │ └── kong.yml
│ │
│ ├── user-service/ # 用户服务
│ │ ├── Dockerfile
│ │ ├── requirements.txt
│ │ └── app/
│ │
│ ├── product-service/ # 商品服务
│ │ ├── Dockerfile
│ │ ├── requirements.txt
│ │ └── app/
│ │
│ ├── order-service/ # 订单服务
│ │ ├── Dockerfile
│ │ ├── requirements.txt
│ │ └── app/
│ │
│ └── payment-service/ # 支付服务
│ ├── Dockerfile
│ ├── requirements.txt
│ └── app/
│
├── mysql/ # MySQL 配置
│ ├── my.cnf
│ └── initdb/
│ ├── 01-users.sql
│ ├── 02-products.sql
│ └── 03-orders.sql
│
├── redis/ # Redis 配置
│ └── redis.conf
│
├── elasticsearch/ # Elasticsearch 配置
│ └── elasticsearch.yml
│
├── prometheus/ # Prometheus 配置
│ ├── prometheus.yml
│ └── rules/
│ └── alerts.yml
│
├── grafana/ # Grafana 配置
│ ├── dashboards/
│ └── datasources/
│
├── scripts/ # 脚本
│ ├── backup.sh
│ ├── deploy.sh
│ └── health-check.sh
│
└── docs/ # 文档
├── api.md
└── deployment.md
16.3 Docker Compose 完整配置
yaml
# docker-compose.yml
version: '3.8'
services:
# ==================== 接入层 ====================
# Nginx 负载均衡
nginx:
image: nginx:1.25-alpine
container_name: ecommerce-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
networks:
- frontend
depends_on:
- gateway
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
timeout: 5s
retries: 3
restart: unless-stopped
deploy:
resources:
limits:
cpus: '2'
memory: 512M
# ==================== 应用服务层 ====================
# API 网关
gateway:
build: ./services/gateway
container_name: ecommerce-gateway
environment:
- KONG_DATABASE=off
- KONG_DECLARATIVE_CONFIG=/etc/kong/kong.yml
- KONG_PROXY_ACCESS_LOG=/dev/stdout
- KONG_ADMIN_ACCESS_LOG=/dev/stdout
- KONG_PROXY_ERROR_LOG=/dev/stderr
- KONG_ADMIN_ERROR_LOG=/dev/stderr
volumes:
- ./services/gateway/kong.yml:/etc/kong/kong.yml:ro
networks:
- frontend
- backend
healthcheck:
test: ["CMD", "kong", "health"]
interval: 30s
timeout: 5s
retries: 3
restart: unless-stopped
deploy:
resources:
limits:
cpus: '1'
memory: 512M
# 用户服务
user-service:
build: ./services/user-service
container_name: ecommerce-user
environment:
- APP_ENV=production
- DB_HOST=mysql
- DB_PORT=3306
- DB_NAME=users
- DB_USER=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
networks:
- backend
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 5s
retries: 3
restart: unless-stopped
deploy:
resources:
limits:
cpus: '1'
memory: 512M
# 商品服务
product-service:
build: ./services/product-service
container_name: ecommerce-product
environment:
- APP_ENV=production
- DB_HOST=mysql
- DB_PORT=3306
- DB_NAME=products
- DB_USER=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- ELASTICSEARCH_HOST=elasticsearch
- ELASTICSEARCH_PORT=9200
networks:
- backend
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
elasticsearch:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 5s
retries: 3
restart: unless-stopped
deploy:
resources:
limits:
cpus: '1'
memory: 512M
# 订单服务
order-service:
build: ./services/order-service
container_name: ecommerce-order
environment:
- APP_ENV=production
- DB_HOST=mysql
- DB_PORT=3306
- DB_NAME=orders
- DB_USER=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- RABBITMQ_HOST=rabbitmq
- RABBITMQ_PORT=5672
- RABBITMQ_USER=${RABBITMQ_USER}
- RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD}
networks:
- backend
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
rabbitmq:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 5s
retries: 3
restart: unless-stopped
deploy:
resources:
limits:
cpus: '1'
memory: 512M
# 支付服务
payment-service:
build: ./services/payment-service
container_name: ecommerce-payment
environment:
- APP_ENV=production
- DB_HOST=mysql
- DB_PORT=3306
- DB_NAME=payments
- DB_USER=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- RABBITMQ_HOST=rabbitmq
- RABBITMQ_PORT=5672
- RABBITMQ_USER=${RABBITMQ_USER}
- RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD}
networks:
- backend
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
rabbitmq:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 5s
retries: 3
restart: unless-stopped
deploy:
resources:
limits:
cpus: '1'
memory: 512M
# ==================== 数据层 ====================
# MySQL 数据库
mysql:
image: mysql:8.0
container_name: ecommerce-mysql
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: ${DB_NAME}
MYSQL_USER: ${DB_USER}
MYSQL_PASSWORD: ${DB_PASSWORD}
volumes:
- mysql-data:/var/lib/mysql
- ./mysql/my.cnf:/etc/mysql/conf.d/custom.cnf:ro
- ./mysql/initdb:/docker-entrypoint-initdb.d
ports:
- "127.0.0.1:3306:3306"
networks:
- backend
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
deploy:
resources:
limits:
cpus: '2'
memory: 2G
# Redis 缓存
redis:
image: redis:7-alpine
container_name: ecommerce-redis
command: redis-server /etc/redis/redis.conf
volumes:
- redis-data:/data
- ./redis/redis.conf:/etc/redis/redis.conf:ro
ports:
- "127.0.0.1:6379:6379"
networks:
- backend
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
deploy:
resources:
limits:
cpus: '1'
memory: 1G
# RabbitMQ 消息队列
rabbitmq:
image: rabbitmq:3.12-management-alpine
container_name: ecommerce-rabbitmq
environment:
RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER}
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD}
volumes:
- rabbitmq-data:/var/lib/rabbitmq
ports:
- "127.0.0.1:5672:5672"
- "127.0.0.1:15672:15672"
networks:
- backend
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "check_running"]
interval: 30s
timeout: 10s
retries: 5
restart: unless-stopped
deploy:
resources:
limits:
cpus: '1'
memory: 1G
# Elasticsearch 搜索引擎
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
container_name: ecommerce-elasticsearch
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
volumes:
- elasticsearch-data:/usr/share/elasticsearch/data
ports:
- "127.0.0.1:9200:9200"
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:9200/_cluster/health || exit 1"]
interval: 30s
timeout: 10s
retries: 5
restart: unless-stopped
deploy:
resources:
limits:
cpus: '2'
memory: 1G
# ==================== 监控层 ====================
# Prometheus 监控
prometheus:
image: prom/prometheus:latest
container_name: ecommerce-prometheus
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
ports:
- "127.0.0.1:9090:9090"
networks:
- monitoring
restart: unless-stopped
# Grafana 可视化
grafana:
image: grafana/grafana:latest
container_name: ecommerce-grafana
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
volumes:
- grafana-data:/var/lib/grafana
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
- ./grafana/datasources:/etc/grafana/provisioning/datasources
ports:
- "127.0.0.1:3000:3000"
networks:
- monitoring
restart: unless-stopped
# ==================== 日志层 ====================
# Elasticsearch (日志)
elasticsearch-log:
image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
container_name: ecommerce-elasticsearch-log
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
volumes:
- elasticsearch-log-data:/usr/share/elasticsearch/data
networks:
- logging
restart: unless-stopped
# Kibana 日志可视化
kibana:
image: docker.elastic.co/kibana/kibana:8.12.0
container_name: ecommerce-kibana
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch-log:9200
ports:
- "127.0.0.1:5601:5601"
networks:
- logging
depends_on:
- elasticsearch-log
restart: unless-stopped
# Filebeat 日志收集
filebeat:
image: docker.elastic.co/beats/filebeat:8.12.0
container_name: ecommerce-filebeat
user: root
volumes:
- ./filebeat/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- logging
depends_on:
- elasticsearch-log
restart: unless-stopped
# ==================== 网络 ====================
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
monitoring:
driver: bridge
logging:
driver: bridge
# ==================== 数据卷 ====================
volumes:
mysql-data:
driver: local
redis-data:
driver: local
rabbitmq-data:
driver: local
elasticsearch-data:
driver: local
elasticsearch-log-data:
driver: local
prometheus-data:
driver: local
grafana-data:
driver: local
16.4 服务 Dockerfile 示例
16.4.1 Python 微服务 Dockerfile
dockerfile
# services/user-service/Dockerfile
FROM python:3.11-slim AS builder
WORKDIR /app
# 安装系统依赖
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
gcc \
libffi-dev \
&& rm -rf /var/lib/apt/lists/*
# 复制依赖文件
COPY requirements.txt .
# 安装 Python 依赖
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# ==================== 运行阶段 ====================
FROM python:3.11-slim AS production
# 设置元数据
LABEL maintainer="devops@example.com"
LABEL version="1.0"
LABEL description="User Service"
# 设置工作目录
WORKDIR /app
# 安装运行时依赖
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
curl \
&& rm -rf /var/lib/apt/lists/*
# 从 builder 阶段复制依赖
COPY --from=builder /install /usr/local
# 创建非 root 用户
RUN groupadd -r appuser && useradd -r -g appuser -d /app -s /sbin/nologin appuser
# 复制应用代码
COPY --chown=appuser:appuser . .
# 切换到非 root 用户
USER appuser
# 设置环境变量
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
APP_ENV=production \
APP_PORT=8000
# 声明端口
EXPOSE 8000
# 健康检查
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD ["python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]
# 启动命令
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
16.4.2 Go 微服务 Dockerfile
dockerfile
# services/product-service/Dockerfile (Go 版本)
# Stage 1: 构建
FROM golang:1.21-alpine AS builder
WORKDIR /app
# 安装依赖
COPY go.mod go.sum ./
RUN go mod download
# 复制源码
COPY . .
# 静态编译
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server .
# Stage 2: 运行
FROM alpine:3.19
# 安装 ca-certificates(用于 HTTPS)
RUN apk --no-cache add ca-certificates
# 创建非 root 用户
RUN addgroup -g 1001 -S appgroup && \
adduser -S appuser -u 1001 -G appgroup
WORKDIR /app
# 复制二进制文件
COPY --from=builder /app/server .
# 切换到非 root 用户
USER appuser
EXPOSE 8000
# 健康检查
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD ["wget", "--quiet", "--tries=1", "--spider", "http://localhost:8000/health"]
ENTRYPOINT ["./server"]
16.5 环境变量配置
bash
# .env
# 数据库配置
DB_NAME=ecommerce
DB_USER=appuser
DB_PASSWORD=apppass123
DB_ROOT_PASSWORD=rootpass123
# Redis 配置
REDIS_PASSWORD=redis123
# RabbitMQ 配置
RABBITMQ_USER=appuser
RABBITMQ_PASSWORD=rabbithello123
# Grafana 配置
GRAFANA_PASSWORD=admin123
# 应用配置
APP_ENV=production
APP_SECRET_KEY=your-secret-key-change-in-production
16.6 部署脚本
bash
#!/bin/bash
# scripts/deploy.sh - 自动化部署脚本
set -e
echo "=========================================="
echo " 电商系统 Docker 部署脚本"
echo "=========================================="
# 检查 Docker 和 Docker Compose
echo "[1/6] 检查环境..."
docker --version
docker compose version
# 停止旧服务
echo "[2/6] 停止旧服务..."
docker compose down
# 拉取最新镜像
echo "[3/6] 拉取最新镜像..."
docker compose pull
# 构建服务镜像
echo "[4/6] 构建服务镜像..."
docker compose build --no-cache
# 启动服务
echo "[5/6] 启动服务..."
docker compose up -d
# 等待服务健康
echo "[6/6] 等待服务健康..."
sleep 30
# 检查服务状态
echo "=========================================="
echo " 服务状态"
echo "=========================================="
docker compose ps
echo ""
echo "=========================================="
echo " 部署完成!"
echo "=========================================="
echo ""
echo "访问地址:"
echo " 前端: http://localhost"
echo " API: http://localhost/api"
echo " Grafana: http://localhost:3000"
echo " Kibana: http://localhost:5601"
echo ""
16.7 健康检查脚本
bash
#!/bin/bash
# scripts/health-check.sh - 健康检查脚本
echo "=========================================="
echo " 服务健康检查"
echo "=========================================="
# 检查所有容器状态
echo ""
echo "容器状态:"
docker compose ps
echo ""
echo "健康状态:"
# 检查 Nginx
if curl -f http://localhost/health > /dev/null 2>&1; then
echo "✓ Nginx: 健康"
else
echo "✗ Nginx: 不健康"
fi
# 检查 API 网关
if curl -f http://localhost:8001/status > /dev/null 2>&1; then
echo "✓ API Gateway: 健康"
else
echo "✗ API Gateway: 不健康"
fi
# 检查 MySQL
if docker exec ecommerce-mysql mysqladmin ping -h localhost > /dev/null 2>&1; then
echo "✓ MySQL: 健康"
else
echo "✗ MySQL: 不健康"
fi
# 检查 Redis
if docker exec ecommerce-redis redis-cli -a redis123 ping > /dev/null 2>&1; then
echo "✓ Redis: 健康"
else
echo "✗ Redis: 不健康"
fi
# 检查 RabbitMQ
if docker exec ecommerce-rabbitmq rabbitmq-diagnostics check_running > /dev/null 2>&1; then
echo "✓ RabbitMQ: 健康"
else
echo "✗ RabbitMQ: 不健康"
fi
# 检查 Elasticsearch
if curl -f http://localhost:9200/_cluster/health > /dev/null 2>&1; then
echo "✓ Elasticsearch: 健康"
else
echo "✗ Elasticsearch: 不健康"
fi
echo ""
echo "=========================================="
echo " 资源使用"
echo "=========================================="
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
16.8 本章小结
本章通过一个完整的电商项目,综合演示了:
| 章节 | 内容 |
|---|---|
| 架构设计 | 微服务架构、网络隔离、服务分层 |
| Docker Compose | 多服务编排、依赖管理、健康检查 |
| Dockerfile | 多阶段构建、安全加固、最佳实践 |
| 数据持久化 | MySQL、Redis、RabbitMQ 数据存储 |
| 监控告警 | Prometheus + Grafana 监控体系 |
| 日志管理 | ELK 日志收集和分析 |
| 部署脚本 | 自动化部署和健康检查 |
16.9 课后练习
- 实践题:按照本章的架构,部署完整的电商系统。
- 优化题:优化 Dockerfile,减小镜像体积。
- 扩展题:添加更多监控指标和告警规则。
🎉 恭喜你完成了企业级 Docker 实战教程的全部内容!
希望这份文档能帮助你从零基础到掌握 Docker 的核心技能。
祝你在容器化技术的道路上越走越远!