# PPanel 双机部署:远程 MySQL 主从、Redis 主从/Sentinel、多实例与监控 本文档说明如何部署两台机器: - 主机:运行主 MySQL、主 Redis、PPanel Server、Grafana、Prometheus、Loki、Tempo。 - 从机:运行 MySQL 从库、Redis 从库、Redis Sentinel、额外 PPanel Server。 - Nginx:只做 HTTP 负载均衡,不做数据库读写分离。 - MCP Grafana:安装在本地电脑,不部署到服务器,用于后续排查线上 bug。 重要原则: - 所有 `ppanel-server` 实例都连接主 MySQL 和主 Redis。 - 从机 MySQL 只做复制/灾备,不给业务直接写入。 - 当前后端只有单个 `MySQL.Addr` 和单个 `Redis.Host`,不要让业务实例连接本机从库。 - 故障切换采用人工确认,避免自动切换造成脑裂。 ## 1. 拓扑与端口 示例变量: ```text 主机公网 IP: MASTER_PUBLIC_IP 从机公网 IP: REPLICA_PUBLIC_IP 业务域名: api.example.com 数据库名: ppanel ``` 主机端口: | 端口 | 用途 | 建议暴露范围 | | --- | --- | --- | | `8080` | 主机 PPanel Server | Nginx 所在机器 | | `3306` | 主 MySQL | 只允许从机 IP | | `6379` | 主 Redis | 只允许从机 IP | | `3333` | Grafana | 默认 `127.0.0.1`,通过 SSH 隧道 | | `9090` | Prometheus | 默认 `127.0.0.1` | | `4317` | Tempo OTLP | 默认 `127.0.0.1` | 从机端口: | 端口 | 用途 | 建议暴露范围 | | --- | --- | --- | | `8080` | 从机 PPanel Server | Nginx 所在机器 | | `3307` | 本机 MySQL 从库调试端口 | 默认 `127.0.0.1` | | `6380` | 本机 Redis 从库调试端口 | 默认 `127.0.0.1` | | `26379` | 本机 Redis Sentinel | 默认 `127.0.0.1` | ## 2. 文件准备 主机上传: ```text docker-compose.cloud.yml .env configs/ mysql/ redis/ loki/ grafana/ prometheus/ tempo/ config/ ``` 从机上传: ```text docker-compose.replica.yml .env configs/ mysql/ redis/ tempo/ cache/ ``` 两台机器都创建运行目录: ```bash mkdir -p configs logs cache tempo_data mysql redis ``` 主机如果使用监控 exporter,还需要准备 MySQL exporter 配置: ```bash cp mysql/.my.cnf.example mysql/.my.cnf ``` 编辑 `mysql/.my.cnf`,把密码改成真实 `MYSQL_ROOT_PASSWORD`。该文件已被 `.gitignore` 忽略,不要提交。 ## 3. 主机 `.env` 在主机复制 `.env.example`: ```bash cp .env.example .env ``` 主机 `.env` 示例: ```dotenv MYSQL_ROOT_PASSWORD=CHANGE_ME_STRONG_MYSQL_ROOT_PASSWORD MYSQL_DATABASE=ppanel MYSQL_PUBLISH_ADDR=0.0.0.0 MYSQL_REPLICATION_USER=replicator MYSQL_REPLICATION_PASSWORD=CHANGE_ME_STRONG_REPLICATION_PASSWORD REDIS_PASSWORD=CHANGE_ME_STRONG_REDIS_PASSWORD REDIS_PUBLISH_ADDR=0.0.0.0 GRAFANA_PASSWORD=CHANGE_ME_STRONG_GRAFANA_PASSWORD PPANEL_SERVER_TAG=latest ``` 如果只想让 MySQL/Redis 监听内网 IP,把 `MYSQL_PUBLISH_ADDR` 和 `REDIS_PUBLISH_ADDR` 改成内网地址。 ## 4. 主机 `configs/ppanel.yaml` 主机业务实例连接本机主库和主 Redis: ```yaml MySQL: Addr: 127.0.0.1:3306 Username: root Password: CHANGE_ME_STRONG_MYSQL_ROOT_PASSWORD Dbname: ppanel Config: charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai MaxIdleConns: 10 MaxOpenConns: 100 SlowThreshold: 1000 Redis: Host: 127.0.0.1:6379 Pass: CHANGE_ME_STRONG_REDIS_PASSWORD DB: 0 ``` 注意:启用当前 compose 后 Redis 有密码,`Redis.Pass` 必须填写。 ## 5. 主机防火墙 主机必须只允许从机 IP 访问 MySQL 和 Redis。 UFW 示例: ```bash ufw allow from REPLICA_PUBLIC_IP to any port 3306 proto tcp ufw allow from REPLICA_PUBLIC_IP to any port 6379 proto tcp ufw deny 3306/tcp ufw deny 6379/tcp ufw reload ``` firewalld 示例: ```bash firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="REPLICA_PUBLIC_IP" port protocol="tcp" port="3306" accept' firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="REPLICA_PUBLIC_IP" port protocol="tcp" port="6379" accept' firewall-cmd --reload ``` 如果使用云厂商安全组,也要同步设置白名单。 ## 6. 启动主机 检查 compose: ```bash docker compose -f docker-compose.cloud.yml config ``` 启动: ```bash docker compose -f docker-compose.cloud.yml up -d ``` 检查服务: ```bash docker ps docker logs --tail=100 ppanel-mysql docker logs --tail=100 ppanel-redis docker logs --tail=100 ppanel-server ``` 检查 MySQL GTID: ```bash docker exec ppanel-mysql mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "SHOW VARIABLES LIKE 'gtid_mode';" docker exec ppanel-mysql mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "SHOW MASTER STATUS\\G" ``` 检查 Redis: ```bash docker exec ppanel-redis redis-cli -a "$REDIS_PASSWORD" ping ``` 如果主库已有数据,`mysql/init-master.sh` 不会自动重跑。手动创建复制账号: ```bash docker exec -i ppanel-mysql mysql -uroot -p"$MYSQL_ROOT_PASSWORD" < ppanel-gtid.sql.gz ``` 把备份复制到从机: ```bash scp ppanel-gtid.sql.gz root@REPLICA_PUBLIC_IP:/root/ppanel-gtid.sql.gz ``` ## 8. 从机 `.env` 从机也复制 `.env.example`: ```bash cp .env.example .env ``` 从机 `.env` 示例: ```dotenv MYSQL_ROOT_PASSWORD=CHANGE_ME_STRONG_MYSQL_ROOT_PASSWORD MYSQL_DATABASE=ppanel MYSQL_REPLICATION_USER=replicator MYSQL_REPLICATION_PASSWORD=CHANGE_ME_STRONG_REPLICATION_PASSWORD REDIS_PASSWORD=CHANGE_ME_STRONG_REDIS_PASSWORD MASTER_MYSQL_HOST=MASTER_PUBLIC_IP MASTER_MYSQL_PORT=3306 MASTER_REDIS_HOST=MASTER_PUBLIC_IP MASTER_REDIS_PORT=6379 GRAFANA_PASSWORD=CHANGE_ME_STRONG_GRAFANA_PASSWORD PPANEL_SERVER_TAG=latest ``` `MYSQL_ROOT_PASSWORD` 建议和主机一致,方便备份导入和后续切换。 ## 9. 从机 `configs/ppanel.yaml` 从机业务实例仍然连接主机 MySQL 和主机 Redis: ```yaml MySQL: Addr: MASTER_PUBLIC_IP:3306 Username: root Password: CHANGE_ME_STRONG_MYSQL_ROOT_PASSWORD Dbname: ppanel Config: charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai MaxIdleConns: 10 MaxOpenConns: 100 SlowThreshold: 1000 Redis: Host: MASTER_PUBLIC_IP:6379 Pass: CHANGE_ME_STRONG_REDIS_PASSWORD DB: 0 Trace: Endpoint: "127.0.0.1:4317" ``` 不要写成: ```yaml MySQL: Addr: 127.0.0.1:3307 Redis: Host: 127.0.0.1:6380 ``` 这样会让业务连接本机从库,写请求会失败或造成状态不一致。 ## 10. 启动从机基础服务 检查 compose: ```bash docker compose -f docker-compose.replica.yml config ``` 启动 MySQL 从库、Redis 从库、Sentinel 和 Tempo: ```bash docker compose -f docker-compose.replica.yml up -d mysql-replica redis-replica redis-sentinel tempo ``` 查看日志: ```bash docker logs --tail=100 ppanel-mysql-replica docker logs --tail=100 ppanel-redis-replica docker logs --tail=100 ppanel-redis-sentinel ``` ## 11. 导入主库备份到从库 导入前临时关闭从库只读: ```bash docker exec -i ppanel-mysql-replica mysql -uroot -p"$MYSQL_ROOT_PASSWORD" <