Skip to content

生产环境部署

作者:唐亚峰 | battcn
字数统计:2.1k 字

重要提醒

生产环境部署需要格外注意安全性、可用性和性能,请仔细阅读本文档

部署架构

单机部署架构

适合小型项目、测试环境:

                    ┌─────────────────┐
                    │     Nginx       │
                    │   (443/80)      │
                    └────────┬────────┘

              ┌──────────────┼──────────────┐
              │              │              │
        ┌─────▼─────┐  ┌─────▼─────┐  ┌─────▼─────┐
        │  前端静态  │  │  Gateway  │  │   Nacos   │
        │   文件    │  │   9000    │  │   8848    │
        └───────────┘  └─────┬─────┘  └───────────┘

                    ┌────────┼────────┐
                    │        │        │
              ┌─────▼────┐ ┌─▼────┐ ┌─▼────┐
              │   IAM    │ │Suite │ │Plugin│
              │   5001   │ │ 5002 │ │ ...  │
              └─────┬────┘ └──┬───┘ └──┬───┘
                    │         │        │
                    └─────────┼────────┘

                    ┌─────────┼─────────┐
                    │         │         │
              ┌─────▼───┐ ┌───▼───┐ ┌───▼───┐
              │  MySQL  │ │ Redis │ │ MinIO │
              │  3306   │ │ 6379  │ │ 9000  │
              └─────────┘ └───────┘ └───────┘

高可用集群架构

适合中大型项目、生产环境:

                         ┌──────────────┐
                         │   SLB/LB     │
                         └──────┬───────┘

              ┌─────────────────┼─────────────────┐
              │                 │                 │
        ┌─────▼─────┐     ┌─────▼─────┐     ┌─────▼─────┐
        │  Nginx-1  │     │  Nginx-2  │     │  Nginx-3  │
        └─────┬─────┘     └─────┬─────┘     └─────┬─────┘
              │                 │                 │
              └─────────────────┼─────────────────┘

              ┌─────────────────┼─────────────────┐
              │                 │                 │
        ┌─────▼─────┐     ┌─────▼─────┐     ┌─────▼─────┐
        │ Gateway-1 │     │ Gateway-2 │     │ Gateway-3 │
        └─────┬─────┘     └─────┬─────┘     └─────┬─────┘
              │                 │                 │
              └─────────────────┼─────────────────┘

                          ┌─────▼─────┐
                          │   Nacos   │
                          │  Cluster  │
                          └─────┬─────┘

        ┌───────────────────────┼───────────────────────┐
        │                       │                       │
  ┌─────▼─────┐           ┌─────▼─────┐           ┌─────▼─────┐
  │ IAM × N   │           │ Suite × N │           │ Plugin × N│
  └─────┬─────┘           └─────┬─────┘           └─────┬─────┘
        │                       │                       │
        └───────────────────────┼───────────────────────┘

              ┌─────────────────┼─────────────────┐
              │                 │                 │
        ┌─────▼─────┐     ┌─────▼─────┐     ┌─────▼─────┐
        │MySQL主从  │     │Redis集群  │     │MinIO集群  │
        └───────────┘     └───────────┘     └───────────┘

生产级 Docker Compose

目录结构

/opt/wemirr/
├── docker-compose.yml
├── .env
├── nginx/
│   ├── nginx.conf
│   ├── conf.d/
│   │   └── default.conf
│   └── ssl/
│       ├── cert.pem
│       └── key.pem
├── mysql/
│   ├── conf/
│   │   └── my.cnf
│   └── init/
│       └── init.sql
├── redis/
│   └── redis.conf
├── logs/
└── data/

docker-compose.yml

yaml
version: '3.8'

networks:
  wemirr-net:
    driver: bridge

volumes:
  mysql-data:
  redis-data:
  nacos-data:
  minio-data:
  gateway-logs:
  iam-logs:
  suite-logs:

services:
  # ==================== 中间件层 ====================
  
  mysql:
    image: mysql:8.0
    container_name: wemirr-mysql
    networks:
      - wemirr-net
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - TZ=Asia/Shanghai
    volumes:
      - mysql-data:/var/lib/mysql
      - ./mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf
      - ./mysql/init:/docker-entrypoint-initdb.d
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_general_ci
      - --lower_case_table_names=1
      - --max_connections=1000
      - --innodb_buffer_pool_size=1G
    deploy:
      resources:
        limits:
          memory: 2G
    restart: always
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-p${MYSQL_ROOT_PASSWORD}"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    container_name: wemirr-redis
    networks:
      - wemirr-net
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
      - ./redis/redis.conf:/usr/local/etc/redis/redis.conf
    command: redis-server /usr/local/etc/redis/redis.conf
    deploy:
      resources:
        limits:
          memory: 512M
    restart: always
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  nacos:
    image: nacos/nacos-server:v2.3.0
    container_name: wemirr-nacos
    networks:
      - wemirr-net
    ports:
      - "8848:8848"
      - "9848:9848"
      - "9849:9849"
    environment:
      - MODE=standalone
      - SPRING_DATASOURCE_PLATFORM=mysql
      - MYSQL_SERVICE_HOST=mysql
      - MYSQL_SERVICE_PORT=3306
      - MYSQL_SERVICE_DB_NAME=nacos
      - MYSQL_SERVICE_USER=root
      - MYSQL_SERVICE_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - NACOS_AUTH_ENABLE=true
      - NACOS_AUTH_TOKEN=${NACOS_AUTH_TOKEN}
      - NACOS_AUTH_IDENTITY_KEY=serverIdentity
      - NACOS_AUTH_IDENTITY_VALUE=${NACOS_IDENTITY_VALUE}
      - JVM_XMS=512m
      - JVM_XMX=1g
    volumes:
      - nacos-data:/home/nacos/data
    depends_on:
      mysql:
        condition: service_healthy
    deploy:
      resources:
        limits:
          memory: 1.5G
    restart: always

  minio:
    image: bitnami/minio:latest
    container_name: wemirr-minio
    networks:
      - wemirr-net
    ports:
      - "19000:9000"
      - "19001:9001"
    environment:
      - MINIO_ROOT_USER=${MINIO_ROOT_USER}
      - MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}
      - MINIO_DEFAULT_BUCKETS=wemirr-bucket
    volumes:
      - minio-data:/bitnami/minio/data
    deploy:
      resources:
        limits:
          memory: 512M
    restart: always

  # ==================== 应用层 ====================

  gateway:
    image: registry.cn-hangzhou.aliyuncs.com/wemirr/wemirr-platform-gateway:${APP_VERSION}
    container_name: wemirr-gateway
    networks:
      - wemirr-net
    ports:
      - "9000:9000"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - NACOS_SERVER_ADDR=nacos:8848
      - NACOS_NAMESPACE=${NACOS_NAMESPACE}
      - TZ=Asia/Shanghai
      - JAVA_OPTS=-Xms512m -Xmx1g -XX:+UseG1GC
    volumes:
      - gateway-logs:/app/logs
    depends_on:
      - nacos
      - redis
    deploy:
      resources:
        limits:
          memory: 1.5G
    restart: always
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/actuator/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  iam:
    image: registry.cn-hangzhou.aliyuncs.com/wemirr/wemirr-platform-iam:${APP_VERSION}
    container_name: wemirr-iam
    networks:
      - wemirr-net
    ports:
      - "5001:5001"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - NACOS_SERVER_ADDR=nacos:8848
      - NACOS_NAMESPACE=${NACOS_NAMESPACE}
      - TZ=Asia/Shanghai
      - JAVA_OPTS=-Xms512m -Xmx1g -XX:+UseG1GC
    volumes:
      - iam-logs:/app/logs
    depends_on:
      - nacos
      - mysql
      - redis
    deploy:
      resources:
        limits:
          memory: 1.5G
    restart: always

  suite:
    image: registry.cn-hangzhou.aliyuncs.com/wemirr/wemirr-platform-suite:${APP_VERSION}
    container_name: wemirr-suite
    networks:
      - wemirr-net
    ports:
      - "5002:5002"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - NACOS_SERVER_ADDR=nacos:8848
      - NACOS_NAMESPACE=${NACOS_NAMESPACE}
      - TZ=Asia/Shanghai
      - JAVA_OPTS=-Xms512m -Xmx1g -XX:+UseG1GC
    volumes:
      - suite-logs:/app/logs
    depends_on:
      - nacos
      - mysql
      - redis
    deploy:
      resources:
        limits:
          memory: 1.5G
    restart: always

  # ==================== 反向代理 ====================

  nginx:
    image: nginx:alpine
    container_name: wemirr-nginx
    networks:
      - wemirr-net
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/ssl:/etc/nginx/ssl
      - ./nginx/html:/usr/share/nginx/html
      - ./logs/nginx:/var/log/nginx
    depends_on:
      - gateway
    restart: always

.env 文件

bash
# MySQL
MYSQL_ROOT_PASSWORD=your_secure_mysql_password_here

# Redis
REDIS_PASSWORD=your_secure_redis_password_here

# Nacos
NACOS_AUTH_TOKEN=SecretKey012345678901234567890123456789012345678901234567890123456789
NACOS_IDENTITY_VALUE=your_identity_value
NACOS_NAMESPACE=prod

# MinIO
MINIO_ROOT_USER=minio_admin
MINIO_ROOT_PASSWORD=your_secure_minio_password_here

# 应用版本
APP_VERSION=v4.0.0

Nginx 配置

nginx
# nginx/conf.d/default.conf
upstream gateway {
    server gateway:9000;
    keepalive 32;
}

server {
    listen 80;
    server_name your-domain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name your-domain.com;

    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

    # 安全头
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;

    # 前端静态资源
    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri $uri/ /index.html;
        
        # 静态资源缓存
        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
            expires 30d;
            add_header Cache-Control "public, immutable";
        }
    }

    # API 代理
    location /api/ {
        proxy_pass http://gateway/;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Connection "";
        
        # 超时设置
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        # WebSocket 支持
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    # 健康检查
    location /health {
        return 200 'OK';
        add_header Content-Type text/plain;
    }
}

使用仓库自带 Nginx 配置(推荐先对齐)

Wemirr 后端仓库在 附件/nginx/ 目录提供了作者线上环境使用的 Nginx 配置,适合你在生产环境落地时参考并做二次调整:

  • wemirr-platform/附件/nginx/nginx-http.conf
  • wemirr-platform/附件/nginx/nginx-https.conf

说明:配置中默认域名为 cloud.battcn.com,静态资源目录为 /opt/wemirr-platform/cloud-ui/dist,你需要替换为自己的域名与前端产物路径。

HTTP 版本示例(来自仓库)

nginx
server {
    listen  80;
    server_name  cloud.battcn.com;
    location / {
        # vue 静态资源打成tar.gz 包 开启压缩 速度更快
        root /opt/wemirr-platform/cloud-ui/dist;
        gzip_static on;
    }
    location /api/ {
        proxy_pass http://localhost:9000/;
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        client_max_body_size 10m;
        client_body_buffer_size 128k;
        proxy_connect_timeout 90;
        proxy_send_timeout 90;
        proxy_read_timeout 90;
        proxy_buffer_size 4k;
        proxy_buffers 4 32k;
        proxy_busy_buffers_size 64k;
        proxy_temp_file_write_size 64k;
    }
}

HTTPS 版本示例(来自仓库)

nginx
server {
    listen       80;
    server_name cloud.battcn.com;
    rewrite ^(.*)$  https://cloud.battcn.com;
}
server {
    listen 443 ssl;
    server_name  cloud.battcn.com;
    ssl on;
    # 证书是从阿里云申请的免费证书
    ssl_certificate   /usr/local/nginx/conf/myconf/cert/cloud/4869096_cloud.battcn.com.pem;
    ssl_certificate_key  /usr/local/nginx/conf/myconf/cert/cloud/4869096_cloud.battcn.com.key;
    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    location / {
        # vue 静态资源打成tar.gz 包 开启压缩 速度更快
        root /opt/wemirr-platform/cloud-ui/dist;
        gzip_static on;
    }
    location /api/ {
        proxy_pass http://localhost:9000/;
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        client_max_body_size 10m;
        client_body_buffer_size 128k;
        proxy_connect_timeout 90;
        proxy_send_timeout 90;
        proxy_read_timeout 90;
        proxy_buffer_size 4k;
        proxy_buffers 4 32k;
        proxy_busy_buffers_size 64k;
        proxy_temp_file_write_size 64k;
    }
}

静态资源路径建议

配置中的 root /opt/wemirr-platform/cloud-ui/dist; 是作者线上路径。

如果你的前端是通过 wemirr-platform-ui 构建产物部署,建议将产物解压到类似目录:

/opt/wemirr-platform/
└── cloud-ui/
    └── dist/

并保持与 Nginx root 一致。

生产部署检查清单

1. 端口暴露

  • 对外只暴露:80/443
  • 3306/6379/8848/15672 等中间件端口:
    • 建议只在内网访问
    • 或通过防火墙限制来源 IP

2. 数据持久化

  • MySQL:必须挂载 /var/lib/mysql
  • Redis:建议开启 AOF 并挂载 /data
  • Nacos:建议挂载 /home/nacos/data

3. 备份与恢复演练

  • 至少保证:数据库备份 + Nacos 配置备份
  • 建议每月至少做一次恢复演练

4. 配置与密钥

  • .env 与证书文件不要提交到仓库
  • Nacos Token、Redis 密码、MySQL 密码必须使用强密码

MySQL 配置

ini
# mysql/conf/my.cnf
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
lower_case_table_names=1

# 连接数
max_connections=1000
max_connect_errors=100

# InnoDB
innodb_buffer_pool_size=1G
innodb_log_file_size=256M
innodb_flush_log_at_trx_commit=2
innodb_flush_method=O_DIRECT

# 日志
slow_query_log=1
slow_query_log_file=/var/lib/mysql/slow.log
long_query_time=2

# 二进制日志(主从复制需要)
server-id=1
log_bin=mysql-bin
binlog_format=ROW
expire_logs_days=7

[client]
default-character-set=utf8mb4

Redis 配置

conf
# redis/redis.conf
bind 0.0.0.0
port 6379
requirepass your_secure_redis_password_here

# 持久化
appendonly yes
appendfsync everysec

# 内存管理
maxmemory 512mb
maxmemory-policy allkeys-lru

# 安全
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG ""

部署流程

1. 准备服务器

bash
# 更新系统
sudo apt update && sudo apt upgrade -y

# 安装 Docker
curl -fsSL https://get.docker.com | sh
sudo systemctl enable docker

# 安装 Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

2. 部署中间件

bash
cd /opt/wemirr

# 启动中间件
docker-compose up -d mysql redis nacos minio

# 等待服务就绪
sleep 30

# 检查服务状态
docker-compose ps

3. 初始化数据

bash
# 导入数据库
docker exec -i wemirr-mysql mysql -uroot -p${MYSQL_ROOT_PASSWORD} < ./mysql/init/wemirr_platform.sql

# 导入 Nacos 配置(通过 Web 界面或 API)

4. 部署应用

bash
# 启动所有应用
docker-compose up -d gateway iam suite nginx

# 查看日志
docker-compose logs -f gateway iam suite

5. 验证部署

bash
# 健康检查
curl -s http://localhost/health
curl -s http://localhost:9000/actuator/health

# 访问系统
echo "请访问: https://your-domain.com"

运维管理

日志管理

bash
# 查看实时日志
docker-compose logs -f --tail=100 gateway

# 日志轮转配置
cat > /etc/docker/daemon.json << EOF
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  }
}
EOF

监控告警

推荐使用 Prometheus + Grafana 进行监控:

yaml
# 添加到 docker-compose.yml
prometheus:
  image: prom/prometheus:latest
  volumes:
    - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
  ports:
    - "9090:9090"

grafana:
  image: grafana/grafana:latest
  ports:
    - "3000:3000"
  environment:
    - GF_SECURITY_ADMIN_PASSWORD=admin123

备份策略

bash
#!/bin/bash
# backup.sh

BACKUP_DIR=/opt/wemirr/backup/$(date +%Y%m%d)
mkdir -p $BACKUP_DIR

# 备份 MySQL
docker exec wemirr-mysql mysqldump -uroot -p${MYSQL_ROOT_PASSWORD} --all-databases > $BACKUP_DIR/mysql.sql

# 备份 Redis
docker exec wemirr-redis redis-cli -a ${REDIS_PASSWORD} BGSAVE
docker cp wemirr-redis:/data/dump.rdb $BACKUP_DIR/

# 备份配置文件
cp -r /opt/wemirr/.env /opt/wemirr/nginx $BACKUP_DIR/

# 清理7天前的备份
find /opt/wemirr/backup -type d -mtime +7 -exec rm -rf {} +

添加到 crontab:

bash
0 2 * * * /opt/wemirr/backup.sh

安全加固

1. 防火墙配置

bash
# 只开放必要端口
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 22/tcp
sudo ufw enable

2. 密码强度

  • MySQL 密码至少 16 位,包含大小写字母、数字、特殊字符
  • Redis 密码至少 32 位
  • Nacos Token 至少 64 位

3. 定期更新

bash
# 更新镜像
docker-compose pull
docker-compose up -d

下一步