Files
HAOVP/docs/Debian 12 高可用集群部署手册.md
T

1111 lines
32 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Debian 12 高可用集群部署手册
## 一、环境准备
### 1.1 虚拟机配置要求
| 节点 | IP地址 | CPU | 内存 | 磁盘 | 操作系统 |
|------|--------|-----|------|------|----------|
| Node-01 | 192.168.1.101 | 4核 | 8GB | 100GB | Debian 12 (Bookworm) |
| Node-02 | 192.168.1.102 | 4核 | 8GB | 100GB | Debian 12 (Bookworm) |
| Node-03 | 192.168.1.103 | 4核 | 8GB | 100GB | Debian 12 (Bookworm) |
### 1.2 Debian 12 安装建议
下载地址: https://www.debian.org/distrib/
推荐使用 **Debian 12 Netinst** 最小化安装:
- 选择 "无图形界面" 安装
- 仅安装 SSH server 和标准系统工具
- 使用 LVM 分区,便于扩展
### 1.3 系统初始化
在每台虚拟机上执行以下操作:
```bash
# 切换到 root 用户
su -
# 上传 init-debian12.sh 脚本并执行
chmod +x init-debian12.sh
./init-debian12.sh
# 使环境变量生效
source /etc/profile
# 验证安装
java -version
mvn -version
docker --version
docker compose version
```
### 1.4 网络配置
#### 配置静态 IP
```bash
# 编辑网络配置
vim /etc/network/interfaces
# 添加以下内容 (根据实际网络调整)
#(VMware虚拟机版)
# Node-01
# The primary network interface
auto ens33
iface ens33 inet static
address 192.168.1.101
netmask 255.255.255.0
gateway 192.168.1.2
dns-nameservers 8.8.8.8 114.114.114.114
# 重启网络
systemctl restart networking
reboot
```
#### 配置主机名
```bash
# Node-01
hostnamectl set-hostname node-01
echo "192.168.1.101 node-01" >> /etc/hosts
echo "192.168.1.102 node-02" >> /etc/hosts
echo "192.168.1.103 node-03" >> /etc/hosts
# Node-02
hostnamectl set-hostname node-02
# 添加 hosts...
# Node-03
hostnamectl set-hostname node-03
# 添加 hosts...
```
#### 配置 SSH 免密登录
```bash
# 在 Node-01 生成密钥
ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa
# 分发公钥到其他节点
ssh-copy-id root@192.168.1.102
ssh-copy-id root@192.168.1.103
# 测试连接
ssh node-02 "hostname"
ssh node-03 "hostname"
```
---
## 二、部署步骤
说明:
- 多虚拟机 HA 部署以 `docker-compose-node-01.yml`、`docker-compose-node-02.yml`、`docker-compose-node-03.yml` 为准。
- 微服务端口以各模块的 `application.yml` 和 Nacos 中的 `video-*.yaml` 为准。
- 根目录的 `backend/docker-compose.yml` 仅用于单机本地联调,允许在基础设施编排方式上与 HA 版不同,但服务端口和数据库编号应保持一致。
### 2.1 克隆项目
建议提前配置好 GitHub 账号和 SSH 密钥
```bash
# 在所有节点执行
cd /opt
git clone <项目地址> HA-OVP
cd HA-OVP
```
### 2.2 构建项目
```bash
# 在 Node-01 执行 (只需执行一次)
# 构建所有模块
mvn clean install -Dmaven.test.skip=true
# 构建 Sentinel 镜像(Node-01 执行一次即可)
cd sentinel-dashboard
docker build -t sentinel-dashboard:1.8.6 .
cd ..
# 验证构建产物
ls -la video-*/target/*.jar
```
### 2.3 同步构建产物
```bash
# 三个节点都需要安装 rsync
apt install rsync -y
# 从 Node-01 同步到其他节点
rsync -avz --progress /opt/HA-OVP/ root@192.168.1.102:/opt/HA-OVP/
rsync -avz --progress /opt/HA-OVP/ root@192.168.1.103:/opt/HA-OVP/
```
### 2.4 Node-01 部署 (主节点)
#### 步骤 1: 启动基础设施
```bash
cd /opt/HA-OVP/backend
# 先只启动基础设施 (MySQL, Redis, Nacos 等)
docker compose -f docker-compose-node-01.yml up -d mysql redis redis-sentinel nacos rmqnamesrv minio sentinel
# 等待 MySQL 完全启动 (约 30-60 秒)
docker compose -f docker-compose-node-01.yml logs -f mysql
# 看到 "ready for connections" 后 Ctrl+C 退出
#停止
docker compose -f docker-compose-node-01.yml down -v
```
#### 步骤 2: 验证数据库初始化
MySQL 容器启动时会自动执行 `sql/` 目录下的初始化脚本(通过 `docker-entrypoint-initdb.d` 挂载),无需手动初始化。
```bash
# 验证数据库已自动创建
docker exec mysql mysql -uroot -proot123456 -e "SHOW DATABASES;"
# 应看到以下数据库:
# - video_auth
# - video_user
# - video_content
# - video_comment
# - video_recommend
# - video_admin
# - nacos_config
```
#### 步骤 3: 等待 Nacos 启动
```bash
# 等待 Nacos 完全启动
docker compose -f docker-compose-node-01.yml logs -f nacos
# 看到 "Nacos started successfully" 后 Ctrl+C 退出
# 创建 Nacos 用户 (重要!微服务需要认证)
# 注意:如果是首次启动,Nacos 默认用户是 nacos/nacos
# 访问控制台验证: http://192.168.1.101:8848/nacos
```
#### 步骤 4: 启动微服务
```bash
# 启动所有微服务
docker compose -f docker-compose-node-01.yml up -d video-gateway video-auth video-user video-content video-comment video-recommend video-admin nginx
# 查看微服务启动日志
docker compose -f docker-compose-node-01.yml logs -f video-gateway
# 确认日志中没有错误,显示 "Started GatewayApplication" 表示成功
```
#### 步骤 5: 验证 Node-01 服务状态
```bash
# 检查所有容器状态
docker ps
# 验证 Nacos 服务注册
curl http://192.168.1.101:8848/nacos/v1/ns/service/list | grep -o '"name":"[^"]*"'
# 验证微服务健康状态
curl http://192.168.1.101:9000/actuator/health
curl http://192.168.1.101:9001/actuator/health
```
### 2.5 Node-02 部署 (从节点)
**重要**:确保 Node-01 的所有服务正常运行后再部署 Node-02
#### 步骤 1: 启动基础设施
```bash
cd /opt/HA-OVP/backend
# 启动基础设施
docker compose -f docker-compose-node-02.yml up -d mysql redis redis-sentinel nacos rmqnamesrv rmqbroker minio sentinel
# 等待 MySQL 和 Nacos 完全启动
docker compose -f docker-compose-node-02.yml logs -f mysql
# 看到 "ready for connections" 后 Ctrl+C 退出
```
#### 步骤 2: 启动微服务
**注意**:
- Node-02 作为 MySQL 从节点,数据库会通过主从复制自动同步,无需手动初始化。
- 对象存储采用“方案A:Node-01 统一写入,Node-02 同步副本”,因此 `video-user` 与 `video-content` 的 MinIO 上传目标必须统一指向 `192.168.1.101:9100`。
- Node-02 本地 MinIO 保留部署,但不再作为业务随机写入点。
```bash
# 等待 Nacos 启动后,启动微服务
docker compose -f docker-compose-node-02.yml up -d video-gateway video-auth video-user video-content video-comment video-recommend video-admin
# 查看日志,确认 Nacos 认证成功 (应显示 username='nacos')
docker compose -f docker-compose-node-02.yml logs -f video-gateway
```
### 2.6 Node-03 部署 (备份节点)
```bash
cd /opt/HA-OVP/backend
# Node-03 只部署基础设施
docker compose -f docker-compose-node-03.yml up -d
# 等待 MySQL 和 Nacos 启动
docker compose -f docker-compose-node-03.yml logs -f mysql
# 看到 "ready for connections" 后 Ctrl+C 退出
```
### 2.7 配置 MySQL 主从复制
#### 在 Node-01 (主节点) 创建复制用户
```bash
docker exec -i mysql mysql -uroot -proot123456 << 'EOF'
CREATE USER IF NOT EXISTS 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'repl_password_2024';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
SHOW MASTER STATUS;
EOF
```
#### 在 Node-02 和 Node-03 (从节点) 配置主从
```bash
# 在 Node-02 执行
docker exec -i mysql mysql -uroot -proot123456 << 'EOF'
CHANGE MASTER TO
MASTER_HOST='192.168.1.101',
MASTER_USER='repl',
MASTER_PASSWORD='repl_password_2024',
MASTER_AUTO_POSITION=1;
START SLAVE;
SHOW SLAVE STATUS\G;
EOF
# 确认 Slave_IO_Running 和 Slave_SQL_Running 都为 Yes
# 配置完成后,启用只读模式 (重要!)
docker exec mysql mysql -uroot -proot123456 -e "SET GLOBAL read_only = ON; SET GLOBAL super_read_only = ON;"
```
```bash
# 在 Node-03 执行 (同上)
docker exec -i mysql mysql -uroot -proot123456 << 'EOF'
CHANGE MASTER TO
MASTER_HOST='192.168.1.101',
MASTER_USER='repl',
MASTER_PASSWORD='repl_password_2024',
MASTER_AUTO_POSITION=1;
START SLAVE;
SHOW SLAVE STATUS\G;
EOF
docker exec mysql mysql -uroot -proot123456 -e "SET GLOBAL read_only = ON; SET GLOBAL super_read_only = ON;"
```
### 2.8 配置 Redis 主从复制
```bash
# 在 Node-02 执行
docker exec redis redis-cli -a redis_HA_Cluster_2024 REPLICAOF 192.168.1.101 6379
# 在 Node-03 执行
docker exec redis redis-cli -a redis_HA_Cluster_2024 REPLICAOF 192.168.1.101 6379
# 验证主从状态 (在 Node-01 执行)
docker exec redis redis-cli -a redis_HA_Cluster_2024 INFO replication | grep -E "role|connected_slaves"
```
### 2.9 配置 Nacos
访问 http://192.168.1.101:8848/nacos (账号: nacos/nacos)
#### 方式一:SQL 脚本初始化(推荐)
```bash
# 在 Node-01 执行,初始化所有配置到 Nacos 数据库
docker exec -i mysql mysql -uroot -proot123456 nacos_config < /opt/HA-OVP/sql/nacos-config-init.sql
# 验证配置是否导入成功
docker exec mysql mysql -uroot -proot123456 -e "SELECT data_id FROM nacos_config.config_info;"
```
SQL 脚本会自动创建以下配置:
| Data ID | 说明 |
|---------|------|
| common.yaml | 公共配置(MySQL连接池、MyBatis、Feign等) |
| common-redis-dev.yaml | 开发环境 Redis 单机配置 |
| common-redis-ha.yaml | 生产环境 Redis Sentinel 配置 |
| video-gateway.yaml | Gateway 服务专属配置 |
| video-auth.yaml | Auth 服务专属配置 |
| video-user.yaml | User 服务专属配置 |
| video-content.yaml | Content 服务专属配置 |
| video-comment.yaml | Comment 服务专属配置 |
| video-recommend.yaml | Recommend 服务专属配置 |
| video-admin.yaml | Admin 服务专属配置 |
#### 方式二:控制台手动创建
1. **创建命名空间**: 命名空间 → 新建命名空间 → 命名空间ID: `public`, 命名空间名: `公共命名空间`
2. **添加配置文件**: 配置管理 → 配置列表 → 创建配置
- Data ID: `common.yaml`
- Group: `DEFAULT_GROUP`
- 配置内容: 参考 `sql/nacos-config-init.sql` 中的内容
3. **重复以上步骤添加其他配置文件**
#### 配置架构说明
配置采用分层加载模式:
```
application.yml (本地,极简)
├── common.yaml (公共配置)
├── common-redis-${profile}.yaml (按环境的Redis配置)
└── ${spring.application.name}.yaml (服务专属配置)
```
**环境切换**:通过 `SPRING_PROFILES_ACTIVE` 环境变量
- `dev`: 加载 `common-redis-dev.yaml`(单机Redis)
- `ha`: 加载 `common-redis-ha.yaml`(Sentinel哨兵)
**配置热更新**:修改 Nacos 中的配置后,服务会自动感知并更新,无需重启!
### 2.10 初始化 MinIO
```bash
# 安装 mc 客户端
wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
mv mc /usr/local/bin/
# 配置别名 (注意端口是 9100)
mc alias set myminio http://192.168.1.101:9100 minioadmin minioadmin123
# 创建存储桶
mc mb myminio/video-platform --ignore-existing
# 设置公开访问策略
mc anonymous set download myminio/video-platform
# 验证
mc ls myminio
```
#### 对象存储高可用方案(方案A)
当前生产部署不采用 MinIO 真正分布式集群模式,而采用更稳定的“单写入点 + 副本同步”方案:
- `Node-01 MinIO (192.168.1.101:9100)`:唯一业务写入点,也是前端统一读取入口
- `Node-02 MinIO (192.168.1.102:9100)`:同步副本,仅用于灾备,不作为业务随机写入点
- 前端媒体访问统一使用 `http://192.168.1.101:9100/video-platform`
- `video-user`、`video-content` 服务的 MinIO 上传目标统一配置为 `MINIO_HOST=192.168.1.101`、`MINIO_PORT=9100`
#### MinIO 同步脚本
仓库已提供同步脚本:
```bash
/opt/HA-OVP/backend/scripts/minio-sync-node01-to-node02.sh
```
脚本作用:
- 将 `Node-01` 的 `video-platform` 桶内容镜像同步到 `Node-02`
- 适用于头像、封面、视频文件的对象复制
#### 配置定时同步任务
在 `Node-01` 上配置 cron,每 5 分钟同步一次:
```bash
crontab -e
*/5 * * * * /bin/bash /opt/HA-OVP/backend/scripts/minio-sync-node01-to-node02.sh >> /var/log/minio-sync.log 2>&1
```
检查定时任务:
```bash
crontab -l
tail -f /var/log/minio-sync.log
```
#### 手动执行一次同步
首次部署完成后,建议在 `Node-01` 手动执行一次:
```bash
cd /opt/HA-OVP/backend
bash scripts/minio-sync-node01-to-node02.sh
```
#### 说明
- 该方案可以避免“上传成功但另一节点读取 404”的问题
- 该方案属于“对象存储主节点写入 + 副本节点同步”的高可用实现
- 严格意义上,这不是 MinIO 分布式纠删码集群,而是适合当前资源条件的稳定答辩版方案
---
## 三、端口分配汇总
### 3.1 各节点端口分配
| 服务 | Node-01 | Node-02 | Node-03 | 说明 |
|------|---------|---------|---------|------|
| **MySQL** | 3306 | 3306 | 3306 | 数据库主从 |
| **Redis** | 6379 | 6379 | 6379 | 缓存主从 |
| **Redis Sentinel** | 26379 | 26379 | 26379 | 哨兵监控 |
| **Nacos** | 8848 | 8848 | 8848 | 配置中心集群 |
| **RocketMQ NameServer** | 9876 | 9876 | 9876 | NameServer集群 |
| **RocketMQ Broker** | - | 10911 | 10911 | Broker双主 |
| **MinIO API** | 9100 | 9100 | 9000 | Node-01 为统一写入与读取入口,Node-02 为同步副本 |
| **MinIO Console** | 9101 | 9101 | 9001 | 对象存储控制台 |
| **Sentinel Dashboard** | 8880 | 8880 | 8880 | 熔断控制台 |
| **video-gateway** | 9000 | 9000 | - | API网关 |
| **video-auth** | 9001 | 9001 | - | 认证服务 |
| **video-user** | 9002 | 9002 | - | 用户服务 |
| **video-content** | 9003 | 9003 | - | 内容服务 |
| **video-comment** | 9004 | 9004 | - | 评论服务 |
| **video-recommend** | 9005 | 9005 | - | 推荐服务 |
| **video-admin** | 9006 | 9006 | - | 管理后台 |
| **Nginx** | 80 | - | - | 负载均衡 |
### 3.2 端口说明
- **MinIO 端口调整**: Node-01 和 Node-02 的 MinIO 端口改为 9100(API) 和 9101(Console),避免与微服务端口冲突
- **MinIO 访问策略**: 所有业务上传统一写入 Node-01 MinIO,Node-02 通过同步脚本保持副本一致性
- **Node-03**: 仅部署基础设施,不部署微服务,作为热备份节点
---
## 四、验证部署
### 4.1 检查服务状态
```bash
# 检查 Docker 容器状态
docker ps
# 检查 Nacos 服务注册
curl http://192.168.1.101:8848/nacos/v1/ns/service/list
# 检查 MySQL 主从
docker exec mysql mysql -uroot -proot123456 -e "SHOW SLAVE STATUS\G" | grep Running
# 检查 Redis 主从
docker exec redis redis-cli -a redis_HA_Cluster_2024 INFO replication | grep role
# 检查 Sentinel 状态
docker exec redis-sentinel redis-cli -p 26379 SENTINEL master mymaster
```
### 4.2 访问控制台
| 服务 | 地址 | 账号密码 |
|------|------|---------|
| Nacos | http://192.168.1.101:8848/nacos | nacos/nacos |
| Sentinel | http://192.168.1.101:8880 | sentinel/sentinel |
| MinIO Console | http://192.168.1.101:9101 | minioadmin/minioadmin123 |
| API Gateway | http://192.168.1.101:9000 | - |
### 4.3 API 测试
```bash
# 获取验证码
curl http://192.168.1.101:9000/auth/captcha
# 健康检查
curl http://192.168.1.101:9000/actuator/health
curl http://192.168.1.102:9000/actuator/health
```
---
## 五、高可用测试
### 5.1 测试服务故障转移
```bash
# 停止 Node-01 的网关
docker stop video-gateway
# 测试 API (应该自动切换到 Node-02)
curl http://192.168.1.102:9000/auth/captcha
# 恢复服务
docker start video-gateway
```
### 5.2 测试 Redis 主从切换
```bash
# 停止 Node-01 的 Redis
docker stop redis
# 查看 Sentinel 日志 (自动将从节点提升为主)
docker logs redis-sentinel
# 验证新的主节点
docker exec redis redis-cli -a redis_HA_Cluster_2024 INFO replication
# 恢复 Redis (会自动变为从节点)
docker start redis
```
### 5.3 测试 Nacos 集群
```bash
# 停止 Node-01 的 Nacos
docker stop nacos
# 验证服务注册仍然可用
curl http://192.168.1.102:8848/nacos/v1/ns/service/list
# 恢复 Nacos
docker start nacos
```
---
## 六、常见问题
### Q1: Docker 镜像拉取失败
```bash
# 配置国内镜像源
vim /etc/docker/daemon.json
# 添加镜像加速器
# 重启 Docker
systemctl restart docker
```
### Q2: Nacos 集群无法启动
```bash
# 检查时间同步
apt-get install -y ntpdate
ntpdate pool.ntp.org
# 检查数据库连接
docker exec mysql mysql -uroot -proot123456 -e "SHOW DATABASES;"
```
### Q3: 内存不足
```bash
# 查看内存使用
free -h
# 调整 JVM 参数
# 编辑 config/.env.node-xx 文件中的 JAVA_OPTS
JAVA_OPTS=-Xms128m -Xmx256m
```
### Q4: 端口冲突
```bash
# 查看端口占用
ss -tlnp | grep -E '3306|6379|8848|9000|9002'
# 停止冲突服务
systemctl stop mysql
systemctl stop redis
```
### Q5: 微服务报 "user not found!" 错误(Nacos 认证失败)
**问题描述**:微服务启动时报错 `com.alibaba.nacos.api.exception.NacosException: user not found!`
**原因**:Spring Boot 3.x 默认不加载 `bootstrap.yml` 文件,导致 Nacos 认证配置未生效。
**解决方案**:已在 docker-compose 中添加 Spring 环境变量:
```yaml
environment:
- SPRING_CLOUD_NACOS_DISCOVERY_USERNAME=nacos
- SPRING_CLOUD_NACOS_DISCOVERY_PASSWORD=nacos
- SPRING_CLOUD_NACOS_CONFIG_USERNAME=nacos
- SPRING_CLOUD_NACOS_CONFIG_PASSWORD=nacos
```
**验证**:启动后日志应显示 `username='nacos'` 而不是 `username=''`
### Q6: MySQL 从节点初始化失败
**问题描述**:MySQL 从节点容器启动报错 `The MySQL server is running with the --super-read-only option`
**原因**:MySQL 从节点配置了 `read_only=ON`,阻止了容器初始化脚本执行(创建数据库、用户等)。
**解决方案**:
1. 确保 `config/mysql/my-slave.cnf` 中已注释掉 `read_only` 和 `super_read_only`
2. 如果已经启动失败,需要删除数据卷重新初始化:
```bash
# 停止容器
docker compose -f docker-compose-node-02.yml down
# 删除 MySQL 数据卷
docker volume rm ha-ovp_mysql_data
# 重新启动
docker compose -f docker-compose-node-02.yml up -d
```
3. 主从复制配置完成后,手动启用只读模式:
```bash
docker exec mysql mysql -uroot -proot123456 -e "SET GLOBAL read_only = ON; SET GLOBAL super_read_only = ON;"
```
#### 主从复制异常修复(复制元数据损坏)
如果从节点执行 `START SLAVE` 或 `START REPLICA` 时报错类似:
```text
Replica failed to initialize applier metadata structure from the repository
```
说明复制元数据已损坏,需要重建从库复制配置。
```bash
# 在 Node-02 执行
docker exec -i mysql mysql -uroot -proot123456 << 'EOF'
STOP REPLICA;
RESET REPLICA ALL;
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='192.168.1.101',
SOURCE_PORT=3306,
SOURCE_USER='repl',
SOURCE_PASSWORD='repl_password_2024',
SOURCE_AUTO_POSITION=1,
GET_SOURCE_PUBLIC_KEY=1;
START REPLICA;
SHOW REPLICA STATUS\G;
EOF
# 确认以下字段为正常状态
# Replica_IO_Running: Yes
# Replica_SQL_Running: Yes
# Seconds_Behind_Source: 0
# 恢复从库只读
docker exec mysql mysql -uroot -proot123456 -e "SET GLOBAL read_only = ON; SET GLOBAL super_read_only = ON;"
```
Node-03 修复方式相同,只需将命令执行节点换为 Node-03。
### Q7: RocketMQ NameServer 健康检查失败
**问题描述**:RocketMQ NameServer 容器显示 unhealthy
**原因**:容器内缺少 `nc` (netcat) 命令
**解决方案**:已修改健康检查命令使用 `pgrep`:
```yaml
healthcheck:
test: ["CMD-SHELL", "pgrep -f 'NamesrvStartup' || exit 1"]
```
### Q8: Redis Sentinel 报 "Read-only file system" 错误
**问题描述**:Redis Sentinel 无法写入配置文件
**解决方案**:已修改为将配置文件复制到可写的数据卷:
```yaml
volumes:
- sentinel_data:/data
- ./config/redis/sentinel.conf:/usr/local/etc/redis/sentinel.conf
command: sh -c "cp /usr/local/etc/redis/sentinel.conf /data/sentinel.conf && redis-sentinel /data/sentinel.conf"
```
### Q9: Redis 日志文件路径错误
**问题描述**:Redis 启动报错 `Can't open the log file`
**解决方案**:修改 `config/redis/redis-master.conf` 和 `redis-slave.conf`:
```conf
# 将 logfile /var/log/redis/redis-server.log 改为
logfile ""
```
### Q10: Nacos 集群模式报 "dataSource or tableName is null"
**问题描述**:Nacos 集群模式启动失败
**解决方案**:确保 docker-compose 中添加了 `SPRING_DATASOURCE_PLATFORM=mysql` 环境变量:
```yaml
environment:
- MODE=cluster
- SPRING_DATASOURCE_PLATFORM=mysql
- MYSQL_SERVICE_HOST=mysql
- MYSQL_SERVICE_PORT=3306
- MYSQL_SERVICE_DB_NAME=nacos_config
```
### Q11: RocketMQ Broker 启动失败报 NullPointerException
**问题描述**:RocketMQ Broker 启动时报错:
```
java.lang.NullPointerException
at org.apache.rocketmq.broker.schedule.ScheduleMessageService.configFilePath
```
**原因**:
1. Docker 命名卷权限问题:官方镜像以 `rocketmq` 用户 (UID 3000) 运行,但命名卷默认属于 root
2. 存储路径配置问题:`delayOffsetStorePath` 等路径指向不存在的子目录
**解决方案**:
1. 在 docker-compose 中添加 `user: root`:
```yaml
rmqbroker:
image: apache/rocketmq:5.1.4
user: root # 添加此行
container_name: rmqbroker
```
2. 清理旧数据并重启:
```bash
docker compose -f docker-compose-node-02.yml rm -f -s rmqbroker
docker volume rm ha-ovp_rmqbroker_store ha-ovp_rmqbroker_logs
docker compose -f docker-compose-node-02.yml up -d rmqbroker
```
### Q12: Nacos 服务注册显示 Docker 内部 IP(假高可用问题)
**问题描述**:
- Nacos 控制台显示服务实例 IP 为 `172.18.0.x` 等 Docker 内部 IP
- 同一服务在不同节点只显示 1 个实例(IP 冲突覆盖)
- 跨节点服务调用失败
**原因**:Docker 桥接网络中,每个容器获得内部虚拟 IP。微服务使用容器 IP 注册到 Nacos,导致:
1. 不同节点的容器可能获得相同 IP,Nacos 认为是同一实例
2. Gateway 无法通过容器内部 IP 访问其他节点的服务
**解决方案**:在 docker-compose 中添加 `SPRING_CLOUD_NACOS_DISCOVERY_IP` 环境变量:
```yaml
# Node-01 (192.168.1.101)
video-gateway:
environment:
- SPRING_CLOUD_NACOS_DISCOVERY_IP=${HOST_IP:-192.168.1.101}
# Node-02 (192.168.1.102)
video-gateway:
environment:
- SPRING_CLOUD_NACOS_DISCOVERY_IP=${HOST_IP:-192.168.1.102}
```
**验证**:重启微服务后,Nacos 控制台应显示真实宿主机 IP(如 `192.168.1.101:9000`、`192.168.1.102:9000`)
### Q13: Sentinel 控制台显示容器内部 IP 或实例失联
**问题描述**:
- Sentinel 控制台显示机器列表 IP 为 `172.18.0.x` 等 Docker 内部 IP
- 部分实例显示"失联"状态,健康实例数不正确
- 跨节点无法进行限流降级
**原因**:与 Nacos 问题类似,Sentinel 客户端使用容器内部 IP 注册到 Dashboard,导致跨节点无法通信。
**解决方案**:在 docker-compose 中添加 `SPRING_CLOUD_SENTINEL_TRANSPORT_CLIENT_IP` 环境变量:
```yaml
# Node-01 (192.168.1.101)
video-gateway:
environment:
- SPRING_CLOUD_SENTINEL_TRANSPORT_CLIENT_IP=${HOST_IP:-192.168.1.101}
# Node-02 (192.168.1.102)
video-gateway:
environment:
- SPRING_CLOUD_SENTINEL_TRANSPORT_CLIENT_IP=${HOST_IP:-192.168.1.102}
```
**注意**:环境变量名必须是 `SPRING_CLOUD_SENTINEL_TRANSPORT_CLIENT_IP`,对应 Spring 属性 `spring.cloud.sentinel.transport.client-ip`。
**重要**:还需要配置以下两项:
1. **端口映射**:每个微服务需要映射 Sentinel 通信端口(默认从 8719 开始)
2. **端口指定**:通过 `SPRING_CLOUD_SENTINEL_TRANSPORT_PORT` 指定固定端口
```yaml
# Node-01 微服务 Sentinel 端口分配
video-gateway:
ports:
- "9000:9000"
- "8719:8719" # Sentinel 端口
environment:
- SPRING_CLOUD_SENTINEL_TRANSPORT_PORT=8719
video-auth:
ports:
- "9001:9001"
- "8720:8720" # Sentinel 端口
environment:
- SPRING_CLOUD_SENTINEL_TRANSPORT_PORT=8720
# 以此类推: user=8721, content=8722, comment=8723, recommend=8724, admin=8725
# Node-02 微服务使用不同端口避免冲突: 8739-8745
```
3. **Dashboard 统一地址**:Node-02 及其他节点的微服务必须连接 Node-01 的 Sentinel Dashboard:
```yaml
# Node-02 微服务
environment:
- SENTINEL_DASHBOARD_HOST=192.168.1.101 # 指向 Node-01
- SENTINEL_DASHBOARD_PORT=8880 # Dashboard 宿主机端口
```
**验证**:重启微服务后,Sentinel 控制台应显示真实宿主机 IP,且所有实例状态为"健康"
### Q14: 微服务报 Redis READONLY 错误
**问题描述**:微服务启动时报错:
```
io.lettuce.core.RedisReadOnlyException: READONLY You can't write against a read only replica.
```
**原因**:微服务使用单机模式连接 Redis(`REDIS_HOST`/`REDIS_PORT`),直接连接到了 Redis 从节点,而从节点是只读的。
**解决方案**:
1. 使用 Nacos 配置中心管理 Redis 配置,通过 `SPRING_PROFILES_ACTIVE` 区分环境:
- `dev`: 加载 `common-redis-dev.yaml`(单机模式)
- `ha`: 加载 `common-redis-ha.yaml`(Sentinel模式)
2. 确保 docker-compose 中配置了正确的环境变量:
```yaml
environment:
- SPRING_PROFILES_ACTIVE=ha
- REDIS_PASSWORD=${REDIS_PASSWORD:-redis_HA_Cluster_2024}
- REDIS_SENTINEL_NODES=${REDIS_SENTINEL_NODES:-192.168.1.101:26379,192.168.1.102:26379,192.168.1.103:26379}
```
3. 确保 Nacos 中 `common-redis-ha.yaml` 配置正确:
```yaml
spring:
data:
redis:
password: ${REDIS_PASSWORD:redis_HA_Cluster_2024}
sentinel:
master: mymaster
nodes: ${REDIS_SENTINEL_NODES:192.168.1.101:26379,192.168.1.102:26379,192.168.1.103:26379}
password: ${REDIS_PASSWORD:redis_HA_Cluster_2024}
```
**验证**:
```bash
# 查看当前 Redis 主节点
docker exec redis-sentinel redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster
```
### Q15: Nacos 配置不生效或配置冲突
**问题描述**:修改 Nacos 配置后服务没有更新,或启动时配置异常。
**原因**:
1. 本地 `application.yml` 与 Nacos 配置冲突
2. 配置加载顺序问题
3. `spring.config.import` 配置错误
**解决方案**:
1. **确保本地 application.yml 极简**:只保留 Nacos 连接配置
```yaml
spring:
application:
name: video-auth
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}
config:
import:
- optional:nacos:common.yaml?refreshEnabled=true
- optional:nacos:common-redis-${spring.profiles.active}.yaml?refreshEnabled=true
- optional:nacos:${spring.application.name}.yaml?refreshEnabled=true
cloud:
nacos:
discovery:
server-addr: ${NACOS_SERVER_ADDR:127.0.0.1:8848}
namespace: ${NACOS_NAMESPACE:}
username: ${NACOS_USERNAME:nacos}
password: ${NACOS_PASSWORD:nacos}
```
2. **配置加载顺序**:
- common.yaml(公共配置)
- common-redis-${profile}.yaml(环境配置)
- ${spring.application.name}.yaml(服务配置)
3. **验证配置加载**:
```bash
# 查看服务启动日志
docker logs video-auth 2>&1 | grep -i nacos
# 应看到类似: "Loading nacos config, dataId: common.yaml"
```
4. **强制刷新配置**:修改 Nacos 配置后,服务会自动感知更新(`refreshEnabled=true`)
### Q16: 头像或封面上传成功但访问 404
**问题描述**:
- 用户头像上传接口返回成功
- 但浏览器访问图片地址时返回 `404 Not Found`
- 常见于头像、视频封面等图片资源
**原因**:
- 对象被上传到了 `Node-02 MinIO`
- 前端固定从 `Node-01 MinIO` 读取
- 由于两个 MinIO 实例不是分布式共享存储,导致对象只存在于上传节点
**解决方案**:
1. 统一所有业务上传写入 `Node-01 MinIO`:
```yaml
MINIO_HOST=192.168.1.101
MINIO_PORT=9100
```
2. 在 `Node-01` 执行同步脚本,把对象同步到 `Node-02`:
```bash
cd /opt/HA-OVP/backend
bash scripts/minio-sync-node01-to-node02.sh
```
3. 配置 cron 定时同步,避免后续对象再次不一致:
```bash
*/5 * * * * /bin/bash /opt/HA-OVP/backend/scripts/minio-sync-node01-to-node02.sh >> /var/log/minio-sync.log 2>&1
```
**说明**:
- 该方案是“统一写 Node-01 + 同步到 Node-02”的稳定方案
- 不要将当前架构表述为真正的 MinIO 分布式集群
---
## 七、日常运维
### 7.1 日志查看
```bash
# 查看服务日志
docker logs -f --tail 100 video-gateway
docker logs -f --tail 100 video-auth
# 查看所有服务状态
docker compose -f docker-compose-node-01.yml ps
```
### 7.2 服务重启
```bash
# 重启单个服务
docker restart video-gateway
# 重启所有服务
docker compose -f docker-compose-node-01.yml restart
```
### 7.3 数据备份
```bash
# MySQL 备份
docker exec mysql mysqldump -uroot -proot123456 --all-databases > backup_$(date +%Y%m%d).sql
# Redis 备份
docker exec redis redis-cli -a redis_HA_Cluster_2024 BGSAVE
docker cp redis:/data/dump.rdb redis_backup_$(date +%Y%m%d).rdb
# MinIO 备份
mc mirror myminio/video-platform minio_backup_$(date +%Y%m%d)
```
---
## 八、性能调优
### 8.1 JVM 调优
根据实际内存调整 JVM 参数:
```bash
# 8GB 内存服务器建议配置
JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -XX:+UseG1GC
# 16GB 内存服务器建议配置
JAVA_OPTS=-Xms1g -Xmx2g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:+UseG1GC
```
### 8.2 MySQL 调优
```bash
# 编辑 MySQL 配置
vim config/mysql/my-master.cnf
# 关键参数
innodb_buffer_pool_size = 2G # 设置为物理内存的 50-70%
innodb_log_file_size = 512M
max_connections = 500
```
### 8.3 Redis 调优
```bash
# 编辑 Redis 配置
vim config/redis/redis-master.conf
# 关键参数
maxmemory 2gb
maxmemory-policy allkeys-lru
```
---
## 九、架构图
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ 高可用集群架构 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ │ Node-01 │ │ Node-02 │ │ Node-03 │
│ │ 192.168.1.101 │ │ 192.168.1.102 │ │ 192.168.1.103 │
│ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤
│ │ MySQL Master │ ──────▶ │ MySQL Slave │ ──────▶ │ MySQL Slave │
│ │ Redis Master │ ◀────── │ Redis Slave │ ◀────── │ Redis Slave │
│ │ Nacos Node1 │ ◀─────▶ │ Nacos Node2 │ ◀─────▶ │ Nacos Node3 │
│ │ NameServer │ │ NameServer │ │ NameServer │
│ │ MinIO :9100 │ │ Broker-A :10911 │ │ Broker-B :10911 │
│ │ Sentinel :8880 │ │ MinIO :9100 │ │ MinIO :9000 │
│ ├─────────────────┤ │ Sentinel :8880 │ │ Sentinel :8880 │
│ │ Gateway :9000 │ ├─────────────────┤ └─────────────────┘
│ │ Auth :9001 │ │ Gateway :9000 │ (备份节点)
│ │ User :9002 │ │ Auth :9001 │
│ │ Content :9003 │ │ User :9002 │
│ │ Comment :9004 │ │ Content :9003 │
│ │ Recommend :9005 │ │ Comment :9004 │
│ │ Admin :9006 │ │ Recommend :9005 │
│ │ Nginx :80 │ │ Admin :9006 │
│ └─────────────────┘ └─────────────────┘
│ (主节点,统一对象写入点) (从节点,对象同步副本)
└─────────────────────────────────────────────────────────────────────────────┘
```