6622 lines
197 KiB
Markdown
6622 lines
197 KiB
Markdown
# Docker Swarm 部署手册(Review 环境)
|
||
|
||
---
|
||
|
||
## 一、部署概览
|
||
|
||
### 环境信息
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| 主节点 | ZD-BAK-APP2 (192.168.x.132) |
|
||
| 操作系统 | CentOS 7 |
|
||
| 网络名称 | review |
|
||
| 网络子网 | 10.18.0.0/16 |
|
||
|
||
### 部署进度
|
||
|
||
| 阶段 | 状态 |
|
||
|------|------|
|
||
| 系统准备 | ✅ 完成 |
|
||
| 数据盘挂载 | ✅ 完成 |
|
||
| Docker 安装 | ✅ 完成 |
|
||
| Swarm 初始化 | ✅ 完成 |
|
||
| Overlay 网络 | ✅ 完成 |
|
||
| Portainer | ✅ 完成 |
|
||
| MySQL 主从复制 (mysql-repl-tool) | ✅ 完成 |
|
||
| RabbitMQ 集群 | ✅ 完成 |
|
||
| Nacos 集群 | ✅ 完成 |
|
||
| XXL-Job-Admin | ✅ 完成 |
|
||
| Canal | ✅ 完成 |
|
||
| Elasticsearch + Kibana | ✅ 完成 |
|
||
| Log (Logstash + Filebeat) | ✅ 完成 |
|
||
| SkyWalking | ✅ 完成 |
|
||
| MongoDB | ✅ 完成 |
|
||
| Redis Sentinel 集群 | ✅ 完成 |
|
||
| 后端服务 (sa-server) | ✅ 完成 |
|
||
| 前端服务 (sa-cc) | ✅ 完成 |
|
||
| Nginx 反向代理 | ✅ 完成 |
|
||
|
||
### 数据盘信息
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| 设备 | /dev/sdb |
|
||
| 容量 | 3.7T |
|
||
| 文件系统 | ext4 |
|
||
| UUID | 988dd535-6531-4a3e-89b7-104de26adcf2 |
|
||
| 挂载点 | /mnt/data |
|
||
| 数据目录 | /mnt/data/volumes |
|
||
|
||
### 集群节点信息
|
||
|
||
| 节点 | 主机名 | IP | 角色 |
|
||
|------|--------|-----|------|
|
||
| 节点1 | ZD-BAK-APP1 | 192.168.3.134 | Worker |
|
||
| 节点2 | ZD-BAK-APP2 | 192.168.3.132 | Manager (Leader) |
|
||
| 节点3 | zd-bak-app3 | 192.168.3.133 | Worker |
|
||
| 节点4 | ZD-FDFS4 | 192.168.3.125 | Worker |
|
||
|
||
---
|
||
|
||
## 二、系统准备
|
||
|
||
### 2.1 更换阿里云 YUM 源
|
||
|
||
```bash
|
||
# 备份官方源
|
||
cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak
|
||
|
||
# 下载阿里云源
|
||
curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
|
||
|
||
# 重建缓存
|
||
yum clean all && yum makecache
|
||
```
|
||
|
||
### 2.2 验证 YUM 源
|
||
|
||
```bash
|
||
grep aliyun /etc/yum.repos.d/CentOS-Base.repo
|
||
```
|
||
|
||
### 2.3 数据盘挂载配置
|
||
|
||
> **重要**:在部署服务前,必须先完成数据盘挂载。如果先部署服务再挂载数据盘,已写入的数据将被"遮盖",需要迁移处理。
|
||
|
||
#### 2.3.1 挂载原理
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Linux 文件系统树 │
|
||
│ / │
|
||
│ │ │
|
||
│ ┌────────────────────┼────────────────────┐ │
|
||
│ │ │ │ │
|
||
│ /home /mnt /var │
|
||
│ (sda3 64G) │ │
|
||
│ /data ◄── /dev/sdb 3.7T 数据盘 │
|
||
│ │ │
|
||
│ /volumes │
|
||
│ │ │
|
||
│ ┌───────────────┼───────────────┐ │
|
||
│ │ │ │ │
|
||
│ /elasticsearch /mongodb /kibana │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**说明:**
|
||
- `/dev/sdb` 是 3.7T 数据盘,挂载到 `/mnt/data`
|
||
- 所有写入 `/mnt/data` 及其子目录的数据都存储在数据盘上
|
||
- 服务数据目录统一放在 `/mnt/data/volumes/` 下
|
||
|
||
#### 2.3.2 查看磁盘信息
|
||
|
||
```bash
|
||
# 查看所有磁盘和分区
|
||
lsblk
|
||
|
||
# 预期输出:
|
||
# NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
|
||
# sda 8:0 0 894.3G 0 disk
|
||
# ├─sda1 8:1 0 200M 0 part /boot/efi
|
||
# ├─sda2 8:2 0 1G 0 part /boot
|
||
# ├─sda3 8:3 0 64G 0 part /home
|
||
# ├─sda4 8:4 0 31.3G 0 part [SWAP]
|
||
# └─sda5 8:5 0 797G 0 part /
|
||
# sdb 8:16 0 3.7T 0 disk ← 数据盘(待挂载或已挂载)
|
||
|
||
# 查看磁盘文件系统类型和 UUID
|
||
blkid /dev/sdb
|
||
```
|
||
|
||
#### 2.3.3 挂载数据盘
|
||
|
||
```bash
|
||
# 创建挂载点
|
||
mkdir -p /mnt/data
|
||
|
||
# 如果数据盘未格式化,先格式化(慎重!会清除数据)
|
||
# mkfs.ext4 /dev/sdb
|
||
|
||
# 临时挂载(重启后失效)
|
||
mount /dev/sdb /mnt/data
|
||
|
||
# 验证挂载
|
||
df
|
||
-h /mnt/data
|
||
```
|
||
|
||
#### 2.3.4 配置开机自动挂载
|
||
|
||
```bash
|
||
# 获取磁盘 UUID
|
||
blkid /dev/sdb
|
||
# 输出示例:/dev/sdb: UUID="988dd535-6531-4a3e-89b7-104de26adcf2" TYPE="ext4"
|
||
|
||
# 备份 fstab
|
||
cp /etc/fstab /etc/fstab.bak
|
||
|
||
# 添加自动挂载配置(使用 UUID 更可靠)
|
||
echo 'UUID=988dd535-6531-4a3e-89b7-104de26adcf2 /mnt/data ext4 defaults 0 0' >> /etc/fstab
|
||
|
||
# 验证 fstab 配置
|
||
cat /etc/fstab
|
||
|
||
# 测试配置是否正确(不应报错)
|
||
mount -a
|
||
|
||
# 确认挂载成功
|
||
df -h /mnt/data
|
||
```
|
||
|
||
**fstab 格式说明:**
|
||
|
||
| 字段 | 说明 | 示例值 |
|
||
|------|------|--------|
|
||
| 设备 | UUID 或设备路径 | UUID=988dd535-... |
|
||
| 挂载点 | 目录路径 | /mnt/data |
|
||
| 文件系统 | ext4/xfs/等 | ext4 |
|
||
| 选项 | 挂载选项 | defaults |
|
||
| dump | 备份标志 | 0 |
|
||
| fsck | 检查顺序 | 0 |
|
||
|
||
#### 2.3.5 创建服务数据目录
|
||
|
||
```bash
|
||
# 创建各服务数据目录
|
||
mkdir -p /mnt/data/volumes/elasticsearch
|
||
mkdir -p /mnt/data/volumes/kibana/data
|
||
mkdir -p /mnt/data/volumes/kibana/config
|
||
mkdir -p /mnt/data/volumes/mongodb
|
||
|
||
# 设置权限(Bitnami 镜像使用 UID 1001)
|
||
chown -R 1001:1001 /mnt/data/volumes/elasticsearch
|
||
chown -R 1001:1001 /mnt/data/volumes/kibana
|
||
chown -R 1001:1001 /mnt/data/volumes/mongodb
|
||
|
||
# 验证
|
||
ls -la /mnt/data/volumes/
|
||
```
|
||
|
||
#### 2.3.6 常见问题
|
||
|
||
**问题 1:fstab 语法错误**
|
||
|
||
错误示例:
|
||
```
|
||
"/dev/sdb /mnt/data ext4 defaults 0 0" ← 整行被引号包裹(错误)
|
||
```
|
||
|
||
正确写法:
|
||
```
|
||
UUID=988dd535-6531-4a3e-89b7-104de26adcf2 /mnt/data ext4 defaults 0 0
|
||
```
|
||
|
||
**问题 2:先部署服务后挂载数据盘**
|
||
|
||
如果在挂载数据盘之前部署了服务,数据会写入根分区。挂载数据盘后,原数据会被"遮盖"。
|
||
|
||
**解决方案:**
|
||
|
||
```bash
|
||
# 1. 停止相关服务
|
||
docker stack rm review_log_es
|
||
docker stack rm review_mongodb
|
||
|
||
# 2. 卸载数据盘
|
||
umount /mnt/data
|
||
|
||
# 3. 查看原数据(此时可以看到写在根分区的数据)
|
||
ls -la /mnt/data/volumes/
|
||
du -sh /mnt/data/volumes/*
|
||
|
||
# 4. 备份原数据
|
||
mv /mnt/data /mnt/data_old
|
||
|
||
# 5. 重新创建挂载点并挂载数据盘
|
||
mkdir -p /mnt/data
|
||
mount /dev/sdb /mnt/data
|
||
|
||
# 6. 迁移数据到数据盘
|
||
cp -a /mnt/data_old/volumes /mnt/data/
|
||
|
||
# 7. 设置权限
|
||
chown -R 1001:1001 /mnt/data/volumes/elasticsearch
|
||
chown -R 1001:1001 /mnt/data/volumes/kibana
|
||
chown -R 1001:1001 /mnt/data/volumes/mongodb
|
||
|
||
# 8. 重新部署服务
|
||
cd /opt/swarm/support/elasticsearch
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_log_es
|
||
|
||
cd /opt/swarm/support/mongodb
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_mongodb
|
||
|
||
# 9. 确认无误后删除备份
|
||
rm -rf /mnt/data_old
|
||
```
|
||
|
||
---
|
||
|
||
## 三、Docker 安装
|
||
|
||
### 3.1 卸载旧版本
|
||
|
||
```bash
|
||
sudo yum remove -y docker \
|
||
docker-client \
|
||
docker-client-latest \
|
||
docker-common \
|
||
docker-latest \
|
||
docker-latest-logrotate \
|
||
docker-logrotate \
|
||
docker-engine
|
||
```
|
||
|
||
### 3.2 安装依赖工具
|
||
|
||
```bash
|
||
sudo yum install -y yum-utils
|
||
```
|
||
|
||
### 3.3 配置 Docker 仓库
|
||
|
||
```bash
|
||
# 使用阿里云镜像源
|
||
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
|
||
```
|
||
|
||
### 3.4 安装 Docker
|
||
|
||
```bash
|
||
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||
```
|
||
|
||
### 3.5 配置 Docker Daemon
|
||
|
||
```bash
|
||
mkdir -p /etc/docker
|
||
|
||
echo '{"log-opts":{"max-size":"1g","max-file":"3"},"registry-mirrors":["https://registry.cn-hangzhou.aliyuncs.com"]}' > /etc/docker/daemon.json
|
||
```
|
||
|
||
**配置说明:**
|
||
|
||
| 配置项 | 值 | 说明 |
|
||
|--------|-----|------|
|
||
| log-opts.max-size | 1g | 单个日志文件最大 1GB |
|
||
| log-opts.max-file | 3 | 最多保留 3 个日志文件 |
|
||
| registry-mirrors | 阿里云 | 镜像加速器 |
|
||
|
||
### 3.6 启动 Docker
|
||
|
||
```bash
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl start docker
|
||
sudo systemctl enable docker
|
||
|
||
# 验证
|
||
systemctl status docker
|
||
docker version
|
||
```
|
||
|
||
### 3.7 关闭防火墙
|
||
|
||
```bash
|
||
systemctl stop firewalld
|
||
systemctl disable firewalld
|
||
```
|
||
|
||
### 3.8 调整文件描述符限制
|
||
|
||
```bash
|
||
# 临时生效
|
||
ulimit -SHn 65536
|
||
|
||
# 永久生效
|
||
echo '* soft nofile 65535' >> /etc/security/limits.conf
|
||
echo '* hard nofile 65535' >> /etc/security/limits.conf
|
||
|
||
# 验证
|
||
tail -2 /etc/security/limits.conf
|
||
```
|
||
|
||
---
|
||
|
||
## 四、Docker Swarm 初始化
|
||
|
||
### 4.1 初始化集群(主节点)
|
||
|
||
```bash
|
||
docker swarm init
|
||
```
|
||
|
||
### 4.2 验证节点
|
||
|
||
```bash
|
||
docker node ls
|
||
```
|
||
|
||
预期输出:
|
||
|
||
```
|
||
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
|
||
xxx * ZD-BAK-APP2 Ready Active Leader
|
||
```
|
||
|
||
---
|
||
|
||
## 五、创建 Overlay 网络
|
||
|
||
```bash
|
||
docker network create \
|
||
--driver=overlay \
|
||
--subnet=10.18.0.0/16 \
|
||
--scope swarm \
|
||
--attachable \
|
||
review
|
||
```
|
||
|
||
**验证:**
|
||
|
||
```bash
|
||
docker network ls | grep review
|
||
```
|
||
|
||
---
|
||
|
||
## 六、部署 Portainer
|
||
|
||
### 6.1 配置文件
|
||
|
||
`docker-compose.yml`:
|
||
|
||
```yaml
|
||
version: '3.2'
|
||
|
||
services:
|
||
agent:
|
||
image: portainer/agent:2.20.3
|
||
volumes:
|
||
- /var/run/docker.sock:/var/run/docker.sock
|
||
- /var/lib/docker/volumes:/var/lib/docker/volumes
|
||
networks:
|
||
- agent_network
|
||
deploy:
|
||
mode: global
|
||
placement:
|
||
constraints: [node.platform.os == linux]
|
||
|
||
portainer:
|
||
image: portainer/portainer-ce:2.20.3
|
||
command: -H tcp://tasks.agent:9001 --tlsskipverify
|
||
ports:
|
||
- "9443:9443"
|
||
- "9000:9000"
|
||
- "8000:8000"
|
||
volumes:
|
||
- portainer_data:/data
|
||
networks:
|
||
- agent_network
|
||
deploy:
|
||
mode: replicated
|
||
replicas: 1
|
||
placement:
|
||
constraints: [node.hostname == ZD-BAK-APP2]
|
||
|
||
networks:
|
||
agent_network:
|
||
driver: overlay
|
||
attachable: true
|
||
|
||
volumes:
|
||
portainer_data:
|
||
```
|
||
|
||
### 6.2 部署命令
|
||
|
||
```bash
|
||
cd /path/to/docker-swarm-review/portainer
|
||
docker stack deploy --compose-file docker-compose.yml portainer
|
||
```
|
||
|
||
### 6.3 验证
|
||
|
||
```bash
|
||
docker stack ps portainer
|
||
docker service ls | grep portainer
|
||
```
|
||
|
||
### 6.4 访问地址
|
||
|
||
- HTTPS: `https://<服务器IP>:9443`
|
||
- HTTP: `http://<服务器IP>:9000`
|
||
|
||
---
|
||
|
||
## 七、多服务器节点管理
|
||
|
||
### 7.1 节点角色说明
|
||
|
||
| 角色 | 说明 |
|
||
|------|------|
|
||
| Manager | 管理节点,负责集群调度和状态管理 |
|
||
| Worker | 工作节点,只运行容器 |
|
||
|
||
> 建议:3 节点集群至少 1 个 Manager,生产环境建议 3 个 Manager。
|
||
|
||
### 7.2 新节点准备工作
|
||
|
||
在新服务器上依次执行(同主节点):
|
||
|
||
1. 更换阿里云 YUM 源
|
||
2. 安装 Docker
|
||
3. 配置 Docker Daemon
|
||
4. 启动 Docker
|
||
5. 关闭防火墙
|
||
6. 调整文件描述符限制
|
||
|
||
### 7.3 获取加入 Token(主节点执行)
|
||
|
||
```bash
|
||
# 获取 Worker 加入 Token
|
||
docker swarm join-token worker
|
||
|
||
# 获取 Manager 加入 Token
|
||
docker swarm join-token manager
|
||
```
|
||
|
||
### 7.4 加入集群(新节点执行)
|
||
|
||
```bash
|
||
docker swarm join --token SWMTKN-1-xxxxx <主节点IP>:2377
|
||
```
|
||
|
||
### 7.5 验证节点(主节点执行)
|
||
|
||
```bash
|
||
docker node ls
|
||
```
|
||
|
||
预期输出:
|
||
|
||
```
|
||
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
|
||
xxx * ZD-BAK-APP2 Ready Active Leader
|
||
xxx 节点2 Ready Active
|
||
xxx 节点3 Ready Active
|
||
```
|
||
|
||
### 7.6 节点标签管理
|
||
|
||
```bash
|
||
# 添加标签
|
||
docker node update --label-add <标签名>=<值> <节点名>
|
||
|
||
# 示例:标记 MySQL 运行节点
|
||
docker node update --label-add review_mysql=1 ZD-BAK-APP2
|
||
|
||
# 查看节点标签
|
||
docker node inspect <节点名> --format '{{.Spec.Labels}}'
|
||
```
|
||
|
||
### 7.7 节点管理命令
|
||
|
||
```bash
|
||
# 查看所有节点
|
||
docker node ls
|
||
|
||
# 将节点设为 Drain(停止接收新任务)
|
||
docker node update --availability drain <节点名>
|
||
|
||
# 将节点恢复为 Active
|
||
docker node update --availability active <节点名>
|
||
|
||
# 移除节点(在主节点执行)
|
||
docker node rm <节点名>
|
||
|
||
# 离开集群(在该节点执行)
|
||
docker swarm leave
|
||
docker swarm leave --force # Manager 节点需要 --force
|
||
```
|
||
|
||
---
|
||
|
||
## 八、Docker Config 管理
|
||
|
||
### 8.1 什么是 Docker Config
|
||
|
||
Docker Config 用于在 Swarm 集群中统一管理配置文件,无需在每个节点放置文件。
|
||
|
||
### 8.2 创建 Config
|
||
|
||
```bash
|
||
# 从文件创建
|
||
docker config create <config名称> <文件路径>
|
||
|
||
# 示例
|
||
docker config create review_mysql_conf_v1 /tmp/mysql_custom.cnf
|
||
```
|
||
|
||
### 8.3 查看 Config
|
||
|
||
```bash
|
||
docker config ls
|
||
docker config inspect <config名称>
|
||
```
|
||
|
||
### 8.4 在 docker-compose.yml 中引用
|
||
|
||
```yaml
|
||
services:
|
||
myservice:
|
||
configs:
|
||
- source: my_config
|
||
target: /path/in/container/config.conf
|
||
|
||
configs:
|
||
my_config:
|
||
external: true
|
||
name: my_config_name
|
||
```
|
||
|
||
### 8.5 删除 Config
|
||
|
||
```bash
|
||
docker config rm <config名称>
|
||
```
|
||
|
||
> 注意:删除前需确保没有服务在使用该 Config。
|
||
|
||
---
|
||
|
||
## 九、Docker 挂载详解
|
||
|
||
### 9.1 Docker 挂载类型
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Docker 挂载类型 │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ 1. Bind Mount(绑定挂载)← 本环境使用的方式 │
|
||
│ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ 宿主机 │ │ 容器 │ │
|
||
│ │ /mnt/data/ │ ═════► │ /bitnami/ │ │
|
||
│ │ volumes/ │ │ mongodb │ │
|
||
│ │ mongodb │ │ │ │
|
||
│ └──────────────┘ └──────────────┘ │
|
||
│ - 直接映射宿主机目录到容器 │
|
||
│ - 路径由用户指定 │
|
||
│ - 适合需要在特定位置存储数据 │
|
||
│ │
|
||
│ 2. Volume(Docker 管理卷) │
|
||
│ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ Docker 管理 │ │ 容器 │ │
|
||
│ │ /var/lib/ │ ═════► │ /bitnami/ │ │
|
||
│ │ docker/ │ │ mongodb │ │
|
||
│ │ volumes/xxx │ │ │ │
|
||
│ └──────────────┘ └──────────────┘ │
|
||
│ - Docker 自动管理存储位置 │
|
||
│ - 默认在 /var/lib/docker/volumes/ │
|
||
│ - 便于 Docker 管理,但位置不可控 │
|
||
│ │
|
||
│ 3. tmpfs(内存挂载) │
|
||
│ - 数据存储在内存中 │
|
||
│ - 容器停止后数据丢失 │
|
||
│ - 用于临时敏感数据 │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 9.2 docker-compose.yml 中的挂载配置
|
||
|
||
**Bind Mount 方式(本环境使用):**
|
||
|
||
```yaml
|
||
volumes:
|
||
- '/mnt/data/volumes/mongodb:/bitnami/mongodb'
|
||
# ↑ 宿主机路径 ↑ 容器内路径
|
||
```
|
||
|
||
**Volume 方式(Docker 管理):**
|
||
|
||
```yaml
|
||
services:
|
||
db:
|
||
volumes:
|
||
- 'mongodb_data:/bitnami/mongodb'
|
||
|
||
volumes:
|
||
mongodb_data:
|
||
driver: local
|
||
```
|
||
|
||
### 9.3 验证挂载是否生效
|
||
|
||
#### 方法 1:检查容器挂载信息
|
||
|
||
```bash
|
||
# 检查 ES 容器挂载
|
||
docker inspect $(docker ps -q -f name=review_log_es_elasticsearch) --format '{{range .Mounts}}{{.Type}}: {{.Source}} -> {{.Destination}}{{"\n"}}{{end}}'
|
||
|
||
# 检查 MongoDB 容器挂载
|
||
docker inspect $(docker ps -q -f name=review_mongodb_db) --format '{{range .Mounts}}{{.Type}}: {{.Source}} -> {{.Destination}}{{"\n"}}{{end}}'
|
||
|
||
# 预期输出:
|
||
# bind: /mnt/data/volumes/elasticsearch -> /bitnami/elasticsearch/data
|
||
# bind: /mnt/data/volumes/mongodb -> /bitnami/mongodb
|
||
```
|
||
|
||
#### 方法 2:确认数据写入位置
|
||
|
||
```bash
|
||
# 查看宿主机目录是否有数据生成
|
||
ls -la /mnt/data/volumes/elasticsearch/
|
||
ls -la /mnt/data/volumes/mongodb/
|
||
|
||
# 查看数据大小
|
||
du -sh /mnt/data/volumes/*
|
||
```
|
||
|
||
#### 方法 3:确认数据在数据盘上
|
||
|
||
```bash
|
||
# 确认路径属于 /dev/sdb(3.7T 数据盘)
|
||
df -h /mnt/data/volumes/elasticsearch
|
||
df -h /mnt/data/volumes/mongodb
|
||
|
||
# 预期输出应显示 /dev/sdb 和 3.7T 容量
|
||
```
|
||
|
||
#### 方法 4:写入测试文件验证
|
||
|
||
```bash
|
||
# 在宿主机创建测试文件
|
||
echo "test from host" > /mnt/data/volumes/mongodb/test_host.txt
|
||
|
||
# 进入容器查看是否存在
|
||
docker exec $(docker ps -q -f name=review_mongodb_db) cat /bitnami/mongodb/test_host.txt
|
||
|
||
# 在容器内创建测试文件
|
||
docker exec $(docker ps -q -f name=review_mongodb_db) sh -c 'echo "test from container" > /bitnami/mongodb/test_container.txt'
|
||
|
||
# 在宿主机查看
|
||
cat /mnt/data/volumes/mongodb/test_container.txt
|
||
|
||
# 清理测试文件
|
||
rm /mnt/data/volumes/mongodb/test_*.txt
|
||
```
|
||
|
||
如果双向都能看到文件,说明挂载完全正确。
|
||
|
||
---
|
||
|
||
## 十、常见问题及解决
|
||
|
||
### 10.1 镜像拉取失败
|
||
|
||
**问题:** `dial tcp xxx:443: i/o timeout`
|
||
|
||
**解决方案:**
|
||
|
||
**方案 1:更换镜像加速器**
|
||
|
||
```bash
|
||
echo '{"log-opts":{"max-size":"1g","max-file":"3"},"registry-mirrors":["https://registry.cn-hangzhou.aliyuncs.com"]}' > /etc/docker/daemon.json
|
||
systemctl daemon-reload
|
||
systemctl restart docker
|
||
```
|
||
|
||
**方案 2:使用其他源拉取后打标签**
|
||
|
||
```bash
|
||
docker pull <其他源>/<镜像名>
|
||
docker tag <其他源>/<镜像名> <原镜像名>
|
||
```
|
||
|
||
**方案 3:从其他机器导入镜像**
|
||
|
||
```bash
|
||
# 在能访问的机器上导出
|
||
docker save <镜像名> -o image.tar
|
||
|
||
# 传输到目标机器后导入
|
||
docker load -i image.tar
|
||
```
|
||
|
||
**方案 4:通过私有仓库中转(推荐)**
|
||
|
||
如果有私有镜像仓库(如 Harbor),可以先在能访问外网的服务器拉取镜像,推送到私有仓库,再从私有仓库拉取:
|
||
|
||
```bash
|
||
# 1. 在能访问 Docker Hub 的服务器(如 50 服务器)上操作
|
||
docker pull <原镜像名>:<版本>
|
||
docker tag <原镜像名>:<版本> harbor.sino-assist.com/library/<镜像名>:<版本>
|
||
docker login harbor.sino-assist.com
|
||
docker push harbor.sino-assist.com/library/<镜像名>:<版本>
|
||
|
||
# 2. 在目标服务器(如 132 服务器)上从私有仓库拉取
|
||
docker login harbor.sino-assist.com
|
||
docker pull harbor.sino-assist.com/library/<镜像名>:<版本>
|
||
docker tag harbor.sino-assist.com/library/<镜像名>:<版本> <原镜像名>:<版本>
|
||
```
|
||
|
||
示例(Canal 镜像):
|
||
|
||
```bash
|
||
# 50 服务器上
|
||
docker pull canal/canal-server:v1.1.5
|
||
docker tag canal/canal-server:v1.1.5 harbor.sino-assist.com/library/canal-server:v1.1.5
|
||
docker push harbor.sino-assist.com/library/canal-server:v1.1.5
|
||
|
||
# 132 服务器上
|
||
docker pull harbor.sino-assist.com/library/canal-server:v1.1.5
|
||
docker tag harbor.sino-assist.com/library/canal-server:v1.1.5 canal/canal-server:v1.1.5
|
||
```
|
||
|
||
### 10.2 镜像标签打错处理
|
||
|
||
**问题:** 给镜像打标签时打错了(如版本号或仓库名写错)
|
||
|
||
**解决方案:**
|
||
|
||
**删除本地错误标签:**
|
||
|
||
```bash
|
||
# 删除错误的本地标签(只删除标签,不删除镜像本身)
|
||
docker rmi <错误的标签名>
|
||
|
||
# 示例:删除打错的标签
|
||
docker rmi harbor.sino-assist.com/bitnami/redis:wrong-version
|
||
|
||
# 然后重新打正确的标签
|
||
docker tag <原镜像名>:<版本> <正确的标签名>
|
||
```
|
||
|
||
**删除已推送到 Harbor 的错误标签:**
|
||
|
||
```bash
|
||
# 方法1:通过 Harbor Web 界面删除
|
||
# 登录 Harbor -> 项目 -> 仓库 -> 选择镜像 -> 删除标签
|
||
|
||
# 方法2:通过 API 删除(需要管理员权限)
|
||
# 1. 先获取镜像的 digest
|
||
curl -u <用户名>:<密码> -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
|
||
https://harbor.sino-assist.com/v2/<项目>/<仓库>/manifests/<标签> -I | grep Docker-Content-Digest
|
||
|
||
# 2. 使用 digest 删除
|
||
curl -X DELETE -u <用户名>:<密码> \
|
||
https://harbor.sino-assist.com/v2/<项目>/<仓库>/manifests/<digest>
|
||
```
|
||
|
||
**示例:**
|
||
|
||
```bash
|
||
# 场景:不小心把 redis:7.0.11 标签打成了 redis:7.0.1
|
||
docker rmi harbor.sino-assist.com/bitnami/redis:7.0.1
|
||
|
||
# 重新打正确标签
|
||
docker tag bitnami/redis:7.0.11 harbor.sino-assist.com/bitnami/redis:7.0.11
|
||
docker push harbor.sino-assist.com/bitnami/redis:7.0.11
|
||
```
|
||
|
||
> **注意:** `docker rmi` 删除的是标签引用。如果镜像只有这一个标签,镜像本身也会被删除。如果镜像有多个标签引用,只会删除指定的标签。
|
||
|
||
### 10.3 Portainer 超时锁定
|
||
|
||
**问题:** `Your Portainer instance timed out for security purposes`
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
docker service update --force portainer_portainer
|
||
```
|
||
|
||
然后立即访问并设置管理员密码(5 分钟内)。
|
||
|
||
### 10.4 iptables 规则错误
|
||
|
||
**问题:** `iptables: No chain/target/match by that name`
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
systemctl restart docker
|
||
```
|
||
|
||
### 10.5 EOF Heredoc 语法问题
|
||
|
||
**问题:** 多行内容写入文件时出现 `>` 等待符号
|
||
|
||
**原因:**
|
||
- 结束的 `EOF` 前面有空格或缩进
|
||
- Windows 与 Linux 换行符不一致
|
||
|
||
**解决:** 使用单行 echo 或 vim 手动创建:
|
||
|
||
```bash
|
||
echo '内容' > /path/to/file
|
||
```
|
||
|
||
或:
|
||
|
||
```bash
|
||
vim /path/to/file
|
||
# 粘贴内容后 :wq 保存
|
||
```
|
||
|
||
### 10.6 Windows 换行符问题
|
||
|
||
**问题:** `yaml: line xx: could not find expected ':'`
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
sed -i 's/\r$//' /path/to/file
|
||
```
|
||
|
||
### 10.7 服务状态为 Ready 不变为 Running
|
||
|
||
**问题:** `docker stack ps` 显示服务状态一直是 `Ready` 或 `Preparing`
|
||
|
||
**排查:**
|
||
|
||
```bash
|
||
docker stack ps <stack名称> --no-trunc
|
||
docker service logs <服务名>
|
||
```
|
||
|
||
**常见原因:**
|
||
- 镜像拉取失败
|
||
- 节点标签不匹配
|
||
- 资源不足
|
||
|
||
### 10.8 Swarm 跨节点服务报 "No such image"
|
||
|
||
**问题:** `docker stack ps` 显示 `Rejected: No such image`
|
||
|
||
**原因:** Swarm 跨节点部署时,目标节点上没有所需镜像,且无法从镜像仓库拉取。
|
||
|
||
**解决:** 在目标节点上手动拉取镜像:
|
||
|
||
```bash
|
||
# 方法1:直接拉取
|
||
docker pull <镜像名>
|
||
|
||
# 方法2:使用国内镜像源
|
||
docker pull dockerproxy.cn/<镜像名>
|
||
docker tag dockerproxy.cn/<镜像名> <原镜像名>
|
||
|
||
# 方法3:从其他机器导入
|
||
docker save <镜像名> -o image.tar
|
||
# 传输到目标节点后
|
||
docker load -i image.tar
|
||
```
|
||
|
||
### 10.9 MySQL Slave 不断重启
|
||
|
||
**问题:** MySQL Slave 服务启动后约 90 秒被关闭,状态显示 `Complete` 后重新启动。
|
||
|
||
**原因:** Bitnami MySQL 的 healthcheck 脚本会检查复制状态,如果复制未建立或有问题,healthcheck 失败导致容器重启。
|
||
|
||
**排查:**
|
||
|
||
```bash
|
||
# 查看日志确认是否被 SHUTDOWN 信号终止
|
||
docker service logs <slave服务名> 2>&1 | grep -i "shutdown"
|
||
|
||
# 在 Slave 运行期间检查复制状态
|
||
docker exec $(docker ps -q -f name=mysql-slave) mysql -uroot -p'<密码>' -e "SHOW SLAVE STATUS\G"
|
||
```
|
||
|
||
**解决方案:**
|
||
|
||
**方案 1:调整 healthcheck 参数(推荐)**
|
||
|
||
增加 `start_period` 给足够的初始化时间:
|
||
|
||
```yaml
|
||
healthcheck:
|
||
test: ['CMD', '/opt/bitnami/scripts/mysql/healthcheck.sh']
|
||
interval: 30s
|
||
timeout: 10s
|
||
retries: 5
|
||
start_period: 180s
|
||
```
|
||
|
||
**方案 2:删除 Slave 数据卷重新初始化**
|
||
|
||
如果 Slave 数据卷有旧数据,会跳过复制配置初始化:
|
||
|
||
```bash
|
||
docker stack rm <stack名称>
|
||
sleep 15
|
||
docker volume rm <slave数据卷名称>
|
||
# 重新部署
|
||
```
|
||
|
||
### 10.10 Swarm 节点间通信问题
|
||
|
||
**问题:** 跨节点的服务无法通过 overlay 网络通信
|
||
|
||
**排查:**
|
||
|
||
```bash
|
||
# 检查节点状态
|
||
docker node ls
|
||
|
||
# 测试节点间基础连通性
|
||
ping <目标节点IP>
|
||
|
||
# 测试 Swarm 通信端口 (7946)
|
||
timeout 3 bash -c "echo >/dev/tcp/<目标节点IP>/7946" && echo "OK" || echo "FAILED"
|
||
|
||
# 测试 overlay 网络内服务连通性
|
||
docker run --rm --network <网络名> <镜像> <测试命令>
|
||
```
|
||
|
||
**解决:**
|
||
|
||
1. 确保所有节点防火墙已关闭:
|
||
```bash
|
||
systemctl stop firewalld
|
||
systemctl disable firewalld
|
||
```
|
||
|
||
2. 或者开放 Swarm 所需端口:
|
||
```bash
|
||
firewall-cmd --permanent --add-port=2377/tcp # 集群管理(仅 Manager)
|
||
firewall-cmd --permanent --add-port=7946/tcp # 节点间通信
|
||
firewall-cmd --permanent --add-port=7946/udp
|
||
firewall-cmd --permanent --add-port=4789/udp # Overlay 网络
|
||
firewall-cmd --reload
|
||
```
|
||
|
||
### 10.11 复制状态为空(SHOW SLAVE STATUS 无输出)
|
||
|
||
**问题:** 进入 Slave 容器执行 `SHOW SLAVE STATUS` 没有任何输出
|
||
|
||
**原因:** Slave 初始化时没有配置复制,可能是:
|
||
- 数据卷已存在旧数据,跳过了初始化
|
||
- 环境变量未正确传递
|
||
- 初始化时无法连接 Master
|
||
|
||
**排查:**
|
||
|
||
```bash
|
||
# 检查环境变量是否正确
|
||
docker service inspect <slave服务名> --pretty | grep -A 30 "Env"
|
||
|
||
# 查看初始化日志是否有复制配置
|
||
docker service logs <slave服务名> 2>&1 | grep -iE "repl|slave|master|configur"
|
||
```
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 删除 stack 和 slave 数据卷
|
||
docker stack rm <stack名称>
|
||
sleep 15
|
||
docker volume rm <slave数据卷名称>
|
||
|
||
# 确保 Master 已启动并可连接
|
||
docker run --rm --network <网络名> <mysql镜像> mysql -h <master服务名> -uroot -p'<密码>' -e "SELECT 1"
|
||
|
||
# 重新部署
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - <stack名称>
|
||
```
|
||
|
||
### 10.12 镜像名称拼写错误
|
||
|
||
**问题:** 部署时提示镜像拉取失败,但镜像名看起来正确
|
||
|
||
**原因:** 镜像名称可能有拼写错误,如 `rabbitmg` 应为 `rabbitmq`
|
||
|
||
**排查:**
|
||
|
||
```bash
|
||
# 检查 docker-compose.yml 中的镜像名
|
||
grep -i "image:" docker-compose.yml
|
||
```
|
||
|
||
**解决:** 修正镜像名称后重新部署
|
||
|
||
### 10.13 RabbitMQ 集群节点无法加入
|
||
|
||
**问题:** RabbitMQ 队列节点无法加入 stats 节点组成集群
|
||
|
||
**原因:**
|
||
- ERL_COOKIE 不一致
|
||
- 节点间网络不通
|
||
- stats 节点尚未启动完成
|
||
|
||
**排查:**
|
||
|
||
```bash
|
||
# 检查集群状态
|
||
docker exec $(docker ps -q -f name=rabbitmq_stats) rabbitmqctl cluster_status
|
||
|
||
# 检查节点日志
|
||
docker service logs <队列服务名> --tail 100
|
||
```
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 确保 ERL_COOKIE 一致(所有节点必须相同)
|
||
# 检查 docker-compose.yml 中 RABBITMQ_ERL_COOKIE 配置
|
||
|
||
# 删除数据卷重新初始化
|
||
docker stack rm <stack名称>
|
||
sleep 15
|
||
docker volume ls | grep rabbitmq | awk '{print $2}' | xargs docker volume rm
|
||
# 重新部署
|
||
```
|
||
|
||
### 10.14 RabbitMQ 网络分区 (Network Partition)
|
||
|
||
**问题:** `rabbitmqctl cluster_status` 显示 `Network Partitions` 不为空
|
||
|
||
**原因:** 节点间网络中断后恢复,导致集群分裂
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 方法1:重启受影响的节点
|
||
docker service update --force <受影响的服务名>
|
||
|
||
# 方法2:手动解决分区(在 stats 节点执行)
|
||
docker exec $(docker ps -q -f name=rabbitmq_stats) rabbitmqctl forget_cluster_node <分区节点名>
|
||
docker service update --force <分区节点服务名>
|
||
```
|
||
|
||
### 10.15 RabbitMQ 无法登录管理界面
|
||
|
||
**问题:** 部署成功但无法使用配置的用户名密码登录管理界面
|
||
|
||
**原因:**
|
||
- 用户不存在
|
||
- 密码不正确
|
||
- 用户没有管理员权限
|
||
|
||
**排查:**
|
||
|
||
```bash
|
||
# 查看用户列表
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl list_users
|
||
```
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 如果用户不存在,创建用户
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl add_user root gkxl650
|
||
|
||
# 设置管理员权限
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl set_user_tags root administrator
|
||
|
||
# 设置 vhost 权限
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl set_permissions -p / root ".*" ".*" ".*"
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl set_permissions -p /review root ".*" ".*" ".*"
|
||
|
||
# 如果用户存在但密码不对,重置密码
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl change_password root <新密码>
|
||
```
|
||
|
||
---
|
||
|
||
## 十一、部署 MySQL 主从复制 (mysql-repl-tool)
|
||
|
||
### 10.1 服务说明
|
||
|
||
用于 Nacos、XXL-Job 等工具类服务的专用 MySQL 数据库,采用主从复制架构。
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| Stack 名称 | review_tool_mysql |
|
||
| 镜像 | bitnami/mysql:8.0 |
|
||
| Master 节点 | ZD-BAK-APP1 |
|
||
| Master 端口 | 25306 |
|
||
| Slave 节点 | ZD-BAK-APP2 |
|
||
| Slave 端口 | 25307 |
|
||
|
||
### 10.2 部署前准备
|
||
|
||
#### 创建 MySQL 配置文件
|
||
|
||
```bash
|
||
# Master 配置
|
||
cat > /tmp/mysql_master_custom.cnf << 'EOF'
|
||
[mysqld]
|
||
max_connections=500
|
||
max_allowed_packet=64M
|
||
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
|
||
log-bin=mysql-bin
|
||
binlog-format=ROW
|
||
server_id=1
|
||
EOF
|
||
|
||
# Slave 配置
|
||
cat > /tmp/mysql_slave_custom.cnf << 'EOF'
|
||
[mysqld]
|
||
max_connections=500
|
||
max_allowed_packet=64M
|
||
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
|
||
log-bin=mysql-bin
|
||
binlog-format=ROW
|
||
server_id=2
|
||
read_only=1
|
||
EOF
|
||
```
|
||
|
||
#### 创建 Docker Config
|
||
|
||
```bash
|
||
docker config create review_tool_mysql_master_conf_v1 /tmp/mysql_master_custom.cnf
|
||
docker config create review_tool_mysql_slave_conf_v1 /tmp/mysql_slave_custom.cnf
|
||
|
||
# 验证
|
||
docker config ls | grep review_tool
|
||
```
|
||
|
||
#### 确保所有节点都有镜像
|
||
|
||
> 重要:Swarm 跨节点部署时,每个节点都需要有镜像。
|
||
|
||
```bash
|
||
# 在每个节点上拉取镜像
|
||
docker pull bitnami/mysql:8.0
|
||
|
||
# 如果官方源拉取失败,使用国内镜像源
|
||
docker pull dockerproxy.cn/bitnami/mysql:8.0
|
||
docker tag dockerproxy.cn/bitnami/mysql:8.0 bitnami/mysql:8.0
|
||
```
|
||
|
||
#### 确保所有节点防火墙已关闭
|
||
|
||
```bash
|
||
# 在每个节点执行
|
||
systemctl stop firewalld
|
||
systemctl disable firewalld
|
||
```
|
||
|
||
### 10.3 部署命令
|
||
|
||
```bash
|
||
cd /path/to/docker-swarm-review/mysql-repl-tool
|
||
|
||
# 转换 Windows 换行符(如果文件从 Windows 传输)
|
||
sed -i 's/\r$//' docker-compose.yml
|
||
sed -i 's/\r$//' env_review
|
||
|
||
# 部署
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_tool_mysql
|
||
```
|
||
|
||
### 10.4 验证部署
|
||
|
||
```bash
|
||
# 查看服务状态
|
||
docker service ls | grep review_tool_mysql
|
||
|
||
# 预期输出:
|
||
# review_tool_mysql_mysql-master replicated 1/1
|
||
# review_tool_mysql_mysql-slave replicated 1/1
|
||
|
||
# 查看详细状态
|
||
docker stack ps review_tool_mysql
|
||
|
||
# 验证复制状态
|
||
docker exec $(docker ps -q -f name=mysql-slave) mysql -uroot -p'gkxl2024#@' -e "SHOW SLAVE STATUS\G" | grep -E "Slave_IO|Slave_SQL|Seconds_Behind"
|
||
|
||
# 预期输出:
|
||
# Slave_IO_Running: Yes
|
||
# Slave_SQL_Running: Yes
|
||
# Seconds_Behind_Master: 0
|
||
```
|
||
|
||
### 10.5 连接信息
|
||
|
||
| 项目 | Master | Slave |
|
||
|------|--------|-------|
|
||
| 主机 | 192.168.3.134 | ZD-BAK-APP2 IP |
|
||
| 端口 | 25306 | 25307 |
|
||
| Root 密码 | gkxl2024#@ | gkxl2024#@ |
|
||
| 普通用户 | zd_tool | zd_tool |
|
||
| 普通密码 | gkxl2024#@ | gkxl2024#@ |
|
||
|
||
### 10.6 常用运维命令
|
||
|
||
```bash
|
||
# 查看服务日志
|
||
docker service logs review_tool_mysql_mysql-master --tail 50
|
||
docker service logs review_tool_mysql_mysql-slave --tail 50
|
||
|
||
# 进入 Master 容器
|
||
docker exec -it $(docker ps -q -f name=mysql-master) bash
|
||
|
||
# 进入 Slave 容器
|
||
docker exec -it $(docker ps -q -f name=mysql-slave) bash
|
||
|
||
# 强制重启服务
|
||
docker service update --force review_tool_mysql_mysql-slave
|
||
|
||
# 删除并重新部署
|
||
docker stack rm review_tool_mysql
|
||
sleep 15
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_tool_mysql
|
||
```
|
||
|
||
---
|
||
|
||
## 十二、部署 RabbitMQ 集群
|
||
|
||
### 11.1 服务说明
|
||
|
||
RabbitMQ 集群采用 3 节点架构,包含 1 个管理节点 (stats) 和 2 个队列节点 (queue1, queue2)。
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| Stack 名称 | review_rabbitmq |
|
||
| 镜像 | harbor.sino-assist.com/bitnami/rabbitmq:3.11 |
|
||
| 管理界面端口 | 15672 |
|
||
| AMQP 端口 | 5672 |
|
||
| STOMP 端口 | 61613 |
|
||
| WebSocket 端口 | 15674 |
|
||
|
||
### 11.2 节点分布
|
||
|
||
| 服务 | 类型 | 节点标签 | 部署节点 |
|
||
|------|------|---------|---------|
|
||
| stats | 管理节点 | `rabbit_stats==1` | ZD-BAK-APP2 |
|
||
| queue1 | 队列节点 (disc) | `rabbit_queue1==1` | ZD-BAK-APP1 |
|
||
| queue2 | 队列节点 (disc) | `rabbit_queue2==1` | zd-bak-app3 |
|
||
|
||
### 11.3 部署前准备
|
||
|
||
#### 添加节点标签
|
||
|
||
```bash
|
||
# stats 部署到 ZD-BAK-APP2
|
||
docker node update --label-add rabbit_stats=1 ZD-BAK-APP2
|
||
|
||
# queue1 部署到 ZD-BAK-APP1
|
||
docker node update --label-add rabbit_queue1=1 ZD-BAK-APP1
|
||
|
||
# queue2 部署到 zd-bak-app3
|
||
docker node update --label-add rabbit_queue2=1 zd-bak-app3
|
||
|
||
# 验证标签
|
||
docker node ls -q | xargs -I {} docker node inspect {} --format '{{.Description.Hostname}}: {{.Spec.Labels}}'
|
||
```
|
||
|
||
#### 确保所有节点有镜像
|
||
|
||
```bash
|
||
# 在每个目标节点上执行
|
||
docker login harbor.sino-assist.com
|
||
docker pull harbor.sino-assist.com/bitnami/rabbitmq:3.11
|
||
```
|
||
|
||
### 11.4 部署命令
|
||
|
||
```bash
|
||
cd /path/to/docker-swarm-review/rabbitmq
|
||
|
||
# 转换 Windows 换行符
|
||
sed -i 's/\r$//' docker-compose-review.yml
|
||
sed -i 's/\r$//' env_review
|
||
|
||
# 部署
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose-review.yml | docker stack deploy --compose-file - review_rabbitmq
|
||
```
|
||
|
||
### 11.5 验证部署
|
||
|
||
#### 查看服务状态
|
||
|
||
```bash
|
||
# 查看服务列表
|
||
docker service ls | grep review_rabbitmq
|
||
|
||
# 预期输出:
|
||
# review_rabbitmq_stats replicated 1/1
|
||
# review_rabbitmq_queue1 replicated 1/1
|
||
# review_rabbitmq_queue2 replicated 1/1
|
||
|
||
# 查看详细状态
|
||
docker stack ps review_rabbitmq --no-trunc | grep -v Shutdown
|
||
```
|
||
|
||
#### 检查集群状态
|
||
|
||
```bash
|
||
# 进入 stats 节点检查集群状态
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl cluster_status
|
||
```
|
||
|
||
预期输出应包含:
|
||
- `Running Nodes`: rabbit@stats, rabbit@queue1, rabbit@queue2
|
||
- `Alarms`: (none)
|
||
- `Network Partitions`: (none)
|
||
|
||
#### 检查节点健康状态
|
||
|
||
```bash
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl node_health_check
|
||
```
|
||
|
||
#### 查看集群中的所有节点
|
||
|
||
```bash
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl list_nodes
|
||
```
|
||
|
||
### 11.6 连接信息
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| 管理界面 | http://<ZD-BAK-APP2_IP>:15672 |
|
||
| 用户名 | root |
|
||
| 密码 | gkxl650 |
|
||
| AMQP 连接 | amqp://root:gkxl650@<服务器IP>:5672/review |
|
||
| Virtual Host | /review |
|
||
|
||
### 11.7 用户管理
|
||
|
||
#### 查看用户列表
|
||
|
||
```bash
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl list_users
|
||
```
|
||
|
||
#### 如果无法登录,重置密码
|
||
|
||
```bash
|
||
# 修改用户密码
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl change_password root <新密码>
|
||
```
|
||
|
||
#### 如果用户不存在,创建用户
|
||
|
||
```bash
|
||
# 创建用户
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl add_user root gkxl650
|
||
|
||
# 设置管理员权限
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl set_user_tags root administrator
|
||
|
||
# 设置 vhost 权限
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl set_permissions -p / root ".*" ".*" ".*"
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl set_permissions -p /review root ".*" ".*" ".*"
|
||
```
|
||
|
||
#### 删除用户
|
||
|
||
```bash
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl delete_user <用户名>
|
||
```
|
||
|
||
### 11.8 常用运维命令
|
||
|
||
```bash
|
||
# 查看服务日志
|
||
docker service logs review_rabbitmq_stats --tail 50
|
||
docker service logs review_rabbitmq_queue1 --tail 50
|
||
docker service logs review_rabbitmq_queue2 --tail 50
|
||
|
||
# 进入 stats 容器
|
||
docker exec -it $(docker ps -q -f name=review_rabbitmq_stats) bash
|
||
|
||
# 查看集群状态
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl cluster_status
|
||
|
||
# 查看队列列表
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl list_queues
|
||
|
||
# 查看连接列表
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl list_connections
|
||
|
||
# 删除并重新部署
|
||
docker stack rm review_rabbitmq
|
||
sleep 15
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose-review.yml | docker stack deploy --compose-file - review_rabbitmq
|
||
```
|
||
|
||
---
|
||
|
||
## 十三、部署 Nacos 集群
|
||
|
||
### 12.1 服务说明
|
||
|
||
Nacos 作为服务注册中心和配置中心,采用 3 节点集群模式部署,使用 MySQL 作为持久化存储。
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| Stack 名称 | review_nacos |
|
||
| 镜像 | nacos/nacos-server:v2.3.0 |
|
||
| 集群模式 | cluster |
|
||
| 数据库 | MySQL (review-tool-mysql-master) |
|
||
|
||
### 12.2 节点分布
|
||
|
||
| 服务 | 部署节点 | Web 端口 | gRPC 客户端端口 | gRPC 集群端口 |
|
||
|------|---------|---------|----------------|--------------|
|
||
| nacos1 | ZD-BAK-APP1 | 21848 | 22848 | 22849 |
|
||
| nacos2 | ZD-BAK-APP2 | 23848 | 24848 | 24849 |
|
||
| nacos3 | zd-bak-app3 | 25848 | 26848 | 26849 |
|
||
|
||
### 12.3 部署前准备
|
||
|
||
#### 创建 Nacos 数据库
|
||
|
||
```bash
|
||
# 连接 MySQL Master
|
||
mysql -h ZD-BAK-APP1 -P 25306 -uroot -p'gkxl2024#@'
|
||
|
||
# 创建数据库
|
||
CREATE DATABASE nacos CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
|
||
|
||
# 授权用户
|
||
GRANT ALL PRIVILEGES ON nacos.* TO 'zd_tool'@'%';
|
||
FLUSH PRIVILEGES;
|
||
|
||
exit;
|
||
```
|
||
|
||
#### 导入 Nacos Schema
|
||
|
||
```bash
|
||
# 导入建表脚本
|
||
mysql -h ZD-BAK-APP1 -P 25306 -uroot -p'gkxl2024#@' nacos < /path/to/nacos-cluser/mysql-schema.sql
|
||
```
|
||
|
||
#### 拉取镜像
|
||
|
||
在每个目标节点上拉取镜像:
|
||
|
||
```bash
|
||
docker pull nacos/nacos-server:v2.3.0
|
||
```
|
||
|
||
### 12.4 部署命令
|
||
|
||
```bash
|
||
cd /path/to/docker-swarm-review/nacos-cluser
|
||
|
||
# 转换 Windows 换行符
|
||
sed -i 's/\r$//' cluster-docker-compose.yml
|
||
sed -i 's/\r$//' env_review
|
||
|
||
# 部署
|
||
env $(cat ./env_review | xargs) envsubst < ./cluster-docker-compose.yml | docker stack deploy --compose-file - review_nacos
|
||
```
|
||
|
||
### 12.5 验证部署
|
||
|
||
#### 查看服务状态
|
||
|
||
```bash
|
||
# 查看服务列表
|
||
docker service ls | grep review_nacos
|
||
|
||
# 预期输出:
|
||
# review_nacos_nacos1 replicated 1/1
|
||
# review_nacos_nacos2 replicated 1/1
|
||
# review_nacos_nacos3 replicated 1/1
|
||
|
||
# 查看详细状态
|
||
docker stack ps review_nacos --no-trunc | grep -v Shutdown
|
||
```
|
||
|
||
#### 检查集群状态
|
||
|
||
通过 API 检查集群节点:
|
||
|
||
```bash
|
||
curl -X GET "http://ZD-BAK-APP1:21848/nacos/v1/ns/operator/cluster/nodes"
|
||
```
|
||
|
||
或登录 Web 控制台,进入 **集群管理 -> 节点列表** 查看 3 个节点是否都处于 UP 状态。
|
||
|
||
### 12.6 连接信息
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| 控制台 (nacos1) | http://ZD-BAK-APP1:21848/nacos |
|
||
| 控制台 (nacos2) | http://ZD-BAK-APP2:23848/nacos |
|
||
| 控制台 (nacos3) | http://zd-bak-app3:25848/nacos |
|
||
| 用户名 | nacos |
|
||
| 密码 | nacos |
|
||
|
||
### 12.7 端口说明
|
||
|
||
| 端口 | 用途 |
|
||
|------|------|
|
||
| 8848 | Web 控制台和 HTTP API |
|
||
| 9848 | gRPC 客户端通信端口 |
|
||
| 9849 | gRPC 集群节点间通信端口 |
|
||
|
||
> 注意:客户端连接时需配置 8848 端口,gRPC 端口会自动计算(默认 8848+1000)
|
||
|
||
### 12.8 常用运维命令
|
||
|
||
```bash
|
||
# 查看服务日志
|
||
docker service logs review_nacos_nacos1 --tail 50
|
||
docker service logs review_nacos_nacos2 --tail 50
|
||
docker service logs review_nacos_nacos3 --tail 50
|
||
|
||
# 强制重启服务
|
||
docker service update --force review_nacos_nacos1
|
||
|
||
# 删除并重新部署
|
||
docker stack rm review_nacos
|
||
sleep 15
|
||
env $(cat ./env_review | xargs) envsubst < ./cluster-docker-compose.yml | docker stack deploy --compose-file - review_nacos
|
||
```
|
||
|
||
### 12.9 常见问题
|
||
|
||
#### Nacos 启动失败:No DataSource set
|
||
|
||
**原因:** 数据库连接失败或用户没有权限
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 检查数据库连接
|
||
mysql -h ZD-BAK-APP1 -P 25306 -uzd_tool -p'gkxl2024#@' -e "USE nacos; SHOW TABLES;"
|
||
|
||
# 如果权限不足,重新授权
|
||
mysql -h ZD-BAK-APP1 -P 25306 -uroot -p'gkxl2024#@' -e "GRANT ALL PRIVILEGES ON nacos.* TO 'zd_tool'@'%'; FLUSH PRIVILEGES;"
|
||
|
||
# 重启 Nacos 服务
|
||
docker service update --force review_nacos_nacos1
|
||
docker service update --force review_nacos_nacos2
|
||
docker service update --force review_nacos_nacos3
|
||
```
|
||
|
||
#### 集群节点无法发现彼此
|
||
|
||
**原因:** 节点间网络不通或 gRPC 端口未开放
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 确保防火墙已关闭
|
||
systemctl stop firewalld
|
||
systemctl disable firewalld
|
||
|
||
# 或开放 Nacos 所需端口
|
||
firewall-cmd --permanent --add-port=8848/tcp
|
||
firewall-cmd --permanent --add-port=9848/tcp
|
||
firewall-cmd --permanent --add-port=9849/tcp
|
||
firewall-cmd --reload
|
||
```
|
||
|
||
---
|
||
|
||
## 十四、部署 XXL-Job-Admin
|
||
|
||
### 13.1 服务说明
|
||
|
||
XXL-Job 是分布式任务调度平台,Admin 为调度中心。
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| Stack 名称 | review_xxl_job |
|
||
| 镜像 | xuxueli/xxl-job-admin:2.4.1 |
|
||
| 端口 | 9991 |
|
||
| 副本数 | 2 |
|
||
| 数据库 | MySQL (review-tool-mysql-master) |
|
||
|
||
### 13.2 部署前准备
|
||
|
||
#### 创建 xxl_job 数据库
|
||
|
||
```bash
|
||
# 连接 MySQL Master
|
||
mysql -h ZD-BAK-APP1 -P 25306 -uroot -p'gkxl2024#@'
|
||
|
||
# 创建数据库
|
||
CREATE DATABASE xxl_job CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
|
||
|
||
# 授权用户
|
||
GRANT ALL PRIVILEGES ON xxl_job.* TO 'zd_tool'@'%';
|
||
FLUSH PRIVILEGES;
|
||
|
||
exit;
|
||
```
|
||
|
||
#### 导入 XXL-Job Schema
|
||
|
||
从官方获取建表脚本:https://github.com/xuxueli/xxl-job/blob/master/doc/db/tables_xxl_job.sql
|
||
|
||
```bash
|
||
mysql -h ZD-BAK-APP1 -P 25306 -uroot -p'gkxl2024#@' xxl_job < tables_xxl_job.sql
|
||
```
|
||
|
||
#### 添加节点标签
|
||
|
||
```bash
|
||
# 选择 2 个节点运行 xxl-job-admin
|
||
docker node update --label-add xxl_job_admin=1 ZD-BAK-APP1
|
||
docker node update --label-add xxl_job_admin=1 ZD-BAK-APP2
|
||
|
||
# 验证标签
|
||
docker node ls -q | xargs -I {} docker node inspect {} --format '{{.Description.Hostname}}: {{.Spec.Labels}}'
|
||
```
|
||
|
||
#### 拉取镜像
|
||
|
||
在目标节点上拉取镜像:
|
||
|
||
```bash
|
||
docker pull xuxueli/xxl-job-admin:2.4.1
|
||
```
|
||
|
||
### 13.3 部署命令
|
||
|
||
```bash
|
||
cd /path/to/docker-swarm-review/xxl-job-admin
|
||
|
||
# 转换 Windows 换行符
|
||
sed -i 's/\r$//' docker-compose.yml
|
||
sed -i 's/\r$//' env_review
|
||
|
||
# 部署
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_xxl_job
|
||
```
|
||
|
||
### 13.4 验证部署
|
||
|
||
```bash
|
||
# 查看服务状态
|
||
docker service ls | grep review_xxl_job
|
||
|
||
# 预期输出:
|
||
# review_xxl_job_server replicated 2/2
|
||
|
||
# 查看详细状态
|
||
docker stack ps review_xxl_job --no-trunc | grep -v Shutdown
|
||
```
|
||
|
||
### 13.5 连接信息
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| 控制台 | http://<节点IP>:9991/xxl-job-admin |
|
||
| 用户名 | admin |
|
||
| 密码 | 123456 |
|
||
|
||
### 13.6 常用运维命令
|
||
|
||
```bash
|
||
# 查看服务日志
|
||
docker service logs review_xxl_job_server --tail 50
|
||
|
||
# 强制重启服务
|
||
docker service update --force review_xxl_job_server
|
||
|
||
# 删除并重新部署
|
||
docker stack rm review_xxl_job
|
||
sleep 15
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_xxl_job
|
||
```
|
||
|
||
### 13.7 常见问题
|
||
|
||
#### 启动失败:Communications link failure
|
||
|
||
**原因:** 无法连接 MySQL 数据库
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 检查数据库连接
|
||
mysql -h ZD-BAK-APP1 -P 25306 -uzd_tool -p'gkxl2024#@' -e "USE xxl_job; SHOW TABLES;"
|
||
|
||
# 确认 env_review 中的数据库配置正确
|
||
cat env_review | grep DATASOURCE
|
||
```
|
||
|
||
#### 启动失败:Table 'xxl_job.xxl_job_info' doesn't exist
|
||
|
||
**原因:** 数据库 Schema 未导入
|
||
|
||
**解决:** 导入 XXL-Job 官方建表脚本
|
||
|
||
---
|
||
|
||
## 十五、部署 Canal
|
||
|
||
### 14.1 服务说明
|
||
|
||
Canal 是阿里巴巴开源的 MySQL binlog 增量订阅&消费组件,用于将 MySQL 数据变更实时同步到其他系统。
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| Stack 名称 | review_canal |
|
||
| 镜像 | canal/canal-server:v1.1.5 |
|
||
| 数据源 | 192.168.3.123:3306 |
|
||
| 输出模式 | RabbitMQ |
|
||
| 部署节点 | ZD-BAK-APP2 |
|
||
|
||
### 14.2 部署前准备
|
||
|
||
#### 确保 MySQL 开启 binlog
|
||
|
||
Canal 依赖 MySQL 的 binlog,需确保数据源 MySQL 已开启:
|
||
|
||
```sql
|
||
-- 检查 binlog 是否开启
|
||
SHOW VARIABLES LIKE 'log_bin';
|
||
|
||
-- 检查 binlog 格式(需要为 ROW)
|
||
SHOW VARIABLES LIKE 'binlog_format';
|
||
```
|
||
|
||
#### 确保 MySQL 用户有复制权限
|
||
|
||
Canal 用户需要以下权限:
|
||
- `SELECT` - 查询表结构
|
||
- `REPLICATION SLAVE` - 读取 binlog
|
||
- `REPLICATION CLIENT` - 查看 binlog 状态
|
||
|
||
```sql
|
||
-- Canal 使用的用户需要有以下权限
|
||
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'%';
|
||
FLUSH PRIVILEGES;
|
||
```
|
||
|
||
#### 验证 MySQL 用户权限
|
||
|
||
由于部署服务器上没有安装 mysql 客户端,使用已部署的 MySQL 容器来测试连接:
|
||
|
||
```bash
|
||
# 通过 MySQL Master 容器连接外部 MySQL 验证权限
|
||
docker exec $(docker ps -q -f name=review_tool_mysql_mysql-master) mysql -h 192.168.3.123 -P 3306 -urepl -p'nczl@sino_db' -e "SELECT 1; SHOW GRANTS; SHOW MASTER STATUS"
|
||
```
|
||
|
||
预期输出:
|
||
|
||
```
|
||
1
|
||
1
|
||
Grants for repl@%
|
||
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO `repl`@`%`
|
||
File Position Binlog_Do_DB Binlog_Ignore_DB Executed_Gtid_Set
|
||
mysql-bin.015149 132458641 zd_rescue,xxl-job,nacos_job information_schema,mysql,performance_schema,sys
|
||
```
|
||
|
||
如果权限不足(缺少 SELECT 或 REPLICATION CLIENT),需要在 MySQL 源服务器上执行授权。
|
||
|
||
#### 添加节点标签
|
||
|
||
```bash
|
||
docker node update --label-add review_canal=1 ZD-BAK-APP2
|
||
|
||
# 验证标签
|
||
docker node inspect ZD-BAK-APP2 --format '{{.Spec.Labels}}'
|
||
```
|
||
|
||
#### 拉取镜像
|
||
|
||
**方法一:直接从 Docker Hub 拉取(如网络可达)**
|
||
|
||
```bash
|
||
docker pull canal/canal-server:v1.1.5
|
||
```
|
||
|
||
**方法二:通过私有仓库中转(推荐)**
|
||
|
||
如果目标服务器无法访问 Docker Hub,可通过私有仓库 harbor.sino-assist.com 中转:
|
||
|
||
```bash
|
||
# 1. 在能访问 Docker Hub 的服务器(如 50 服务器)上拉取并推送到私有仓库
|
||
docker pull canal/canal-server:v1.1.5
|
||
docker tag canal/canal-server:v1.1.5 harbor.sino-assist.com/library/canal-server:v1.1.5
|
||
docker login harbor.sino-assist.com
|
||
docker push harbor.sino-assist.com/library/canal-server:v1.1.5
|
||
|
||
# 2. 在目标服务器(132/ZD-BAK-APP2)上从私有仓库拉取
|
||
docker login harbor.sino-assist.com
|
||
docker pull harbor.sino-assist.com/library/canal-server:v1.1.5
|
||
docker tag harbor.sino-assist.com/library/canal-server:v1.1.5 canal/canal-server:v1.1.5
|
||
```
|
||
|
||
### 14.3 配置说明
|
||
|
||
`env_review` 配置项:
|
||
|
||
| 配置项 | 值 | 说明 |
|
||
|--------|-----|------|
|
||
| canal_instance_master_address | 192.168.3.123:3306 | MySQL 数据源地址 |
|
||
| canal_instance_dbUsername | repl | MySQL 用户名 |
|
||
| canal_instance_dbPassword | nczl@sino_db | MySQL 密码 |
|
||
| canal_instance_filter_regex | zd_rescue.* | 订阅的表正则表达式 |
|
||
| canal_mq_topic | canal_mysql_bin | MQ Topic 名称 |
|
||
| rabbitmq_host | review-rabbitmq-stats:5672 | RabbitMQ 地址(overlay 网络内) |
|
||
| rabbitmq_exchange | canal_exchange | RabbitMQ Exchange |
|
||
| rabbitmq_username | root | RabbitMQ 用户名 |
|
||
| rabbitmq_password | gkxl650 | RabbitMQ 密码 |
|
||
| rabbitmq_virtual_host | review | RabbitMQ VHost |
|
||
|
||
### 14.4 部署命令
|
||
|
||
```bash
|
||
cd /path/to/docker-swarm-review/canal
|
||
|
||
# 转换 Windows 换行符
|
||
sed -i 's/\r$//' docker-compose.yml
|
||
sed -i 's/\r$//' env_review
|
||
|
||
# 部署
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_canal
|
||
```
|
||
|
||
### 14.5 验证部署
|
||
|
||
#### 查看服务状态
|
||
|
||
```bash
|
||
# 查看服务列表
|
||
docker service ls | grep review_canal
|
||
|
||
# 预期输出:
|
||
# review_canal_db replicated 1/1
|
||
|
||
# 查看详细状态
|
||
docker stack ps review_canal --no-trunc | grep -v Shutdown
|
||
```
|
||
|
||
#### 查看日志
|
||
|
||
```bash
|
||
docker service logs review_canal_db --tail 100
|
||
```
|
||
|
||
日志中应能看到:
|
||
- `destination:example start successful` 表示启动成功
|
||
- `position:xxx` 表示已连接 MySQL 并获取 binlog 位置
|
||
|
||
#### 验证 RabbitMQ 接收
|
||
|
||
登录 RabbitMQ 管理界面,检查:
|
||
1. Exchange `canal_exchange` 是否已创建
|
||
2. 是否有消息进入
|
||
|
||
### 14.6 连接信息
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| MySQL 数据源 | 192.168.3.123:3306 |
|
||
| RabbitMQ | review-rabbitmq-stats:5672 |
|
||
| RabbitMQ Exchange | canal_exchange |
|
||
| RabbitMQ VHost | review |
|
||
|
||
### 14.7 常用运维命令
|
||
|
||
```bash
|
||
# 查看服务日志
|
||
docker service logs review_canal_db --tail 50
|
||
|
||
# 强制重启服务
|
||
docker service update --force review_canal_db
|
||
|
||
# 删除并重新部署
|
||
docker stack rm review_canal
|
||
sleep 15
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_canal
|
||
```
|
||
|
||
### 14.8 常见问题
|
||
|
||
#### 启动失败:Authentication failed
|
||
|
||
**原因:** MySQL 用户名或密码错误,或用户没有复制权限
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 检查 MySQL 连接
|
||
mysql -h 192.168.3.123 -P 3306 -urepl -p'nczl@sino_db' -e "SHOW MASTER STATUS"
|
||
|
||
# 如果连接失败,在 MySQL 源服务器上授权
|
||
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'%';
|
||
FLUSH PRIVILEGES;
|
||
```
|
||
|
||
#### 启动失败:Could not find first log file name in binary log index file
|
||
|
||
**原因:** binlog 文件不存在或已被清理
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 检查 MySQL binlog 状态
|
||
mysql -h 192.168.3.123 -P 3306 -urepl -p'nczl@sino_db' -e "SHOW MASTER STATUS; SHOW BINARY LOGS;"
|
||
|
||
# 如果 binlog 被清理,需要重新初始化 Canal
|
||
docker stack rm review_canal
|
||
# 删除 Canal 数据卷(如有)
|
||
docker volume ls | grep canal | awk '{print $2}' | xargs docker volume rm
|
||
# 重新部署
|
||
```
|
||
|
||
#### 无法连接 RabbitMQ
|
||
|
||
**原因:** RabbitMQ 地址错误或网络不通
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 确保 Canal 和 RabbitMQ 在同一 overlay 网络
|
||
docker network inspect review
|
||
|
||
# 测试网络连通性(在 Canal 容器内)
|
||
docker exec $(docker ps -q -f name=review_canal_db) ping review-rabbitmq-stats
|
||
|
||
# 检查 RabbitMQ VHost 是否存在
|
||
docker exec $(docker ps -q -f name=review_rabbitmq_stats) rabbitmqctl list_vhosts
|
||
```
|
||
|
||
---
|
||
|
||
## 15. FastDFS
|
||
|
||
### 15.1 服务说明
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 镜像 | `harbor.sino-assist.com/season/fastdfs:1.2` |
|
||
| 部署方式 | Docker Compose(非 Swarm) |
|
||
| Tracker 端口 | 22122 |
|
||
| Storage 端口 | 23000 |
|
||
| HTTP 访问端口 | 8088 |
|
||
| 数据目录 | `/opt/fastdfs/` |
|
||
| 当前部署机器 | 192.168.1.171 |
|
||
|
||
### 15.2 目录结构
|
||
|
||
```
|
||
/opt/fastdfs/
|
||
├── docker-compose.yml # 容器编排配置
|
||
├── storage.conf # storage 配置(挂载进容器)
|
||
├── nginx.conf # nginx 配置(挂载进容器)
|
||
├── tracker_data/ # tracker 数据目录
|
||
├── storage_base_path/ # storage base 目录
|
||
└── store_path0/ # 文件实际存储目录
|
||
```
|
||
|
||
### 15.3 docker-compose.yml
|
||
|
||
```yaml
|
||
version: '3'
|
||
services:
|
||
tracker:
|
||
image: harbor.sino-assist.com/season/fastdfs:1.2
|
||
container_name: tracker
|
||
network_mode: host
|
||
restart: always
|
||
volumes:
|
||
- "./tracker_data:/fastdfs/tracker/data"
|
||
command: "tracker"
|
||
|
||
storage:
|
||
image: harbor.sino-assist.com/season/fastdfs:1.2
|
||
container_name: storage
|
||
network_mode: host
|
||
restart: always
|
||
volumes:
|
||
- "./storage.conf:/fdfs_conf/storage.conf"
|
||
- "./storage_base_path:/fastdfs/storage/data"
|
||
- "./store_path0:/fastdfs/store_path"
|
||
environment:
|
||
TRACKER_SERVER: "<本机IP>:22122"
|
||
command: "storage"
|
||
|
||
nginx:
|
||
image: harbor.sino-assist.com/season/fastdfs:1.2
|
||
container_name: fdfs-nginx
|
||
network_mode: host
|
||
restart: always
|
||
volumes:
|
||
- "./nginx.conf:/etc/nginx/conf/nginx.conf"
|
||
- "./store_path0:/fastdfs/store_path"
|
||
environment:
|
||
TRACKER_SERVER: "<本机IP>:22122"
|
||
command: "nginx"
|
||
```
|
||
|
||
> 迁移到新机器时,将 `TRACKER_SERVER` 中的 IP 替换为新机器 IP。
|
||
|
||
### 15.4 storage.conf 关键配置
|
||
|
||
```
|
||
base_path=/fastdfs/storage
|
||
store_path_count=1
|
||
store_path0=/fastdfs/store_path
|
||
tracker_server=<本机IP>:22122
|
||
http.server_port=8888
|
||
```
|
||
|
||
### 15.5 nginx.conf 说明
|
||
|
||
- 监听端口:`8088`
|
||
- 文件访问路径:`/group1/M00`
|
||
- 文件存储根目录:`/fastdfs/storage/data`(容器内路径)
|
||
- 已配置跨域 `Access-Control-Allow-Origin: *`
|
||
|
||
### 15.6 常用运维命令
|
||
|
||
```bash
|
||
# 启动
|
||
cd /opt/fastdfs && docker compose up -d
|
||
|
||
# 停止
|
||
cd /opt/fastdfs && docker compose stop
|
||
|
||
# 查看状态
|
||
docker ps | grep -E 'tracker|storage|nginx'
|
||
|
||
# 查看日志
|
||
docker logs tracker
|
||
docker logs storage
|
||
docker logs fdfs-nginx
|
||
|
||
# 验证端口
|
||
ss -tlnp | grep -E '22122|23000|8088'
|
||
```
|
||
|
||
### 15.7 迁移步骤
|
||
|
||
适用于将 FastDFS 从一台机器迁移到另一台机器(可停服)。
|
||
|
||
#### 1. 在目标机器安装 Docker
|
||
|
||
CentOS 7 需先修复官方源(已停止维护):
|
||
|
||
```bash
|
||
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak
|
||
curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
|
||
yum clean all && yum makecache
|
||
|
||
yum install -y yum-utils
|
||
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
|
||
yum install -y docker-ce docker-ce-cli containerd.io
|
||
systemctl enable docker && systemctl start docker
|
||
```
|
||
|
||
> Docker Engine 26.x 已内置 Compose 插件,无需单独安装 docker-compose。使用 `docker compose`(中间空格)而非 `docker-compose`。
|
||
|
||
配置镜像加速(参考 [dongyubin/DockerHub](https://github.com/dongyubin/DockerHub)):
|
||
|
||
```bash
|
||
cat > /etc/docker/daemon.json <<'EOF'
|
||
{
|
||
"registry-mirrors": [
|
||
"https://docker.1panel.live",
|
||
"https://hub.rat.dev",
|
||
"https://dockerpull.org",
|
||
"https://dockerhub.icu"
|
||
]
|
||
}
|
||
EOF
|
||
|
||
systemctl daemon-reload && systemctl restart docker
|
||
```
|
||
|
||
#### 2. 登录 harbor
|
||
|
||
```bash
|
||
docker login harbor.sino-assist.com
|
||
```
|
||
|
||
#### 3. 在源机器停服并同步数据
|
||
|
||
```bash
|
||
# 停止容器
|
||
cd /opt/fastdfs && docker compose stop
|
||
|
||
# 后台同步数据到目标机器
|
||
nohup rsync -avz --progress /opt/fastdfs/ root@<目标IP>:/opt/fastdfs/ > /tmp/rsync_fastdfs.log 2>&1 &
|
||
|
||
# 查看同步进度
|
||
tail -f /tmp/rsync_fastdfs.log
|
||
|
||
# 确认同步完成
|
||
ps aux | grep rsync
|
||
tail -20 /tmp/rsync_fastdfs.log
|
||
```
|
||
|
||
#### 4. 修改目标机器配置中的 IP
|
||
|
||
```bash
|
||
# 修改 storage.conf
|
||
sed -i 's/tracker_server=<源IP>:22122/tracker_server=<目标IP>:22122/' /opt/fastdfs/storage.conf
|
||
|
||
# 修改 docker-compose.yml
|
||
sed -i 's/<源IP>/<目标IP>/g' /opt/fastdfs/docker-compose.yml
|
||
```
|
||
|
||
#### 5. 启动并验证
|
||
|
||
```bash
|
||
cd /opt/fastdfs && docker compose up -d
|
||
|
||
# 验证容器状态
|
||
docker ps
|
||
|
||
# 验证端口
|
||
ss -tlnp | grep -E '22122|23000|8088'
|
||
|
||
# 验证文件访问
|
||
curl http://<目标IP>:8088/group1/M00/<文件路径>
|
||
```
|
||
|
||
#### 6. 修改上层应用配置
|
||
|
||
将应用中 FastDFS 地址从源机器 IP 改为目标机器 IP,端口不变。
|
||
|
||
#### 数据未同步到 RabbitMQ
|
||
|
||
**原因:** 订阅的表正则表达式不匹配
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 检查 env_review 中的 canal_instance_filter_regex 配置
|
||
# 格式:schema.table,支持正则
|
||
# 示例:zd_rescue\\..* 订阅 zd_rescue 库下所有表
|
||
|
||
# 修改配置后重新部署
|
||
docker stack rm review_canal
|
||
sleep 10
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_canal
|
||
```
|
||
|
||
---
|
||
|
||
## 十六、部署 Elasticsearch + Kibana
|
||
|
||
### 15.1 服务说明
|
||
|
||
Elasticsearch 是分布式搜索和分析引擎,Kibana 是其可视化平台。本环境用于日志存储和分析。
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| Stack 名称 | review_log_es |
|
||
| ES 镜像 | bitnami/elasticsearch:8.13.4 |
|
||
| Kibana 镜像 | bitnami/kibana:8.13.4 |
|
||
| ES HTTP 端口 | 9200 |
|
||
| ES Transport 端口 | 9300 |
|
||
| Kibana 端口 | 5601 |
|
||
| ES 堆内存 | 8192m |
|
||
| 部署节点 | 标签 review_es=1 的节点 |
|
||
|
||
### 15.2 部署前准备
|
||
|
||
#### 添加节点标签
|
||
|
||
选择部署 ES 的节点(建议选择内存较大的节点):
|
||
|
||
```bash
|
||
# 将 ES 部署到 ZD-BAK-APP2
|
||
docker node update --label-add review_es=1 ZD-BAK-APP2
|
||
|
||
# 验证标签
|
||
docker node inspect ZD-BAK-APP2 --format '{{.Spec.Labels}}'
|
||
```
|
||
|
||
#### 创建数据目录
|
||
|
||
在目标节点上创建数据目录并设置权限:
|
||
|
||
```bash
|
||
# 创建目录
|
||
mkdir -p /mnt/data/volumes/elasticsearch
|
||
mkdir -p /mnt/data/volumes/kibana/data
|
||
mkdir -p /mnt/data/volumes/kibana/config
|
||
|
||
# 设置权限(Bitnami 镜像使用 UID 1001)
|
||
chown -R 1001:1001 /mnt/data/volumes/elasticsearch
|
||
chown -R 1001:1001 /mnt/data/volumes/kibana
|
||
```
|
||
|
||
**目录说明:**
|
||
|
||
| 目录 | 用途 |
|
||
|------|------|
|
||
| /mnt/data/volumes/elasticsearch | ES 数据存储 |
|
||
| /mnt/data/volumes/kibana/data | Kibana 数据存储 |
|
||
| /mnt/data/volumes/kibana/config | Kibana 配置文件 |
|
||
|
||
#### 调整系统参数
|
||
|
||
ES 需要调整 `vm.max_map_count` 参数:
|
||
|
||
```bash
|
||
# 临时生效
|
||
sysctl -w vm.max_map_count=262144
|
||
|
||
# 永久生效
|
||
echo 'vm.max_map_count=262144' >> /etc/sysctl.conf
|
||
sysctl -p
|
||
|
||
# 验证
|
||
sysctl vm.max_map_count
|
||
```
|
||
|
||
#### 拉取镜像
|
||
|
||
**方法一:直接从 Docker Hub 拉取**
|
||
|
||
```bash
|
||
docker pull bitnami/elasticsearch:8.13.4
|
||
docker pull bitnami/kibana:8.13.4
|
||
```
|
||
|
||
**方法二:通过私有仓库中转(推荐)**
|
||
|
||
```bash
|
||
# 1. 在能访问 Docker Hub 的服务器(如 50 服务器)上操作
|
||
docker pull bitnami/elasticsearch:8.13.4
|
||
docker tag bitnami/elasticsearch:8.13.4 harbor.sino-assist.com/bitnami/elasticsearch:8.13.4
|
||
docker push harbor.sino-assist.com/bitnami/elasticsearch:8.13.4
|
||
|
||
docker pull bitnami/kibana:8.13.4
|
||
docker tag bitnami/kibana:8.13.4 harbor.sino-assist.com/bitnami/kibana:8.13.4
|
||
docker push harbor.sino-assist.com/bitnami/kibana:8.13.4
|
||
|
||
# 2. 在目标服务器上从私有仓库拉取
|
||
docker login harbor.sino-assist.com
|
||
docker pull harbor.sino-assist.com/bitnami/elasticsearch:8.13.4
|
||
docker tag harbor.sino-assist.com/bitnami/elasticsearch:8.13.4 bitnami/elasticsearch:8.13.4
|
||
|
||
docker pull harbor.sino-assist.com/bitnami/kibana:8.13.4
|
||
docker tag harbor.sino-assist.com/bitnami/kibana:8.13.4 bitnami/kibana:8.13.4
|
||
```
|
||
|
||
#### 创建 Kibana 配置文件(可选)
|
||
|
||
如需自定义 Kibana 配置,创建配置文件:
|
||
|
||
```bash
|
||
cat > /mnt/data/volumes/kibana/config/kibana.yml << 'EOF'
|
||
i18n.locale: "zh-CN"
|
||
path:
|
||
data: /bitnami/kibana/data
|
||
pid:
|
||
file: /opt/bitnami/kibana/tmp/kibana.pid
|
||
server:
|
||
host: 0.0.0.0
|
||
port: 5601
|
||
elasticsearch:
|
||
hosts: http://review-es-elasticsearch:9200
|
||
EOF
|
||
|
||
chown 1001:1001 /mnt/data/volumes/kibana/config/kibana.yml
|
||
```
|
||
|
||
### 15.3 配置文件说明
|
||
|
||
`env_review` 配置项:
|
||
|
||
| 配置项 | 值 | 说明 |
|
||
|--------|-----|------|
|
||
| NAMESPACE | review | 命名空间,用于网络和主机名 |
|
||
| NODE_PORT | 9200 | ES HTTP 端口 |
|
||
| NODE_PORT_2 | 9300 | ES Transport 端口 |
|
||
| NODE_PORT_KIBANA | 5601 | Kibana Web 端口 |
|
||
|
||
`docker-compose.yml` 关键配置:
|
||
|
||
| 配置项 | 值 | 说明 |
|
||
|--------|-----|------|
|
||
| ELASTICSEARCH_HEAP_SIZE | 8192m | ES JVM 堆内存大小 |
|
||
| hostname | ${NAMESPACE}-es-elasticsearch | ES 容器主机名 |
|
||
| KIBANA_ELASTICSEARCH_URL | ${NAMESPACE}-es-elasticsearch | Kibana 连接的 ES 地址 |
|
||
|
||
### 15.3.1 安装 IK 分词器(可选)
|
||
|
||
IK 分词器是 Elasticsearch 的中文分词插件,版本必须与 ES 版本完全匹配。
|
||
|
||
#### 创建插件目录
|
||
|
||
在 ES 部署节点上执行:
|
||
|
||
```bash
|
||
# 创建 plugins 目录
|
||
mkdir -p /mnt/data/volumes/elasticsearch-plugins
|
||
|
||
# 设置权限(Bitnami 镜像使用 UID 1001)
|
||
chown -R 1001:1001 /mnt/data/volumes/elasticsearch-plugins
|
||
```
|
||
|
||
#### 下载并安装 IK 分词器
|
||
|
||
```bash
|
||
cd /mnt/data/volumes/elasticsearch-plugins
|
||
|
||
# 下载 IK 分词器(版本 8.13.4 对应 ES 8.13.4)
|
||
curl -L -o analysis-ik.zip https://get.infini.cloud/elasticsearch/analysis-ik/8.13.4
|
||
|
||
# 解压到 analysis-ik 目录(使用 Python)
|
||
python3 -c "import zipfile; zipfile.ZipFile('analysis-ik.zip').extractall('analysis-ik')"
|
||
|
||
# 删除压缩包
|
||
rm -f analysis-ik.zip
|
||
|
||
# 设置权限
|
||
chown -R 1001:1001 analysis-ik
|
||
```
|
||
|
||
> **注意**:IK 分词器版本必须与 Elasticsearch 版本完全一致。如果 ES 升级,需要同步更新 IK 分词器版本。
|
||
|
||
#### 验证 IK 分词器安装
|
||
|
||
部署完成后,验证插件是否加载:
|
||
|
||
```bash
|
||
# 查看已安装的插件
|
||
curl http://<ES节点IP>:9200/_cat/plugins?v
|
||
|
||
# 预期输出应包含 analysis-ik
|
||
|
||
# 测试 IK 分词器(ik_max_word 细粒度分词)
|
||
curl -X POST "http://<ES节点IP>:9200/_analyze?pretty" -H 'Content-Type: application/json' -d'
|
||
{
|
||
"analyzer": "ik_max_word",
|
||
"text": "中华人民共和国国歌"
|
||
}'
|
||
|
||
# 测试 IK 分词器(ik_smart 粗粒度分词)
|
||
curl -X POST "http://<ES节点IP>:9200/_analyze?pretty" -H 'Content-Type: application/json' -d'
|
||
{
|
||
"analyzer": "ik_smart",
|
||
"text": "中华人民共和国国歌"
|
||
}'
|
||
```
|
||
|
||
**IK 分词模式说明:**
|
||
|
||
| 分词器 | 说明 | 示例 |
|
||
|--------|------|------|
|
||
| ik_max_word | 细粒度分词,穷尽所有可能的组合 | "中华人民共和国" → 中华人民共和国、中华人民、中华、华人、人民共和国、人民、共和国、共和、国 |
|
||
| ik_smart | 粗粒度分词,做最少切分 | "中华人民共和国" → 中华人民共和国 |
|
||
|
||
### 15.4 部署命令
|
||
|
||
```bash
|
||
cd /path/to/docker-swarm-review/elasticsearch
|
||
|
||
# 转换 Windows 换行符(如果文件从 Windows 传输)
|
||
sed -i 's/\r$//' docker-compose.yml
|
||
sed -i 's/\r$//' env_review
|
||
|
||
# 部署
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_log_es
|
||
```
|
||
|
||
### 15.5 验证部署
|
||
|
||
#### 查看服务状态
|
||
|
||
```bash
|
||
# 查看服务列表
|
||
docker service ls | grep review_log_es
|
||
|
||
# 预期输出:
|
||
# review_log_es_elasticsearch replicated 1/1
|
||
# review_log_es_kibana replicated 1/1
|
||
|
||
# 查看详细状态
|
||
docker stack ps review_log_es --no-trunc | grep -v Shutdown
|
||
```
|
||
|
||
#### 测试 ES 连通性
|
||
|
||
> **注意**:`localhost` 只能在 ES 实际运行的节点上使用。推荐使用节点 IP 或容器内测试。
|
||
|
||
```bash
|
||
# 方法1:通过节点 IP 测试(推荐,任意位置可执行)
|
||
# 将 <ES节点IP> 替换为 ES 实际运行节点的 IP
|
||
curl http://<ES节点IP>:9200
|
||
|
||
# 方法2:通过容器内部测试(在 ES 所在节点执行)
|
||
docker exec $(docker ps -q -f name=review_log_es_elasticsearch) curl -s http://localhost:9200
|
||
|
||
# 方法3:在 ES 所在节点上使用 localhost
|
||
curl http://localhost:9200
|
||
|
||
# 预期输出(JSON 格式):
|
||
# {
|
||
# "name" : "review-es-elasticsearch",
|
||
# "cluster_name" : "elasticsearch",
|
||
# "cluster_uuid" : "xxx",
|
||
# "version" : {
|
||
# "number" : "8.13.4",
|
||
# ...
|
||
# },
|
||
# "tagline" : "You Know, for Search"
|
||
# }
|
||
|
||
# 检查集群健康状态
|
||
curl http://<ES节点IP>:9200/_cluster/health?pretty
|
||
|
||
# 检查节点信息
|
||
curl http://<ES节点IP>:9200/_cat/nodes?v
|
||
```
|
||
|
||
#### 确认 ES 运行节点
|
||
|
||
```bash
|
||
# 查看 ES 实际运行在哪个节点
|
||
docker service ps review_log_es_elasticsearch --format "table {{.Node}}\t{{.CurrentState}}"
|
||
```
|
||
|
||
#### 测试 Kibana 连通性
|
||
|
||
> **注意**:同 ES,`localhost` 只能在 Kibana 实际运行的节点上使用。
|
||
|
||
```bash
|
||
# 方法1:通过节点 IP 测试(推荐)
|
||
curl http://<Kibana节点IP>:5601/api/status
|
||
|
||
# 方法2:通过容器内部测试
|
||
docker exec $(docker ps -q -f name=review_log_es_kibana) curl -s http://localhost:5601/api/status
|
||
|
||
# 浏览器访问
|
||
# http://<节点IP>:5601
|
||
```
|
||
|
||
### 15.6 连接信息
|
||
|
||
| 服务 | 地址 | 说明 |
|
||
|------|------|------|
|
||
| Elasticsearch HTTP | http://<节点IP>:9200 | REST API 接口 |
|
||
| Elasticsearch Transport | <节点IP>:9300 | 节点间通信 |
|
||
| Kibana | http://<节点IP>:5601 | Web 可视化界面 |
|
||
| ES 内部地址 | review-es-elasticsearch:9200 | overlay 网络内访问 |
|
||
|
||
### 15.7 常用运维命令
|
||
|
||
```bash
|
||
# 查看 ES 服务日志
|
||
docker service logs review_log_es_elasticsearch --tail 100
|
||
|
||
# 查看 Kibana 服务日志
|
||
docker service logs review_log_es_kibana --tail 100
|
||
|
||
# 强制重启 ES 服务
|
||
docker service update --force review_log_es_elasticsearch
|
||
|
||
# 强制重启 Kibana 服务
|
||
docker service update --force review_log_es_kibana
|
||
|
||
# 进入 ES 容器
|
||
docker exec -it $(docker ps -q -f name=review_log_es_elasticsearch) bash
|
||
|
||
# 进入 Kibana 容器
|
||
docker exec -it $(docker ps -q -f name=review_log_es_kibana) bash
|
||
|
||
# 删除并重新部署
|
||
docker stack rm review_log_es
|
||
sleep 15
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_log_es
|
||
```
|
||
|
||
### 15.8 ES 常用 API
|
||
|
||
```bash
|
||
# 查看所有索引
|
||
curl http://localhost:9200/_cat/indices?v
|
||
|
||
# 查看集群健康状态
|
||
curl http://localhost:9200/_cluster/health?pretty
|
||
|
||
# 查看节点状态
|
||
curl http://localhost:9200/_cat/nodes?v
|
||
|
||
# 查看分片分配
|
||
curl http://localhost:9200/_cat/shards?v
|
||
|
||
# 查看集群设置
|
||
curl http://localhost:9200/_cluster/settings?pretty
|
||
|
||
# 创建索引
|
||
curl -X PUT "http://localhost:9200/test_index"
|
||
|
||
# 删除索引
|
||
curl -X DELETE "http://localhost:9200/test_index"
|
||
|
||
# 查看索引映射
|
||
curl http://localhost:9200/test_index/_mapping?pretty
|
||
```
|
||
|
||
### 15.9 常见问题及解决
|
||
|
||
#### 15.9.1 ES 启动失败:max virtual memory areas vm.max_map_count is too low
|
||
|
||
**问题:** ES 日志显示 `max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]`
|
||
|
||
**原因:** 系统 `vm.max_map_count` 参数默认值过低
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 临时生效
|
||
sysctl -w vm.max_map_count=262144
|
||
|
||
# 永久生效
|
||
echo 'vm.max_map_count=262144' >> /etc/sysctl.conf
|
||
sysctl -p
|
||
|
||
# 重启 ES 服务
|
||
docker service update --force review_log_es_elasticsearch
|
||
```
|
||
|
||
#### 15.9.2 ES 启动失败:Permission denied
|
||
|
||
**问题:** ES 无法写入数据目录,日志显示权限错误
|
||
|
||
**原因:** 数据目录权限不正确,Bitnami 镜像使用 UID 1001 运行
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 修正目录权限
|
||
chown -R 1001:1001 /mnt/data/volumes/elasticsearch
|
||
chmod 755 /mnt/data/volumes/elasticsearch
|
||
|
||
# 重启 ES 服务
|
||
docker service update --force review_log_es_elasticsearch
|
||
```
|
||
|
||
#### 15.9.3 ES 启动失败:Disk usage exceeded flood-stage watermark
|
||
|
||
**问题:** ES 拒绝写入,日志显示磁盘空间不足
|
||
|
||
**原因:** 磁盘使用率超过 95%(flood stage watermark)
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 检查磁盘使用
|
||
df -h /mnt/data/volumes/elasticsearch
|
||
|
||
# 清理旧数据或扩容磁盘
|
||
|
||
# 临时解除只读限制(不推荐生产环境使用)
|
||
curl -X PUT "http://localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
|
||
{
|
||
"persistent": {
|
||
"cluster.routing.allocation.disk.watermark.flood_stage": "98%"
|
||
}
|
||
}'
|
||
|
||
# 解除索引只读状态
|
||
curl -X PUT "http://localhost:9200/_all/_settings" -H 'Content-Type: application/json' -d'
|
||
{
|
||
"index.blocks.read_only_allow_delete": null
|
||
}'
|
||
```
|
||
|
||
#### 15.9.4 curl localhost:9200 无响应,但 IP 可以访问
|
||
|
||
**问题:** 在服务器上执行 `curl http://localhost:9200` 无响应,但 `curl http://<节点IP>:9200` 可以
|
||
|
||
**原因:** `localhost` 只指向当前机器。如果你在 A 节点执行 curl,但 ES 运行在 B 节点,localhost 无法访问
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 1. 确认 ES 运行在哪个节点
|
||
docker service ps review_log_es_elasticsearch --format "table {{.Node}}\t{{.CurrentState}}"
|
||
|
||
# 2. 使用正确的访问方式:
|
||
# - 在 ES 所在节点:可以用 localhost
|
||
# - 在其他节点:必须用 ES 节点的 IP
|
||
curl http://<ES节点IP>:9200
|
||
|
||
# 3. 或通过容器内部测试(在 ES 所在节点执行)
|
||
docker exec $(docker ps -q -f name=review_log_es_elasticsearch) curl -s http://localhost:9200
|
||
```
|
||
|
||
**说明:** 只要通过 IP 能访问,ES 部署就是成功的。这是正常现象,不是故障。
|
||
|
||
#### 15.9.5 Kibana 启动失败:Unable to retrieve version information from Elasticsearch
|
||
|
||
**问题:** Kibana 无法连接 ES
|
||
|
||
**原因:** ES 尚未启动完成,或网络不通
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 检查 ES 是否正常运行(使用节点 IP)
|
||
curl http://<ES节点IP>:9200
|
||
|
||
# 检查网络连通性(在 Kibana 容器内)
|
||
docker exec $(docker ps -q -f name=review_log_es_kibana) curl review-es-elasticsearch:9200
|
||
|
||
# 如果 ES 正常但 Kibana 无法连接,检查 overlay 网络
|
||
docker network inspect review
|
||
|
||
# 重启 Kibana 服务
|
||
docker service update --force review_log_es_kibana
|
||
```
|
||
|
||
#### 15.9.6 Kibana 启动失败:FATAL Error: Port 5601 is already in use
|
||
|
||
**问题:** Kibana 端口被占用
|
||
|
||
**原因:** 端口被其他进程占用
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 检查端口占用
|
||
netstat -tlnp | grep 5601
|
||
# 或
|
||
ss -tlnp | grep 5601
|
||
|
||
# 停止占用端口的进程,或修改 env_review 中的端口配置
|
||
# NODE_PORT_KIBANA=5602
|
||
|
||
# 重新部署
|
||
docker stack rm review_log_es
|
||
sleep 10
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_log_es
|
||
```
|
||
|
||
#### 15.9.7 ES 集群状态为 Yellow
|
||
|
||
**问题:** `curl http://localhost:9200/_cluster/health` 返回 `status: yellow`
|
||
|
||
**原因:** 单节点部署时,副本分片无法分配(正常现象)
|
||
|
||
**解决:**
|
||
|
||
单节点环境下,yellow 状态是正常的。如需变为 green:
|
||
|
||
```bash
|
||
# 将所有索引的副本数设为 0
|
||
curl -X PUT "http://localhost:9200/_settings" -H 'Content-Type: application/json' -d'
|
||
{
|
||
"index": {
|
||
"number_of_replicas": 0
|
||
}
|
||
}'
|
||
```
|
||
|
||
#### 15.9.8 ES 内存不足 (OOM)
|
||
|
||
**问题:** ES 进程被 OOM Killer 杀死
|
||
|
||
**原因:** 堆内存设置过大,超过系统可用内存
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 检查系统内存
|
||
free -h
|
||
|
||
# 修改 docker-compose.yml 中的堆内存设置
|
||
# ELASTICSEARCH_HEAP_SIZE=4096m # 根据实际情况调整
|
||
|
||
# 重新部署
|
||
docker stack rm review_log_es
|
||
sleep 10
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_log_es
|
||
```
|
||
|
||
**堆内存建议:**
|
||
- 堆内存不超过物理内存的 50%
|
||
- 堆内存不超过 32GB(JVM 压缩指针限制)
|
||
- 为操作系统和文件缓存预留足够内存
|
||
|
||
#### 15.9.9 无法拉取镜像
|
||
|
||
**问题:** `dial tcp xxx:443: i/o timeout` 或 `no such image`
|
||
|
||
**原因:** 无法访问 Docker Hub
|
||
|
||
**解决:** 参考 9.1 节「镜像拉取失败」,通过私有仓库中转
|
||
|
||
```bash
|
||
# 在能访问外网的服务器上
|
||
docker pull bitnami/elasticsearch:8.13.4
|
||
docker tag bitnami/elasticsearch:8.13.4 harbor.sino-assist.com/bitnami/elasticsearch:8.13.4
|
||
docker push harbor.sino-assist.com/bitnami/elasticsearch:8.13.4
|
||
|
||
# 在目标服务器上
|
||
docker pull harbor.sino-assist.com/bitnami/elasticsearch:8.13.4
|
||
docker tag harbor.sino-assist.com/bitnami/elasticsearch:8.13.4 bitnami/elasticsearch:8.13.4
|
||
```
|
||
|
||
#### 15.9.10 Kibana 界面显示英文
|
||
|
||
**问题:** Kibana 界面为英文
|
||
|
||
**原因:** 未配置中文语言
|
||
|
||
**解决:**
|
||
|
||
创建或修改 Kibana 配置文件:
|
||
|
||
```bash
|
||
# 添加中文配置
|
||
echo 'i18n.locale: "zh-CN"' >> /mnt/data/volumes/kibana/config/kibana.yml
|
||
|
||
# 重启 Kibana
|
||
docker service update --force review_log_es_kibana
|
||
```
|
||
|
||
---
|
||
|
||
## 十七、部署 Log 服务(Logstash + Filebeat)
|
||
|
||
### 16.1 服务说明
|
||
|
||
Log 服务用于收集、处理和存储应用日志,由 Filebeat(日志收集)和 Logstash(日志处理)组成。
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| Stack 名称 | review_log |
|
||
| Logstash 镜像 | docker.elastic.co/logstash/logstash:8.13.4 |
|
||
| Filebeat 镜像 | docker.elastic.co/beats/filebeat:8.13.4 |
|
||
| Logstash 端口 | 5044 |
|
||
| 日志卷 | review_logs |
|
||
| 依赖 | Elasticsearch |
|
||
|
||
### 16.2 架构说明
|
||
|
||
```
|
||
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||
│ Filebeat │────▶│ Logstash │────▶│ Elasticsearch│
|
||
│ (每个节点) │ │ (ES 节点) │ │ │
|
||
└──────────────┘ └──────────────┘ └──────────────┘
|
||
│ │ │
|
||
收集日志 过滤/转换 存储/索引
|
||
/logs/*/*.log
|
||
```
|
||
|
||
**组件说明:**
|
||
|
||
| 组件 | 部署模式 | 说明 |
|
||
|------|---------|------|
|
||
| Filebeat | global(每个节点一个) | 轻量级日志收集器,监控 /logs 目录 |
|
||
| Logstash | replicated(部署在 ES 节点) | 日志处理管道,解析、过滤、转换日志 |
|
||
|
||
### 16.3 部署前准备
|
||
|
||
#### 创建日志卷
|
||
|
||
```bash
|
||
# 创建外部卷用于收集应用日志
|
||
docker volume create review_logs
|
||
|
||
# 验证
|
||
docker volume ls | grep review_logs
|
||
```
|
||
|
||
> **重要**:此卷需要在所有节点上创建,用于应用服务挂载日志目录。
|
||
|
||
#### 拉取镜像
|
||
|
||
**方法一:直接从 Elastic 官方拉取**
|
||
|
||
```bash
|
||
# 在所有节点上执行(因为 Filebeat 是全局部署)
|
||
docker pull docker.elastic.co/logstash/logstash:8.13.4
|
||
docker pull docker.elastic.co/beats/filebeat:8.13.4
|
||
```
|
||
|
||
**方法二:通过私有仓库中转(推荐)**
|
||
|
||
```bash
|
||
# 1. 在能访问外网的服务器(如 50 服务器)上操作
|
||
docker pull docker.elastic.co/logstash/logstash:8.13.4
|
||
docker tag docker.elastic.co/logstash/logstash:8.13.4 harbor.sino-assist.com/elastic/logstash:8.13.4
|
||
docker push harbor.sino-assist.com/elastic/logstash:8.13.4
|
||
|
||
docker pull docker.elastic.co/beats/filebeat:8.13.4
|
||
docker tag docker.elastic.co/beats/filebeat:8.13.4 harbor.sino-assist.com/elastic/filebeat:8.13.4
|
||
docker push harbor.sino-assist.com/elastic/filebeat:8.13.4
|
||
|
||
# 2. 在所有目标节点上从私有仓库拉取
|
||
docker login harbor.sino-assist.com
|
||
|
||
docker pull harbor.sino-assist.com/elastic/logstash:8.13.4
|
||
docker tag harbor.sino-assist.com/elastic/logstash:8.13.4 docker.elastic.co/logstash/logstash:8.13.4
|
||
|
||
docker pull harbor.sino-assist.com/elastic/filebeat:8.13.4
|
||
docker tag harbor.sino-assist.com/elastic/filebeat:8.13.4 docker.elastic.co/beats/filebeat:8.13.4
|
||
```
|
||
|
||
> **注意**:Filebeat 是全局部署(每个节点一个),所以需要在**所有 Swarm 节点**上拉取 Filebeat 镜像。
|
||
|
||
### 16.4 配置文件说明
|
||
|
||
#### env_review 配置项
|
||
|
||
| 配置项 | 值 | 说明 |
|
||
|--------|-----|------|
|
||
| NAMESPACE | review | 命名空间 |
|
||
| NODE_PORT | 5044 | Logstash 监听端口 |
|
||
|
||
#### logstash.conf 说明
|
||
|
||
```
|
||
filter {
|
||
grok {
|
||
# 解析日志格式:时间戳 [服务名] [TID:链路ID] [线程] 日志级别 类名 - 消息
|
||
match => { "message" => "..." }
|
||
}
|
||
date {
|
||
# 将日志时间戳设置为 @timestamp
|
||
}
|
||
mutate {
|
||
# 清理多余字段
|
||
}
|
||
}
|
||
|
||
output {
|
||
elasticsearch {
|
||
hosts => [ "review-es-elasticsearch:9200" ]
|
||
index => "sslog-%{[service]}" # 按服务名创建索引
|
||
}
|
||
}
|
||
```
|
||
|
||
#### filebeat.yml 说明
|
||
|
||
| 配置项 | 值 | 说明 |
|
||
|--------|-----|------|
|
||
| paths | /logs/*/*.log | 监控的日志文件路径 |
|
||
| multiline.pattern | ^[0-9]{4}-[0-9]{2}-[0-9]{2} | 多行日志合并(以日期开头为新日志) |
|
||
| output.logstash.hosts | review-log-logstash:5044 | Logstash 地址 |
|
||
|
||
### 16.5 部署命令
|
||
|
||
```bash
|
||
cd /path/to/docker-swarm-review/log
|
||
|
||
# 转换 Windows 换行符
|
||
sed -i 's/\r$//' docker-compose.yml
|
||
sed -i 's/\r$//' env_review
|
||
sed -i 's/\r$//' logstash.conf
|
||
sed -i 's/\r$//' filebeat.yml
|
||
|
||
# 部署
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_log
|
||
```
|
||
|
||
### 16.6 验证部署
|
||
|
||
#### 查看服务状态
|
||
|
||
```bash
|
||
# 查看服务列表
|
||
docker service ls | grep review_log
|
||
|
||
# 预期输出:
|
||
# review_log_logstash replicated 1/1
|
||
# review_log_filebeat global 3/3 (取决于节点数)
|
||
|
||
# 查看详细状态
|
||
docker stack ps review_log --no-trunc | grep -v Shutdown
|
||
```
|
||
|
||
#### 查看服务日志
|
||
|
||
```bash
|
||
# 查看 Logstash 日志
|
||
docker service logs review_log_logstash --tail 50
|
||
|
||
# 查看 Filebeat 日志
|
||
docker service logs review_log_filebeat --tail 50
|
||
```
|
||
|
||
#### 验证日志流转
|
||
|
||
```bash
|
||
# 检查 ES 中是否有日志索引
|
||
curl http://<ES节点IP>:9200/_cat/indices?v | grep sslog
|
||
|
||
# 预期输出(有应用日志时):
|
||
# yellow open sslog-service1 xxx 1 1 100 0 50kb 50kb
|
||
```
|
||
|
||
### 16.7 连接信息
|
||
|
||
| 服务 | 地址 | 说明 |
|
||
|------|------|------|
|
||
| Logstash | review-log-logstash:5044 | overlay 网络内部地址 |
|
||
| 日志卷 | review_logs:/logs | 应用日志挂载点 |
|
||
| ES 索引 | sslog-{service} | 日志索引命名规则 |
|
||
|
||
### 16.8 应用接入说明
|
||
|
||
业务应用需要将日志输出到 `review_logs` 卷中,才能被 Filebeat 收集。
|
||
|
||
#### 应用 docker-compose.yml 示例
|
||
|
||
```yaml
|
||
services:
|
||
my-service:
|
||
image: my-app:latest
|
||
volumes:
|
||
- review_logs:/logs
|
||
# 应用日志输出到 /logs/my-service/xxx.log
|
||
|
||
volumes:
|
||
review_logs:
|
||
external: true
|
||
```
|
||
|
||
#### 日志格式要求
|
||
|
||
为了 Logstash 正确解析,应用日志应遵循以下格式:
|
||
|
||
```
|
||
2024-01-01 12:00:00.000 [service-name] [TID:trace-id] [thread-name] INFO com.example.Class - Log message
|
||
```
|
||
|
||
### 16.9 常用运维命令
|
||
|
||
```bash
|
||
# 查看 Logstash 日志
|
||
docker service logs review_log_logstash --tail 100
|
||
|
||
# 查看 Filebeat 日志
|
||
docker service logs review_log_filebeat --tail 100
|
||
|
||
# 进入 Logstash 容器
|
||
docker exec -it $(docker ps -q -f name=review_log_logstash) bash
|
||
|
||
# 进入 Filebeat 容器(指定节点)
|
||
docker exec -it $(docker ps -q -f name=review_log_filebeat) bash
|
||
|
||
# 强制重启 Logstash
|
||
docker service update --force review_log_logstash
|
||
|
||
# 强制重启 Filebeat(所有节点)
|
||
docker service update --force review_log_filebeat
|
||
|
||
# 删除并重新部署
|
||
docker stack rm review_log
|
||
sleep 15
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_log
|
||
```
|
||
|
||
### 16.10 日志流转详解
|
||
|
||
#### 完整数据流向图
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||
│ Docker Swarm 集群 │
|
||
│ │
|
||
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
||
│ │ Docker Volume: review_logs │ │
|
||
│ │ │ │
|
||
│ │ /logs/sa-gateway/sa-gateway.log │ │
|
||
│ │ /logs/sa-auth/sa-auth.log │ │
|
||
│ │ /logs/sa-system/sa-system.log │ │
|
||
│ │ │ │
|
||
│ └───────────────────────────────┬─────────────────────────────────────────┘ │
|
||
│ │ │
|
||
│ │ 读取 │
|
||
│ ▼ │
|
||
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
||
│ │ Filebeat (每节点一个) │ │
|
||
│ │ │ │
|
||
│ │ - 监控 /logs/*/*.log │ │
|
||
│ │ - 多行日志合并(异常堆栈等) │ │
|
||
│ │ - 发送到 Logstash:5044 │ │
|
||
│ │ │ │
|
||
│ └───────────────────────────────┬─────────────────────────────────────────┘ │
|
||
│ │ │
|
||
│ │ TCP 5044 │
|
||
│ ▼ │
|
||
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
||
│ │ Logstash │ │
|
||
│ │ │ │
|
||
│ │ - Grok 解析日志格式 │ │
|
||
│ │ - 提取字段: service, tid, thread, loglevel, class, message │ │
|
||
│ │ - 时间戳转换 │ │
|
||
│ │ - 输出到 Elasticsearch │ │
|
||
│ │ │ │
|
||
│ └───────────────────────────────┬─────────────────────────────────────────┘ │
|
||
│ │ │
|
||
│ │ HTTP 9200 │
|
||
│ ▼ │
|
||
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
||
│ │ Elasticsearch │ │
|
||
│ │ │ │
|
||
│ │ 索引: │ │
|
||
│ │ - sslog-sa-gateway │ │
|
||
│ │ - sslog-sa-auth │ │
|
||
│ │ - sslog-sa-system │ │
|
||
│ │ - sslog-default(解析失败的日志) │ │
|
||
│ │ │ │
|
||
│ └───────────────────────────────┬─────────────────────────────────────────┘ │
|
||
│ │ │
|
||
│ │ HTTP 5601 │
|
||
│ ▼ │
|
||
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
||
│ │ Kibana │ │
|
||
│ │ │ │
|
||
│ │ - 可视化查询 │ │
|
||
│ │ - 日志检索 │ │
|
||
│ │ - 仪表盘 │ │
|
||
│ │ │ │
|
||
│ └─────────────────────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
└──────────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
#### Grok 解析示例
|
||
|
||
**原始日志:**
|
||
```
|
||
2025-12-23 10:30:45.123 [sa-gateway] [TID:abc123def456] [http-nio-8080-exec-1] INFO c.s.g.filter.AuthFilter - 用户登录成功
|
||
```
|
||
|
||
**Grok 表达式:**
|
||
```
|
||
%{TIMESTAMP_ISO8601:oldtimestamp}\s+\[%{DATA:service}\]\s+\[TID:%{NOTSPACE:tid}\]\s+\[%{DATA:thread}\]\s+%{LOGLEVEL:loglevel}\s+%{NOTSPACE:class}\s+-%{GREEDYDATA:oldmessage}
|
||
```
|
||
|
||
**解析后的字段:**
|
||
|
||
| 字段 | 值 | 说明 |
|
||
|------|-----|------|
|
||
| `@timestamp` | 2025-12-23T10:30:45.123+08:00 | 日志时间(转换后) |
|
||
| `service` | sa-gateway | 服务名(用于创建索引) |
|
||
| `tid` | abc123def456 | SkyWalking Trace ID |
|
||
| `thread` | http-nio-8080-exec-1 | 线程名 |
|
||
| `loglevel` | INFO | 日志级别 |
|
||
| `class` | c.s.g.filter.AuthFilter | 类名 |
|
||
| `message` | 用户登录成功 | 日志内容 |
|
||
|
||
#### SkyWalking Trace ID 关联
|
||
|
||
日志中的 `TID` 字段就是 SkyWalking 的 Trace ID,由 SkyWalking Logback 插件自动注入。
|
||
|
||
**配置要求:** 应用需要引入 SkyWalking Logback 依赖:
|
||
|
||
```xml
|
||
<dependency>
|
||
<groupId>org.apache.skywalking</groupId>
|
||
<artifactId>apm-toolkit-logback-1.x</artifactId>
|
||
</dependency>
|
||
```
|
||
|
||
**关联查询:**
|
||
1. 在 Kibana 中根据 `tid` 字段搜索特定请求的所有日志
|
||
2. 在 SkyWalking UI 中根据 Trace ID 查看完整调用链
|
||
3. 实现日志与链路追踪的关联分析
|
||
|
||
### 16.11 Kibana 日志查询
|
||
|
||
#### 访问地址
|
||
|
||
```
|
||
http://192.168.3.132:5601
|
||
```
|
||
|
||
#### 索引模式配置
|
||
|
||
1. 进入 **Management → Stack Management → Index Patterns**
|
||
2. 创建索引模式:`sslog-*`
|
||
3. 选择时间字段:`@timestamp`
|
||
|
||
#### 常用查询语法
|
||
|
||
```bash
|
||
# 查询某个服务的日志
|
||
service: "sa-gateway"
|
||
|
||
# 查询某个 Trace ID 的所有日志(跨服务追踪)
|
||
tid: "abc123def456"
|
||
|
||
# 查询错误日志
|
||
loglevel: "ERROR"
|
||
|
||
# 查询某个服务的错误日志
|
||
service: "sa-gateway" AND loglevel: "ERROR"
|
||
|
||
# 查询包含特定关键词的日志
|
||
message: "登录失败"
|
||
|
||
# 查询某个时间范围内的错误
|
||
loglevel: "ERROR" AND @timestamp >= "2025-12-23T10:00:00"
|
||
|
||
# 排除某些日志
|
||
service: "sa-gateway" AND NOT message: "健康检查"
|
||
```
|
||
|
||
#### 日志级别查询
|
||
|
||
| 级别 | 查询语法 | 说明 |
|
||
|------|---------|------|
|
||
| ERROR | `loglevel: "ERROR"` | 错误日志 |
|
||
| WARN | `loglevel: "WARN"` | 警告日志 |
|
||
| INFO | `loglevel: "INFO"` | 信息日志 |
|
||
| DEBUG | `loglevel: "DEBUG"` | 调试日志 |
|
||
|
||
### 16.12 常见问题及解决
|
||
|
||
#### 16.12.1 Filebeat 启动失败:No such volume
|
||
|
||
**问题:** Filebeat 无法启动,提示找不到卷
|
||
|
||
**原因:** review_logs 卷未创建
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 创建卷
|
||
docker volume create review_logs
|
||
|
||
# 重启服务
|
||
docker service update --force review_log_filebeat
|
||
```
|
||
|
||
#### 16.10.2 Logstash 无法连接 Elasticsearch
|
||
|
||
**问题:** Logstash 日志显示无法连接 ES
|
||
|
||
**原因:** ES 服务未启动或网络不通
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 检查 ES 是否正常运行
|
||
curl http://<ES节点IP>:9200
|
||
|
||
# 检查网络连通性(在 Logstash 容器内)
|
||
docker exec $(docker ps -q -f name=review_log_logstash) curl review-es-elasticsearch:9200
|
||
|
||
# 确保 Logstash 和 ES 在同一 overlay 网络
|
||
docker network inspect review
|
||
|
||
# 重启 Logstash
|
||
docker service update --force review_log_logstash
|
||
```
|
||
|
||
#### 16.10.3 日志未出现在 ES 中
|
||
|
||
**问题:** 应用日志未被收集到 ES
|
||
|
||
**原因:**
|
||
- 应用未挂载 review_logs 卷
|
||
- 日志路径不匹配 /logs/*/*.log
|
||
- 日志格式不符合 Logstash grok 规则
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 1. 检查日志卷是否有内容
|
||
docker run --rm -v review_logs:/logs alpine ls -la /logs
|
||
|
||
# 2. 检查 Filebeat 是否在收集
|
||
docker service logs review_log_filebeat --tail 50 | grep -i "harvest"
|
||
|
||
# 3. 检查 Logstash 是否在处理
|
||
docker service logs review_log_logstash --tail 50 | grep -i "event"
|
||
```
|
||
|
||
#### 16.10.4 Filebeat 镜像拉取失败
|
||
|
||
**问题:** 某些节点上 Filebeat 启动失败,提示 No such image
|
||
|
||
**原因:** Filebeat 是全局部署,需要所有节点都有镜像
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 在缺少镜像的节点上拉取
|
||
docker pull harbor.sino-assist.com/elastic/filebeat:8.13.4
|
||
docker tag harbor.sino-assist.com/elastic/filebeat:8.13.4 docker.elastic.co/beats/filebeat:8.13.4
|
||
|
||
# 重启服务
|
||
docker service update --force review_log_filebeat
|
||
```
|
||
|
||
#### 16.10.5 Logstash grok 解析失败
|
||
|
||
**问题:** 日志写入 ES 但字段未正确解析
|
||
|
||
**原因:** 日志格式与 grok 规则不匹配
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 查看 Logstash 日志中的 grok 解析错误
|
||
docker service logs review_log_logstash --tail 100 | grep -i "grok"
|
||
|
||
# 进入容器测试 grok 规则
|
||
docker exec -it $(docker ps -q -f name=review_log_logstash) bash
|
||
|
||
# 修改 logstash.conf 后重新部署
|
||
docker stack rm review_log
|
||
sleep 10
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_log
|
||
```
|
||
|
||
#### 16.10.6 磁盘空间不足
|
||
|
||
**问题:** 日志卷占用大量磁盘空间
|
||
|
||
**原因:** 日志文件未定期清理
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 检查卷使用情况
|
||
docker system df -v | grep review_logs
|
||
|
||
# 清理旧日志(在应用层面配置日志轮转)
|
||
# 或手动清理
|
||
docker run --rm -v review_logs:/logs alpine find /logs -name "*.log" -mtime +7 -delete
|
||
```
|
||
|
||
---
|
||
|
||
## 十八、部署 SkyWalking
|
||
|
||
### 17.1 服务说明
|
||
|
||
SkyWalking 是一款开源的 APM(应用性能监控)系统,用于分布式系统的链路追踪、性能指标分析和服务拓扑可视化。
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| Stack 名称 | review_skywalking |
|
||
| OAP 镜像 | apache/skywalking-oap-server:10.0.0 |
|
||
| UI 镜像 | apache/skywalking-ui:10.0.0 |
|
||
| gRPC 端口 | 11800 (Agent 上报) |
|
||
| HTTP 端口 | 12800 (OAP REST API) |
|
||
| UI 端口 | 18080 |
|
||
| 存储 | Elasticsearch |
|
||
| JVM 内存 | 2048m |
|
||
| 依赖 | Elasticsearch |
|
||
|
||
### 17.2 架构说明
|
||
|
||
```
|
||
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||
│ Java Agent │────▶│ OAP Server │────▶│ Elasticsearch│
|
||
│ (应用服务) │ │ :11800 │ │ :9200 │
|
||
└──────────────┘ └──────┬───────┘ └──────────────┘
|
||
│
|
||
┌─────▼─────┐
|
||
│ UI │
|
||
│ :18080 │
|
||
└───────────┘
|
||
```
|
||
|
||
**组件说明:**
|
||
|
||
| 组件 | 端口 | 说明 |
|
||
|------|------|------|
|
||
| OAP Server | 11800 (gRPC), 12800 (HTTP) | 数据收集、分析、存储 |
|
||
| UI | 18080 | Web 可视化界面 |
|
||
|
||
### 17.3 部署前准备
|
||
|
||
#### 添加节点标签
|
||
|
||
```bash
|
||
# 将 SkyWalking 部署到 ZD-BAK-APP2(建议与 ES 同节点)
|
||
docker node update --label-add review_skywalking=1 ZD-BAK-APP2
|
||
|
||
# 验证标签
|
||
docker node inspect ZD-BAK-APP2 --format '{{.Spec.Labels}}'
|
||
```
|
||
|
||
#### 在 ES 中创建索引生命周期策略(可选但推荐)
|
||
|
||
SkyWalking 会产生大量索引数据,建议配置生命周期策略自动清理旧数据:
|
||
|
||
```bash
|
||
# 创建 sw-policy 索引生命周期策略(保留 7 天数据)
|
||
curl -X PUT "http://<ES节点IP>:9200/_ilm/policy/sw-policy" -H 'Content-Type: application/json' -d'
|
||
{
|
||
"policy": {
|
||
"phases": {
|
||
"hot": {
|
||
"min_age": "0ms",
|
||
"actions": {
|
||
"rollover": {
|
||
"max_age": "1d",
|
||
"max_primary_shard_size": "50gb"
|
||
}
|
||
}
|
||
},
|
||
"delete": {
|
||
"min_age": "7d",
|
||
"actions": {
|
||
"delete": {}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}'
|
||
|
||
# 验证策略
|
||
curl "http://<ES节点IP>:9200/_ilm/policy/sw-policy?pretty"
|
||
```
|
||
|
||
#### 拉取镜像
|
||
|
||
**方法一:直接拉取**
|
||
|
||
```bash
|
||
docker pull apache/skywalking-oap-server:10.0.0
|
||
docker pull apache/skywalking-ui:10.0.0
|
||
```
|
||
|
||
**方法二:通过私有仓库中转(推荐)**
|
||
|
||
```bash
|
||
# 1. 在能访问外网的服务器(如 50 服务器)上操作
|
||
docker pull apache/skywalking-oap-server:10.0.0
|
||
docker tag apache/skywalking-oap-server:10.0.0 harbor.sino-assist.com/apache/skywalking-oap-server:10.0.0
|
||
docker push harbor.sino-assist.com/apache/skywalking-oap-server:10.0.0
|
||
|
||
docker pull apache/skywalking-ui:10.0.0
|
||
docker tag apache/skywalking-ui:10.0.0 harbor.sino-assist.com/apache/skywalking-ui:10.0.0
|
||
docker push harbor.sino-assist.com/apache/skywalking-ui:10.0.0
|
||
|
||
# 2. 在目标服务器上从私有仓库拉取
|
||
docker login harbor.sino-assist.com
|
||
docker pull harbor.sino-assist.com/apache/skywalking-oap-server:10.0.0
|
||
docker tag harbor.sino-assist.com/apache/skywalking-oap-server:10.0.0 apache/skywalking-oap-server:10.0.0
|
||
|
||
docker pull harbor.sino-assist.com/apache/skywalking-ui:10.0.0
|
||
docker tag harbor.sino-assist.com/apache/skywalking-ui:10.0.0 apache/skywalking-ui:10.0.0
|
||
```
|
||
|
||
### 17.4 配置文件说明
|
||
|
||
#### env_review 配置项
|
||
|
||
| 配置项 | 值 | 说明 |
|
||
|--------|-----|------|
|
||
| NAMESPACE | review | 命名空间 |
|
||
| NODE_PORT | 11800 | OAP gRPC 端口(Agent 上报) |
|
||
| NODE_PORT_2 | 12800 | OAP HTTP 端口(REST API) |
|
||
| NODE_PORT_UI | 18080 | UI Web 端口 |
|
||
|
||
#### docker-compose.yml 关键配置
|
||
|
||
| 配置项 | 值 | 说明 |
|
||
|--------|-----|------|
|
||
| SW_STORAGE | elasticsearch | 存储类型 |
|
||
| SW_STORAGE_ES_CLUSTER_NODES | review-es-elasticsearch:9200 | ES 集群地址 |
|
||
| SW_HEALTH_CHECKER | default | 健康检查器 |
|
||
| SW_TELEMETRY | prometheus | 遥测类型 |
|
||
| SW_STORAGE_ES_ADVANCED | {"index.lifecycle.name":"sw-policy"} | ES 索引生命周期策略 |
|
||
| JAVA_OPTS | -Xms2048m -Xmx2048m | JVM 内存设置 |
|
||
|
||
### 17.5 部署命令
|
||
|
||
```bash
|
||
cd /path/to/docker-swarm-review/skywalking
|
||
|
||
# 转换 Windows 换行符
|
||
sed -i 's/\r$//' docker-compose.yml
|
||
sed -i 's/\r$//' env_review
|
||
|
||
# 部署
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_skywalking
|
||
```
|
||
|
||
### 17.6 验证部署
|
||
|
||
#### 查看服务状态
|
||
|
||
```bash
|
||
# 查看服务列表
|
||
docker service ls | grep review_skywalking
|
||
|
||
# 预期输出:
|
||
# review_skywalking_oap replicated 1/1
|
||
# review_skywalking_ui replicated 1/1
|
||
|
||
# 查看详细状态
|
||
docker stack ps review_skywalking --no-trunc | grep -v Shutdown
|
||
```
|
||
|
||
#### 查看服务日志
|
||
|
||
```bash
|
||
# 查看 OAP 日志
|
||
docker service logs review_skywalking_oap --tail 100
|
||
|
||
# 查看 UI 日志
|
||
docker service logs review_skywalking_ui --tail 50
|
||
|
||
# 确认 OAP 启动成功(查找 started 关键字)
|
||
docker service logs review_skywalking_oap --tail 100 | grep -i "started"
|
||
```
|
||
|
||
#### 访问 UI
|
||
|
||
在浏览器访问:
|
||
```
|
||
http://<节点IP>:18080
|
||
```
|
||
|
||
如果 UI 能正常打开并显示界面,说明 SkyWalking 部署成功。
|
||
|
||
### 17.7 连接信息
|
||
|
||
| 服务 | 地址 | 说明 |
|
||
|------|------|------|
|
||
| OAP gRPC | <节点IP>:11800 | Agent 上报地址 |
|
||
| OAP HTTP | <节点IP>:12800 | REST API / GraphQL |
|
||
| UI | http://<节点IP>:18080 | Web 界面 |
|
||
| 内部 OAP gRPC | review-skywalking-oap:11800 | overlay 网络内访问 |
|
||
| 内部 OAP HTTP | review-skywalking-oap:12800 | overlay 网络内访问 |
|
||
|
||
### 17.8 应用接入说明
|
||
|
||
业务应用需要集成 SkyWalking Agent 才能上报链路数据。
|
||
|
||
#### Java 应用接入示例
|
||
|
||
1. 下载 SkyWalking Agent:
|
||
```bash
|
||
wget https://archive.apache.org/dist/skywalking/java-agent/9.0.0/apache-skywalking-java-agent-9.0.0.tgz
|
||
tar -zxvf apache-skywalking-java-agent-9.0.0.tgz
|
||
```
|
||
|
||
2. 启动应用时添加 Agent 参数:
|
||
```bash
|
||
java -javaagent:/path/to/skywalking-agent/skywalking-agent.jar \
|
||
-Dskywalking.agent.service_name=your-service-name \
|
||
-Dskywalking.collector.backend_service=<节点IP>:11800 \
|
||
-jar your-app.jar
|
||
```
|
||
|
||
#### Docker 应用接入示例
|
||
|
||
```yaml
|
||
services:
|
||
my-service:
|
||
image: my-app:latest
|
||
environment:
|
||
- JAVA_TOOL_OPTIONS=-javaagent:/skywalking-agent/skywalking-agent.jar
|
||
- SW_AGENT_NAME=my-service
|
||
- SW_AGENT_COLLECTOR_BACKEND_SERVICES=review-skywalking-oap:11800
|
||
volumes:
|
||
- /path/to/skywalking-agent:/skywalking-agent
|
||
```
|
||
|
||
### 17.9 常用运维命令
|
||
|
||
```bash
|
||
# 查看 OAP 日志
|
||
docker service logs review_skywalking_oap --tail 100
|
||
|
||
# 查看 UI 日志
|
||
docker service logs review_skywalking_ui --tail 100
|
||
|
||
# 进入 OAP 容器
|
||
docker exec -it $(docker ps -q -f name=review_skywalking_oap) bash
|
||
|
||
# 进入 UI 容器
|
||
docker exec -it $(docker ps -q -f name=review_skywalking_ui) bash
|
||
|
||
# 强制重启 OAP
|
||
docker service update --force review_skywalking_oap
|
||
|
||
# 强制重启 UI
|
||
docker service update --force review_skywalking_ui
|
||
|
||
# 删除并重新部署
|
||
docker stack rm review_skywalking
|
||
sleep 15
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_skywalking
|
||
```
|
||
|
||
### 17.10 常见问题及解决
|
||
|
||
#### 17.10.1 OAP 启动失败:无法连接 Elasticsearch
|
||
|
||
**问题:** OAP 日志显示无法连接 ES
|
||
|
||
**原因:** ES 服务未启动或网络不通
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 检查 ES 是否正常运行
|
||
curl http://<ES节点IP>:9200
|
||
|
||
# 检查网络连通性(在 OAP 容器内)
|
||
docker exec $(docker ps -q -f name=review_skywalking_oap) curl review-es-elasticsearch:9200
|
||
|
||
# 确保 OAP 和 ES 在同一 overlay 网络
|
||
docker network inspect review
|
||
|
||
# 重启 OAP
|
||
docker service update --force review_skywalking_oap
|
||
```
|
||
|
||
#### 17.10.2 UI 启动失败:无法连接 OAP
|
||
|
||
**问题:** UI 无法显示数据或无法访问
|
||
|
||
**原因:** OAP 尚未启动完成
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 确认 OAP 已启动
|
||
docker service logs review_skywalking_oap --tail 50 | grep -i "started"
|
||
|
||
# 重启 UI
|
||
docker service update --force review_skywalking_ui
|
||
```
|
||
|
||
#### 17.10.3 OAP 内存不足 (OOM)
|
||
|
||
**问题:** OAP 进程被 OOM Killer 杀死
|
||
|
||
**原因:** JVM 内存设置过大或数据量过多
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 检查系统内存
|
||
free -h
|
||
|
||
# 修改 docker-compose.yml 中的 JAVA_OPTS
|
||
# JAVA_OPTS=-Xms1024m -Xmx1024m # 根据实际情况调整
|
||
|
||
# 重新部署
|
||
docker stack rm review_skywalking
|
||
sleep 10
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_skywalking
|
||
```
|
||
|
||
#### 17.10.4 ES 索引占用过多磁盘空间
|
||
|
||
**问题:** SkyWalking 索引数据增长过快
|
||
|
||
**原因:** 未配置索引生命周期策略
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 1. 创建索引生命周期策略(参考 17.3 节)
|
||
|
||
# 2. 手动删除旧索引
|
||
curl -X DELETE "http://<ES节点IP>:9200/sw_*_20241201*"
|
||
|
||
# 3. 查看 SkyWalking 索引
|
||
curl "http://<ES节点IP>:9200/_cat/indices?v" | grep sw_
|
||
```
|
||
|
||
#### 17.10.5 Agent 无法连接 OAP
|
||
|
||
**问题:** 应用接入 Agent 后无数据上报
|
||
|
||
**原因:**
|
||
- OAP 地址配置错误
|
||
- 网络不通(Agent 在容器外,需要用宿主机 IP)
|
||
- 端口未开放
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 1. 确认 Agent 配置的 OAP 地址
|
||
# 如果 Agent 在容器外,使用宿主机 IP:11800
|
||
# 如果 Agent 在同一 overlay 网络,使用 review-skywalking-oap:11800
|
||
|
||
# 2. 测试网络连通性
|
||
telnet <OAP地址> 11800
|
||
|
||
# 3. 检查 OAP gRPC 端口是否监听
|
||
netstat -tlnp | grep 11800
|
||
```
|
||
|
||
#### 17.10.6 UI 显示无服务/无数据
|
||
|
||
**问题:** UI 可以访问但没有显示任何服务或数据
|
||
|
||
**原因:**
|
||
- 尚未有应用接入 Agent
|
||
- Agent 配置错误
|
||
- 时间范围选择不对
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 1. 检查 OAP 是否收到数据
|
||
docker service logs review_skywalking_oap --tail 100 | grep -i "register"
|
||
|
||
# 2. 在 UI 中调整时间范围(右上角)
|
||
|
||
# 3. 确认应用已正确配置 Agent
|
||
```
|
||
|
||
---
|
||
|
||
## 十九、部署 MongoDB
|
||
|
||
### 18.1 服务说明
|
||
|
||
MongoDB 是一款开源的 NoSQL 文档数据库,本环境采用单机模式部署。
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| Stack 名称 | review_mongodb |
|
||
| 镜像 | bitnami/mongodb:6.0 |
|
||
| 端口 | 27017 |
|
||
| Root 用户 | root |
|
||
| Root 密码 | 123456 |
|
||
| 默认数据库 | gps_data |
|
||
| 部署节点 | 标签 review_mongodb=1 的节点 |
|
||
|
||
### 18.2 部署前准备
|
||
|
||
#### 添加节点标签
|
||
|
||
```bash
|
||
# 将 MongoDB 部署到 ZD-BAK-APP2
|
||
docker node update --label-add review_mongodb=1 ZD-BAK-APP2
|
||
|
||
# 验证标签
|
||
docker node inspect ZD-BAK-APP2 --format '{{.Spec.Labels}}'
|
||
```
|
||
|
||
#### 创建数据目录
|
||
|
||
```bash
|
||
# 在目标节点创建数据目录
|
||
mkdir -p /mnt/data/volumes/mongodb
|
||
|
||
# 设置权限(Bitnami 镜像使用 UID 1001)
|
||
chown -R 1001:1001 /mnt/data/volumes/mongodb
|
||
```
|
||
|
||
#### 拉取镜像
|
||
|
||
**方法一:直接拉取**
|
||
|
||
```bash
|
||
docker pull bitnami/mongodb:6.0
|
||
```
|
||
|
||
**方法二:通过私有仓库中转(推荐)**
|
||
|
||
```bash
|
||
# 1. 在能访问外网的服务器(如 50 服务器)上操作
|
||
docker pull bitnami/mongodb:6.0
|
||
docker tag bitnami/mongodb:6.0 harbor.sino-assist.com/bitnami/mongodb:6.0
|
||
docker push harbor.sino-assist.com/bitnami/mongodb:6.0
|
||
|
||
# 2. 在目标服务器上从私有仓库拉取
|
||
docker login harbor.sino-assist.com
|
||
docker pull harbor.sino-assist.com/bitnami/mongodb:6.0
|
||
docker tag harbor.sino-assist.com/bitnami/mongodb:6.0 bitnami/mongodb:6.0
|
||
```
|
||
|
||
### 18.3 配置文件说明
|
||
|
||
#### env_review 配置项
|
||
|
||
| 配置项 | 值 | 说明 |
|
||
|--------|-----|------|
|
||
| NAMESPACE | review | 命名空间 |
|
||
| NODE_PORT | 27017 | MongoDB 端口 |
|
||
| MONGODB_DATABASE | gps_data | 默认创建的数据库 |
|
||
|
||
#### docker-compose.yml 关键配置
|
||
|
||
| 配置项 | 值 | 说明 |
|
||
|--------|-----|------|
|
||
| MONGODB_ROOT_USER | root | 管理员用户名 |
|
||
| MONGODB_ROOT_PASSWORD | 123456 | 管理员密码 |
|
||
| MONGODB_DATABASE | ${MONGODB_DATABASE} | 默认数据库(来自 env 文件) |
|
||
| volumes | /mnt/data/volumes/mongodb:/bitnami/mongodb | 数据持久化目录 |
|
||
|
||
### 18.4 部署命令
|
||
|
||
```bash
|
||
cd /opt/swarm/support/mongodb
|
||
|
||
# 转换 Windows 换行符
|
||
sed -i 's/\r$//' docker-compose.yml
|
||
sed -i 's/\r$//' env_review
|
||
|
||
# 部署
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_mongodb
|
||
```
|
||
|
||
### 18.5 验证部署
|
||
|
||
#### 查看服务状态
|
||
|
||
```bash
|
||
# 查看服务列表
|
||
docker service ls | grep review_mongodb
|
||
|
||
# 预期输出:
|
||
# review_mongodb_db replicated 1/1
|
||
|
||
# 查看详细状态
|
||
docker stack ps review_mongodb --no-trunc | grep -v Shutdown
|
||
```
|
||
|
||
#### 测试连接
|
||
|
||
```bash
|
||
# 方法1:通过容器内部测试
|
||
docker exec $(docker ps -q -f name=review_mongodb_db) mongosh -u root -p 123456 --eval "db.adminCommand('ping')"
|
||
|
||
# 预期输出:{ ok: 1 }
|
||
|
||
# 方法2:查看数据库列表
|
||
docker exec $(docker ps -q -f name=review_mongodb_db) mongosh -u root -p 123456 --eval "show dbs"
|
||
```
|
||
|
||
#### 验证挂载目录
|
||
|
||
```bash
|
||
# 查看容器挂载信息
|
||
docker inspect $(docker ps -q -f name=review_mongodb) --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{"\n"}}{{end}}'
|
||
|
||
# 预期输出:/mnt/data/volumes/mongodb -> /bitnami/mongodb
|
||
|
||
# 检查目录是否有数据
|
||
ls -la /mnt/data/volumes/mongodb/
|
||
```
|
||
|
||
### 18.6 连接信息
|
||
|
||
| 项目 | 值 |
|
||
|------|------|
|
||
| 连接地址 | mongodb://root:123456@<节点IP>:27017 |
|
||
| 内部地址 | mongodb://root:123456@review-mongodb-db:27017 |
|
||
| Root 用户 | root |
|
||
| Root 密码 | 123456 |
|
||
| 默认数据库 | gps_data |
|
||
|
||
### 18.7 常用运维命令
|
||
|
||
```bash
|
||
# 查看服务日志
|
||
docker service logs review_mongodb_db --tail 100
|
||
|
||
# 进入 MongoDB 容器
|
||
docker exec -it $(docker ps -q -f name=review_mongodb_db) bash
|
||
|
||
# 进入 MongoDB Shell
|
||
docker exec -it $(docker ps -q -f name=review_mongodb_db) mongosh -u root -p 123456
|
||
|
||
# 强制重启服务
|
||
docker service update --force review_mongodb_db
|
||
|
||
# 删除并重新部署
|
||
docker stack rm review_mongodb
|
||
sleep 15
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_mongodb
|
||
```
|
||
|
||
### 18.8 MongoDB 常用命令
|
||
|
||
```bash
|
||
# 进入 MongoDB Shell 后执行
|
||
|
||
# 查看所有数据库
|
||
show dbs
|
||
|
||
# 切换数据库
|
||
use gps_data
|
||
|
||
# 查看当前数据库的集合
|
||
show collections
|
||
|
||
# 创建集合
|
||
db.createCollection("test_collection")
|
||
|
||
# 插入文档
|
||
db.test_collection.insertOne({ name: "test", value: 123 })
|
||
|
||
# 查询文档
|
||
db.test_collection.find()
|
||
|
||
# 创建用户
|
||
db.createUser({
|
||
user: "app_user",
|
||
pwd: "app_password",
|
||
roles: [{ role: "readWrite", db: "gps_data" }]
|
||
})
|
||
|
||
# 查看用户
|
||
db.getUsers()
|
||
```
|
||
|
||
### 18.9 常见问题及解决
|
||
|
||
#### 18.9.1 MongoDB 启动失败:Permission denied
|
||
|
||
**问题:** MongoDB 无法写入数据目录
|
||
|
||
**原因:** 数据卷权限不正确,Bitnami 镜像使用 UID 1001 运行
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 如果使用本地目录挂载,设置权限
|
||
chown -R 1001:1001 /path/to/mongodb/data
|
||
|
||
# 重启服务
|
||
docker service update --force review_mongodb_db
|
||
```
|
||
|
||
#### 18.9.2 无法连接 MongoDB
|
||
|
||
**问题:** 应用无法连接 MongoDB
|
||
|
||
**原因:**
|
||
- 网络不通
|
||
- 认证信息错误
|
||
- 端口未开放
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 1. 确认服务运行正常
|
||
docker service ls | grep review_mongodb
|
||
|
||
# 2. 确认端口监听(在 MongoDB 所在节点)
|
||
netstat -tlnp | grep 27017
|
||
|
||
# 3. 测试连接
|
||
docker exec $(docker ps -q -f name=review_mongodb_db) mongosh -u root -p 123456 --eval "db.adminCommand('ping')"
|
||
|
||
# 4. 检查网络(从其他容器)
|
||
docker run --rm --network review bitnami/mongodb:6.0 mongosh mongodb://root:123456@review-mongodb-db:27017 --eval "db.adminCommand('ping')"
|
||
```
|
||
|
||
#### 18.9.3 认证失败
|
||
|
||
**问题:** `Authentication failed`
|
||
|
||
**原因:** 用户名或密码错误
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 确认连接字符串中的用户名和密码
|
||
# mongodb://root:123456@<地址>:27017
|
||
|
||
# 如果需要重置密码,删除数据卷重新部署
|
||
docker stack rm review_mongodb
|
||
sleep 10
|
||
docker volume rm review_mongodb_data_db
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_mongodb
|
||
```
|
||
|
||
#### 18.9.4 磁盘空间不足
|
||
|
||
**问题:** MongoDB 写入失败,提示磁盘空间不足
|
||
|
||
**原因:** 数据卷所在磁盘空间不足
|
||
|
||
**解决:**
|
||
|
||
```bash
|
||
# 检查磁盘使用
|
||
df -h
|
||
|
||
# 检查 MongoDB 数据大小
|
||
docker exec $(docker ps -q -f name=review_mongodb_db) du -sh /bitnami/mongodb
|
||
|
||
# 清理不需要的数据或扩容磁盘
|
||
```
|
||
|
||
---
|
||
|
||
## 二十、部署 Redis Sentinel 集群(灾备从节点模式)
|
||
|
||
### 20.1 服务说明
|
||
|
||
本环境的 Redis 作为生产环境的**灾备从节点**部署,平时仅同步数据不提供服务,生产故障时可手动切换为独立集群。
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| Stack 名称 | review_redis |
|
||
| 镜像 | bitnami/redis:7.0.11, bitnami/redis-sentinel:7.0.11 |
|
||
| Redis 端口 | 6379 |
|
||
| Sentinel 端口 | 26379(故障切换后启用) |
|
||
| 模式 | 隐藏从节点 + 级联复制(灾备模式) |
|
||
| 生产主节点 | 192.168.10.56:6379 |
|
||
| 密码 | sino#650 |
|
||
| Sentinel 密码 | sino#650 |
|
||
| 状态 | ✅ 已部署 |
|
||
|
||
### 20.2 架构说明
|
||
|
||
#### 20.2.1 平时状态(数据同步,不提供服务)
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||
│ 生产环境 │
|
||
│ │
|
||
│ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ Sentinel-1 │ │ Sentinel-2 │ │
|
||
│ │ 10.55:26379 │ │ 10.56:26379 │ │
|
||
│ └──────┬───────┘ └──────┬───────┘ │
|
||
│ │ 监控 mymaster │ │
|
||
│ └────────────┬──────────┘ │
|
||
│ ↓ │
|
||
│ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ 10.55 │←───────│ 10.56 │ │
|
||
│ │ (Slave) │ 复制 │ (Master) │ │
|
||
│ └──────────────┘ └──────┬───────┘ │
|
||
│ │ │
|
||
└───────────────────────────────────┼────────────────────────────────────────┘
|
||
│
|
||
│ 复制 (replica-announced no,生产看不到)
|
||
↓
|
||
┌───────────────────────────────────────────────────────────────────────────────┐
|
||
│ Review 环境(灾备) │
|
||
│ │
|
||
│ ┌──────────────────┐ │
|
||
│ │ 132 (master) │ ← 从 10.56 同步,对生产 Sentinel 隐藏 │
|
||
│ │ ZD-BAK-APP2 │ │
|
||
│ │ 隐藏 Slave │ │
|
||
│ └────────┬─────────┘ │
|
||
│ │ │
|
||
│ ┌────────┴────────┐ │
|
||
│ │ 级联复制 │ │
|
||
│ ↓ ↓ │
|
||
│ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ 133 (slave-1)│ │ 134 (slave-2)│ ← 从 132 同步,可被 Review Sentinel 发现 │
|
||
│ │ zd-bak-app3 │ │ ZD-BAK-APP1 │ │
|
||
│ └──────────────┘ └──────────────┘ │
|
||
│ │
|
||
│ ⚠️ 平时无 Sentinel,仅同步数据 │
|
||
└───────────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
#### 20.2.2 故障切换后(独立 Sentinel 集群)
|
||
|
||
```
|
||
┌───────────────────────────────────────────────────────────────────────────────┐
|
||
│ Review 环境(接管服务) │
|
||
│ │
|
||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ Sentinel-1 │ │ Sentinel-2 │ │ Sentinel-3 │ │
|
||
│ │ 132:26379 │ │ 133:26379 │ │ 134:26379 │ │
|
||
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
|
||
│ │ │ │ │
|
||
│ └─────────────────┼─────────────────┘ │
|
||
│ │ 监控 reviewmaster │
|
||
│ ↓ │
|
||
│ ┌──────────────────┐ │
|
||
│ │ 132 (Master) │ ← SLAVEOF NO ONE 提升为主节点 │
|
||
│ │ ZD-BAK-APP2 │ │
|
||
│ └────────┬─────────┘ │
|
||
│ │ │
|
||
│ ┌────────┴────────┐ │
|
||
│ │ 复制 │ │
|
||
│ ↓ ↓ │
|
||
│ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ 133 (Slave) │ │ 134 (Slave) │ │
|
||
│ │ zd-bak-app3 │ │ ZD-BAK-APP1 │ │
|
||
│ └──────────────┘ └──────────────┘ │
|
||
│ │
|
||
│ ✅ 完整 Sentinel 集群,支持自动故障转移 │
|
||
└───────────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
#### 20.2.3 关键配置说明
|
||
|
||
| 节点 | replica-announced no | replica-priority 0 | 说明 |
|
||
|------|---------------------|-------------------|------|
|
||
| 132 | ✅ 是 | ✅ 是 | 对生产 Sentinel 隐藏,不参与生产选举 |
|
||
| 133 | ❌ 否 | ❌ 否 | 可被 Review Sentinel 发现 |
|
||
| 134 | ❌ 否 | ❌ 否 | 可被 Review Sentinel 发现 |
|
||
|
||
**为什么这样配置?**
|
||
|
||
- **132 需要隐藏**:132 直接从生产 10.56 复制,如果不隐藏,生产 Sentinel 会发现它并可能在故障时将其选为主节点
|
||
- **133/134 不需要隐藏**:它们从 132 复制,生产 Sentinel 本来就看不到;故障切换后需要被 Review Sentinel 发现
|
||
|
||
### 20.3 部署前准备
|
||
|
||
#### 20.3.1 验证生产主节点可访问
|
||
|
||
```bash
|
||
# 1. 测试网络连通性
|
||
ping 192.168.10.56
|
||
|
||
# 2. 测试 Redis 端口是否开放
|
||
nc -zv 192.168.10.56 6379
|
||
|
||
# 3. 测试 Redis 连接和认证(如果服务器有 redis-cli)
|
||
redis-cli -h 192.168.10.56 -p 6379 -a 'sino#650' PING
|
||
# 应返回 PONG
|
||
|
||
# 4. 检查 10.56 Redis 是否为主节点
|
||
redis-cli -h 192.168.10.56 -p 6379 -a 'sino#650' INFO replication
|
||
# 应显示 role:master
|
||
|
||
# 如果没有 redis-cli,用 Docker 临时执行
|
||
docker run --rm bitnami/redis:7.0.11 redis-cli -h 192.168.10.56 -p 6379 -a 'sino#650' PING
|
||
docker run --rm bitnami/redis:7.0.11 redis-cli -h 192.168.10.56 -p 6379 -a 'sino#650' INFO replication
|
||
```
|
||
|
||
#### 20.3.2 拉取镜像(所有节点)
|
||
|
||
由于服务分布在 3 个节点上,需要在每个节点都拉取镜像。
|
||
|
||
**方法一:直接拉取(每个节点执行)**
|
||
|
||
```bash
|
||
docker pull bitnami/redis:7.0.11
|
||
docker pull bitnami/redis-sentinel:7.0.11
|
||
```
|
||
|
||
**方法二:通过私有仓库中转(推荐)**
|
||
|
||
```bash
|
||
# 1. 在能访问外网的服务器上操作
|
||
docker pull bitnami/redis:7.0.11
|
||
docker pull bitnami/redis-sentinel:7.0.11
|
||
docker tag bitnami/redis:7.0.11 harbor.sino-assist.com/bitnami/redis:7.0.11
|
||
docker tag bitnami/redis-sentinel:7.0.11 harbor.sino-assist.com/bitnami/redis-sentinel:7.0.11
|
||
docker push harbor.sino-assist.com/bitnami/redis:7.0.11
|
||
docker push harbor.sino-assist.com/bitnami/redis-sentinel:7.0.11
|
||
|
||
# 2. 在每个目标节点上拉取
|
||
docker login harbor.sino-assist.com
|
||
docker pull harbor.sino-assist.com/bitnami/redis:7.0.11
|
||
docker pull harbor.sino-assist.com/bitnami/redis-sentinel:7.0.11
|
||
docker tag harbor.sino-assist.com/bitnami/redis:7.0.11 bitnami/redis:7.0.11
|
||
docker tag harbor.sino-assist.com/bitnami/redis-sentinel:7.0.11 bitnami/redis-sentinel:7.0.11
|
||
```
|
||
|
||
### 20.4 配置文件说明
|
||
|
||
#### env_review 配置项
|
||
|
||
| 配置项 | 值 | 说明 |
|
||
|--------|-----|------|
|
||
| NAMESPACE | review | 命名空间 |
|
||
| REDIS_PASSWORD | sino#650 | Redis 密码(与生产相同) |
|
||
| REDIS_SENTINEL_PASSWORD | sino#650 | Sentinel 密码 |
|
||
| REDIS_PROD_MASTER_HOST | 192.168.10.56 | 生产主节点地址 |
|
||
| REDIS_PROD_MASTER_PORT | 6379 | 生产主节点端口 |
|
||
| REDIS_REVIEW_PRIMARY_HOST | 192.168.3.132 | Review 主同步节点(132) |
|
||
|
||
#### docker-compose.yml 关键配置
|
||
|
||
**132 节点(主同步节点)配置:**
|
||
|
||
| 配置项 | 值 | 说明 |
|
||
|--------|-----|------|
|
||
| REDIS_REPLICATION_MODE | slave | 从节点模式 |
|
||
| REDIS_MASTER_HOST | ${REDIS_PROD_MASTER_HOST} | 生产主节点地址 (10.56) |
|
||
| REDIS_EXTRA_FLAGS | --replica-announced no --replica-priority 0 | 隐藏 + 不参与选举 |
|
||
| ports mode | host | 使用主机网络端口 |
|
||
|
||
**133/134 节点(级联从节点)配置:**
|
||
|
||
| 配置项 | 值 | 说明 |
|
||
|--------|-----|------|
|
||
| REDIS_REPLICATION_MODE | slave | 从节点模式 |
|
||
| REDIS_MASTER_HOST | ${REDIS_REVIEW_PRIMARY_HOST} | Review 主节点 (132) |
|
||
| ports mode | host | 使用主机网络端口 |
|
||
|
||
**REDIS_EXTRA_FLAGS 详解:**
|
||
|
||
| 参数 | 作用 |
|
||
|------|------|
|
||
| `--replica-announced no` | 不向 Sentinel 通告自己,对 Sentinel 隐藏 |
|
||
| `--replica-priority 0` | 设置优先级为 0,永不被选举为主节点 |
|
||
|
||
### 20.5 部署命令
|
||
|
||
```bash
|
||
cd /opt/swarm/support/redis-review-132
|
||
|
||
# 转换 Windows 换行符(如有需要)
|
||
sed -i 's/\r$//' docker-compose.yml
|
||
sed -i 's/\r$//' env_review
|
||
|
||
# 部署
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_redis
|
||
```
|
||
|
||
### 20.6 验证部署
|
||
|
||
#### 20.6.1 查看服务状态
|
||
|
||
```bash
|
||
# 查看服务列表
|
||
docker service ls | grep review_redis
|
||
|
||
# 预期输出(3 个服务):
|
||
# review_redis_master replicated 1/1
|
||
# review_redis_slave-1 replicated 1/1
|
||
# review_redis_slave-2 replicated 1/1
|
||
|
||
# 查看详细状态
|
||
docker stack ps review_redis --no-trunc | grep -v Shutdown
|
||
```
|
||
|
||
#### 20.6.2 验证同步状态
|
||
|
||
> **注意:** 以下命令统一使用 Docker 方式执行,无需在服务器上安装 redis-cli。优先使用已运行的 `review_redis_master` 容器,如果容器未运行可使用临时容器方式。
|
||
|
||
**方式一:使用已运行的容器(推荐)**
|
||
|
||
```bash
|
||
# 检查 132 (从 10.56 同步)
|
||
docker exec $(docker ps -q -f name=review_redis_master) redis-cli -h 192.168.3.132 -a 'sino#650' --no-auth-warning INFO replication
|
||
# 预期输出:
|
||
# role:slave
|
||
# master_host:192.168.10.56
|
||
# master_link_status:up
|
||
|
||
# 检查 133 (从 132 同步)
|
||
docker exec $(docker ps -q -f name=review_redis_master) redis-cli -h 192.168.3.133 -a 'sino#650' --no-auth-warning INFO replication
|
||
# 预期输出:
|
||
# role:slave
|
||
# master_host:192.168.3.132
|
||
# master_link_status:up
|
||
|
||
# 检查 134 (从 132 同步)
|
||
docker exec $(docker ps -q -f name=review_redis_master) redis-cli -h 192.168.3.134 -a 'sino#650' --no-auth-warning INFO replication
|
||
# 预期输出:
|
||
# role:slave
|
||
# master_host:192.168.3.132
|
||
# master_link_status:up
|
||
```
|
||
|
||
**方式二:使用临时容器(备选,容器未运行时使用)**
|
||
|
||
```bash
|
||
# 定义 Redis 镜像
|
||
REDIS_IMAGE="bitnami/redis:7.0.11"
|
||
|
||
# 检查 132
|
||
docker run --rm --network host $REDIS_IMAGE redis-cli -h 192.168.3.132 -a 'sino#650' --no-auth-warning INFO replication
|
||
|
||
# 检查 133
|
||
docker run --rm --network host $REDIS_IMAGE redis-cli -h 192.168.3.133 -a 'sino#650' --no-auth-warning INFO replication
|
||
|
||
# 检查 134
|
||
docker run --rm --network host $REDIS_IMAGE redis-cli -h 192.168.3.134 -a 'sino#650' --no-auth-warning INFO replication
|
||
```
|
||
|
||
#### 20.6.3 验证生产 Sentinel 看不到 Review 节点
|
||
|
||
```bash
|
||
# 在生产 Sentinel 上查看从节点列表(使用已运行容器)
|
||
docker exec $(docker ps -q -f name=review_redis_master) redis-cli -h 192.168.10.55 -p 26379 -a 'sino#650' --no-auth-warning SENTINEL replicas mymaster
|
||
|
||
# 备选:使用临时容器
|
||
docker run --rm --network host bitnami/redis:7.0.11 redis-cli -h 192.168.10.55 -p 26379 -a 'sino#650' --no-auth-warning SENTINEL replicas mymaster
|
||
|
||
# 预期:不应该看到 192.168.3.132、192.168.3.133、192.168.3.134
|
||
```
|
||
|
||
#### 20.6.4 测试数据同步
|
||
|
||
```bash
|
||
# 在从节点查询数据(只读)- 使用已运行容器
|
||
docker exec $(docker ps -q -f name=review_redis_master) redis-cli -h 192.168.3.132 -a 'sino#650' --no-auth-warning KEYS '*' | head -10
|
||
|
||
# 备选:使用临时容器
|
||
docker run --rm --network host bitnami/redis:7.0.11 redis-cli -h 192.168.3.132 -a 'sino#650' --no-auth-warning KEYS '*' | head -10
|
||
|
||
# 尝试写入(应该失败,从节点只读)
|
||
docker exec $(docker ps -q -f name=review_redis_master) redis-cli -h 192.168.3.132 -a 'sino#650' --no-auth-warning SET test_key test_value
|
||
# 预期输出:(error) READONLY You can't write against a read only replica.
|
||
```
|
||
|
||
### 20.7 连接信息
|
||
|
||
**平时状态(灾备同步):**
|
||
|
||
| 节点 | Redis 地址 | 角色 |
|
||
|------|-----------|------|
|
||
| ZD-BAK-APP2 | 192.168.3.132:6379 | 隐藏从节点(从 10.56 同步) |
|
||
| zd-bak-app3 | 192.168.3.133:6379 | 从节点(从 132 同步) |
|
||
| ZD-BAK-APP1 | 192.168.3.134:6379 | 从节点(从 132 同步) |
|
||
|
||
| 项目 | 值 |
|
||
|------|------|
|
||
| Redis 密码 | sino#650 |
|
||
| 生产主节点 | 192.168.10.56:6379 |
|
||
|
||
**故障切换后:**
|
||
|
||
| 项目 | 值 |
|
||
|------|------|
|
||
| Review Master | 192.168.3.132:6379 |
|
||
| Review Sentinel | 192.168.3.132:26379, 192.168.3.133:26379, 192.168.3.134:26379 |
|
||
| Sentinel Master 名称 | reviewmaster |
|
||
|
||
### 20.8 故障切换
|
||
|
||
#### 20.8.1 切换脚本
|
||
|
||
项目目录下提供了 `failover-to-review.sh` 脚本用于故障切换:
|
||
|
||
```bash
|
||
# 添加执行权限
|
||
chmod +x failover-to-review.sh
|
||
|
||
# 查看当前状态
|
||
./failover-to-review.sh status
|
||
|
||
# 完整故障切换(提升 132 为 Master + 部署 Sentinel)
|
||
./failover-to-review.sh full
|
||
|
||
# 仅执行故障切换(不部署 Sentinel)
|
||
./failover-to-review.sh failover
|
||
|
||
# 仅部署 Sentinel
|
||
./failover-to-review.sh sentinel
|
||
|
||
# 回滚到生产
|
||
./failover-to-review.sh rollback
|
||
```
|
||
|
||
#### 20.8.2 手动切换步骤
|
||
|
||
如果不使用脚本,可按以下步骤手动操作:
|
||
|
||
> **注意:** 以下命令优先使用已运行的 `review_redis_master` 容器执行,如果容器未运行可使用临时容器方式(备选命令)。
|
||
|
||
**步骤 1:提升 132 为 Master**
|
||
|
||
```bash
|
||
# 使用已运行容器
|
||
docker exec $(docker ps -q -f name=review_redis_master) redis-cli -a 'sino#650' --no-auth-warning SLAVEOF NO ONE
|
||
|
||
# 备选:使用临时容器
|
||
docker run --rm --network host bitnami/redis:7.0.11 redis-cli -h 192.168.3.132 -a 'sino#650' --no-auth-warning SLAVEOF NO ONE
|
||
```
|
||
|
||
**步骤 2:确认 133/134 指向 132**
|
||
|
||
```bash
|
||
# 检查 133(使用已运行容器)
|
||
docker exec $(docker ps -q -f name=review_redis_master) redis-cli -h 192.168.3.133 -a 'sino#650' --no-auth-warning INFO replication | grep master_host
|
||
# 如果不是 132,重新配置:
|
||
docker exec $(docker ps -q -f name=review_redis_master) redis-cli -h 192.168.3.133 -a 'sino#650' --no-auth-warning SLAVEOF 192.168.3.132 6379
|
||
|
||
# 检查 134(使用已运行容器)
|
||
docker exec $(docker ps -q -f name=review_redis_master) redis-cli -h 192.168.3.134 -a 'sino#650' --no-auth-warning INFO replication | grep master_host
|
||
# 如果不是 132,重新配置:
|
||
docker exec $(docker ps -q -f name=review_redis_master) redis-cli -h 192.168.3.134 -a 'sino#650' --no-auth-warning SLAVEOF 192.168.3.132 6379
|
||
|
||
# 备选:使用临时容器
|
||
docker run --rm --network host bitnami/redis:7.0.11 redis-cli -h 192.168.3.133 -a 'sino#650' --no-auth-warning INFO replication | grep master_host
|
||
docker run --rm --network host bitnami/redis:7.0.11 redis-cli -h 192.168.3.133 -a 'sino#650' --no-auth-warning SLAVEOF 192.168.3.132 6379
|
||
```
|
||
|
||
**步骤 3:部署 Review Sentinel**
|
||
|
||
```bash
|
||
cd /opt/swarm/support/redis-review-132
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose-sentinel.yml | docker stack deploy --compose-file - review_sentinel
|
||
```
|
||
|
||
**步骤 4:验证 Sentinel**
|
||
|
||
```bash
|
||
# 使用已运行容器
|
||
docker exec $(docker ps -q -f name=review_redis_master) redis-cli -h 192.168.3.132 -p 26379 -a 'sino#650' --no-auth-warning SENTINEL master reviewmaster
|
||
|
||
# 备选:使用临时容器
|
||
docker run --rm --network host bitnami/redis:7.0.11 redis-cli -h 192.168.3.132 -p 26379 -a 'sino#650' --no-auth-warning SENTINEL master reviewmaster
|
||
|
||
# 应显示 ip=192.168.3.132, port=6379, flags=master
|
||
```
|
||
|
||
**步骤 5:修改应用配置**
|
||
|
||
```yaml
|
||
# Spring Boot 配置
|
||
spring:
|
||
redis:
|
||
sentinel:
|
||
master: reviewmaster
|
||
nodes: 192.168.3.132:26379,192.168.3.133:26379,192.168.3.134:26379
|
||
password: sino#650
|
||
password: sino#650
|
||
```
|
||
|
||
#### 20.8.3 回滚到生产
|
||
|
||
> **推荐使用安全回滚脚本:** `./failover-to-review.sh rollback`,该脚本会先将故障期间的数据同步到生产,确保数据不丢失。
|
||
|
||
**使用已运行容器(推荐):**
|
||
|
||
```bash
|
||
# 1. 移除 Review Sentinel
|
||
docker stack rm review_sentinel
|
||
|
||
# 2. 重新配置 132 指向生产
|
||
docker exec $(docker ps -q -f name=review_redis_master) redis-cli -a 'sino#650' --no-auth-warning SLAVEOF 192.168.10.56 6379
|
||
|
||
# 3. 确认 133/134 指向 132
|
||
docker exec $(docker ps -q -f name=review_redis_master) redis-cli -h 192.168.3.133 -a 'sino#650' --no-auth-warning SLAVEOF 192.168.3.132 6379
|
||
docker exec $(docker ps -q -f name=review_redis_master) redis-cli -h 192.168.3.134 -a 'sino#650' --no-auth-warning SLAVEOF 192.168.3.132 6379
|
||
|
||
# 4. 验证同步状态
|
||
docker exec $(docker ps -q -f name=review_redis_master) redis-cli -a 'sino#650' --no-auth-warning INFO replication
|
||
```
|
||
|
||
**备选:使用临时容器**
|
||
|
||
```bash
|
||
REDIS_IMAGE="bitnami/redis:7.0.11"
|
||
|
||
# 1. 移除 Review Sentinel
|
||
docker stack rm review_sentinel
|
||
|
||
# 2. 重新配置 132 指向生产
|
||
docker run --rm --network host $REDIS_IMAGE redis-cli -h 192.168.3.132 -a 'sino#650' --no-auth-warning SLAVEOF 192.168.10.56 6379
|
||
|
||
# 3. 确认 133/134 指向 132
|
||
docker run --rm --network host $REDIS_IMAGE redis-cli -h 192.168.3.133 -a 'sino#650' --no-auth-warning SLAVEOF 192.168.3.132 6379
|
||
docker run --rm --network host $REDIS_IMAGE redis-cli -h 192.168.3.134 -a 'sino#650' --no-auth-warning SLAVEOF 192.168.3.132 6379
|
||
|
||
# 4. 验证同步状态
|
||
docker run --rm --network host $REDIS_IMAGE redis-cli -h 192.168.3.132 -a 'sino#650' --no-auth-warning INFO replication
|
||
```
|
||
|
||
### 20.9 常用运维命令
|
||
|
||
**使用已运行容器(推荐):**
|
||
|
||
```bash
|
||
# 查看所有服务状态
|
||
docker stack ps review_redis
|
||
|
||
# 查看某个服务日志
|
||
docker service logs review_redis_master --tail 100
|
||
docker service logs review_redis_slave-1 --tail 100
|
||
|
||
# 在指定节点查看容器
|
||
docker ps | grep review_redis
|
||
|
||
# 进入 Redis CLI(交互模式)
|
||
docker exec -it $(docker ps -q -f name=review_redis_master) redis-cli -a 'sino#650' --no-auth-warning
|
||
|
||
# 查看复制状态
|
||
docker exec $(docker ps -q -f name=review_redis_master) redis-cli -a 'sino#650' --no-auth-warning INFO replication
|
||
|
||
# 查看远程节点复制状态
|
||
docker exec $(docker ps -q -f name=review_redis_master) redis-cli -h 192.168.3.133 -a 'sino#650' --no-auth-warning INFO replication
|
||
|
||
# 强制重启某个服务
|
||
docker service update --force review_redis_master
|
||
|
||
# 删除整个 Stack
|
||
docker stack rm review_redis
|
||
```
|
||
|
||
**备选:使用临时容器(容器未运行时使用)**
|
||
|
||
```bash
|
||
REDIS_IMAGE="bitnami/redis:7.0.11"
|
||
|
||
# 进入 Redis CLI(交互模式)
|
||
docker run --rm -it --network host $REDIS_IMAGE redis-cli -h 192.168.3.132 -a 'sino#650' --no-auth-warning
|
||
|
||
# 查看复制状态
|
||
docker run --rm --network host $REDIS_IMAGE redis-cli -h 192.168.3.132 -a 'sino#650' --no-auth-warning INFO replication
|
||
|
||
# 查看远程节点复制状态
|
||
docker run --rm --network host $REDIS_IMAGE redis-cli -h 192.168.3.133 -a 'sino#650' --no-auth-warning INFO replication
|
||
```
|
||
|
||
### 20.10 本次部署遇到的问题及解决方案总结
|
||
|
||
#### 20.10.1 问题一:Review Redis 节点被生产 Sentinel 发现
|
||
|
||
**现象:**
|
||
- 将 Review Redis (132, 133, 134) 部署为生产 10.56 的从节点
|
||
- 生产 Sentinel 发现了 Review 节点,并在故障时将 Review 节点选为主节点
|
||
- 导致生产 Master 切换到 Review 环境
|
||
|
||
**原因:**
|
||
- Redis 从节点默认会向 Sentinel 通告自己的存在
|
||
- Sentinel 通过 `INFO replication` 命令自动发现所有从节点
|
||
- 生产 Sentinel 将 Review 节点纳入了选举范围
|
||
|
||
**解决方案:**
|
||
```yaml
|
||
# 使用 REDIS_EXTRA_FLAGS 配置隐藏
|
||
environment:
|
||
- REDIS_EXTRA_FLAGS=--replica-announced no --replica-priority 0
|
||
```
|
||
|
||
| 参数 | 作用 |
|
||
|------|------|
|
||
| `--replica-announced no` | 不向 Sentinel 通告自己,对 Sentinel 隐藏 |
|
||
| `--replica-priority 0` | 设置优先级为 0,即使被发现也不会被选举为主节点 |
|
||
|
||
**注意:** `REDIS_REPLICA_PRIORITY=0` 环境变量在 Bitnami 镜像中**不生效**,必须使用 `REDIS_EXTRA_FLAGS`。
|
||
|
||
#### 20.10.2 问题二:Review Sentinel 无法正常监控
|
||
|
||
**现象:**
|
||
- 部署 Review Sentinel 监控 132 作为 master
|
||
- Sentinel 不断触发故障转移,将 master 从 132 切换到 133 或 134
|
||
- 切换后又切回,形成循环
|
||
|
||
**原因:**
|
||
- Sentinel 通过 `INFO replication` 检测节点角色
|
||
- 132 因为从 10.56 复制,role 永远是 `slave`
|
||
- Sentinel 发现监控的 "master" 是 slave,认为需要故障转移
|
||
|
||
**解决方案:**
|
||
|
||
采用"灾备模式":
|
||
- 平时不部署 Review Sentinel,仅同步数据
|
||
- 生产故障时,先将 132 提升为真正的 Master(`SLAVEOF NO ONE`)
|
||
- 然后再部署 Review Sentinel
|
||
|
||
#### 20.10.3 问题三:env_review 文件中的注释导致部署失败
|
||
|
||
**现象:**
|
||
```bash
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_redis
|
||
```
|
||
报错或环境变量为空
|
||
|
||
**原因:**
|
||
- `xargs` 无法正确处理 `#` 开头的注释行
|
||
- 导致环境变量解析错误
|
||
|
||
**解决方案:**
|
||
- env_review 文件中不要使用注释
|
||
- 或使用过滤:`env $(cat ./env_review | grep -v '^#' | xargs)`
|
||
|
||
#### 20.10.4 问题四:Sentinel 数据卷缓存旧配置
|
||
|
||
**现象:**
|
||
- 修改 Sentinel 配置后重新部署
|
||
- Sentinel 仍然使用旧的 master 配置
|
||
|
||
**原因:**
|
||
- Sentinel 会将运行时配置写入数据卷
|
||
- 重新部署时读取了旧的配置文件
|
||
|
||
**解决方案:**
|
||
```bash
|
||
# 删除 Sentinel 数据卷后重新部署
|
||
docker volume rm review_redis_sentinel_data_1
|
||
docker volume rm review_redis_sentinel_data_2
|
||
docker volume rm review_redis_sentinel_data_3
|
||
```
|
||
|
||
#### 20.10.5 问题五:133/134 配置 replica-announced no 导致故障切换后 Sentinel 发现不了
|
||
|
||
**现象:**
|
||
- 所有节点都配置了 `replica-announced no`
|
||
- 故障切换后,Review Sentinel 无法发现 133/134 作为从节点
|
||
|
||
**原因:**
|
||
- `replica-announced no` 对所有 Sentinel 生效,不区分生产和 Review
|
||
- 133/134 从 132 复制,生产 Sentinel 本来就看不到它们
|
||
|
||
**解决方案:**
|
||
- 只有 132 需要配置 `replica-announced no`(因为它直接从生产复制)
|
||
- 133/134 不需要配置,因为生产 Sentinel 通过 132 也发现不了它们
|
||
|
||
### 20.11 架构设计总结
|
||
|
||
| 设计目标 | 实现方式 |
|
||
|----------|----------|
|
||
| Review 同步生产数据 | 132 从 10.56 复制,133/134 从 132 级联复制 |
|
||
| 对生产 Sentinel 隐藏 | 132 配置 `replica-announced no` |
|
||
| 不影响生产选举 | 132 配置 `replica-priority 0` |
|
||
| 故障时可独立运行 | 提供切换脚本和独立 Sentinel 配置 |
|
||
| 切换后支持高可用 | 部署 Review Sentinel 监控 `reviewmaster` |
|
||
| 可回滚到生产 | 提供 rollback 命令重新指向 10.56 |
|
||
|
||
### 20.12 文件清单
|
||
|
||
| 文件 | 说明 |
|
||
|------|------|
|
||
| `docker-compose.yml` | Review Redis 配置(平时使用) |
|
||
| `docker-compose-sentinel.yml` | Review Sentinel 配置(故障切换后部署) |
|
||
| `env_review` | 环境变量 |
|
||
| `failover-to-review.sh` | 故障切换脚本 |
|
||
|
||
---
|
||
|
||
## 二十一、服务部署命令汇总
|
||
|
||
### 20.1 通用部署模式
|
||
|
||
```bash
|
||
cd /path/to/service
|
||
|
||
# 使用环境变量文件部署
|
||
env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - <stack名称>
|
||
```
|
||
|
||
### 19.2 常用管理命令
|
||
|
||
```bash
|
||
# 查看所有 Stack
|
||
docker stack ls
|
||
|
||
# 查看 Stack 中的服务
|
||
docker stack ps <stack名称>
|
||
|
||
# 查看服务日志
|
||
docker service logs <服务名>
|
||
|
||
# 删除 Stack
|
||
docker stack rm <stack名称>
|
||
|
||
# 更新服务
|
||
docker service update --force <服务名>
|
||
|
||
# 查看所有服务
|
||
docker service ls
|
||
|
||
# 查看镜像
|
||
docker images | grep <关键字>
|
||
|
||
# 查看 Docker Config
|
||
docker config ls
|
||
```
|
||
|
||
---
|
||
|
||
## 二十二、待部署服务清单
|
||
|
||
### 无依赖服务
|
||
|
||
| 服务 | 端口 | 状态 | 说明 |
|
||
|------|------|------|------|
|
||
| Redis Sentinel | 6379, 26379 | ✅ 已部署 | 缓存服务(从 10.56 同步) |
|
||
| MongoDB | 27017 | ✅ 已部署 | NoSQL 数据库 |
|
||
| RabbitMQ | 5672, 15672 | ✅ 已部署 | 消息队列 |
|
||
| Elasticsearch | 9200, 9300 | ✅ 已部署 | 搜索引擎 |
|
||
| MinIO | 9000 | ⏳ 待部署 | 对象存储 |
|
||
| Jenkins | 8081 | ⏳ 待部署 | CI/CD |
|
||
| Monitor | 3000 | ⏳ 待部署 | Grafana + Prometheus |
|
||
| ClickHouse | 8123 | ⏳ 待部署 | OLAP 分析数据库 |
|
||
|
||
### 有依赖服务
|
||
|
||
| 服务 | 依赖 | 状态 | 说明 |
|
||
|------|------|------|------|
|
||
| MySQL 主从 (mysql-repl-tool) | 无 | ✅ 已部署 | 工具类服务专用数据库 |
|
||
| Nacos | MySQL | ✅ 已部署 | 服务注册/配置中心 |
|
||
| XXL-Job-Admin | MySQL | ✅ 已部署 | 分布式任务调度 |
|
||
| Kibana | Elasticsearch | ✅ 已部署 | ES 可视化 |
|
||
| SkyWalking | Elasticsearch | ✅ 已部署 | APM 链路追踪 |
|
||
| Log (Logstash + Filebeat) | Elasticsearch | ✅ 已部署 | 日志处理 |
|
||
| Canal | MySQL, RabbitMQ | ✅ 已部署 | 数据同步 |
|
||
|
||
---
|
||
|
||
## 二十三、服务依赖关系图
|
||
|
||
```
|
||
┌─────────┐
|
||
│ Network │ ← 必须最先创建
|
||
└────┬────┘
|
||
│
|
||
┌────┴────┐
|
||
│Portainer│ ← 管理入口
|
||
└────┬────┘
|
||
│
|
||
┌───────────────┼───────────────┐
|
||
▼ ▼ ▼
|
||
┌─────────┐ ┌─────────┐ ┌─────────┐
|
||
│ MySQL │ │ Redis │ │ ES │
|
||
└────┬────┘ └─────────┘ └────┬────┘
|
||
│ │
|
||
┌────┴────────────┐ ┌────┴────┐
|
||
│ │ │ │
|
||
▼ ▼ ▼ ▼
|
||
┌───────┐ ┌──────────┐ ┌───────┐ ┌──────────┐
|
||
│ Nacos │ │XXL-Job │ │Kibana │ │SkyWalking│
|
||
└───┬───┘ │Admin │ └───────┘ └──────────┘
|
||
│ └──────────┘
|
||
▼
|
||
┌─────────────────┐
|
||
│ 业务微服务 │
|
||
│ (注册到 Nacos) │
|
||
└─────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 附录:目录结构
|
||
|
||
```
|
||
docker-swarm-review/
|
||
├── 10.5x环境配置记录/ # 环境部署文档
|
||
├── portainer/ # Portainer 管理平台
|
||
├── monitor/ # Grafana + Prometheus
|
||
├── mysql/ # MySQL 单机
|
||
├── mysql-repl-tool/ # MySQL 主从复制
|
||
├── redis/ # Redis 主从 + Sentinel
|
||
├── redis-review-132/ # Review 环境 Redis
|
||
├── mongodb/ # MongoDB
|
||
├── rabbitmq/ # RabbitMQ
|
||
├── elasticsearch/ # Elasticsearch
|
||
├── nacos/ # Nacos 单机
|
||
├── nacos-cluser/ # Nacos 集群
|
||
├── xxl-job-admin/ # XXL-Job
|
||
├── jenkins/ # Jenkins
|
||
├── canal/ # Canal
|
||
├── clickhouse/ # ClickHouse
|
||
├── minio/ # MinIO
|
||
├── nginx/ # Nginx
|
||
├── nginx-review-132/ # Review 环境 Nginx
|
||
├── skywalking/ # SkyWalking
|
||
├── log/ # ELK 日志
|
||
└── datart/ # BI 数据可视化
|
||
```
|
||
|
||
---
|
||
|
||
## 二十四、基础镜像详解
|
||
|
||
### 22.1 什么是基础镜像
|
||
|
||
基础镜像是构建应用镜像的"地基",包含运行应用所需的操作系统、运行时环境和通用工具。
|
||
|
||
**基础镜像地址:** `harbor.sino-assist.com/marsal1212/java11:latest`
|
||
|
||
### 22.2 基础镜像结构
|
||
|
||
```
|
||
harbor.sino-assist.com/marsal1212/java11:latest
|
||
│
|
||
├── FROM openjdk:11-jdk # 官方 OpenJDK 11 镜像(Debian 系统)
|
||
│
|
||
├── 系统工具
|
||
│ ├── curl # HTTP 请求工具(健康检查用)
|
||
│ ├── wget # 下载工具
|
||
│ ├── procps # 进程管理工具(ps、top)
|
||
│ ├── lsof # 查看打开文件
|
||
│ ├── iotop # IO 监控
|
||
│ └── lib32gcc-s1 # 32位兼容库
|
||
│
|
||
└── /skywalking-agent/ # SkyWalking Agent(链路追踪)
|
||
├── skywalking-agent.jar # Agent 主程序
|
||
├── config/
|
||
│ └── agent.config # 配置文件
|
||
├── plugins/ # 默认插件(自动加载)
|
||
├── optional-plugins/ # 可选插件
|
||
├── activations/ # 工具包激活器
|
||
└── bootstrap-plugins/ # 引导插件
|
||
```
|
||
|
||
### 22.3 Dockerfile 解析
|
||
|
||
```dockerfile
|
||
FROM --platform=linux/amd64 openjdk:11-jdk
|
||
# 基于官方 OpenJDK 11 镜像,强制使用 amd64 架构(兼容性)
|
||
|
||
RUN apt-get update && apt-get install -y curl iotop wget procps lsof lib32gcc-s1
|
||
# 安装运维/调试常用工具:
|
||
# - curl: 用于容器内健康检查(curl localhost:8080/actuator/health)
|
||
# - procps: 提供 ps、top 命令,排查问题时查看进程
|
||
# - lsof: 查看端口占用、文件句柄
|
||
# - iotop: IO 性能排查
|
||
|
||
ADD skywalking-agent /skywalking-agent
|
||
# 将本地 skywalking-agent 目录复制到镜像的 /skywalking-agent 路径
|
||
# 这样所有基于此镜像的应用都可以直接使用 Agent
|
||
|
||
CMD ["java","--version"]
|
||
# 默认命令(会被应用镜像覆盖)
|
||
```
|
||
|
||
### 22.4 镜像层级关系
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ 应用镜像 (harbor.sino-assist.com/sa-server/sa-gateway) │ ← Jib 构建
|
||
│ - 应用 JAR 包 │
|
||
│ - 应用配置 │
|
||
├─────────────────────────────────────────────────────────┤
|
||
│ 基础镜像 (harbor.sino-assist.com/marsal1212/java11) │ ← 自定义镜像
|
||
│ - SkyWalking Agent │
|
||
│ - curl/wget/procps 等工具 │
|
||
├─────────────────────────────────────────────────────────┤
|
||
│ openjdk:11-jdk │ ← 官方镜像
|
||
│ - JDK 11 运行时 │
|
||
│ - Debian 操作系统 │
|
||
└─────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 22.5 基础镜像如何被使用
|
||
|
||
#### Jib 构建时引用
|
||
|
||
在 `sa-server` 项目的 `build.gradle` 中配置:
|
||
|
||
```groovy
|
||
jib {
|
||
from {
|
||
image = "harbor.sino-assist.com/marsal1212/java11:latest" // 基础镜像
|
||
}
|
||
to {
|
||
image = "${docker_hub}/sa-server/${project.name}" // 目标镜像
|
||
tags = ["${docker_version}"]
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 构建流程
|
||
|
||
```
|
||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||
│ 源代码编译 │ ──► │ Jib 打包 │ ──► │ 推送到 Harbor │
|
||
│ (Gradle) │ │ (基于基础镜像) │ │ │
|
||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||
|
||
Jib 做的事情:
|
||
1. 拉取基础镜像 (java11:latest)
|
||
2. 在其之上添加应用层(JAR、配置)
|
||
3. 设置启动命令
|
||
4. 推送到 Harbor
|
||
```
|
||
|
||
#### 最终应用镜像内容
|
||
|
||
```
|
||
harbor.sino-assist.com/sa-server/sa-gateway:master
|
||
│
|
||
├── /skywalking-agent/ # 来自基础镜像
|
||
│ └── skywalking-agent.jar
|
||
│
|
||
├── /app/ # Jib 添加的应用层
|
||
│ ├── classes/ # 编译后的类文件
|
||
│ ├── libs/ # 依赖 JAR
|
||
│ └── resources/ # 配置文件
|
||
│
|
||
└── ENTRYPOINT: java -cp /app/... MainClass
|
||
```
|
||
|
||
### 22.6 运行时如何启用 SkyWalking
|
||
|
||
#### 配置位置说明
|
||
|
||
**重要:** SkyWalking 相关配置已在 `build.gradle` 的 Jib entrypoint 中设置,无需在 pipeline-script 中重复配置。
|
||
|
||
```groovy
|
||
// build.gradle 中的 Jib entrypoint 配置
|
||
entrypoint = ["/bin/sh", "-c",
|
||
'java -javaagent:/skywalking-agent/skywalking-agent.jar ' +
|
||
'-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=${namespace}-skywalking-oap:11800 ' +
|
||
'-DSW_AGENT_NAME=$project_name ' +
|
||
'-DSW_AGENT_INSTANCE_NAME=$project_name:${nativeIp} ' +
|
||
...
|
||
]
|
||
```
|
||
|
||
#### 容器启动流程
|
||
|
||
```
|
||
1. Docker 启动容器
|
||
│
|
||
2. 执行 Jib 配置的 entrypoint(包含 -javaagent 参数)
|
||
│ 命令: java -javaagent:/skywalking-agent/skywalking-agent.jar ...
|
||
│
|
||
3. SkyWalking Agent 加载
|
||
│
|
||
4. Agent 读取系统属性(-D 参数)
|
||
│ - -DSW_AGENT_NAME=$project_name → 服务名
|
||
│ - -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=${namespace}-skywalking-oap:11800 → OAP 地址
|
||
│
|
||
5. Agent 开始采集链路数据,上报到 OAP
|
||
```
|
||
|
||
#### 关键环境变量(由 pipeline-script 设置,被 entrypoint 引用)
|
||
|
||
| 环境变量 | 作用 | 示例值 |
|
||
|---------|------|-----|
|
||
| `namespace` | 用于构建 OAP 地址 `${namespace}-skywalking-oap:11800` | `review` |
|
||
| `project_name` | 服务名(SkyWalking UI 显示) | `sa-gateway` |
|
||
| `nativeIp` | 实例名后缀 | `192.168.3.132` |
|
||
|
||
### 22.7 基础镜像的优势
|
||
|
||
| 优势 | 说明 |
|
||
|------|------|
|
||
| **统一管理** | 所有服务使用相同的 JDK 版本和工具 |
|
||
| **减少重复** | SkyWalking Agent 只需打包一次 |
|
||
| **快速构建** | Jib 只需添加应用层,不用重复下载基础层 |
|
||
| **便于升级** | 更新基础镜像后,重新构建应用即可生效 |
|
||
| **运维友好** | 内置 curl/lsof 等工具,方便排查问题 |
|
||
|
||
### 22.8 如何更新基础镜像
|
||
|
||
如果需要更新 SkyWalking Agent 版本或添加工具:
|
||
|
||
```bash
|
||
# 1. 修改 Dockerfile 或更新 skywalking-agent 目录
|
||
|
||
# 2. 重新构建基础镜像
|
||
cd builder-docker/java11
|
||
docker build -f Dockerfile -t harbor.sino-assist.com/marsal1212/java11:latest .
|
||
|
||
# 3. 推送到 Harbor
|
||
docker push harbor.sino-assist.com/marsal1212/java11:latest
|
||
|
||
# 4. 重新构建所有应用镜像(Jenkins 中重新打包)
|
||
```
|
||
|
||
---
|
||
|
||
## 二十五、Jenkins Pipeline 部署脚本
|
||
|
||
### 23.1 脚本概述
|
||
|
||
Jenkins Pipeline 脚本用于自动化构建和部署微服务应用到 Docker Swarm 集群。
|
||
|
||
**脚本位置:** `docker-swarm-review/pipeline-script`
|
||
|
||
**备份文件:** `docker-swarm-review/jenkins/pipeline-script-backup-with-comments`
|
||
|
||
### 23.2 脚本功能
|
||
|
||
| 阶段 | 功能 |
|
||
|------|------|
|
||
| checkout | 从 GitLab 拉取代码 |
|
||
| docker-build-push | 使用 Gradle Jib 构建镜像并推送到 Harbor |
|
||
| docker-deploy | 通过 SSH 远程部署到 Docker Swarm |
|
||
|
||
### 23.3 Review 环境配置
|
||
|
||
| 配置项 | 值 | 说明 |
|
||
|--------|-----|------|
|
||
| harbor | harbor.sino-assist.com | 镜像仓库地址 |
|
||
| deploy_server | 192.168.3.132 | 部署服务器(Manager 节点) |
|
||
| profile | review | Spring 环境 |
|
||
| nacos_address | review-nacos1:8848,review-nacos2:8848,review-nacos3:8848 | Nacos 集群地址 |
|
||
| namespace | review | 命名空间(同时用于构建 SkyWalking OAP 地址) |
|
||
| SSH 用户 | root | SSH 登录用户 |
|
||
| 网络子网 | 10.18.0.0/16 | Docker Overlay 网络 |
|
||
|
||
### 23.4 节点映射表
|
||
|
||
| IP | 主机名 | 角色 |
|
||
|----|--------|------|
|
||
| 192.168.3.132 | ZD-BAK-APP2 | Manager (Leader) |
|
||
| 192.168.3.133 | zd-bak-app3 | Worker |
|
||
| 192.168.3.134 | ZD-BAK-APP1 | Worker |
|
||
|
||
### 23.5 完整脚本(含详细注释)
|
||
|
||
```groovy
|
||
#!/usr/bin/env groovy
|
||
// ============================================================================
|
||
// Jenkins Pipeline 脚本(Review 环境)
|
||
// 更新时间:2025-12-23
|
||
// ============================================================================
|
||
|
||
import groovy.json.JsonSlurperClassic
|
||
// 导入 Groovy JSON 解析类,用于解析 Jenkins 参数传入的 JSON 配置
|
||
|
||
//properties(projectProperties)
|
||
def jsonOption = new JsonSlurperClassic().parseText(params.modulesOption)
|
||
// 解析 Jenkins 参数 modulesOption(JSON 格式),转换为 Groovy 对象
|
||
// params.modulesOption 是 Jenkins 参数化构建时传入的 JSON 字符串
|
||
|
||
echo "jsonOption ${jsonOption}"
|
||
// 打印解析后的配置信息,用于调试
|
||
|
||
// ============================================================================
|
||
// 环境固定配置(Review 环境)
|
||
// ============================================================================
|
||
jsonOption.harbor = "harbor.sino-assist.com"
|
||
// Harbor 私有镜像仓库地址
|
||
|
||
jsonOption.deploy_server = "192.168.3.132"
|
||
// 部署目标服务器 IP(Swarm Manager 节点 ZD-BAK-APP2)
|
||
|
||
jsonOption.profile = "review"
|
||
// Spring Boot 激活的配置环境(application-review.yml)
|
||
|
||
jsonOption.nacos_address = "review-nacos1:8848,review-nacos2:8848,review-nacos3:8848"
|
||
// Nacos 集群地址(使用 Docker Overlay 网络内的服务名 + 内部端口)
|
||
// 三个 Nacos 节点分别部署在:
|
||
// - review-nacos1 -> ZD-BAK-APP1 (192.168.3.134)
|
||
// - review-nacos2 -> ZD-BAK-APP2 (192.168.3.132)
|
||
// - review-nacos3 -> zd-bak-app3 (192.168.3.133)
|
||
|
||
jsonOption.namespace = "review"
|
||
// Nacos 命名空间,同时也是 Docker Overlay 网络名称
|
||
// 注意:namespace 也用于 build.gradle 中构建 SkyWalking OAP 地址:${namespace}-skywalking-oap:11800
|
||
|
||
// ============================================================================
|
||
// 变量定义
|
||
// ============================================================================
|
||
def branch = params.branch
|
||
// Git 分支名(从 Jenkins 参数传入)
|
||
|
||
def DOCKER_CREDENTIAL_ID = 'harbor'
|
||
// Jenkins 凭据 ID,用于登录 Harbor 镜像仓库
|
||
|
||
def REGISTRY_URL = jsonOption.harbor
|
||
// 镜像仓库地址
|
||
|
||
def IMAGE_TAG = params.branch
|
||
// 镜像标签(使用 Git 分支名作为标签)
|
||
|
||
def deploy_modules = jsonOption.deploy_modules
|
||
// 要部署的模块列表(从 JSON 参数中获取)
|
||
|
||
def deploy_server = jsonOption.deploy_server
|
||
// 部署服务器 IP
|
||
|
||
def deploy_step = jsonOption.deploy_step
|
||
// 部署步骤(可能包含"打包镜像"和/或"部署服务")
|
||
|
||
def deploy_project_names = ""
|
||
// Gradle 构建的项目名列表(拼接后的字符串)
|
||
|
||
// ============================================================================
|
||
// 构建模块名拼接
|
||
// ============================================================================
|
||
for (module in deploy_modules) {
|
||
if (module.o == true) {
|
||
// module.o 表示该模块是否被选中部署
|
||
deploy_project_names += " ${module.module}:jib "
|
||
// 拼接 Gradle jib 任务名
|
||
// 结果类似: " sa-gateway:jib sa-auth:jib sa-system:jib "
|
||
// jib 是 Google 的容器镜像构建工具,无需 Docker daemon
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 主流程 node 块
|
||
// ============================================================================
|
||
node {
|
||
// node 块定义 Jenkins 执行节点,在此节点上执行所有 stage
|
||
|
||
def gradleHome = tool 'gradle'
|
||
// 获取 Jenkins 全局工具配置中名为 'gradle' 的 Gradle 安装路径
|
||
|
||
def gradle = "${gradleHome}/bin/gradle"
|
||
// Gradle 可执行文件的完整路径
|
||
|
||
// ========================================================================
|
||
// Stage 1: 拉取代码
|
||
// ========================================================================
|
||
stage('checkout') {
|
||
git branch: branch, credentialsId: 'gitlab', url: 'https://git.sino-assist.com/server/sa-server.git'
|
||
// 从 GitLab 克隆代码
|
||
// - branch: 要拉取的分支(从参数传入)
|
||
// - credentialsId: Jenkins 中配置的 GitLab 凭据 ID
|
||
// - url: Git 仓库地址
|
||
}
|
||
|
||
// ========================================================================
|
||
// Stage 2: 构建镜像并推送到 Harbor
|
||
// ========================================================================
|
||
stage('docker-build-push') {
|
||
if (deploy_step.contains("打包镜像")) {
|
||
// 判断部署步骤是否包含"打包镜像"
|
||
|
||
withCredentials([usernamePassword(passwordVariable: 'DOCKER_PASSWORD', usernameVariable: 'DOCKER_USERNAME', credentialsId: "${DOCKER_CREDENTIAL_ID}",)]) {
|
||
// 从 Jenkins 凭据库获取 Harbor 的用户名和密码
|
||
// 用户名存入 DOCKER_USERNAME 变量
|
||
// 密码存入 DOCKER_PASSWORD 变量
|
||
|
||
sh "docker login $REGISTRY_URL -u '$DOCKER_USERNAME' -p '$DOCKER_PASSWORD'"
|
||
// 登录 Harbor 镜像仓库
|
||
}
|
||
|
||
sh "$gradle $deploy_project_names -x test --parallel --build-cache -Pdocker_hub='$REGISTRY_URL' -Pdocker_version=$IMAGE_TAG -Djib.console=plain"
|
||
// 执行 Gradle jib 任务,构建并推送 Docker 镜像
|
||
// 参数说明:
|
||
// $deploy_project_names: 要构建的模块列表(如 sa-gateway:jib)
|
||
// -x test: 跳过单元测试
|
||
// --parallel: 并行构建多个模块
|
||
// --build-cache: 启用构建缓存,加速构建
|
||
// -Pdocker_hub: 传递镜像仓库地址给 build.gradle
|
||
// -Pdocker_version: 传递镜像版本标签(分支名)
|
||
// -Djib.console=plain: jib 输出格式为纯文本(适合 CI 日志)
|
||
}
|
||
}
|
||
|
||
// ========================================================================
|
||
// Stage 3: 部署服务到 Docker Swarm
|
||
// ========================================================================
|
||
if (deploy_step.contains("部署服务")) {
|
||
// 判断部署步骤是否包含"部署服务"
|
||
|
||
stage('docker-deploy') {
|
||
for (final module in deploy_modules) {
|
||
if (module.o == true) {
|
||
// 遍历所有被选中的模块
|
||
|
||
def modules = module.module.split(":")
|
||
module.projectName = modules[modules.length - 1]
|
||
// 解析模块名
|
||
// 如 "sa-modules:sa-gateway" -> 取最后一段 "sa-gateway"
|
||
|
||
module.imageTag = IMAGE_TAG
|
||
// 设置镜像标签
|
||
|
||
echo "deploy module ${module.module}"
|
||
// 打印正在部署的模块名
|
||
|
||
def services = docker_service_param(module, jsonOption)
|
||
// 调用 docker_service_param 函数生成服务部署参数
|
||
|
||
echo "部署服务"
|
||
for (final def svc in services) {
|
||
// 遍历每个服务实例(一个模块可能部署多个实例)
|
||
|
||
String yml = makeYML(svc)
|
||
// 调用 makeYML 函数生成 Docker Stack YAML 配置
|
||
|
||
String serverName = svc.get("serviceName")
|
||
// 获取服务名(如 ss132_sa-gateway)
|
||
|
||
String ymlFile = "/data/swarm/${serverName}.yml"
|
||
// YAML 文件存放路径
|
||
|
||
String deploy = "sshpass -p 'Sino.2025' ssh root@${deploy_server} \" mkdir -p /data/swarm/ && echo '''${yml}''' > ${ymlFile} && docker stack deploy -c ${ymlFile} ${serverName} --prune --with-registry-auth\""
|
||
// 通过 SSH 远程执行部署命令:
|
||
// sshpass -p 'Sino.2025': 使用密码登录(避免交互式输入)
|
||
// ssh root@${deploy_server}: SSH 到部署服务器
|
||
// mkdir -p /data/swarm/: 创建存放 yml 文件的目录
|
||
// echo '''${yml}''' > ${ymlFile}: 将生成的 YAML 写入文件
|
||
// docker stack deploy: 部署 Docker Stack
|
||
// -c ${ymlFile}: 指定 compose 文件
|
||
// ${serverName}: Stack 名称
|
||
// --prune: 删除不再存在于 compose 文件中的服务
|
||
// --with-registry-auth: 将 registry 认证信息传递给 swarm agents
|
||
|
||
echo deploy
|
||
// 打印部署命令(用于调试)
|
||
|
||
sh deploy
|
||
// 执行部署命令
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// makeYML 函数:生成 Docker Stack YAML 配置
|
||
// ============================================================================
|
||
def makeYML(params) {
|
||
return """
|
||
version: \\"3.8\\"
|
||
services:
|
||
svc:
|
||
image: ${params.IMAGE}
|
||
# 镜像地址,如 harbor.sino-assist.com/sa-server/sa-gateway:master
|
||
|
||
environment:
|
||
- active_profile=${params.profile}
|
||
# Spring Boot 激活的配置环境
|
||
|
||
- nacos_address=${params.nacos_address}
|
||
# Nacos 服务地址
|
||
|
||
- nacos_password=gkxl2024#@
|
||
# Nacos 登录密码
|
||
|
||
- namespace=${params.namespace}
|
||
# Nacos 命名空间
|
||
|
||
- project_name=${params.projectName}
|
||
# 项目名称(用于日志、监控等标识)
|
||
|
||
- params=${params.params}
|
||
# 额外的 JVM 参数(如网络配置)
|
||
|
||
- nativeIp=${params.nativeIp}
|
||
# 服务实例绑定的 IP 地址
|
||
|
||
- reservationsMemory=${params.reservationsMemory}
|
||
# 预留内存(传递给应用)
|
||
|
||
- limitMemory=${params.limitMemory}
|
||
# 内存限制(传递给应用)
|
||
|
||
- TZ=Asia/Shanghai
|
||
# 时区设置
|
||
|
||
# 注意:SkyWalking 相关配置(SW_AGENT_NAME、SW_AGENT_COLLECTOR_BACKEND_SERVICES、JAVA_TOOL_OPTIONS)
|
||
# 已在 build.gradle 的 Jib entrypoint 中配置,无需在此重复设置
|
||
# OAP 地址通过 namespace 变量构建:${namespace}-skywalking-oap:11800
|
||
|
||
ports:
|
||
- '${params.port}:8080'
|
||
# 端口映射:宿主机端口:容器端口
|
||
# 容器内应用统一监听 8080 端口
|
||
|
||
healthcheck:
|
||
test: \\"curl --fail --silent localhost:8080/actuator/health/ping | grep UP || exit 1\\"
|
||
# 健康检查命令:
|
||
# curl 访问 Spring Boot Actuator 健康检查端点
|
||
# grep UP 检查返回值是否包含 UP
|
||
# 失败则返回 exit 1
|
||
|
||
interval: 15s
|
||
# 检查间隔:每 15 秒检查一次
|
||
|
||
timeout: 5s
|
||
# 超时时间:5 秒内无响应视为失败
|
||
|
||
retries: 20
|
||
# 重试次数:连续 20 次失败后标记为 unhealthy
|
||
|
||
volumes:
|
||
- ${params.namespace}_logs:/logs
|
||
# 日志卷挂载:将容器内 /logs 目录映射到命名卷
|
||
|
||
logging:
|
||
driver: json-file
|
||
# 日志驱动:使用 Docker 默认的 json-file 驱动
|
||
|
||
options:
|
||
max-size: "1G"
|
||
# 单个日志文件最大 1GB
|
||
|
||
max-file: "3"
|
||
# 最多保留 3 个日志文件(滚动)
|
||
|
||
extra_hosts:
|
||
- "hostname:127.0.0.1"
|
||
# 添加 hosts 记录
|
||
|
||
- "open.property.cic.cn:59.46.218.8"
|
||
# 外部服务 hosts 记录
|
||
|
||
deploy:
|
||
mode: replicated
|
||
# 部署模式:replicated(指定副本数)
|
||
|
||
replicas: 1
|
||
# 副本数量:1 个实例
|
||
|
||
restart_policy:
|
||
condition: on-failure
|
||
# 重启策略:仅在失败时重启
|
||
|
||
delay: 5s
|
||
# 重启延迟:5 秒后重启
|
||
|
||
max_attempts: 3
|
||
# 最大重启次数:3 次
|
||
|
||
update_config:
|
||
order: stop-first
|
||
# 更新策略:先停止旧容器,再启动新容器
|
||
# 适合资源紧张的环境,但会有短暂服务中断
|
||
|
||
resources:
|
||
limits:
|
||
cpus: \\"${params.limitCpu}\\"
|
||
# CPU 限制(如 "2" 表示最多使用 2 核)
|
||
|
||
memory: ${params.limitMemory}
|
||
# 内存限制(如 "2G")
|
||
|
||
reservations:
|
||
cpus: \\"${params.reservationsCpu}\\"
|
||
# CPU 预留(保证最少可用的 CPU 资源)
|
||
|
||
memory: ${params.reservationsMemory}
|
||
# 内存预留(保证最少可用的内存资源)
|
||
|
||
placement:
|
||
constraints:
|
||
- "node.hostname==${params.hostname}"
|
||
# 节点约束:只在指定主机名的节点上运行
|
||
# 用于将服务固定到特定服务器
|
||
|
||
networks:
|
||
default:
|
||
name: ${params.namespace}
|
||
# 网络名称:review
|
||
|
||
external: true
|
||
# 使用外部已存在的网络(不自动创建)
|
||
|
||
volumes:
|
||
${params.namespace}_logs:
|
||
external: true
|
||
# 使用外部已存在的卷(不自动创建)
|
||
"""
|
||
}
|
||
|
||
// ============================================================================
|
||
// docker_service_param 函数:生成服务部署参数
|
||
// ============================================================================
|
||
def docker_service_param(module, jsonOption) {
|
||
|
||
def ipHostnameMap = [
|
||
'192.168.3.132': 'ZD-BAK-APP2',
|
||
'192.168.3.133': 'zd-bak-app3',
|
||
'192.168.3.134': 'ZD-BAK-APP1',
|
||
]
|
||
// IP 到主机名的映射表(Review 环境)
|
||
// 用于 Swarm 部署时的节点约束(placement constraints)
|
||
// 节点信息:
|
||
// - ZD-BAK-APP1 (192.168.3.134): Worker 节点
|
||
// - ZD-BAK-APP2 (192.168.3.132): Manager 节点 (Leader)
|
||
// - zd-bak-app3 (192.168.3.133): Worker 节点
|
||
|
||
|
||
def projectName = module.projectName
|
||
// 项目名称(如 sa-gateway)
|
||
|
||
def node = module.node
|
||
// 副本数量配置
|
||
|
||
def cpu = module.cpu.split("-")
|
||
// CPU 配置,格式 "预留-限制",如 "0.5-2"
|
||
// cpu[0] = 预留值, cpu[1] = 限制值
|
||
|
||
def memory = module.memory.split("-")
|
||
// 内存配置,格式 "预留-限制",如 "512M-2G"
|
||
// memory[0] = 预留值, memory[1] = 限制值
|
||
|
||
def address = module.address.split("\n")
|
||
// 部署地址配置,格式 "IP:端口",多个地址换行分隔
|
||
// 如:
|
||
// 192.168.3.132:18080
|
||
// 192.168.3.133:18080
|
||
|
||
|
||
def services = []
|
||
// 服务列表(一个模块可能部署到多台服务器)
|
||
|
||
for (final def add in address) {
|
||
// 遍历每个部署地址
|
||
|
||
def addSplit = add.split(":")
|
||
def ip = addSplit[0]
|
||
// 服务器 IP
|
||
|
||
def port = addSplit[1]
|
||
// 服务端口
|
||
|
||
def hostname = ipHostnameMap.get(ip)
|
||
// 通过 IP 查找对应的主机名
|
||
|
||
def serviceName = """ss${ip.split("\\.")[3]}_${projectName}"""
|
||
// 服务名生成规则:ss + IP最后一段 + _ + 项目名
|
||
// 如 192.168.3.132 + sa-gateway -> ss132_sa-gateway
|
||
|
||
def par = """-Dspring.cloud.inetutils.preferredNetworks=10.18"""
|
||
// JVM 参数:指定 Spring Cloud 优先使用的网络段
|
||
// 10.18 是 Review 环境 Docker Overlay 网络的子网段
|
||
// 确保服务注册到 Nacos 时使用正确的 IP 地址
|
||
|
||
services.add([
|
||
nacos_address : jsonOption.nacos_address,
|
||
// Nacos 集群地址
|
||
|
||
namespace : jsonOption.namespace,
|
||
// 命名空间
|
||
|
||
projectName : projectName,
|
||
// 项目名
|
||
|
||
IMAGE : "$jsonOption.harbor/sa-server/$projectName:$module.imageTag",
|
||
// 完整镜像地址,如 harbor.sino-assist.com/sa-server/sa-gateway:master
|
||
|
||
profile : jsonOption.profile,
|
||
// Spring 环境
|
||
|
||
node : node,
|
||
// 副本数量
|
||
|
||
reservationsCpu : cpu[0],
|
||
// CPU 预留值
|
||
|
||
limitCpu : cpu[1],
|
||
// CPU 限制值
|
||
|
||
reservationsMemory: memory[0],
|
||
// 内存预留值
|
||
|
||
limitMemory : memory[1],
|
||
// 内存限制值
|
||
|
||
serviceName : serviceName,
|
||
// 服务名(Stack 名称)
|
||
|
||
hostname : hostname,
|
||
// 目标主机名(节点约束)
|
||
|
||
port : port,
|
||
// 服务端口
|
||
|
||
nativeIp : ip,
|
||
// 部署目标 IP
|
||
|
||
params : par
|
||
// 额外 JVM 参数
|
||
])
|
||
}
|
||
echo "params ${params}"
|
||
// 打印参数信息(调试用)
|
||
|
||
|
||
return services
|
||
// 返回服务列表
|
||
}
|
||
|
||
// vim: ft=groovy
|
||
// 告诉 vim 编辑器使用 groovy 语法高亮
|
||
```
|
||
|
||
### 23.6 修改记录
|
||
|
||
| 日期 | 修改内容 |
|
||
|------|----------|
|
||
| 2025-12-23 | 初始化 Review 环境配置:namespace (prod → review)、SSH 用户 (sasys → root)、IP 映射表、网络子网 (10.17 → 10.18) |
|
||
| 2025-12-23 | 移除冗余 SkyWalking 配置:SkyWalking 相关配置已在 build.gradle 的 Jib entrypoint 中设置,无需在部署时重复配置环境变量 |
|
||
|
||
### 23.7 Jenkins 配置要求
|
||
|
||
在 Jenkins 中使用此脚本需要以下配置:
|
||
|
||
#### 凭据配置
|
||
|
||
| 凭据 ID | 类型 | 用途 |
|
||
|---------|------|------|
|
||
| harbor | Username with password | Harbor 镜像仓库登录 |
|
||
| gitlab | Username with password | GitLab 代码仓库访问 |
|
||
|
||
#### 全局工具配置
|
||
|
||
| 工具名 | 类型 | 说明 |
|
||
|--------|------|------|
|
||
| gradle | Gradle | Gradle 构建工具路径 |
|
||
|
||
#### 参数化构建配置
|
||
|
||
| 参数名 | 类型 | 说明 |
|
||
|--------|------|------|
|
||
| branch | String/Git Parameter | Git 分支名 |
|
||
| modulesOption | String | JSON 格式的模块配置 |
|
||
|
||
#### modulesOption JSON 格式示例
|
||
|
||
```json
|
||
{
|
||
"deploy_modules": [
|
||
{
|
||
"module": "sa-gateway",
|
||
"o": true,
|
||
"node": 1,
|
||
"cpu": "0.5-2",
|
||
"memory": "512M-2G",
|
||
"address": "192.168.3.132:18080"
|
||
}
|
||
],
|
||
"deploy_step": "打包镜像,部署服务"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 二十四、前端服务部署 (sa-cc)
|
||
|
||
### 24.1 概述
|
||
|
||
前端服务 sa-cc 是 Vue 项目,部署采用 Docker Swarm Stack 方式。
|
||
|
||
### 24.2 Jenkins Pipeline 脚本
|
||
|
||
脚本文件:`docker-swarm-review/pipeline-script-cc`
|
||
|
||
#### 配置说明
|
||
|
||
| 配置项 | Review 值 | 说明 |
|
||
|--------|-----------|------|
|
||
| deploy_server | 192.168.3.132 | 部署目标服务器 |
|
||
| profile | review | 环境标识 |
|
||
| NAMESPACE | review | Docker 网络命名空间 |
|
||
| REGISTRY_URL | harbor.sino-assist.com | 镜像仓库地址 |
|
||
| SSH 用户 | root | 远程执行用户 |
|
||
| SSH 密码 | Sino.2025 | 远程执行密码 |
|
||
|
||
#### 脚本内容
|
||
|
||
```groovy
|
||
#!/usr/bin/env groovy
|
||
|
||
def projectProperties = [
|
||
[$class: 'BuildDiscarderProperty', strategy: [$class: 'LogRotator', numToKeepStr: '5']],
|
||
parameters([
|
||
string(name: 'branch', description: '分支'),
|
||
booleanParam(name: 'yarnInstall', description: '是否更新node_modules'),
|
||
string(name: 'backUrl', defaultValue: 'https://api1.sino-assist.com', description: 'backend server url')
|
||
])
|
||
]
|
||
|
||
def deploy_server = "192.168.3.132"
|
||
def profile = "review"
|
||
def NAMESPACE = "review"
|
||
def REGISTRY_URL = "harbor.sino-assist.com"
|
||
|
||
node {
|
||
def workspace = pwd()
|
||
def DOCKER_CREDENTIAL_ID = 'harbor'
|
||
|
||
stage('checkout') {
|
||
git branch: branch, credentialsId: 'gitlab', url: 'https://git.sino-assist.com/server/sa-cc.git'
|
||
}
|
||
|
||
if(params.build==true){
|
||
|
||
stage('build') {
|
||
nodejs(nodeJSInstallationName: 'nodejs-v22') {
|
||
if(params.yarnInstall == true){
|
||
sh "yarn"
|
||
}
|
||
sh "export NODE_OPTIONS=--openssl-legacy-provider"
|
||
String backUrl = params.backUrl
|
||
if(profile == 'prod'){
|
||
sh "sed -i 's|VUE_APP_BACK_REST_URL_PLACE_HOLDER|${backUrl}|' ${workspace}/.env.prod"
|
||
sh "yarn build-prod"
|
||
}else{
|
||
sh "sed -i 's|VUE_APP_BACK_REST_URL_PLACE_HOLDER|${backUrl}|' ${workspace}/.env.alpha"
|
||
sh "yarn build"
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
stage('docker-login') {
|
||
withCredentials([usernamePassword(passwordVariable: 'DOCKER_PASSWORD', usernameVariable: 'DOCKER_USERNAME', credentialsId: "${DOCKER_CREDENTIAL_ID}",)]) {
|
||
sh "echo '$DOCKER_PASSWORD' | docker login ${REGISTRY_URL} -u '$DOCKER_USERNAME' --password-stdin"
|
||
}
|
||
}
|
||
|
||
stage('docker-build') {
|
||
sh " docker build -f k8s/Dockerfile -t ${REGISTRY_URL}/new-sino/sa-cc:${NAMESPACE} ."
|
||
}
|
||
|
||
stage('docker-push') {
|
||
sh "docker push ${REGISTRY_URL}/new-sino/sa-cc:${NAMESPACE}"
|
||
}
|
||
}
|
||
|
||
stage('deploy') {
|
||
String yml = """
|
||
version: \\"3.8\\"
|
||
services:
|
||
svc:
|
||
image: ${REGISTRY_URL}/new-sino/sa-cc:${NAMESPACE}
|
||
ports:
|
||
- '8081:8080'
|
||
environment:
|
||
- TZ=Asia/Shanghai
|
||
deploy:
|
||
mode: replicated
|
||
replicas: 1
|
||
restart_policy:
|
||
condition: on-failure
|
||
delay: 5s
|
||
max_attempts: 3
|
||
update_config:
|
||
order: start-first
|
||
|
||
resources:
|
||
limits:
|
||
cpus: \\"1\\"
|
||
memory: "300M"
|
||
reservations:
|
||
cpus: \\"4\\"
|
||
memory: "4G"
|
||
placement:
|
||
constraints:
|
||
- "node.labels.${NAMESPACE}_sa-cc==1"
|
||
networks:
|
||
default:
|
||
name: ${NAMESPACE}
|
||
external: true
|
||
"""
|
||
String serverName = "${NAMESPACE}_ss_sa-cc"
|
||
String ymlFile = "/data/swarm/${serverName}.yml"
|
||
String deploy = "sshpass -p 'Sino.2025' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@${deploy_server} \" mkdir -p /data/swarm/ && echo '''${yml}''' > ${ymlFile} && docker stack deploy -c ${ymlFile} ${serverName} --prune --with-registry-auth\""
|
||
echo deploy
|
||
sh deploy
|
||
}
|
||
}
|
||
```
|
||
|
||
### 24.3 部署前准备
|
||
|
||
#### 添加节点标签
|
||
|
||
前端服务通过节点标签约束部署位置,需要先在目标节点添加标签:
|
||
|
||
```bash
|
||
# 查看集群节点
|
||
docker node ls
|
||
|
||
# 在目标节点添加标签(示例:部署到 132 节点)
|
||
docker node update --label-add review_sa-cc=1 ZD-BAK-APP2
|
||
```
|
||
|
||
### 24.4 Jenkins 配置
|
||
|
||
#### 参数化构建配置
|
||
|
||
| 参数名 | 类型 | 说明 |
|
||
|--------|------|------|
|
||
| branch | String | Git 分支名 |
|
||
| build | Boolean | 是否执行构建(可只部署不构建) |
|
||
| yarnInstall | Boolean | 是否更新 node_modules |
|
||
| backUrl | String | 后端 API 地址 |
|
||
|
||
### 24.5 访问验证
|
||
|
||
部署完成后,前端服务监听 8081 端口:
|
||
|
||
```
|
||
http://192.168.3.132:8081
|
||
```
|
||
|
||
---
|
||
|
||
## 二十五、Nginx 反向代理(解决跨域)
|
||
|
||
### 25.1 问题背景
|
||
|
||
直接通过 IP:端口 访问前端时,前端 (8081) 调用后端 (28092) 会因端口不同触发浏览器跨域限制:
|
||
|
||
```
|
||
Access to XMLHttpRequest at 'http://192.168.3.132:28092/common/auth/token'
|
||
from origin 'http://192.168.3.132:8081' has been blocked by CORS policy
|
||
```
|
||
|
||
**同源策略**:协议 + 域名/IP + 端口 三者必须完全相同,端口不同即为跨域。
|
||
|
||
### 25.2 解决方案
|
||
|
||
部署 Nginx 反向代理,将前后端统一到同一端口:
|
||
|
||
```
|
||
http://192.168.3.132:8080
|
||
├── / → 前端 (8081)
|
||
└── /api路径/ → 后端网关 (28092)
|
||
```
|
||
|
||
### 25.3 部署步骤
|
||
|
||
#### 25.3.1 创建配置目录
|
||
|
||
```bash
|
||
mkdir -p /data/nginx-proxy
|
||
```
|
||
|
||
#### 25.3.2 创建 Nginx 配置文件
|
||
|
||
```bash
|
||
cat > /data/nginx-proxy/nginx.conf << 'EOF'
|
||
worker_processes auto;
|
||
|
||
events {
|
||
worker_connections 1024;
|
||
}
|
||
|
||
http {
|
||
include mime.types;
|
||
default_type application/octet-stream;
|
||
sendfile on;
|
||
keepalive_timeout 65;
|
||
client_max_body_size 100M;
|
||
|
||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||
'$status $body_bytes_sent "$http_referer" '
|
||
'"$http_user_agent"';
|
||
|
||
access_log /var/log/nginx/access.log main;
|
||
error_log /var/log/nginx/error.log warn;
|
||
|
||
# 18092:接收来自 3.110 nginx 的 apireview 转发请求,记录日志后再转发到网关
|
||
server {
|
||
listen 18092;
|
||
|
||
access_log /var/log/nginx/access.log main;
|
||
|
||
location / {
|
||
proxy_pass http://192.168.3.132:28092;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
}
|
||
}
|
||
|
||
server {
|
||
listen 8080;
|
||
|
||
# 前端
|
||
location / {
|
||
proxy_pass http://192.168.3.132:8081/;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
}
|
||
|
||
# 后端网关 - 所有 API 请求
|
||
location ~ ^/(common|order|supplier|contract|base|export-app|auth|user|system|api|ws|return|returnVehicle|returnOrder|supplierManage|agg-api|zgs|gps|data-search)/ {
|
||
proxy_pass http://192.168.3.132:28092;
|
||
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 Upgrade $http_upgrade;
|
||
proxy_set_header Connection "upgrade";
|
||
}
|
||
}
|
||
}
|
||
EOF
|
||
```
|
||
|
||
#### 25.3.3 启动 Nginx 容器
|
||
|
||
```bash
|
||
mkdir -p /data/nginx-proxy/logs
|
||
|
||
docker run -d \
|
||
--name nginx-proxy \
|
||
--restart always \
|
||
-p 8080:8080 \
|
||
-p 18092:18092 \
|
||
-v /data/nginx-proxy/nginx.conf:/etc/nginx/nginx.conf:ro \
|
||
-v /data/nginx-proxy/logs:/var/log/nginx \
|
||
nginx:alpine
|
||
```
|
||
|
||
#### 25.3.4 配置日志按天分割(logrotate)
|
||
|
||
日志写入宿主机 `/data/nginx-proxy/logs/`,由 logrotate 每天切割,保留 90 天(约3个月)。
|
||
|
||
```bash
|
||
cat > /etc/logrotate.d/nginx-proxy << 'EOF'
|
||
/data/nginx-proxy/logs/*.log {
|
||
daily
|
||
rotate 90
|
||
dateext
|
||
dateformat -%Y%m%d
|
||
missingok
|
||
notifempty
|
||
compress
|
||
delaycompress
|
||
sharedscripts
|
||
postrotate
|
||
docker kill --signal=USR1 nginx-proxy 2>/dev/null || true
|
||
endscript
|
||
}
|
||
EOF
|
||
```
|
||
|
||
验证配置并手动执行一次:
|
||
|
||
```bash
|
||
# 验证配置语法
|
||
logrotate -d /etc/logrotate.d/nginx-proxy
|
||
|
||
# 手动执行一次(不影响生产)
|
||
logrotate -f /etc/logrotate.d/nginx-proxy
|
||
```
|
||
|
||
> logrotate 默认由系统 cron 每天凌晨自动执行(`/etc/cron.daily/logrotate`),无需额外配置定时任务。
|
||
|
||
#### 25.3.5 nginx-review-132 日志按天分割(logrotate)
|
||
|
||
`nginx-review-132` 通过 Docker Swarm 部署在 `ZD-BAK-APP2` 节点上,日志挂载在宿主机 `/opt/logs/nginx/`,在该节点上执行以下配置。
|
||
|
||
```bash
|
||
cat > /etc/logrotate.d/nginx-review << 'EOF'
|
||
/opt/logs/nginx/*.log {
|
||
daily
|
||
rotate 90
|
||
dateext
|
||
dateformat -%Y%m%d
|
||
missingok
|
||
notifempty
|
||
compress
|
||
delaycompress
|
||
sharedscripts
|
||
postrotate
|
||
docker kill --signal=USR1 $(docker ps -qf name=nginx-review) 2>/dev/null || true
|
||
endscript
|
||
}
|
||
EOF
|
||
```
|
||
|
||
验证配置并手动执行一次:
|
||
|
||
```bash
|
||
# 验证配置语法
|
||
logrotate -d /etc/logrotate.d/nginx-review
|
||
|
||
# 手动执行一次(不影响生产)
|
||
logrotate -f /etc/logrotate.d/nginx-review
|
||
```
|
||
|
||
#### 25.3.6 生产环境 nginx 日志按天分割(logrotate)
|
||
|
||
生产 nginx(`prod_nginx_server1`)部署在 ZD-CRM1,日志挂载在宿主机 `/opt/logs/nginx/`。需在 **ZD-CRM1 和 ZD-CRM2 两台节点上分别执行**(`prod_nginx_server2` 后续恢复后同样生效)。
|
||
|
||
> 注意:`/opt/logs/` 目录权限为 777,需在配置中加 `su root sasys` 指令,否则 logrotate 会因权限问题跳过。
|
||
|
||
```bash
|
||
cat > /etc/logrotate.d/nginx-prod << 'EOF'
|
||
/opt/logs/nginx/*.log {
|
||
su root sasys
|
||
daily
|
||
rotate 90
|
||
dateext
|
||
dateformat -%Y%m%d
|
||
missingok
|
||
notifempty
|
||
compress
|
||
delaycompress
|
||
sharedscripts
|
||
postrotate
|
||
docker kill --signal=USR1 $(docker ps -qf name=prod_nginx) 2>/dev/null || true
|
||
endscript
|
||
}
|
||
EOF
|
||
```
|
||
|
||
验证配置并手动执行一次:
|
||
|
||
```bash
|
||
# 验证配置语法
|
||
logrotate -d /etc/logrotate.d/nginx-prod
|
||
|
||
# 手动执行一次(不影响生产)
|
||
logrotate -f /etc/logrotate.d/nginx-prod
|
||
```
|
||
|
||
#### 25.3.7 常见问题排查
|
||
|
||
**问题一:logrotate 显示已执行但无归档文件生成**
|
||
|
||
现象:`/var/lib/logrotate/logrotate.status` 中有记录,但 `/opt/logs/nginx/` 目录下没有 `.gz` 归档文件。
|
||
|
||
原因:logrotate 首次执行时日志文件因长期权限问题积压过大,压缩超时或 IO 中断导致归档失败,但 status 已写入,后续每天判断"今天已转过"而跳过。
|
||
|
||
排查命令:
|
||
|
||
```bash
|
||
# 查看 status 记录
|
||
cat /var/lib/logrotate/logrotate.status | grep nginx
|
||
|
||
# 查看 cron 执行日志
|
||
grep logrotate /var/log/cron | tail -10
|
||
```
|
||
|
||
---
|
||
|
||
**问题二:logrotate 执行后 nginx 未切换到新日志文件(仍写入 access.log-old)**
|
||
|
||
现象:发送 `USR1` 信号后,新的 `access.log` 没有日志写入,nginx 仍在写旧文件。
|
||
|
||
原因:nginx 容器内进程以 `uid 1001` 运行,`tail -c` 生成的临时文件默认属主为 root,`1001` 无写入权限,`USR1` 后重新 `open` 失败,继续持有旧文件描述符。
|
||
|
||
解决:
|
||
|
||
```bash
|
||
# 修正新 access.log 的权限
|
||
chmod 777 /opt/logs/nginx/access.log
|
||
|
||
# 重新发送 USR1
|
||
docker kill --signal=USR1 $(docker ps -qf name=prod_nginx)
|
||
|
||
# 确认已切换
|
||
tail -f /opt/logs/nginx/access.log
|
||
```
|
||
|
||
---
|
||
|
||
**问题三:日志文件积压过大(数百 GB),需手动清理后重新切割**
|
||
|
||
适用场景:logrotate 长期未生效导致 `access.log` 积压,需保留最近部分数据后重新切割。
|
||
|
||
```bash
|
||
# 1. 截取最后 10G 到临时文件(保留近期数据)
|
||
tail -c 10G /opt/logs/nginx/access.log > /opt/logs/nginx/access.log.tmp
|
||
|
||
# 2. 原子替换(nginx 此时仍写旧文件,不中断)
|
||
mv /opt/logs/nginx/access.log /opt/logs/nginx/access.log-old
|
||
mv /opt/logs/nginx/access.log.tmp /opt/logs/nginx/access.log
|
||
|
||
# 3. 修正权限(nginx 以 1001 运行,需可写)
|
||
chmod 777 /opt/logs/nginx/access.log
|
||
|
||
# 4. 发送 USR1,让 nginx 重新打开文件
|
||
docker kill --signal=USR1 $(docker ps -qf name=prod_nginx)
|
||
|
||
# 5. 确认 nginx 已切换到新文件
|
||
tail -f /opt/logs/nginx/access.log
|
||
|
||
# 6. 确认后删除旧的积压文件
|
||
rm /opt/logs/nginx/access.log-old
|
||
|
||
# 7. 强制执行 logrotate,重新建立切割基准
|
||
logrotate -f /etc/logrotate.d/nginx-prod
|
||
```
|
||
|
||
> 注意:`mv` 是原子操作,nginx 在收到 `USR1` 之前始终持有旧文件的文件描述符,文件被 `mv` 走不会中断写入,`USR1` 之后才重新 `open` 新的 `access.log`。
|
||
|
||
#### 25.3.8 将 3.110 宿主机 nginx 迁移到 3.132(Swarm)
|
||
|
||
**背景**:原 3.110 为宿主机直接安装的 nginx,承载所有对外域名的反向代理(CRM1/CRM2/Review/UAT/Pay 等)。迁移目标:将全部配置合并到 3.132 的 Docker Swarm nginx service,去除 3.110→3.132:18092 的中转层,直连后端网关。
|
||
|
||
**配置文件位置**:`docker-swarm-review/nginx-review-132/swarm/`
|
||
|
||
```
|
||
swarm/
|
||
├── nginx.conf # 主配置
|
||
├── ssl.sino_assist.conf # sino-assist.com 证书 include(23368363 目录)
|
||
├── ssl.conf # sinoassist.com 证书 include(2026 目录)
|
||
├── sites/
|
||
│ ├── crm1.conf # crm1/api1/api-sit/portainer/oem-jlr
|
||
│ ├── crm2.conf # api2/crm2/cc.crm2/stomp2/test-api
|
||
│ ├── review.conf # apireview/ccreview/jenkins(直连 28092,无中转)
|
||
│ ├── fastdfs.conf # file-gk(18888/18889)
|
||
│ ├── pay.conf # pay/pay-manager/pay-client
|
||
│ ├── wx.conf # supplierwxtest/site.sinoassist
|
||
│ ├── git.conf # jira/itsm/wiki/vote/harbor/git/maven 等
|
||
│ ├── uat.conf # uat/api-uat/api-pre
|
||
│ └── zd_report.conf # report/bi.sino-assist/bi.sinoassist
|
||
├── docker-compose.yml
|
||
└── logrotate-nginx
|
||
```
|
||
|
||
**部署步骤**:
|
||
|
||
**1. 在 ZD-BAK-APP2 上创建目录并上传文件**
|
||
|
||
```bash
|
||
mkdir -p /data/nginx/sites
|
||
mkdir -p /data/nginx/ssl/23368363_sino-assist.com_nginx
|
||
mkdir -p /data/nginx/ssl/2026
|
||
mkdir -p /opt/logs/nginx
|
||
|
||
# 上传文件:
|
||
# nginx.conf、ssl.sino_assist.conf、ssl.conf → /data/nginx/
|
||
# sites/*.conf → /data/nginx/sites/
|
||
# 证书文件 → /data/nginx/ssl/ 对应目录
|
||
# docker-compose.yml、logrotate-nginx → /data/nginx/
|
||
```
|
||
|
||
**2. 部署 Swarm service(在 manager 节点执行)**
|
||
|
||
```bash
|
||
docker stack deploy -c /data/nginx/docker-compose.yml nginx-review
|
||
```
|
||
|
||
验证:
|
||
|
||
```bash
|
||
docker service ls | grep nginx-review
|
||
docker service ps nginx-review_nginx-review
|
||
```
|
||
|
||
**3. 验证各域名转发正常**
|
||
|
||
```bash
|
||
# review 接口(返回 200 或 4xx 均表示 nginx 转发正常)
|
||
curl -k -o /dev/null -s -w "%{http_code}" --resolve "apireview.sino-assist.com:443:192.168.3.132" https://apireview.sino-assist.com/common/auth/token
|
||
|
||
# review 前端
|
||
curl -k -o /dev/null -s -w "%{http_code}" --resolve "ccreview.sino-assist.com:443:192.168.3.132" https://ccreview.sino-assist.com/
|
||
```
|
||
|
||
**4. 配置 logrotate(在 ZD-BAK-APP2 上执行)**
|
||
|
||
```bash
|
||
cp /data/nginx/logrotate-nginx /etc/logrotate.d/nginx-review
|
||
logrotate -d /etc/logrotate.d/nginx-review # 验证
|
||
logrotate -f /etc/logrotate.d/nginx-review # 手动执行一次建立基准
|
||
```
|
||
|
||
> 注意:ZD-BAK-APP2 上无 `sasys` 组,`/opt/logs/nginx/` 属主为 root,logrotate 配置中不需要 `su root sasys` 指令。
|
||
|
||
**5. DNS 切流**
|
||
|
||
联系 DNS 负责人,将所有域名 A 记录从 `192.168.3.110` 改为 `192.168.3.132`。
|
||
|
||
建议分批切流:
|
||
1. Review 环境(`apireview`、`ccreview`)
|
||
2. 内网工具(`jira`、`wiki`、`harbor`、`git` 等)
|
||
3. 生产核心(`crm1`、`api2`、`crm2`、`uat`)
|
||
|
||
**6. 停止 3.132 旧的 nginx-proxy 容器**
|
||
|
||
DNS 切流并确认所有域名访问正常后执行:
|
||
|
||
```bash
|
||
docker stop nginx-proxy && docker rm nginx-proxy
|
||
```
|
||
|
||
**7. 迁移验证**
|
||
|
||
DNS 切流前,可通过本地 hosts 绑定提前验证(Windows:`C:\Windows\System32\drivers\etc\hosts`):
|
||
|
||
```
|
||
192.168.3.132 apireview.sino-assist.com
|
||
192.168.3.132 ccreview.sino-assist.com
|
||
192.168.3.132 crm1.sino-assist.com
|
||
192.168.3.132 api2.sino-assist.com
|
||
192.168.3.132 crm2.sino-assist.com
|
||
192.168.3.132 uat.sino-assist.com
|
||
# 其他需要验证的域名...
|
||
```
|
||
|
||
验证地址清单:
|
||
|
||
| 域名 | 测试地址 | 期望结果 |
|
||
|---|---|---|
|
||
| Review 接口 | `https://apireview.sino-assist.com/common/auth/token` | 200 |
|
||
| Review 前端 | `https://ccreview.sino-assist.com/` | 200 |
|
||
| CRM1 | `https://crm1.sino-assist.com/` | 200 |
|
||
| CRM2 接口 | `https://api2.sino-assist.com/common/auth/token` | 200 |
|
||
| CRM2 前端 | `https://crm2.sino-assist.com/` | 200/301 |
|
||
| UAT | `https://uat.sino-assist.com/common/auth/token` | 200 |
|
||
| Portainer | `https://portainer.sino-assist.com/` | 200 |
|
||
| Git | `https://git.sino-assist.com/` | 200 |
|
||
| Harbor | `https://harbor.sino-assist.com/` | 200 |
|
||
| Report | `https://report.sino-assist.com/` | 200 |
|
||
|
||
> 注意:根路径 `/` 返回 404 属正常现象(Spring Boot 网关无根路由),需测试实际接口路径如 `/common/auth/token`。
|
||
|
||
验证完成后删除 hosts 中的临时绑定记录。
|
||
|
||
DNS 切流后,在 3.132 上确认日志有请求写入,同时确认 3.110 上 nginx 日志停止增长:
|
||
|
||
```bash
|
||
# 3.132 上
|
||
tail -f /opt/logs/nginx/access.log
|
||
|
||
# 3.110 上
|
||
tail -f /zd/src/nginx/logs/access.log
|
||
```
|
||
|
||
### 25.4 访问验证
|
||
|
||
部署完成后,通过统一入口访问:
|
||
|
||
```
|
||
http://192.168.3.132:8080
|
||
```
|
||
|
||
### 25.5 配置更新
|
||
|
||
如需添加新的后端 API 路径,修改 `/data/nginx-proxy/nginx.conf` 中的正则表达式:
|
||
|
||
```nginx
|
||
location ~ ^/(common|order|新路径1|新路径2)/ {
|
||
```
|
||
|
||
然后重启容器:
|
||
|
||
```bash
|
||
docker restart nginx-proxy
|
||
```
|
||
|
||
### 25.6 当前支持的 API 路径
|
||
|
||
| 路径前缀 | 说明 |
|
||
|----------|------|
|
||
| common | 通用服务 |
|
||
| order | 订单服务 |
|
||
| supplier | 供应商服务 |
|
||
| contract | 合同服务 |
|
||
| base | 基础服务 |
|
||
| export-app | 导出服务 |
|
||
| auth | 认证服务 |
|
||
| user | 用户服务 |
|
||
| system | 系统服务 |
|
||
| api | 通用 API |
|
||
| ws | WebSocket |
|
||
| return | 返程服务 |
|
||
| returnVehicle | 返程车辆 |
|
||
| returnOrder | 返程订单 |
|
||
| supplierManage | 供应商管理 |
|
||
| agg-api | 聚合 API |
|
||
| zgs | 中钢服务 |
|
||
| gps | GPS 服务 |
|
||
| data-search | 数据搜索 |
|
||
|
||
### 25.7 常见问题
|
||
|
||
#### 问题 1:504 Gateway Timeout
|
||
|
||
后端响应超时,可在 location 块中添加:
|
||
|
||
```nginx
|
||
proxy_connect_timeout 60s;
|
||
proxy_send_timeout 60s;
|
||
proxy_read_timeout 60s;
|
||
```
|
||
|
||
#### 问题 2:WebSocket 连接失败
|
||
|
||
确保已配置 Upgrade 头:
|
||
|
||
```nginx
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection "upgrade";
|
||
```
|
||
|
||
#### 问题 3:新增 API 路径 404
|
||
|
||
检查 location 正则是否包含该路径,更新配置后重启容器。
|
||
|
||
---
|
||
|
||
### 25.9 nginx-review-132 变更记录
|
||
|
||
#### 25.9.1 新增 oss.sinoassist.com 路由(2026-04-29)
|
||
|
||
新增 `sites/oss.conf`,将 `oss.sinoassist.com` 的请求直接转发到 3.125(ZD-FDFS4)的 file-oss 服务,不再经过 3.132:28092 网关。
|
||
|
||
```
|
||
# /data/nginx/sites/oss.conf
|
||
server {
|
||
listen 80;
|
||
server_name oss.sinoassist.com;
|
||
|
||
include /etc/nginx/ssl.sino_assist.conf;
|
||
|
||
location / {
|
||
proxy_pass http://192.168.3.125:25773;
|
||
proxy_set_header X-Forwarded-Host $server_name;
|
||
if ($request_filename ~ .*\.(htm|html)$) {
|
||
add_header Cache-Control no-cache;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
在 3.132 上创建文件后热重载生效,无需重新部署:
|
||
|
||
```bash
|
||
docker exec $(docker ps --filter name=nginx-review -q) nginx -t
|
||
docker exec $(docker ps --filter name=nginx-review -q) nginx -s reload
|
||
```
|
||
|
||
#### 25.9.2 修复日志 IP 显示为 10.0.0.2 及时区问题(2026-04-29)
|
||
|
||
**问题:** 日志中客户端 IP 全部显示为 `10.0.0.2`(Swarm ingress IPVS 地址),时间为 UTC 无 +8。
|
||
|
||
**原因:** 端口以默认 `ingress` 模式发布时,流量经过 Swarm ingress 网络的 IPVS 转发,真实源 IP 在到达容器前已被替换,无法通过 `log_format` 或 `X-Forwarded-For` 恢复。
|
||
|
||
**解决:**
|
||
1. 端口发布模式改为 `mode: host`,绕过 ingress 网络,nginx 直接收到真实客户端 IP
|
||
2. 新增 `TZ=Asia/Shanghai` 环境变量修正时区
|
||
|
||
`docker-compose.yml` 关键变更:
|
||
|
||
```yaml
|
||
services:
|
||
nginx-review:
|
||
environment:
|
||
- TZ=Asia/Shanghai
|
||
ports:
|
||
- target: 80
|
||
published: 80
|
||
protocol: tcp
|
||
mode: host
|
||
- target: 443
|
||
published: 443
|
||
protocol: tcp
|
||
mode: host
|
||
# 其他端口同样改为 mode: host
|
||
```
|
||
|
||
> 注意:`mode: host` 下 `docker service ls` 不显示端口映射,属正常现象,实际端口仍正常监听。
|
||
|
||
重新部署生效(会有秒级中断):
|
||
|
||
```bash
|
||
cd /opt/swarm/support/nginx-review-132
|
||
docker stack deploy --compose-file docker-compose.yml nginx-review
|
||
```
|
||
|
||
#### 25.9.3 修复 crm1/crm2 切换后域名访问 404(2026-04-29)
|
||
|
||
**问题:** DNS 从 3.110 切到 3.132 后,`crm1.sino-assist.com` 访问返回 Whitelabel 404,crm2 同理。
|
||
|
||
**根因:** `192.168.1.209:8080` 后端会根据请求的 `Host` 头决定返回前端页面还是 404。nginx 有一个隐性规则:**location 块内只要出现任何 `proxy_set_header`,就会覆盖 http 块全局定义的所有 `proxy_set_header`**。3.132 的 crm1.conf 中 location 块有 `proxy_set_header X-Forwarded-Host`,导致全局的 `proxy_set_header Host $host` 被覆盖,Host 头未传递到后端,后端无法识别域名返回 404。
|
||
|
||
**排查过程:**
|
||
```bash
|
||
# 3.110 走域名返回 200 html,3.132 走域名返回 404 json
|
||
curl -v --resolve "crm1.sino-assist.com:443:192.168.3.110" https://crm1.sino-assist.com/ -k -s -w "%{http_code}\n" # 200
|
||
curl -v --resolve "crm1.sino-assist.com:443:192.168.3.132" https://crm1.sino-assist.com/ -k -s -w "%{http_code}\n" # 404
|
||
|
||
# 带 Host 头直接访问后端,返回正常 html
|
||
curl -s -H "Host: crm1.sino-assist.com" http://192.168.1.209:8080/ | head -5 # 返回 html
|
||
```
|
||
|
||
**修复:** 在 crm1.conf 和 crm2.conf 中所有转发到 `192.168.1.209` 的 location 块内补全 `proxy_set_header Host $host`:
|
||
|
||
```nginx
|
||
location / {
|
||
proxy_pass http://192.168.1.209:8080/;
|
||
proxy_set_header Host $host; # 补充此行
|
||
proxy_set_header X-Forwarded-Host $server_name;
|
||
...
|
||
}
|
||
```
|
||
|
||
**生效方式(无中断):**
|
||
```bash
|
||
# 将修改后的 crm1.conf、crm2.conf 上传到 /data/nginx/sites/ 后执行
|
||
docker exec $(docker ps --filter name=nginx-review -q) nginx -t
|
||
docker exec $(docker ps --filter name=nginx-review -q) nginx -s reload
|
||
```
|
||
|
||
> 注意:凡是 location 块内有自定义 `proxy_set_header` 的,必须同时显式声明 `proxy_set_header Host $host`,否则全局设置会被静默覆盖。
|
||
|
||
#### 25.9.4 修复 uat 域名访问 404(2026-05-07)
|
||
|
||
**问题:** `uat.sino-assist.com` 访问返回 404。
|
||
|
||
**根因:** 与 25.9.3 同一问题。`uat.conf` 所有 location 块只有 `proxy_set_header X-Forwarded-Host`,没有 `proxy_set_header Host $host`,导致全局 Host 头被覆盖,后端无法识别域名。
|
||
|
||
**修复:** 在 `uat.conf` 的所有 location 块内补全 `proxy_set_header Host $host;`,涉及以下四个 location:`/`、`/h5/supplier/dispatch`、`/h5/client`、`/dev/h5/rentCar`。
|
||
|
||
**生效方式(无中断):**
|
||
```bash
|
||
docker exec $(docker ps --filter name=nginx-review -q) nginx -t
|
||
docker exec $(docker ps --filter name=nginx-review -q) nginx -s reload
|
||
```
|
||
|
||
#### 25.9.5 新增 fastdfs stream 层,支持 8888/38888 端口 HTTP/HTTPS 自动路由(2026-05-07)
|
||
|
||
**背景:** fastdfs 需要通过单一端口同时支持 HTTP 和 HTTPS 访问,原 `fastdfs.conf` 分别监听 `18888`(HTTP)和 `18889`(HTTPS),外部无法通过同一端口区分协议。
|
||
|
||
**方案:** 在 `nginx.conf` 追加 `stream {}` 块,利用 `ssl_preread` 在 TCP 层识别协议,将 `8888`/`38888` 的流量自动路由到 `18888` 或 `18889`:
|
||
|
||
```nginx
|
||
stream {
|
||
upstream http_gateway {
|
||
server 127.0.0.1:18888;
|
||
}
|
||
upstream https_gateway {
|
||
server 127.0.0.1:18889;
|
||
}
|
||
map $ssl_preread_protocol $upstream {
|
||
default http_gateway;
|
||
"TLSv1.0" https_gateway;
|
||
"TLSv1.1" https_gateway;
|
||
"TLSv1.2" https_gateway;
|
||
"TLSv1.3" https_gateway;
|
||
}
|
||
server {
|
||
listen 8888;
|
||
listen 38888;
|
||
ssl_preread on;
|
||
proxy_pass $upstream;
|
||
}
|
||
}
|
||
```
|
||
|
||
流量路径:
|
||
```
|
||
客户端 → 8888 或 38888
|
||
├── HTTP → 127.0.0.1:18888 → fastdfs.conf server (listen 18888)
|
||
└── HTTPS → 127.0.0.1:18889 → fastdfs.conf server (listen 18889 ssl)
|
||
```
|
||
|
||
同步在 `docker-compose.yml` 新增 `38888` 端口映射(`mode: host`)。
|
||
|
||
**生效方式(有秒级中断):**
|
||
```bash
|
||
cd /opt/swarm/support/nginx-review-132
|
||
docker stack deploy --compose-file docker-compose.yml nginx-review
|
||
```
|
||
|
||
**验证:**
|
||
```bash
|
||
# 验证 8888 HTTP 转发
|
||
curl -o /dev/null -s -w "%{http_code}" http://192.168.3.132:8888/
|
||
|
||
# 验证 38888
|
||
curl -o /dev/null -s -w "%{http_code}" http://192.168.3.132:38888/
|
||
|
||
# 验证 nginx 转发到后端 3.119:8888
|
||
curl -o /dev/null -s -w "%{http_code}" http://192.168.3.119:8888/
|
||
```
|
||
|
||
---
|
||
|
||
## 二十六、部署进度更新
|
||
|
||
### 26.1 已完成服务
|
||
|
||
| 阶段 | 状态 |
|
||
|------|------|
|
||
| 系统准备 | ✅ 完成 |
|
||
| 数据盘挂载 | ✅ 完成 |
|
||
| Docker 安装 | ✅ 完成 |
|
||
| Swarm 初始化 | ✅ 完成 |
|
||
| Overlay 网络 | ✅ 完成 |
|
||
| Portainer | ✅ 完成 |
|
||
| MySQL 主从复制 (mysql-repl-tool) | ✅ 完成 |
|
||
| RabbitMQ 集群 | ✅ 完成 |
|
||
| Nacos 集群 | ✅ 完成 |
|
||
| XXL-Job-Admin | ✅ 完成 |
|
||
| Canal | ✅ 完成 |
|
||
| Elasticsearch + Kibana | ✅ 完成 |
|
||
| Log (Logstash + Filebeat) | ✅ 完成 |
|
||
| SkyWalking | ✅ 完成 |
|
||
| MongoDB | ✅ 完成 |
|
||
| Redis Sentinel 集群 | ✅ 完成 |
|
||
| 后端服务 (sa-server) | ✅ 完成 |
|
||
| 前端服务 (sa-cc) | ✅ 完成 |
|
||
| Nginx 反向代理 | ✅ 完成 |
|
||
|
||
### 26.2 修改记录
|
||
|
||
| 日期 | 修改内容 |
|
||
|------|----------|
|
||
| 2025-12-23 | 初始化 Review 环境配置 |
|
||
| 2025-12-25 | 新增前端服务 (sa-cc) 部署章节 |
|
||
| 2025-12-25 | 新增 Nginx 反向代理章节,解决跨域问题 |
|
||
| 2025-12-25 | 新增 Redis 故障切换与回滚策略章节 |
|
||
| 2026-04-29 | 新增节点 ZD-FDFS4 (192.168.3.125) 作为 Worker 节点 |
|
||
| 2026-04-29 | 新增 oss.sinoassist.com 路由,直连 3.125:25773,不经过网关 |
|
||
| 2026-04-29 | nginx 端口改为 host 模式修复日志 IP,新增 TZ=Asia/Shanghai 修正时区 |
|
||
| 2026-04-29 | 修复 crm1/crm2 切换后 404:location 块补全 proxy_set_header Host $host |
|
||
| 2026-05-07 | 修复 uat 域名 404:uat.conf 所有 location 块补全 proxy_set_header Host $host |
|
||
| 2026-05-07 | 新增 fastdfs stream 层:nginx.conf 追加 stream{},8888/38888 端口按协议自动路由到 18888/18889 |
|
||
| 2026-05-07 | docker-compose.yml 新增 38888 端口映射(mode: host) |
|
||
|
||
---
|
||
|
||
## 二十七、Redis 故障切换与回滚策略
|
||
|
||
### 27.1 架构背景
|
||
|
||
Review 环境的 Redis 采用级联复制架构,作为生产环境的灾备方案:
|
||
|
||
```
|
||
正常运行时(级联复制):
|
||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||
│ 生产 10.56 │────▶│ Review 132 │────▶│ Review 133 │
|
||
│ (Master) │ │ (隐藏 Slave) │ │ (二级 Slave) │
|
||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────┐
|
||
│ Review 134 │
|
||
│ (二级 Slave) │
|
||
└─────────────────┘
|
||
|
||
故障切换后(独立运行):
|
||
┌─────────────────┐ ┌─────────────────┐
|
||
│ Review 132 │────▶│ Review 133 │
|
||
│ (Master) │ │ (Slave) │
|
||
└─────────────────┘ └─────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────┐
|
||
│ Review 134 │
|
||
│ (Slave) │
|
||
└─────────────────┘
|
||
+
|
||
Review Sentinel 集群监控
|
||
```
|
||
|
||
### 27.2 节点信息
|
||
|
||
| 节点 | IP | 正常角色 | 故障切换后角色 |
|
||
|------|-----|---------|---------------|
|
||
| 生产主节点 | 192.168.10.56 | Master | - |
|
||
| Review 节点1 | 192.168.3.132 | 隐藏 Slave | Master |
|
||
| Review 节点2 | 192.168.3.133 | 二级 Slave | Slave |
|
||
| Review 节点3 | 192.168.3.134 | 二级 Slave | Slave |
|
||
|
||
### 27.3 故障切换脚本
|
||
|
||
脚本文件:`docker-swarm-review/redis-review-132/failover-to-review.sh`
|
||
|
||
#### 命令说明
|
||
|
||
| 命令 | 功能 | 使用场景 |
|
||
|------|------|----------|
|
||
| `status` | 查看所有节点状态 | 日常检查、故障排查 |
|
||
| `failover` | 提升 132 为 Master | 生产故障时 |
|
||
| `sentinel` | 部署 Review Sentinel | 单独部署 Sentinel |
|
||
| `full` | 完整切换 (failover + sentinel) | 生产故障时(推荐) |
|
||
| `rollback` | 安全回滚 | 生产恢复后 |
|
||
| `unsafe-rollback` | 不安全回滚 | 紧急情况(可能丢数据) |
|
||
|
||
#### 可选参数
|
||
|
||
| 参数 | 说明 |
|
||
|------|------|
|
||
| `-y` / `--yes` | 自动确认普通提示 |
|
||
| `--force` | 强制模式,跳过所有确认(慎用) |
|
||
|
||
### 27.4 故障切换流程
|
||
|
||
当生产 Redis 故障时,执行以下步骤:
|
||
|
||
#### 步骤 1:检查状态
|
||
|
||
```bash
|
||
cd /path/to/redis-review-132
|
||
./failover-to-review.sh status
|
||
```
|
||
|
||
输出示例:
|
||
```
|
||
========================================
|
||
当前 Redis 状态
|
||
========================================
|
||
|
||
[192.168.3.132]
|
||
角色: slave
|
||
主节点: 192.168.10.56:6379
|
||
同步状态: down # 生产故障时显示 down
|
||
|
||
[192.168.3.133]
|
||
角色: slave
|
||
主节点: 192.168.3.132:6379
|
||
同步状态: up
|
||
|
||
[192.168.3.134]
|
||
角色: slave
|
||
主节点: 192.168.3.132:6379
|
||
同步状态: up
|
||
```
|
||
|
||
#### 步骤 2:执行完整切换
|
||
|
||
```bash
|
||
./failover-to-review.sh full
|
||
```
|
||
|
||
脚本会自动执行:
|
||
|
||
1. **提升 132 为 Master**
|
||
```redis
|
||
SLAVEOF NO ONE
|
||
```
|
||
|
||
2. **配置 133/134 指向 132**
|
||
```redis
|
||
SLAVEOF 192.168.3.132 6379
|
||
```
|
||
|
||
3. **部署 Review Sentinel**
|
||
- 监控名称:`reviewmaster`
|
||
- Sentinel 端口:26379
|
||
|
||
#### 步骤 3:修改应用配置
|
||
|
||
切换应用连接到 Review Sentinel:
|
||
|
||
```yaml
|
||
# Spring Boot 配置
|
||
spring:
|
||
redis:
|
||
sentinel:
|
||
master: reviewmaster
|
||
nodes: 192.168.3.132:26379,192.168.3.133:26379,192.168.3.134:26379
|
||
password: sino#650
|
||
password: sino#650
|
||
```
|
||
|
||
或直连模式:
|
||
|
||
```yaml
|
||
spring:
|
||
redis:
|
||
host: 192.168.3.132
|
||
port: 6379
|
||
password: sino#650
|
||
```
|
||
|
||
### 27.5 安全回滚流程
|
||
|
||
当生产 Redis 恢复后,需要将数据同步回生产并恢复原有架构。
|
||
|
||
#### 回滚前提条件
|
||
|
||
- ✅ 132 当前是 Master(处于故障切换状态)
|
||
- ✅ 生产 10.56 已恢复并可连接
|
||
- ✅ **所有应用已切换到 Review Sentinel**
|
||
- ✅ **生产 Sentinel 已停止**
|
||
|
||
#### 步骤 1:停止生产 Sentinel(手动)
|
||
|
||
**重要**:必须先在生产服务器上执行!
|
||
|
||
```bash
|
||
# SSH 到生产服务器
|
||
ssh root@192.168.10.56
|
||
|
||
# 停止生产 Sentinel
|
||
docker stack rm prod_sentinel
|
||
```
|
||
|
||
**原因**:
|
||
- 回滚步骤会让 10.56 临时变成 132 的从节点
|
||
- 如果 Sentinel 还在运行,会检测到 master 下线并触发故障转移
|
||
- 可能导致脑裂和数据不一致
|
||
|
||
#### 步骤 2:执行安全回滚
|
||
|
||
```bash
|
||
./failover-to-review.sh rollback
|
||
```
|
||
|
||
脚本会执行以下步骤:
|
||
|
||
**2.1 配置 10.56 作为 132 的从节点(反向同步)**
|
||
```redis
|
||
# 在 10.56 执行
|
||
SLAVEOF 192.168.3.132 6379
|
||
```
|
||
|
||
**2.2 等待数据同步完成**
|
||
- 监控 `master_repl_offset` 和 `slave_repl_offset`
|
||
- 偏移量差异 < 100 字节时自动继续
|
||
- 建议此时停止 Review 应用对 Redis 的写入
|
||
|
||
**2.3 切换主从关系**
|
||
```redis
|
||
# 将 132 设为只读,防止切换期间数据写入
|
||
CONFIG SET min-replicas-to-write 99
|
||
|
||
# 提升 10.56 为 Master
|
||
# 在 10.56 执行
|
||
SLAVEOF NO ONE
|
||
|
||
# 132 重新指向 10.56
|
||
SLAVEOF 192.168.10.56 6379
|
||
|
||
# 恢复 132 隐藏配置
|
||
CONFIG SET replica-announced no
|
||
CONFIG SET replica-priority 0
|
||
|
||
# 133/134 指向 132
|
||
SLAVEOF 192.168.3.132 6379
|
||
```
|
||
|
||
#### 步骤 3:重启生产 Sentinel
|
||
|
||
```bash
|
||
# 在生产服务器执行
|
||
docker stack deploy -c sentinel-compose.yml prod_sentinel
|
||
```
|
||
|
||
#### 步骤 4:修改应用配置
|
||
|
||
将应用连接切换回生产 Sentinel。
|
||
|
||
### 27.6 数据一致性保证
|
||
|
||
| 阶段 | 措施 | 说明 |
|
||
|------|------|------|
|
||
| 回滚前 | 反向同步 | 10.56 先作为 132 的从节点,同步故障期间的数据 |
|
||
| 回滚中 | 132 只读 | `min-replicas-to-write=99` 防止切换期间写入 |
|
||
| 回滚后 | 偏移量验证 | 确认数据完全同步 |
|
||
|
||
### 27.7 不安全回滚(不推荐)
|
||
|
||
如果紧急情况需要快速恢复,可以使用不安全回滚:
|
||
|
||
```bash
|
||
./failover-to-review.sh unsafe-rollback
|
||
```
|
||
|
||
**警告**:
|
||
- 直接让 132 重新指向 10.56
|
||
- 故障期间 132 上的所有写入数据将被覆盖
|
||
- 仅在确认故障期间无重要数据写入时使用
|
||
|
||
### 27.8 常见问题
|
||
|
||
#### 问题 1:无法连接生产 Redis
|
||
|
||
```
|
||
生产环境 192.168.10.56:6379 无法连接
|
||
```
|
||
|
||
**解决**:检查生产 Redis 是否已恢复,网络是否通畅。
|
||
|
||
#### 问题 2:同步超时
|
||
|
||
```
|
||
同步超时,请检查网络和 Redis 状态
|
||
```
|
||
|
||
**解决**:
|
||
- 检查网络连通性
|
||
- 检查 Redis 日志
|
||
- 可选择强制继续(有数据丢失风险)
|
||
|
||
#### 问题 3:脚本无法远程执行命令
|
||
|
||
脚本会提示手动在生产服务器执行命令,按提示操作即可。
|
||
|
||
### 27.9 操作检查清单
|
||
|
||
#### 故障切换检查清单
|
||
|
||
- [ ] 确认生产 Redis 确实故障
|
||
- [ ] 执行 `./failover-to-review.sh status` 检查状态
|
||
- [ ] 执行 `./failover-to-review.sh full` 完成切换
|
||
- [ ] 验证 132 已成为 Master
|
||
- [ ] 验证 Sentinel 正常运行
|
||
- [ ] 修改应用配置连接 Review Sentinel
|
||
- [ ] 验证应用功能正常
|
||
|
||
#### 安全回滚检查清单
|
||
|
||
- [ ] 确认生产 Redis 已恢复
|
||
- [ ] 确认所有应用已切换到 Review Sentinel
|
||
- [ ] **在生产服务器停止 Sentinel**:`docker stack rm prod_sentinel`
|
||
- [ ] 建议停止 Review 应用对 Redis 的写入
|
||
- [ ] 执行 `./failover-to-review.sh rollback`
|
||
- [ ] 等待数据同步完成
|
||
- [ ] 验证回滚成功
|
||
- [ ] 重启生产 Sentinel
|
||
- [ ] 修改应用配置连接生产 Sentinel
|
||
- [ ] 验证应用功能正常
|