From 5074e9cc427919dc6002d5010abf3a526e9fafa2 Mon Sep 17 00:00:00 2001 From: yumengcheng Date: Wed, 13 May 2026 15:23:46 +0800 Subject: [PATCH] docker-swarm-review --- .../10.5x环境配置记录/0.使用阿里云的源.md | 15 + .../10.5x环境配置记录/1. 安装docker.md | 92 + .../2. docker-swarm 初始化.md | 28 + .../10.5x环境配置记录/3. 基本服务安装.md | 101 + .../10.5x环境配置记录/portainer_template.json | 105 + .../10.5x环境配置记录/前期处理.md | 2 + docker-swarm-review/Dockerfile | 30 + docker-swarm-review/build.gradle | 338 + docker-swarm-review/canal/README | 9 + docker-swarm-review/canal/docker-compose.yml | 33 + docker-swarm-review/canal/env_crm1 | 11 + docker-swarm-review/canal/env_review | 11 + docker-swarm-review/clickhouse/README | 7 + .../clickhouse/docker-compose.yml | 36 + docker-swarm-review/clickhouse/env_crm1 | 4 + docker-swarm-review/clickhouse/env_review | 4 + docker-swarm-review/datart/README | 10 + docker-swarm-review/datart/docker-compose.yml | 89 + docker-swarm-review/datart/env_crm1 | 9 + docker-swarm-review/datart/env_review | 9 + docker-swarm-review/datart/my.conf | 6 + docker-swarm-review/elasticsearch/README | 9 + .../elasticsearch/docker-compose.yml | 48 + docker-swarm-review/elasticsearch/env_crm1 | 4 + docker-swarm-review/elasticsearch/env_review | 4 + docker-swarm-review/elasticsearch/kibana.yml | 191 + .../elasticsearch/node.options | 15 + .../fastdfs/docker-compose.yml | 28 + docker-swarm-review/jenkins/README | 9 + .../jenkins/docker-compose.yml | 16 + docker-swarm-review/jenkins/env_crm1 | 11 + docker-swarm-review/jenkins/env_review | 11 + docker-swarm-review/log/README | 15 + docker-swarm-review/log/docker-compose.yml | 52 + docker-swarm-review/log/env_crm1 | 2 + docker-swarm-review/log/env_review | 2 + docker-swarm-review/log/filebeat.yml | 16 + docker-swarm-review/log/jvm.options | 90 + docker-swarm-review/log/logstash.conf | 36 + docker-swarm-review/minio/README | 18 + docker-swarm-review/minio/docker-compose.yml | 35 + docker-swarm-review/minio/env_crm1 | 5 + docker-swarm-review/minio/env_review | 5 + docker-swarm-review/mongodb/README | 12 + .../mongodb/docker-compose.yml | 25 + .../mongodb/docker-stack-rs.yml | 64 + docker-swarm-review/mongodb/env_crm1 | 3 + docker-swarm-review/mongodb/env_review | 3 + docker-swarm-review/monitor/README | 19 + .../monitor/docker-compose.yml | 70 + docker-swarm-review/monitor/docker-stack.yml | 125 + docker-swarm-review/monitor/prometheus.yml | 30 + docker-swarm-review/mysql-repl-tool/README.md | 50 + .../mysql-repl-tool/docker-compose.yml | 92 + .../mysql-repl-tool/env_review | 9 + docker-swarm-review/mysql/README | 36 + docker-swarm-review/mysql/docker-compose.yml | 42 + docker-swarm-review/mysql/env_review | 7 + docker-swarm-review/nacos-cluser/README | 10 + .../nacos-cluser/cluster-docker-compose.yml | 103 + docker-swarm-review/nacos-cluser/env_crm1 | 5 + docker-swarm-review/nacos-cluser/env_review | 17 + .../nacos-cluser/mysql-schema.sql | 213 + .../nacos-cluser/standalone-derby.yml | 38 + docker-swarm-review/nacos/README | 5 + docker-swarm-review/nacos/cluser.yml | 155 + docker-swarm-review/nacos/env_crm1 | 5 + .../nacos/standalone-derby.yml | 38 + .../nginx-proxy-simple/crm1.conf | 68 + .../nginx-proxy-simple/deploy.sh | 53 + .../nginx-proxy-simple/docker-compose.yml | 26 + .../nginx-proxy-simple/nginx.conf | 35 + .../nginx-proxy-simple/review.conf | 68 + .../nginx-proxy-simple/review_temp.conf | 126 + docker-swarm-review/nginx-review-132/README | 10 + .../nginx-review-132/docker-compose.yml | 58 + docker-swarm-review/nginx-review-132/env_crm1 | 4 + .../nginx-review-132/env_review | 1 + .../nginx-review-132/nginx.conf | 60 + .../nginx-review-132/nginx_review_config_v1 | 282 + .../nginx_ssl_sinoassist_conf_v1 | 11 + .../nginx-review-132/swarm/docker-compose.yml | 59 + .../nginx-review-132/swarm/logrotate-nginx | 14 + .../nginx-review-132/swarm/nginx.conf | 73 + .../nginx-review-132/swarm/sites/crm1.conf | 95 + .../nginx-review-132/swarm/sites/crm2.conf | 75 + .../nginx-review-132/swarm/sites/fastdfs.conf | 27 + .../nginx-review-132/swarm/sites/git.conf | 242 + .../nginx-review-132/swarm/sites/oss.conf | 14 + .../nginx-review-132/swarm/sites/pay.conf | 70 + .../nginx-review-132/swarm/sites/review.conf | 80 + .../nginx-review-132/swarm/sites/uat.conf | 43 + .../nginx-review-132/swarm/sites/wx.conf | 32 + .../swarm/sites/zd_report.conf | 60 + .../nginx-review-132/swarm/ssl.conf | 8 + .../swarm/ssl.sino_assist.conf | 8 + docker-swarm-review/nginx/README | 4 + docker-swarm-review/nginx/docker-compose.yml | 26 + docker-swarm-review/nginx/env_crm1 | 3 + docker-swarm-review/pipeline-script | 211 + docker-swarm-review/pipeline-script-cc | 101 + docker-swarm-review/portainer/README.md | 1 + .../portainer/docker-compose.yml | 39 + docker-swarm-review/rabbitmq/README | 38 + .../rabbitmq/docker-compose-review.yml | 88 + .../rabbitmq/docker-compose.yml | 81 + docker-swarm-review/rabbitmq/env_crm1 | 3 + docker-swarm-review/rabbitmq/env_review | 3 + docker-swarm-review/redis-review-132/README | 8 + .../docker-compose-sentinel.yml | 100 + .../redis-review-132/docker-compose.yml | 96 + docker-swarm-review/redis-review-132/env_crm1 | 4 + .../redis-review-132/env_review | 6 + .../redis-review-132/failover-to-review.sh | 869 +++ .../redis-review-132/shake.toml | 30 + docker-swarm-review/redis/README | 4 + .../redis/docker-compose copy.yml | 89 + docker-swarm-review/redis/docker-compose.yml | 71 + docker-swarm-review/redis/env_crm1 | 4 + docker-swarm-review/skywalking/README | 13 + .../skywalking/docker-compose.yml | 48 + docker-swarm-review/skywalking/env_crm1 | 4 + docker-swarm-review/skywalking/env_review | 4 + docker-swarm-review/xxl-job-admin/README | 8 + .../xxl-job-admin/docker-compose.yml | 24 + docker-swarm-review/xxl-job-admin/env_crm1 | 6 + docker-swarm-review/xxl-job-admin/env_review | 6 + docker-swarm-review/部署手册.md | 6621 +++++++++++++++++ 128 files changed, 13062 insertions(+) create mode 100644 docker-swarm-review/10.5x环境配置记录/0.使用阿里云的源.md create mode 100644 docker-swarm-review/10.5x环境配置记录/1. 安装docker.md create mode 100644 docker-swarm-review/10.5x环境配置记录/2. docker-swarm 初始化.md create mode 100644 docker-swarm-review/10.5x环境配置记录/3. 基本服务安装.md create mode 100644 docker-swarm-review/10.5x环境配置记录/portainer_template.json create mode 100644 docker-swarm-review/10.5x环境配置记录/前期处理.md create mode 100644 docker-swarm-review/Dockerfile create mode 100644 docker-swarm-review/build.gradle create mode 100644 docker-swarm-review/canal/README create mode 100644 docker-swarm-review/canal/docker-compose.yml create mode 100644 docker-swarm-review/canal/env_crm1 create mode 100644 docker-swarm-review/canal/env_review create mode 100644 docker-swarm-review/clickhouse/README create mode 100644 docker-swarm-review/clickhouse/docker-compose.yml create mode 100644 docker-swarm-review/clickhouse/env_crm1 create mode 100644 docker-swarm-review/clickhouse/env_review create mode 100644 docker-swarm-review/datart/README create mode 100644 docker-swarm-review/datart/docker-compose.yml create mode 100644 docker-swarm-review/datart/env_crm1 create mode 100644 docker-swarm-review/datart/env_review create mode 100644 docker-swarm-review/datart/my.conf create mode 100644 docker-swarm-review/elasticsearch/README create mode 100644 docker-swarm-review/elasticsearch/docker-compose.yml create mode 100644 docker-swarm-review/elasticsearch/env_crm1 create mode 100644 docker-swarm-review/elasticsearch/env_review create mode 100644 docker-swarm-review/elasticsearch/kibana.yml create mode 100644 docker-swarm-review/elasticsearch/node.options create mode 100644 docker-swarm-review/fastdfs/docker-compose.yml create mode 100644 docker-swarm-review/jenkins/README create mode 100644 docker-swarm-review/jenkins/docker-compose.yml create mode 100644 docker-swarm-review/jenkins/env_crm1 create mode 100644 docker-swarm-review/jenkins/env_review create mode 100644 docker-swarm-review/log/README create mode 100644 docker-swarm-review/log/docker-compose.yml create mode 100644 docker-swarm-review/log/env_crm1 create mode 100644 docker-swarm-review/log/env_review create mode 100644 docker-swarm-review/log/filebeat.yml create mode 100644 docker-swarm-review/log/jvm.options create mode 100644 docker-swarm-review/log/logstash.conf create mode 100644 docker-swarm-review/minio/README create mode 100644 docker-swarm-review/minio/docker-compose.yml create mode 100644 docker-swarm-review/minio/env_crm1 create mode 100644 docker-swarm-review/minio/env_review create mode 100644 docker-swarm-review/mongodb/README create mode 100644 docker-swarm-review/mongodb/docker-compose.yml create mode 100644 docker-swarm-review/mongodb/docker-stack-rs.yml create mode 100644 docker-swarm-review/mongodb/env_crm1 create mode 100644 docker-swarm-review/mongodb/env_review create mode 100644 docker-swarm-review/monitor/README create mode 100644 docker-swarm-review/monitor/docker-compose.yml create mode 100644 docker-swarm-review/monitor/docker-stack.yml create mode 100644 docker-swarm-review/monitor/prometheus.yml create mode 100644 docker-swarm-review/mysql-repl-tool/README.md create mode 100644 docker-swarm-review/mysql-repl-tool/docker-compose.yml create mode 100644 docker-swarm-review/mysql-repl-tool/env_review create mode 100644 docker-swarm-review/mysql/README create mode 100644 docker-swarm-review/mysql/docker-compose.yml create mode 100644 docker-swarm-review/mysql/env_review create mode 100644 docker-swarm-review/nacos-cluser/README create mode 100644 docker-swarm-review/nacos-cluser/cluster-docker-compose.yml create mode 100644 docker-swarm-review/nacos-cluser/env_crm1 create mode 100644 docker-swarm-review/nacos-cluser/env_review create mode 100644 docker-swarm-review/nacos-cluser/mysql-schema.sql create mode 100644 docker-swarm-review/nacos-cluser/standalone-derby.yml create mode 100644 docker-swarm-review/nacos/README create mode 100644 docker-swarm-review/nacos/cluser.yml create mode 100644 docker-swarm-review/nacos/env_crm1 create mode 100644 docker-swarm-review/nacos/standalone-derby.yml create mode 100644 docker-swarm-review/nginx-proxy-simple/crm1.conf create mode 100644 docker-swarm-review/nginx-proxy-simple/deploy.sh create mode 100644 docker-swarm-review/nginx-proxy-simple/docker-compose.yml create mode 100644 docker-swarm-review/nginx-proxy-simple/nginx.conf create mode 100644 docker-swarm-review/nginx-proxy-simple/review.conf create mode 100644 docker-swarm-review/nginx-proxy-simple/review_temp.conf create mode 100644 docker-swarm-review/nginx-review-132/README create mode 100644 docker-swarm-review/nginx-review-132/docker-compose.yml create mode 100644 docker-swarm-review/nginx-review-132/env_crm1 create mode 100644 docker-swarm-review/nginx-review-132/env_review create mode 100644 docker-swarm-review/nginx-review-132/nginx.conf create mode 100644 docker-swarm-review/nginx-review-132/nginx_review_config_v1 create mode 100644 docker-swarm-review/nginx-review-132/nginx_ssl_sinoassist_conf_v1 create mode 100644 docker-swarm-review/nginx-review-132/swarm/docker-compose.yml create mode 100644 docker-swarm-review/nginx-review-132/swarm/logrotate-nginx create mode 100644 docker-swarm-review/nginx-review-132/swarm/nginx.conf create mode 100644 docker-swarm-review/nginx-review-132/swarm/sites/crm1.conf create mode 100644 docker-swarm-review/nginx-review-132/swarm/sites/crm2.conf create mode 100644 docker-swarm-review/nginx-review-132/swarm/sites/fastdfs.conf create mode 100644 docker-swarm-review/nginx-review-132/swarm/sites/git.conf create mode 100644 docker-swarm-review/nginx-review-132/swarm/sites/oss.conf create mode 100644 docker-swarm-review/nginx-review-132/swarm/sites/pay.conf create mode 100644 docker-swarm-review/nginx-review-132/swarm/sites/review.conf create mode 100644 docker-swarm-review/nginx-review-132/swarm/sites/uat.conf create mode 100644 docker-swarm-review/nginx-review-132/swarm/sites/wx.conf create mode 100644 docker-swarm-review/nginx-review-132/swarm/sites/zd_report.conf create mode 100644 docker-swarm-review/nginx-review-132/swarm/ssl.conf create mode 100644 docker-swarm-review/nginx-review-132/swarm/ssl.sino_assist.conf create mode 100644 docker-swarm-review/nginx/README create mode 100644 docker-swarm-review/nginx/docker-compose.yml create mode 100644 docker-swarm-review/nginx/env_crm1 create mode 100644 docker-swarm-review/pipeline-script create mode 100644 docker-swarm-review/pipeline-script-cc create mode 100644 docker-swarm-review/portainer/README.md create mode 100644 docker-swarm-review/portainer/docker-compose.yml create mode 100644 docker-swarm-review/rabbitmq/README create mode 100644 docker-swarm-review/rabbitmq/docker-compose-review.yml create mode 100644 docker-swarm-review/rabbitmq/docker-compose.yml create mode 100644 docker-swarm-review/rabbitmq/env_crm1 create mode 100644 docker-swarm-review/rabbitmq/env_review create mode 100644 docker-swarm-review/redis-review-132/README create mode 100644 docker-swarm-review/redis-review-132/docker-compose-sentinel.yml create mode 100644 docker-swarm-review/redis-review-132/docker-compose.yml create mode 100644 docker-swarm-review/redis-review-132/env_crm1 create mode 100644 docker-swarm-review/redis-review-132/env_review create mode 100644 docker-swarm-review/redis-review-132/failover-to-review.sh create mode 100644 docker-swarm-review/redis-review-132/shake.toml create mode 100644 docker-swarm-review/redis/README create mode 100644 docker-swarm-review/redis/docker-compose copy.yml create mode 100644 docker-swarm-review/redis/docker-compose.yml create mode 100644 docker-swarm-review/redis/env_crm1 create mode 100644 docker-swarm-review/skywalking/README create mode 100644 docker-swarm-review/skywalking/docker-compose.yml create mode 100644 docker-swarm-review/skywalking/env_crm1 create mode 100644 docker-swarm-review/skywalking/env_review create mode 100644 docker-swarm-review/xxl-job-admin/README create mode 100644 docker-swarm-review/xxl-job-admin/docker-compose.yml create mode 100644 docker-swarm-review/xxl-job-admin/env_crm1 create mode 100644 docker-swarm-review/xxl-job-admin/env_review create mode 100644 docker-swarm-review/部署手册.md diff --git a/docker-swarm-review/10.5x环境配置记录/0.使用阿里云的源.md b/docker-swarm-review/10.5x环境配置记录/0.使用阿里云的源.md new file mode 100644 index 0000000..7508f4c --- /dev/null +++ b/docker-swarm-review/10.5x环境配置记录/0.使用阿里云的源.md @@ -0,0 +1,15 @@ +更换yum源为阿里云的yum源,因为后续Centos7可能也会停止官方的yum源支持,所以需要手动更换 + +备份官方yum源配置文件: + +cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak + +下载阿里云yum源配置文件: + +curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo + +4、清除缓存生产新的缓存 + +yum clean all + +yum makecache \ No newline at end of file diff --git a/docker-swarm-review/10.5x环境配置记录/1. 安装docker.md b/docker-swarm-review/10.5x环境配置记录/1. 安装docker.md new file mode 100644 index 0000000..9797442 --- /dev/null +++ b/docker-swarm-review/10.5x环境配置记录/1. 安装docker.md @@ -0,0 +1,92 @@ +1、卸载旧版本 +执行如下指令对旧版本进行卸载: + +sudo yum remove docker \ + docker-client \ + docker-client-latest \ + docker-common \ + docker-latest \ + docker-latest-logrotate \ + docker-logrotate \ + docker-engine +执行完毕后,如果输入docker version发现docker依然还存在,则说明当前机器上存在的是历史版本,输入如下指令进行卸载即可: + +sudo yum remove docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras + +在卸载完毕后,也要对之前的数据进行清理(这里是默认路径,如果自己更改过的话,则需要清理自己更改后的数据所在路径): + +sudo rm -rf /var/lib/docker +sudo rm -rf /var/lib/containerd + +2、配置仓库 +这里我们可以借助yum-config-manager这个工具来实现仓库的配置,在利用之前,先下载一个yum工具包 + +sudo yum install -y yum-utils + +在安装完毕后,输入如下指令进行配置: + +sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo + +接下来为了提高安装速度,我们进行配置使用国内源,输入如下指令: + + ### sed -i 's@//download.docker.com@//mirrors.ustc.edu.cn/docker-ce@g' /etc/yum.repos.d/docker-ce.repo + + sed -i 's@//download.docker.com@//mirrors.aliyun.com/docker-ce@g' /etc/yum.repos.d/docker-ce.repo + + +3、安装 +输入如下指令,进行docker安装: + +sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin d + +设置日志大小 + +vim /etc/docker/daemon.json + + +{ + "log-opts": {"max-size":"1g", "max-file":"3"}, + "registry-mirrors": ["https://docker.hlmirror.com"] +} + +systemctl reload docker + + +4、启动docker +安装完毕后,进行启动,先输入如下指令加载配置: + + sudo systemctl daemon-reload + +接下来启动服务: + +sudo systemctl start docker + +启动完毕后我们可以输入如下指令,查看是否已经启动: + +systemctl status docker + +systemctl enable docker + +5. 停用firewalld + +``` +[root@ZD-CRM1 ~]# systemctl stop firewalld +[root@ZD-CRM1 ~]# systemctl disable firewalld +``` + + +6. ulimit + +ulimit -SHn 65536 + + +vim /etc/security/limits.conf + + +* soft nofile 65535 +* hard nofile 65535 + diff --git a/docker-swarm-review/10.5x环境配置记录/2. docker-swarm 初始化.md b/docker-swarm-review/10.5x环境配置记录/2. docker-swarm 初始化.md new file mode 100644 index 0000000..cfc0659 --- /dev/null +++ b/docker-swarm-review/10.5x环境配置记录/2. docker-swarm 初始化.md @@ -0,0 +1,28 @@ +1. 10.51上执行 docker swarm init +``` +[root@ZD-CRM1 ~]# docker swarm init +Swarm initialized: current node (pbbaiutisn0vsvwt8tfxwusev) is now a manager. + +To add a worker to this swarm, run the following command: + + docker swarm join --token SWMTKN-1-2jliqh8rns5afbnzrrwr036p7c0kkj38188290at4xb35zgctg-ek7ku7qskkfiu2pl0dmu8q5v6 192.168.10.51:2377 + +To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions. +``` + +2. 其他机器执行上面的 docker swarm join + +``` + docker swarm join --token SWMTKN-1-2jliqh8rns5afbnzrrwr036p7c0kkj38188290at4xb35zgctg-ek7ku7qskkfiu2pl0dmu8q5v6 192.168.10.51:2377 + +``` + +3. 10.51上查看集群情况 + +``` +[root@ZD-CRM1 ~]# docker node ls +ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION +pbbaiutisn0vsvwt8tfxwusev * ZD-CRM1 Ready Active Leader 25.0.1 +je9g46e68diiryiz1cddd7765 ZD-CRM2 Ready Active 25.0.1 +fqim3l4inkscd4px8jzi9j7nc ZD-CRM3 Ready Active 25.0.1 +``` diff --git a/docker-swarm-review/10.5x环境配置记录/3. 基本服务安装.md b/docker-swarm-review/10.5x环境配置记录/3. 基本服务安装.md new file mode 100644 index 0000000..3d6334c --- /dev/null +++ b/docker-swarm-review/10.5x环境配置记录/3. 基本服务安装.md @@ -0,0 +1,101 @@ + +## 基础配置 + +### 1. 初始化网路 review + docker network create \ + --driver=overlay \ + --subnet=10.17.0.0/16 \ + --scope swarm \ + --attachable \ + review + +### 2. [portainer](../portainer) 管理工具 + + docker stack deploy --compose-file docker-compose.yml portainer + + 管理界面: https://192.168.10.51:9443 + + 页面出现报错,docker service update portainer_agent --force + + +### 3. [monitor](../monitor) + + 用于 grafana 监控 + + 管理界面: 192.168.10.51:3000 + admin gkxl2024#@ + +## 软件包 + +部署方法均参见文件夹内readme,变量在对于的env文件。 + +### 1. [rabbitmq](../rabbitmq) + 管理界面: 192.168.10.51:15672 + + 内部:review_rabbitmq_stats:5672,review_rabbitmq_queue1:5672,review_rabbitmq_queue2:5672 + + stomp-url:192.168.10.51:15674 + +### 2. [xxl-job-adin](../xxl-job-admin) + + 管理界面: 192.168.10.51:9991 + + 内部:review-xxl-job-admin:8080 + + +### 3. [nacos](../nacos-cluser) + + 管理界面: http://192.168.10.51:25848/nacos/ + + +### 4. [redis](../redis-review-50) + + 端口:192.168.10.51:6379 + + 内部:review_redis_redis-sentinel:16379 + +### 5. [elasticsearch](../elasticsearch) + + 管理界面: 192.168.10.51:5601 + + 外部端口: 192.168.10.51:9200 + + 内部端口: review-es-elasticsearch:9200 + +### 6. [log](../log) + + 无管理界面,仅需要加载对于的volumes:review-log + +### 7. [elasticsearch](../mysql-repl-tool) + + 管理界面: 192.168.10.51:5601 + + 外部端口: 192.168.10.51:9200 + + 内部端口: review-es-elasticsearch:9200 + +### 8. [skywalking](../skywalking) + + 管理界面: 192.168.10.51:18080 + + 内部端口: review-skywalking-oap:11800 + + +### 8. [mysql](../mysql-repl-tool) + + 用于 [nacos](../nacos-cluser/mysql-schema.sql),xxl-job + + 管理界面: 192.168.10.51:25306 + + 内部端口: review-tool-mysql-master:3306 review-tool-mysql-salve:3306 + + +## 其余服务 + +[clickhouse](../clickhouse) 应用于服务 3.123 + +[datart](../datart) 依赖的服务,其中mysql服务bi正在使用,在3.123上 + +[jenkins](../jenkins) 3.120上专门用于部署 + +[canal](../canal) 用于数据同步只clickhouse,在3.120 \ No newline at end of file diff --git a/docker-swarm-review/10.5x环境配置记录/portainer_template.json b/docker-swarm-review/10.5x环境配置记录/portainer_template.json new file mode 100644 index 0000000..cb642af --- /dev/null +++ b/docker-swarm-review/10.5x环境配置记录/portainer_template.json @@ -0,0 +1,105 @@ +{ + "version": "3", + "templates": [ + { + "id": 52, + "type": 2, + "title": "nacos cluser", + "description": "nacos集群", + "categories": ["开发组件"], + "platform": "linux", + "logo": "", + "repository": { + "url": "https://git.sino-assist.com/sa-charts/", + "stackfile": "docker-swarm/nacos-cluser/cluster-docker-compose.yml" + }, + "env": [ + { + "name": "NAMESPACE", + "label": "NAMESPACE", + "default": "review" + }, + { + "name": "NACOS_VERSION", + "label": "NACOS_VERSION", + "default": "v2.3.0" + }, + { + "name": "NODE_PORT_11", + "label": "NODE_PORT_11", + "default": "21848" + }, + { + "name": "NODE_PORT_12", + "label": "NODE_PORT_12", + "default": "22848" + }, + { + "name": "NODE_PORT_13", + "label": "NODE_PORT_13", + "default": "22849" + }, + { + "name": "NODE_PORT_21", + "label": "NODE_PORT_21", + "default": "23848" + }, + { + "name": "NODE_PORT_22", + "label": "NODE_PORT_22", + "default": "24848" + }, + { + "name": "NODE_PORT_23", + "label": "NODE_PORT_23", + "default": "24849" + }, + { + "name": "NODE_PORT_31", + "label": "NODE_PORT_31", + "default": "25848" + }, + { + "name": "NODE_PORT_32", + "label": "NODE_PORT_32", + "default": "26848" + }, + { + "name": "NODE_PORT_33", + "label": "NODE_PORT_33", + "default": "26849" + }, + { + "name": "MYSQL_SERVICE_HOST", + "label": "MYSQL_SERVICE_HOST", + "default": "review-tool-mysql-master" + }, + { + "name": "MYSQL_SERVICE_USER", + "label": "MYSQL_SERVICE_USER", + "default": "zd_tool" + }, + { + "name": "MYSQL_SERVICE_PASSWORD", + "label": "MYSQL_SERVICE_PASSWORD", + "default": "gkxl2024#@" + }, + { + "name": "NACOS_AUTH_IDENTITY_KEY", + "label": "NACOS_AUTH_IDENTITY_KEY", + "default": "nacos" + }, + { + "name": "NACOS_AUTH_IDENTITY_VALUE", + "label": "NACOS_AUTH_IDENTITY_VALUE", + "default": "gkxl2024#@" + }, + { + "name": "NACOS_AUTH_TOKEN", + "label": "NACOS_AUTH_TOKEN", + "default": "OTg1NjRzZnJ0Z2RmZzIwMjQ1NTU1NTExZWZnZGVmZGVz" + } + ] + } + ] +} \ No newline at end of file diff --git a/docker-swarm-review/10.5x环境配置记录/前期处理.md b/docker-swarm-review/10.5x环境配置记录/前期处理.md new file mode 100644 index 0000000..bcadf9f --- /dev/null +++ b/docker-swarm-review/10.5x环境配置记录/前期处理.md @@ -0,0 +1,2 @@ +1. redis的数据需要全部切换为无状态 +2. \ No newline at end of file diff --git a/docker-swarm-review/Dockerfile b/docker-swarm-review/Dockerfile new file mode 100644 index 0000000..060521b --- /dev/null +++ b/docker-swarm-review/Dockerfile @@ -0,0 +1,30 @@ +FROM harbor.sino-assist.com/marsal1212/java11:latest as builder + +ENV active_profile '' +ENV nacos_address '' +ENV nacos_password '' +ENV project_name '' +ENV namespace '' +ENV params '-Dserver.port=8080' +ARG FILE_JAR + +ENV TZ=Asia/Shanghai +RUN cp /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +WORKDIR application + +COPY $FILE_JAR application.jar +RUN java -Djarmode=layertools -jar application.jar extract + + +FROM harbor.sino-assist.com/marsal1212/java11:latest +WORKDIR application +VOLUME /tmp +COPY --from=builder application/dependencies/ ./ +COPY --from=builder application/spring-boot-loader/ ./ +COPY --from=builder application/snapshot-dependencies/ ./ +COPY --from=builder application/application/ ./ +#ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"] + +ENTRYPOINT java -javaagent:/skywalking-agent/skywalking-agent.jar -DSW_AGENT_NAMESPACE=$namespace -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=${namespace}-skywalking-oap:11800 -DSW_AGENT_NAME=$project_name -Dspring.profiles.active=$active_profile -Dsa.nacos.namespace=$namespace -Dsa.nacos.password=$nacos_password -Dsa.nacos.addr=$nacos_address $params -Dfile.encoding=UTF-8 org.springframework.boot.loader.JarLauncher +#ENTRYPOINT java -javaagent:/skywalking-agent/skywalking-agent.jar -DSW_AGENT_NAMESPACE=$namespace -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=${namespace}-skywalking-oap:11800 -DSW_AGENT_NAME=$project_name -Dserver.port=8080 -Dspring.profiles.active=$active_profile -Dsa.nacos.namespace=$namespace -Dsa.nacos.password=$nacos_password -Dsa.nacos.addr=$nacos_address -Dfile.encoding=UTF-8 org.springframework.boot.loader.JarLauncher diff --git a/docker-swarm-review/build.gradle b/docker-swarm-review/build.gradle new file mode 100644 index 0000000..9f0cb70 --- /dev/null +++ b/docker-swarm-review/build.gradle @@ -0,0 +1,338 @@ +import java.time.Instant +import java.time.format.DateTimeFormatter + +plugins { + id 'org.springframework.boot' version '2.7.18' apply false + id 'io.spring.dependency-management' version '1.1.0' apply false + id 'com.github.shalousun.smart-doc' version '2.6.9' apply false + id "org.sonarqube" version "4.0.0.2929" + id 'com.google.cloud.tools.jib' version '3.3.2' apply false +} + +ext { // 自定义扩展 字段 这里定义版本信息 + jjwtVersion = '0.11.1' + mapstructPlusVersion = '1.2.4' + jacksonVersion = '2.17.1' + woodstoxVersion = '6.6.2' + lombokVersion = '1.18.32' + guavaVersion = '33.2.0-jre' + slf4jVersion = '1.7.36' + log4jVersion = '1.2.17' + springCloudVersion = '2021.0.9' + springCloudAlibabaVersion = '2021.0.6.0' + springBootVersion = '2.7.18' + junitVersion = '5.9.3' + skywalkingVersion = '8.15.0' + canalVersion = '1.1.5' + mysqlVersion = '8.0.27' + colaVersion = '4.0.1' + pagehelperVersion = '1.4.7' + smartDocVersion = '2.6.9' + hibernateValidatorVersion = '8.0.1.Final' + javaxAnnotationVersion = '1.3.2' + prometheusVersion = '1.10.4' + minioVersion = '8.5.4' + easyexcelVersion = '3.3.1' + hutoolVersion = '5.8.18' + zxingVersion = '3.4.0' + shardingsphereVersion = '5.2.1' + snakeyamlVersion = '1.33' + oauth2AuthorizationServerVersion = '0.4.1' + springSecurityVersion = '5.8.2' + wxJavaVersion = '4.4.0' + bootAdminVersion = '2.7.15' + fastjsonVersion = '1.2.83' + curvesapiVersion = '1.06' + jeepayVersion = '1.5.0' +} + + + +//配置所有项目公共内容 +allprojects { + group 'com.sa' + version '1.1-SNAPSHOT' + + //配置仓库 + repositories { + maven { + url 'https://maven.aliyun.com/repository/public' + } + maven { + url 'https://oss.sonatype.org/content/groups/public/' + } + maven { + url 'https://maven.sino-assist.com/repository/thirdparty/' + } + mavenLocal() + mavenCentral() + } +} + + +def pomProjects = ['starters', 'infra-config', 'job', 'interface', 'infra', 'infra', 'admin', 'search', 'doc', 'commons', 'ns'] + +def bootRunProjects = [ + 'data-search', 'gps-data', 'data-statistics', 'data-report', 'boot-admin', + 'zgs-manager', 'return-order', 'aggregation', 'api-gate', 'sa-uaa', 'supplier-setting', + 'finance-connector', 'sa-gateway', 'export-app', 'sa-response', 'sa-api', + 'sa-app', 'sa-admin', 'gps-pass', 'file-oss', 'supplier-app','call-center','toc-user-client','supplier-manage' +] + +static def getMainClass(project) { + if (project.hasProperty('mainClassName')) { + return project.mainClassName + } else { + return null + } +} + +//配置子工程 +subprojects { + + if (!pomProjects.contains(it.name)) { + apply plugin: "io.spring.dependency-management" + apply plugin: "java" + + // 设置编码格式 + tasks.withType(JavaCompile).configureEach { + options.encoding = "UTF-8" + java { + sourceCompatibility = "8" + targetCompatibility = "8" + } + } + + dependencyManagement { + resolutionStrategy { + cacheChangingModulesFor 0, 'seconds' + } + imports { + mavenBom "com.alibaba.cloud:spring-cloud-alibaba-dependencies:${springCloudAlibabaVersion}" + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" + mavenBom "org.springframework.boot:spring-boot-dependencies:${springBootVersion}" + mavenBom "com.fasterxml.jackson:jackson-bom:${jacksonVersion}" +// mavenBom "org.springframework.security:spring-security-bom:${springSecurityVersion}" + } + + dependencies { + dependency "org.slf4j:slf4j-api:${slf4jVersion}" + dependency "log4j:log4j:${log4jVersion}" + dependency "com.alibaba.otter:canal.client:${canalVersion}" + dependency "com.alibaba.otter:canal.protocol:${canalVersion}" + dependency "mysql:mysql-connector-java:${mysqlVersion}" + dependency "com.github.pagehelper:pagehelper-spring-boot-starter:${pagehelperVersion}" + dependency "com.github.shalousun:smart-doc:${smartDocVersion}" + dependency "org.hibernate:hibernate-validator:${hibernateValidatorVersion}" + dependency "javax.annotation:javax.annotation-api:${javaxAnnotationVersion}" + dependency "org.projectlombok:lombok:${lombokVersion}" + dependency "io.micrometer:micrometer-registry-prometheus:${prometheusVersion}" + dependency "io.minio:minio:${minioVersion}" + dependency "com.alibaba:easyexcel:${easyexcelVersion}" + dependency "com.alibaba:easyexcel-core:${easyexcelVersion}" + dependency "cn.hutool:hutool-core:${hutoolVersion}" + dependency "cn.hutool:hutool-all:${hutoolVersion}" + dependency "com.alibaba:fastjson:${fastjsonVersion}" + dependency "com.google.zxing:core:${zxingVersion}" + dependency "com.google.zxing:javase:${zxingVersion}" + dependency "org.yaml:snakeyaml:${snakeyamlVersion}" + dependency "org.apache.shardingsphere:shardingsphere-jdbc-core-spring-boot-starter:${shardingsphereVersion}" + dependency "org.springframework.security:spring-security-oauth2-authorization-server:${oauth2AuthorizationServerVersion}" + dependency "com.github.binarywang:weixin-java-pay:${wxJavaVersion}" + dependency "com.github.binarywang:weixin-java-miniapp:${wxJavaVersion}" + dependency "de.codecentric:spring-boot-admin-starter-client:${bootAdminVersion}" + dependency "com.github.binarywang:weixin-java-mp:${wxJavaVersion}" + dependency "com.jeequan:jeepay-sdk-java:${jeepayVersion}" + dependency "com.google.guava:guava:${guavaVersion}" + dependency "org.apache.skywalking:apm-toolkit-logback-1.x:${skywalkingVersion}" + dependency "com.fasterxml.jackson.core:jackson-core:${jacksonVersion}" + dependency "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}" + dependency "com.fasterxml.jackson.core:jackson-annotations:${jacksonVersion}" + dependency "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:${jacksonVersion}" + dependency "com.fasterxml.jackson.dataformat:jackson-dataformat-smile:${jacksonVersion}" + dependency "com.fasterxml.jackson.dataformat:jackson-dataformat-xml:${jacksonVersion}" + dependency "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${jacksonVersion}" + dependency "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:${jacksonVersion}" + dependency "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${jacksonVersion}" + dependency "com.fasterxml.jackson.module:jackson-module-parameter-names:${jacksonVersion}" + dependency "com.fasterxml.woodstox:woodstox-core:${woodstoxVersion}" + dependency "com.github.virtuald:curvesapi:${curvesapiVersion}" + dependency "com.google.errorprone:error_prone_annotations:2.11.0" + dependency "com.google.protobuf:protobuf-java:3.17.1" + dependency "com.nimbusds:nimbus-jose-jwt:9.24.4" + dependency "com.squareup.okhttp3:okhttp:4.9.3" + dependency "com.squareup.okio:okio:2.8.0" + dependency "commons-codec:commons-codec:1.15" + dependency "commons-collections:commons-collections:3.2.2" + dependency "commons-fileupload:commons-fileupload:1.4" + dependency "commons-logging:commons-logging:1.2" + dependency "commons-io:commons-io:2.11.0" + dependency "commons-lang:commons-lang:2.6" + dependency 'commons-beanutils:commons-beanutils:1.9.4' + dependency "net.java.dev.jna:jna:5.10.0" + dependency "org.apache.commons:commons-pool2:2.11.1" + dependency "org.apache.commons:commons-compress:1.23.0" + dependency "org.apache.httpcomponents:httpclient:4.5.14" + dependency "org.apache.httpcomponents:httpmime:4.5.14" + dependency "org.apache.groovy:groovy:4.0.21" + dependency "org.apache.poi:poi:4.1.2" + dependency "org.apache.poi:poi-ooxml:4.1.2" + dependency "org.apache.poi:poi-ooxml-schemas:4.1.2" + dependency "org.apache.poi:poi-scratchpad:4.1.2" + dependency "org.bouncycastle:bcprov-jdk15on:1.70" + dependency "org.bouncycastle:bcpkix-jdk15on:1.70" + dependency "org.mybatis:mybatis:3.5.14" + dependency "org.mybatis:mybatis-spring:2.1.2" + dependency "org.ow2.ams:asm:9.3" + dependency "org.objenesis:objenesis:3.2" + dependency "org.beust:jcommander:1.82" + + + } + } + dependencies { + compileOnly "com.google.guava:guava" + compileOnly "cn.hutool:hutool-all" + + compileOnly "org.projectlombok:lombok:${lombokVersion}" + annotationProcessor "org.projectlombok:lombok:${lombokVersion}" + + testCompileOnly "org.projectlombok:lombok:${lombokVersion}" + testAnnotationProcessor "org.projectlombok:lombok:${lombokVersion}" + + compileOnly "io.github.linpeilie:mapstruct-plus:${mapstructPlusVersion}" + annotationProcessor "io.github.linpeilie:mapstruct-plus-processor:${mapstructPlusVersion}" + + testCompileOnly "io.github.linpeilie:mapstruct-plus:${mapstructPlusVersion}" + testAnnotationProcessor "io.github.linpeilie:mapstruct-plus-processor:${mapstructPlusVersion}" + } + } + + if (it.name.contains("-starter")) { + apply plugin: "java-library" + } + + if (bootRunProjects.contains(it.name)) { + apply plugin: "java" + apply plugin: "idea" + apply plugin: "org.springframework.boot" + apply plugin: 'com.github.shalousun.smart-doc' + apply plugin: 'com.google.cloud.tools.jib' + + smartdoc { + configFile = file("src/main/resources/smart-doc.json") + exclude 'org.springframework.boot:spring-boot-starter-tomcat' + exclude 'org.springframework.boot.*' + exclude 'com.xuxueli:xxl-job-core:*' + exclude 'com.alibaba:easyexcel-core:*' + exclude 'org.apache.*' + } + + //配置子模块依赖 + dependencies { + implementation "org.apache.skywalking:apm-toolkit-logback-1.x" + + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junitVersion}" + } + + sourceSets { + main { + resources { + srcDirs("${rootProject.projectDir}/doc/spring-cloud-config") + } + } + } + + test { + useJUnitPlatform() + } + + + + jib{ + String imageName = "harbor.sino-assist.com/marsal1212/java11:latest" + + from{ + image = imageName + } + to{ + image = "harbor.sino-assist.com/sa-server/${project.name}:${project.properties.get('docker_version')}" + } + + container{ + environment = [active_profile:'dev', nacos_address:'', nacos_password:'nacos', project_name:"", namespace:"", params: "", nativeIp: "", limitMemory: "1G", reservationsMemory: "1G"] + creationTime = DateTimeFormatter.ISO_INSTANT.format(Instant.now()) + entrypoint = ["/bin/sh", "-c", + 'java -javaagent:/skywalking-agent/skywalking-agent.jar -Dskywalking.trace.ignore_path=/actuator,/actuator/* ' + + ' -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=${namespace}-skywalking-oap:11800 -DSW_AGENT_NAME=$project_name ' + + ' -DSW_AGENT_INSTANCE_NAME=$project_name:${nativeIp} -Dspring.cloud.nacos.discovery.metadata.ip=${nativeIp} ' + + ' -Dspring.devtools.add-properties=false -Xms${reservationsMemory} -Xmx${limitMemory}' + + ' -Dserver.port=8080 -Dspring.profiles.active=$active_profile -Dsa.nacos.namespace=$namespace ' + + ' -Dsa.nacos.password=$nacos_password -Dsa.nacos.addr=$nacos_address $params -Dfile.encoding=UTF-8 ' + + ' -cp @/app/jib-classpath-file @/app/jib-main-class-file' + ] + } + } + + afterEvaluate { + + bootJar { + layered { + application { + intoLayer("spring-boot-loader") { + include "org/springframework/boot/loader/**" + } + intoLayer("application") + } + dependencies { + intoLayer("application") { + includeProjectDependencies() + } + intoLayer("snapshot-dependencies") { + include "*:*:*SNAPSHOT*" + } + intoLayer("dependencies") + } + layerOrder = ["dependencies", "spring-boot-loader", "snapshot-dependencies", "application"] + } + } + } + } +} + + +// 在文件底部更新gradle wrapper的版本 +task updateWrapper(type: UpdateWrapper) { + gradleVersion = '8.7' +} + +// 自定义任务用于更新wrapper +class UpdateWrapper extends DefaultTask { + @Input + String gradleVersion + + @TaskAction + void update() { + File wrapperDir = new File(project.getProjectDir(), "gradle/wrapper") + File propertiesFile = new File(wrapperDir, "gradle-wrapper.properties") + + Properties properties = new Properties() + propertiesFile.withInputStream { input -> + properties.load(input) + } + + properties.setProperty("distributionUrl", "https://services.gradle.org/distributions/gradle-$gradleVersion-bin.zip") + properties.setProperty("distributionSha256Sum", "some-sha-sum") // 这里填写正确的SHA-256校验和 + + propertiesFile.withOutputStream { output -> + properties.store(output, null) + } + + File wrapperJar = new File(wrapperDir, "gradle-wrapper.jar") + if (wrapperJar.exists()) { + wrapperJar.delete() + } + } +} \ No newline at end of file diff --git a/docker-swarm-review/canal/README b/docker-swarm-review/canal/README new file mode 100644 index 0000000..3a2c56e --- /dev/null +++ b/docker-swarm-review/canal/README @@ -0,0 +1,9 @@ + +# crm1环境下 部署canal + +docker network create --driver overlay review + +env $(cat ./env_crm1 | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - crm1_canal + + +env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_canal \ No newline at end of file diff --git a/docker-swarm-review/canal/docker-compose.yml b/docker-swarm-review/canal/docker-compose.yml new file mode 100644 index 0000000..b311a8b --- /dev/null +++ b/docker-swarm-review/canal/docker-compose.yml @@ -0,0 +1,33 @@ +version: '3.8' + +networks: + default: + name: ${NAMESPACE} + external: true +services: + db: + image: canal/canal-server:v1.1.5 + environment: + - TZ=Asia/Shanghai + - canal.instance.master.address=${canal_instance_master_address} + - canal.instance.dbUsername=${canal_instance_dbUsername} + - canal.instance.dbPassword=${canal_instance_dbPassword} + - canal.instance.gtidon=false + - canal.instance.connectionCharset=UTF-8 + - canal.instance.tsdb.enable=true + - canal.instance.enableDruid=false + - canal.instance.filter.regex=${canal_instance_filter_regex} + - canal.instance.parser.parallel=true + - canal.serverMode=rabbitMQ + - canal.mq.topic=${canal_mq_topic} + - rabbitmq.host=${rabbitmq_host} + - rabbitmq.exchange=${rabbitmq_exchange} + - rabbitmq.username=${rabbitmq_username} + - rabbitmq.password=${rabbitmq_password} + - rabbitmq.virtual.host=${rabbitmq_virtual_host} + deploy: + update_config: + order: start-first + placement: + constraints: + - node.labels.${NAMESPACE}_canal==1 diff --git a/docker-swarm-review/canal/env_crm1 b/docker-swarm-review/canal/env_crm1 new file mode 100644 index 0000000..44b98a4 --- /dev/null +++ b/docker-swarm-review/canal/env_crm1 @@ -0,0 +1,11 @@ +NAMESPACE=crm1 +canal_instance_master_address=crm_mysql_db:3306 +canal_instance_dbUsername=root +canal_instance_dbPassword=gkxl650 +canal_instance_filter_regex=zd_rescue\\.user_order_20.*,zd_rescue\\.task_order_20.*,zd_rescue\\.task_order_cost_20.*,zd_rescue\\.supplier_account_record_20.*,zd_rescue\\.customer_order_account_20.*,zd_rescue\\.customer_order_relation_20.* +canal_mq_topic=canal_mysql_bin +rabbitmq_host=crm1_rabbitmq_stats:5672 +rabbitmq_exchange=canal_exchange +rabbitmq_username=root +rabbitmq_password=gkxl650 +rabbitmq_virtual_host=canal diff --git a/docker-swarm-review/canal/env_review b/docker-swarm-review/canal/env_review new file mode 100644 index 0000000..bc68a7d --- /dev/null +++ b/docker-swarm-review/canal/env_review @@ -0,0 +1,11 @@ +NAMESPACE=review +canal_instance_master_address=192.168.3.123:3306 +canal_instance_dbUsername=repl +canal_instance_dbPassword=nczl@sino_db +canal_instance_filter_regex=zd_rescue.user_order_20.*,zd_rescue.task_order_20.*,zd_rescue.task_order_cost_20.*,zd_rescue.supplier_account_record_20.*,zd_rescue.customer_order_account_20.*,zd_rescue.customer_order_relation_20.*,zd_rescue.order_lowest_record +canal_mq_topic=canal_mysql_bin +rabbitmq_host=review-rabbitmq-stats:5672 +rabbitmq_exchange=canal_exchange +rabbitmq_username=root +rabbitmq_password=gkxl650 +rabbitmq_virtual_host=review diff --git a/docker-swarm-review/clickhouse/README b/docker-swarm-review/clickhouse/README new file mode 100644 index 0000000..2e0d7fa --- /dev/null +++ b/docker-swarm-review/clickhouse/README @@ -0,0 +1,7 @@ + +# crm1环境下 部署clickhouse + +env $(cat ./env_crm1 | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - crm1_clickhouse + + +env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_clickhouse \ No newline at end of file diff --git a/docker-swarm-review/clickhouse/docker-compose.yml b/docker-swarm-review/clickhouse/docker-compose.yml new file mode 100644 index 0000000..38701d6 --- /dev/null +++ b/docker-swarm-review/clickhouse/docker-compose.yml @@ -0,0 +1,36 @@ +version: '3.8' + +networks: + default: + name: ${NAMESPACE} + external: true +services: + db: + image: docker.io/bitnami/clickhouse:23 + ports: + - '${NODE_PORT}:8123' + environment: + - TZ=Asia/Shanghai + - CLICKHOUSE_ADMIN_USER=${CLICKHOUSE_ADMIN_USER} + - CLICKHOUSE_ADMIN_PASSWORD=${CLICKHOUSE_ADMIN_PASSWORD} + volumes: + - 'data_db:/bitnami/clickhouse' + - 'data_config:/opt/bitnami/clickhouse/etc' + deploy: + update_config: + order: start-first + resources: + limits: + cpus: "8" + memory: 24G + reservations: + cpus: "2" + memory: 12G + placement: + constraints: + - node.labels.${NAMESPACE}_clickhouse==1 +volumes: + data_db: + driver: local + data_config: + driver: local diff --git a/docker-swarm-review/clickhouse/env_crm1 b/docker-swarm-review/clickhouse/env_crm1 new file mode 100644 index 0000000..c879b47 --- /dev/null +++ b/docker-swarm-review/clickhouse/env_crm1 @@ -0,0 +1,4 @@ +NAMESPACE=crm1 +NODE_PORT=8123 +CLICKHOUSE_ADMIN_USER=default +CLICKHOUSE_ADMIN_PASSWORD=gkxl650 \ No newline at end of file diff --git a/docker-swarm-review/clickhouse/env_review b/docker-swarm-review/clickhouse/env_review new file mode 100644 index 0000000..e441da1 --- /dev/null +++ b/docker-swarm-review/clickhouse/env_review @@ -0,0 +1,4 @@ +NAMESPACE=review +NODE_PORT=8123 +CLICKHOUSE_ADMIN_USER=default +CLICKHOUSE_ADMIN_PASSWORD=gkxl650 \ No newline at end of file diff --git a/docker-swarm-review/datart/README b/docker-swarm-review/datart/README new file mode 100644 index 0000000..401cd3e --- /dev/null +++ b/docker-swarm-review/datart/README @@ -0,0 +1,10 @@ + +# crm1环境下 部署redis sentinel + +env $(cat ./env_crm1 | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - crm1_redis + + +env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_datart + + +java -Dspring.profiles.active=review -Xms512M -Dsa.nacos.namespace=review -Dspring.cloud.nacos.config.server-addr=192.168.10.12:8848 -Dspring.cloud.nacos.discovery.server-addr=192.168.10.12:8848 -Dfile.encoding=UTF-8 -jar /zd/gps/zhongdao-gps.jar diff --git a/docker-swarm-review/datart/docker-compose.yml b/docker-swarm-review/datart/docker-compose.yml new file mode 100644 index 0000000..280273c --- /dev/null +++ b/docker-swarm-review/datart/docker-compose.yml @@ -0,0 +1,89 @@ +version: '3.8' + +networks: + default: + name: ${NAMESPACE} + external: true +services: + redis: + image: 'bitnami/redis:7.0.11' + environment: + - TZ=Asia/Shanghai + - REDIS_REPLICATION_MODE=master + - REDIS_PASSWORD=${REDIS_PASSWORD} + ports: + - '${REDIS_PORT}:6379' + deploy: + update_config: + order: start-first + resources: + limits: + cpus: "1" + memory: 1G + reservations: + cpus: "0.1" + memory: 200M + placement: + constraints: + - node.labels.${NAMESPACE}_datart==1 + db: + image: docker.io/bitnami/mysql:8.0 + ports: + - '${MYSQL_PORT}:3306' + environment: + - TZ=Asia/Shanghai + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} + - MYSQL_DATABASE=${MYSQL_DATABASE} + - MYSQL_ENABLE_SLOW_QUERY=0 + - MYSQL_LONG_QUERY_TIME=10 + - MYSQL_USER=${MYSQL_USER} + - MYSQL_PASSWORD=${MYSQL_PASSWORD} + - MYSQL_AUTHENTICATION_PLUGIN=mysql_native_password + volumes: + - 'mysql:/bitnami/mysql/data' + healthcheck: + test: ['CMD', '/opt/bitnami/scripts/mysql/healthcheck.sh'] + interval: 15s + timeout: 5s + retries: 6 + configs: + - source: my_conf + target: /opt/bitnami/mysql/conf/my_custom.cnf + deploy: + resources: + limits: + cpus: "2" + memory: 2G + reservations: + cpus: "0.1" + memory: 500M + placement: + constraints: + - node.labels.${NAMESPACE}_datart==1 + chrome: + image: 'selenium/standalone-chrome:latest' + environment: + - TZ=Asia/Shanghai + ports: + - '${CHROME_PORT}:4444' + deploy: + resources: + limits: + cpus: "2" + memory: 6G + reservations: + cpus: "1" + memory: 2G + update_config: + order: start-first + placement: + constraints: + - node.labels.${NAMESPACE}_datart==1 +volumes: + mysql: + driver: local + +configs: + my_conf: + file: ./my.conf + diff --git a/docker-swarm-review/datart/env_crm1 b/docker-swarm-review/datart/env_crm1 new file mode 100644 index 0000000..65b8798 --- /dev/null +++ b/docker-swarm-review/datart/env_crm1 @@ -0,0 +1,9 @@ +NAMESPACE=crm1 +REDIS_PORT=16379 +REDIS_PASSWORD=gkxl650 +MYSQL_PORT=13306 +MYSQL_ROOT_PASSWORD=gkxl650 +MYSQL_DATABASE=datart +MYSQL_USER=datart +MYSQL_PASSWORD=gkxl650 +CHROME_PORT=14444 diff --git a/docker-swarm-review/datart/env_review b/docker-swarm-review/datart/env_review new file mode 100644 index 0000000..9db5996 --- /dev/null +++ b/docker-swarm-review/datart/env_review @@ -0,0 +1,9 @@ +NAMESPACE=review +REDIS_PORT=16379 +REDIS_PASSWORD=gkxl650 +MYSQL_PORT=13306 +MYSQL_ROOT_PASSWORD=gkxl650 +MYSQL_DATABASE=datart +MYSQL_USER=datart +MYSQL_PASSWORD=gkxl650 +CHROME_PORT=14444 diff --git a/docker-swarm-review/datart/my.conf b/docker-swarm-review/datart/my.conf new file mode 100644 index 0000000..a9b2603 --- /dev/null +++ b/docker-swarm-review/datart/my.conf @@ -0,0 +1,6 @@ +[mysqld] +max_allowed_packet=64M +sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES +log-bin=mysql-bin # 开启 binlog +binlog-format=ROW # 选择 ROW 模式 +server_id=123 \ No newline at end of file diff --git a/docker-swarm-review/elasticsearch/README b/docker-swarm-review/elasticsearch/README new file mode 100644 index 0000000..f8ab334 --- /dev/null +++ b/docker-swarm-review/elasticsearch/README @@ -0,0 +1,9 @@ + +# crm1环境下 部署 单机 es + +env $(cat ./env_crm1 | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - crm1_es --with-registry-auth + + +# review环境下 部署 单机 es 仅用于日志 + +env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_log_es --with-registry-auth \ No newline at end of file diff --git a/docker-swarm-review/elasticsearch/docker-compose.yml b/docker-swarm-review/elasticsearch/docker-compose.yml new file mode 100644 index 0000000..e6c2cf7 --- /dev/null +++ b/docker-swarm-review/elasticsearch/docker-compose.yml @@ -0,0 +1,48 @@ +version: '3.8' +networks: + default: + name: ${NAMESPACE} + external: true +services: + elasticsearch: + image: docker.io/bitnami/elasticsearch:8.13.4 + hostname: ${NAMESPACE}-es-elasticsearch + ports: + - '${NODE_PORT}:9200' + - '${NODE_PORT_2}:9300' + environment: + - TZ=Asia/Shanghai + - ELASTICSEARCH_HEAP_SIZE=8192m + volumes: + - '/mnt/data/volumes/elasticsearch:/bitnami/elasticsearch/data' + - '/mnt/data/volumes/elasticsearch-plugins:/opt/bitnami/elasticsearch/plugins' + deploy: + placement: + constraints: + - node.labels.${NAMESPACE}_es==1 + kibana: + image: docker.io/bitnami/kibana:8.13.4 + hostname: ${NAMESPACE}-es-kibana + ports: + - "${NODE_PORT_KIBANA}:5601" + volumes: + - "/mnt/data/volumes/kibana/data:/bitnami/kibana/data" + - "/mnt/data/volumes/kibana/config:/opt/bitnami/kibana/config" + environment: + - TZ=Asia/Shanghai + - KIBANA_ELASTICSEARCH_URL=${NAMESPACE}-es-elasticsearch + depends_on: + - elasticsearch + deploy: + update_config: + order: start-first + placement: + constraints: + - node.labels.${NAMESPACE}_es==1 +# volumes: +# data_db: +# driver: local +# kibana_data: +# driver: local + + diff --git a/docker-swarm-review/elasticsearch/env_crm1 b/docker-swarm-review/elasticsearch/env_crm1 new file mode 100644 index 0000000..68c8256 --- /dev/null +++ b/docker-swarm-review/elasticsearch/env_crm1 @@ -0,0 +1,4 @@ +NAMESPACE=crm1 +NODE_PORT=9200 +NODE_PORT_2=9300 +NODE_PORT_KIBANA=5601 diff --git a/docker-swarm-review/elasticsearch/env_review b/docker-swarm-review/elasticsearch/env_review new file mode 100644 index 0000000..7cff6c6 --- /dev/null +++ b/docker-swarm-review/elasticsearch/env_review @@ -0,0 +1,4 @@ +NAMESPACE=review +NODE_PORT=9200 +NODE_PORT_2=9300 +NODE_PORT_KIBANA=5601 diff --git a/docker-swarm-review/elasticsearch/kibana.yml b/docker-swarm-review/elasticsearch/kibana.yml new file mode 100644 index 0000000..4b9e0a4 --- /dev/null +++ b/docker-swarm-review/elasticsearch/kibana.yml @@ -0,0 +1,191 @@ +# For more configuration options see the configuration guide for Kibana in +# https://www.elastic.co/guide/index.html + +# =================== System: Kibana Server =================== +# Kibana is served by a back end server. This setting specifies the port to use. +#server.port: 5601 + +# Specifies the address to which the Kibana server will bind. IP addresses and host names are both valid values. +# The default is 'localhost', which usually means remote machines will not be able to connect. +# To allow connections from remote users, set this parameter to a non-loopback address. +#server.host: "localhost" + +# Enables you to specify a path to mount Kibana at if you are running behind a proxy. +# Use the `server.rewriteBasePath` setting to tell Kibana if it should remove the basePath +# from requests it receives, and to prevent a deprecation warning at startup. +# This setting cannot end in a slash. +#server.basePath: "" + +# Specifies whether Kibana should rewrite requests that are prefixed with +# `server.basePath` or require that they are rewritten by your reverse proxy. +# Defaults to `false`. +#server.rewriteBasePath: false + +# Specifies the public URL at which Kibana is available for end users. If +# `server.basePath` is configured this URL should end with the same basePath. +#server.publicBaseUrl: "" + +# The maximum payload size in bytes for incoming server requests. +#server.maxPayload: 1048576 + +# The Kibana server's name. This is used for display purposes. +#server.name: "your-hostname" + +# =================== System: Kibana Server (Optional) =================== +# Enables SSL and paths to the PEM-format SSL certificate and SSL key files, respectively. +# These settings enable SSL for outgoing requests from the Kibana server to the browser. +#server.ssl.enabled: false +#server.ssl.certificate: /path/to/your/server.crt +#server.ssl.key: /path/to/your/server.key + +# =================== System: Elasticsearch =================== +# The URLs of the Elasticsearch instances to use for all your queries. +#elasticsearch.hosts: ["http://localhost:9200"] + +# If your Elasticsearch is protected with basic authentication, these settings provide +# the username and password that the Kibana server uses to perform maintenance on the Kibana +# index at startup. Your Kibana users still need to authenticate with Elasticsearch, which +# is proxied through the Kibana server. +#elasticsearch.username: "kibana_system" +#elasticsearch.password: "pass" + +# Kibana can also authenticate to Elasticsearch via "service account tokens". +# Service account tokens are Bearer style tokens that replace the traditional username/password based configuration. +# Use this token instead of a username/password. +# elasticsearch.serviceAccountToken: "my_token" + +# Time in milliseconds to wait for Elasticsearch to respond to pings. Defaults to the value of +# the elasticsearch.requestTimeout setting. +#elasticsearch.pingTimeout: 1500 + +# Time in milliseconds to wait for responses from the back end or Elasticsearch. This value +# must be a positive integer. +#elasticsearch.requestTimeout: 30000 + +# The maximum number of sockets that can be used for communications with elasticsearch. +# Defaults to `Infinity`. +#elasticsearch.maxSockets: 1024 + +# Specifies whether Kibana should use compression for communications with elasticsearch +# Defaults to `false`. +#elasticsearch.compression: false + +# List of Kibana client-side headers to send to Elasticsearch. To send *no* client-side +# headers, set this value to [] (an empty list). +#elasticsearch.requestHeadersWhitelist: [ authorization ] + +# Header names and values that are sent to Elasticsearch. Any custom headers cannot be overwritten +# by client-side headers, regardless of the elasticsearch.requestHeadersWhitelist configuration. +#elasticsearch.customHeaders: {} + +# Time in milliseconds for Elasticsearch to wait for responses from shards. Set to 0 to disable. +#elasticsearch.shardTimeout: 30000 + +# =================== System: Elasticsearch (Optional) =================== +# These files are used to verify the identity of Kibana to Elasticsearch and are required when +# xpack.security.http.ssl.client_authentication in Elasticsearch is set to required. +#elasticsearch.ssl.certificate: /path/to/your/client.crt +#elasticsearch.ssl.key: /path/to/your/client.key + +# Enables you to specify a path to the PEM file for the certificate +# authority for your Elasticsearch instance. +#elasticsearch.ssl.certificateAuthorities: [ "/path/to/your/CA.pem" ] + +# To disregard the validity of SSL certificates, change this setting's value to 'none'. +#elasticsearch.ssl.verificationMode: full + +# =================== System: Logging =================== +# Set the value of this setting to off to suppress all logging output, or to debug to log everything. Defaults to 'info' +#logging.root.level: debug + +# Enables you to specify a file where Kibana stores log output. +#logging.appenders.default: +# type: file +# fileName: /var/logs/kibana.log +# layout: +# type: json + +# Example with size based log rotation +#logging.appenders.default: +# type: rolling-file +# fileName: /var/logs/kibana.log +# policy: +# type: size-limit +# size: 256mb +# strategy: +# type: numeric +# max: 10 +# layout: +# type: json + +# Logs queries sent to Elasticsearch. +#logging.loggers: +# - name: elasticsearch.query +# level: debug + +# Logs http responses. +#logging.loggers: +# - name: http.server.response +# level: debug + +# Logs system usage information. +#logging.loggers: +# - name: metrics.ops +# level: debug + +# Enables debug logging on the browser (dev console) +#logging.browser.root: +# level: debug + +# =================== System: Other =================== +# The path where Kibana stores persistent data not saved in Elasticsearch. Defaults to data +#path.data: data + +# Specifies the path where Kibana creates the process ID file. +#pid.file: /run/kibana/kibana.pid + +# Set the interval in milliseconds to sample system and process performance +# metrics. Minimum is 100ms. Defaults to 5000ms. +#ops.interval: 5000 + +# Specifies locale to be used for all localizable strings, dates and number formats. +# Supported languages are the following: English (default) "en", Chinese "zh-CN", Japanese "ja-JP", French "fr-FR". +i18n.locale: "zh-CN" +# =================== Frequently used (Optional)=================== + +# =================== Saved Objects: Migrations =================== +# Saved object migrations run at startup. If you run into migration-related issues, you might need to adjust these settings. + +# The number of documents migrated at a time. +# If Kibana can't start up or upgrade due to an Elasticsearch `circuit_breaking_exception`, +# use a smaller batchSize value to reduce the memory pressure. Defaults to 1000 objects per batch. +#migrations.batchSize: 1000 + +# The maximum payload size for indexing batches of upgraded saved objects. +# To avoid migrations failing due to a 413 Request Entity Too Large response from Elasticsearch. +# This value should be lower than or equal to your Elasticsearch cluster’s `http.max_content_length` +# configuration option. Default: 100mb +#migrations.maxBatchSizeBytes: 100mb + +# The number of times to retry temporary migration failures. Increase the setting +# if migrations fail frequently with a message such as `Unable to complete the [...] step after +# 15 attempts, terminating`. Defaults to 15 +#migrations.retryAttempts: 15 + +# =================== Search Autocomplete =================== +# Time in milliseconds to wait for autocomplete suggestions from Elasticsearch. +# This value must be a whole number greater than zero. Defaults to 1000ms +#unifiedSearch.autocomplete.valueSuggestions.timeout: 1000 + +# Maximum number of documents loaded by each shard to generate autocomplete suggestions. +# This value must be a whole number greater than zero. Defaults to 100_000 +#unifiedSearch.autocomplete.valueSuggestions.terminateAfter: 100000 +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 diff --git a/docker-swarm-review/elasticsearch/node.options b/docker-swarm-review/elasticsearch/node.options new file mode 100644 index 0000000..abcb40a --- /dev/null +++ b/docker-swarm-review/elasticsearch/node.options @@ -0,0 +1,15 @@ +## Node command line options +## See `node --help` and `node --v8-options` for available options +## Please note you should specify one option per line + +## max size of old space in megabytes +#--max-old-space-size=4096 + +## do not terminate process on unhandled promise rejection + --unhandled-rejections=warn + +## restore < Node 16 default DNS lookup behavior +--dns-result-order=ipv4first + +## enable OpenSSL 3 legacy provider +--openssl-legacy-provider diff --git a/docker-swarm-review/fastdfs/docker-compose.yml b/docker-swarm-review/fastdfs/docker-compose.yml new file mode 100644 index 0000000..2ba5603 --- /dev/null +++ b/docker-swarm-review/fastdfs/docker-compose.yml @@ -0,0 +1,28 @@ +version: '3' +services: + tracker: + container_name: tracker + image: harbor.sino-assist.com/ygqygq2/fastdfs-nginx:latest + command: tracker + network_mode: host + volumes: + - /data/tracker:/var/fdfs + ports: + - 22122:22122 + storage0: + container_name: storage0 + image: harbor.sino-assist.com/ygqygq2/fastdfs-nginx:latest + command: storage + network_mode: host + extra_hosts: + - "tracker:192.168.3.125" + environment: + - TRACKER_SERVER=tracker:22122 + volumes: + - /data/storage0:/var/fdfs + - ./conf.d:/nginx_conf/conf.d + depends_on: + - tracker + + +## https://github.com/ygqygq2/fastdfs-nginx diff --git a/docker-swarm-review/jenkins/README b/docker-swarm-review/jenkins/README new file mode 100644 index 0000000..3a2c56e --- /dev/null +++ b/docker-swarm-review/jenkins/README @@ -0,0 +1,9 @@ + +# crm1环境下 部署canal + +docker network create --driver overlay review + +env $(cat ./env_crm1 | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - crm1_canal + + +env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_canal \ No newline at end of file diff --git a/docker-swarm-review/jenkins/docker-compose.yml b/docker-swarm-review/jenkins/docker-compose.yml new file mode 100644 index 0000000..13e2d1e --- /dev/null +++ b/docker-swarm-review/jenkins/docker-compose.yml @@ -0,0 +1,16 @@ +version: '3.8' +networks: + jenkins: + external: false +services: + jenkins: + image: harbor.sino-assist.com/marsal1212/jenkins:latest + environment: + - TZ=Asia/Shanghai + networks: + - 'jenkins' + volumes: + - './jenkins_home:/var/jenkins_home' + ports: + - 8081:8080 + diff --git a/docker-swarm-review/jenkins/env_crm1 b/docker-swarm-review/jenkins/env_crm1 new file mode 100644 index 0000000..44b98a4 --- /dev/null +++ b/docker-swarm-review/jenkins/env_crm1 @@ -0,0 +1,11 @@ +NAMESPACE=crm1 +canal_instance_master_address=crm_mysql_db:3306 +canal_instance_dbUsername=root +canal_instance_dbPassword=gkxl650 +canal_instance_filter_regex=zd_rescue\\.user_order_20.*,zd_rescue\\.task_order_20.*,zd_rescue\\.task_order_cost_20.*,zd_rescue\\.supplier_account_record_20.*,zd_rescue\\.customer_order_account_20.*,zd_rescue\\.customer_order_relation_20.* +canal_mq_topic=canal_mysql_bin +rabbitmq_host=crm1_rabbitmq_stats:5672 +rabbitmq_exchange=canal_exchange +rabbitmq_username=root +rabbitmq_password=gkxl650 +rabbitmq_virtual_host=canal diff --git a/docker-swarm-review/jenkins/env_review b/docker-swarm-review/jenkins/env_review new file mode 100644 index 0000000..8614a32 --- /dev/null +++ b/docker-swarm-review/jenkins/env_review @@ -0,0 +1,11 @@ +NAMESPACE=review +canal_instance_master_address=192.168.10.10:3306 +canal_instance_dbUsername=repl +canal_instance_dbPassword=nczl@sino_db +canal_instance_filter_regex=zd_rescue\\.user_order_20.*,zd_rescue\\.task_order_20.*,zd_rescue\\.task_order_cost_20.*,zd_rescue\\.supplier_account_record_20.*,zd_rescue\\.customer_order_account_20.*,zd_rescue\\.customer_order_relation_20.* +canal_mq_topic=canal_mysql_bin +rabbitmq_host=192.168.3.110:5672 +rabbitmq_exchange=canal_exchange +rabbitmq_username=root +rabbitmq_password=gkxl650 +rabbitmq_virtual_host=review diff --git a/docker-swarm-review/log/README b/docker-swarm-review/log/README new file mode 100644 index 0000000..dbcd602 --- /dev/null +++ b/docker-swarm-review/log/README @@ -0,0 +1,15 @@ + +# es index生命周期参考 https://developer.aliyun.com/article/793119,为索引配置索引模板即可,索引模板中添加ilm +# { +# "index": { +# "lifecycle": { +# "name": "90-days-default" +# } +# } +# } + + +env $(cat ./env_crm1 | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - crm1_log --with-registry-auth + + +env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_log --with-registry-auth \ No newline at end of file diff --git a/docker-swarm-review/log/docker-compose.yml b/docker-swarm-review/log/docker-compose.yml new file mode 100644 index 0000000..764128c --- /dev/null +++ b/docker-swarm-review/log/docker-compose.yml @@ -0,0 +1,52 @@ +version: '3.8' +networks: + default: + name: ${NAMESPACE} + external: true +services: + logstash: + image: docker.elastic.co/logstash/logstash:8.13.4 + hostname: ${NAMESPACE}-log-logstash + ports: + - '${NODE_PORT}:5044' + environment: + - TZ=Asia/Shanghai + configs: + - source: logstash_conf + target: /usr/share/logstash/pipeline/my.conf + logging: + driver: json-file + options: + max-size: "500m" + max-file: "3" + deploy: + placement: + constraints: + - node.labels.${NAMESPACE}_es==1 + filebeat: + image: docker.elastic.co/beats/filebeat:8.13.4 + volumes: + - "kibana_data:/bitnami/kibana" + environment: + - TZ=Asia/Shanghai + - LOGSTASH_URL=${NAMESPACE}-log-logstash:5044 + - KIBANA_HOSTS=${NAMESPACE}-es-kibana + configs: + - source: filebeat_conf + target: /usr/share/filebeat/filebeat.yml + volumes: + - ${NAMESPACE}_logs:/logs + deploy: + update_config: + order: start-first + mode: global + placement: + constraints: [node.platform.os == linux] +configs: + logstash_conf: + file: ./logstash.conf + filebeat_conf: + file: ./filebeat.yml +volumes: + ${NAMESPACE}_logs: + external: true diff --git a/docker-swarm-review/log/env_crm1 b/docker-swarm-review/log/env_crm1 new file mode 100644 index 0000000..bf5e2f1 --- /dev/null +++ b/docker-swarm-review/log/env_crm1 @@ -0,0 +1,2 @@ +NAMESPACE=crm1 +NODE_PORT=5045 diff --git a/docker-swarm-review/log/env_review b/docker-swarm-review/log/env_review new file mode 100644 index 0000000..c666723 --- /dev/null +++ b/docker-swarm-review/log/env_review @@ -0,0 +1,2 @@ +NAMESPACE=review +NODE_PORT=5044 diff --git a/docker-swarm-review/log/filebeat.yml b/docker-swarm-review/log/filebeat.yml new file mode 100644 index 0000000..e378300 --- /dev/null +++ b/docker-swarm-review/log/filebeat.yml @@ -0,0 +1,16 @@ +filebeat.inputs: +- type: filestream + id: new-sino-log + paths: + - "/logs/*/*.log" + parsers: + - multiline: + type: pattern + pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}' + negate: true + match: after + +setup.kibana.host: "${KIBANA_HOSTS:kibana:5601}" + +output.logstash: + hosts: '${LOGSTASH_URL:logstash:5044}' \ No newline at end of file diff --git a/docker-swarm-review/log/jvm.options b/docker-swarm-review/log/jvm.options new file mode 100644 index 0000000..b729fce --- /dev/null +++ b/docker-swarm-review/log/jvm.options @@ -0,0 +1,90 @@ +## JVM configuration + +# Xms represents the initial size of total heap space +# Xmx represents the maximum size of total heap space + +-Xms1g +-Xmx1g + +################################################################ +## Expert settings +################################################################ +## +## All settings below this section are considered +## expert settings. Don't tamper with them unless +## you understand what you are doing +## +################################################################ + +## GC configuration +11-13:-XX:+UseConcMarkSweepGC +11-13:-XX:CMSInitiatingOccupancyFraction=75 +11-13:-XX:+UseCMSInitiatingOccupancyOnly + +## Locale +# Set the locale language +#-Duser.language=en + +# Set the locale country +#-Duser.country=US + +# Set the locale variant, if any +#-Duser.variant= + +## basic + +# set the I/O temp directory +#-Djava.io.tmpdir=$HOME + +# set to headless, just in case +-Djava.awt.headless=true + +# ensure UTF-8 encoding by default (e.g. filenames) +-Dfile.encoding=UTF-8 + +# use our provided JNA always versus the system one +#-Djna.nosys=true + +# Turn on JRuby invokedynamic +-Djruby.compile.invokedynamic=true + +## heap dumps + +# generate a heap dump when an allocation from the Java heap fails +# heap dumps are created in the working directory of the JVM +-XX:+HeapDumpOnOutOfMemoryError + +# specify an alternative path for heap dumps +# ensure the directory exists and has sufficient space +#-XX:HeapDumpPath=${LOGSTASH_HOME}/heapdump.hprof + +## GC logging +#-Xlog:gc*,gc+age=trace,safepoint:file=@loggc@:utctime,pid,tags:filecount=32,filesize=64m + +# log GC status to a file with time stamps +# ensure the directory exists +#-Xloggc:${LS_GC_LOG_FILE} + +# Entropy source for randomness +-Djava.security.egd=file:/dev/urandom + +# Copy the logging context from parent threads to children +-Dlog4j2.isThreadContextMapInheritable=true + +# FasterXML/jackson defaults +# +# Sets the maximum string length (in chars or bytes, depending on input context). +# This limit is not exact and an exception will happen at sizes greater than this limit. +# Some text values that are a little bigger than the limit may be treated as valid but no +# text values with sizes less than or equal to this limit will be treated as invalid. +# This value should be higher than `logstash.jackson.stream-read-constraints.max-number-length`. +# The jackson library defaults to 20000000 or 20MB, whereas Logstash defaults to 200MB or 200000000 characters. +-Dlogstash.jackson.stream-read-constraints.max-string-length=200000000 +# +# Sets the maximum number length (in chars or bytes, depending on input context). +# The jackson library defaults to 1000, whereas Logstash defaults to 10000. +-Dlogstash.jackson.stream-read-constraints.max-number-length=10000 +# +# Sets the maximum nesting depth. The depth is a count of objects and arrays that have not +# been closed, `{` and `[` respectively. +#-Dlogstash.jackson.stream-read-constraints.max-nesting-depth=1000 \ No newline at end of file diff --git a/docker-swarm-review/log/logstash.conf b/docker-swarm-review/log/logstash.conf new file mode 100644 index 0000000..4a1110d --- /dev/null +++ b/docker-swarm-review/log/logstash.conf @@ -0,0 +1,36 @@ +filter { + grok { + match => { "message" => "%{TIMESTAMP_ISO8601:oldtimestamp}\s+\[%{DATA:service}\]\s+\[TID:%{NOTSPACE:tid}\]\s+\[%{DATA:thread}\]\s+%{LOGLEVEL:loglevel}\s+%{NOTSPACE:class}\s+-%{GREEDYDATA:oldmessage}"} + } + + + date { + match => ["oldtimestamp", "ISO8601"] + target => "@timestamp" + } + mutate { + replace => { "message" => "%{oldmessage}" } + remove_field => [ "oldmessage","oldtimestamp","agent","host","input","log.flags","log.flags.keyword","tags" ] + } +} + + +output { + if [service] { + elasticsearch { + hosts => [ "review-es-elasticsearch:9200" ] + index => "sslog-%{[service]}" + action => "create" + ilm_enabled => false + } + }else{ + elasticsearch { + hosts => [ "review-es-elasticsearch:9200" ] + index => "sslog-default" + action => "create" + ilm_enabled => false + } + } +} + + diff --git a/docker-swarm-review/minio/README b/docker-swarm-review/minio/README new file mode 100644 index 0000000..55b823d --- /dev/null +++ b/docker-swarm-review/minio/README @@ -0,0 +1,18 @@ + +# es index生命周期参考 https://developer.aliyun.com/article/793119,为索引配置索引模板即可,索引模板中添加ilm +# { +# "index": { +# "lifecycle": { +# "name": "90-days-default" +# } +# } +# } + + +docker network create \ +--driver overlay \ + review + +env $(cat ./env_crm1 | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - crm1_log --with-registry-auth + +env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - minio --with-registry-auth \ No newline at end of file diff --git a/docker-swarm-review/minio/docker-compose.yml b/docker-swarm-review/minio/docker-compose.yml new file mode 100644 index 0000000..3698cc9 --- /dev/null +++ b/docker-swarm-review/minio/docker-compose.yml @@ -0,0 +1,35 @@ +version: '3.8' + +networks: + default: + name: ${NAMESPACE} + external: true +services: + minio: + image: docker.io/bitnami/minio:2023 + hostname: ${NAMESPACE}-minio + ports: + - '${NODE_PORT}:9000' + - '${NODE_PORT_1}:9001' + environment: + # 时区上海 + TZ: Asia/Shanghai + # 管理后台用户名 + MINIO_ROOT_USER: ${MINIO_ROOT_USER} + # 管理后台密码,最小8个字符 + MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD} + volumes: + - 'data:/data' + deploy: + update_config: + order: start-first + placement: + constraints: + - node.labels.${NAMESPACE}_minio==1 +volumes: + data: + driver: local + + + + diff --git a/docker-swarm-review/minio/env_crm1 b/docker-swarm-review/minio/env_crm1 new file mode 100644 index 0000000..962a3a2 --- /dev/null +++ b/docker-swarm-review/minio/env_crm1 @@ -0,0 +1,5 @@ +NAMESPACE=crm1 +NODE_PORT=9000 +NODE_PORT_1=9001 +MINIO_ROOT_USER=minioadmin +MINIO_ROOT_PASSWORD=gkxl###650 \ No newline at end of file diff --git a/docker-swarm-review/minio/env_review b/docker-swarm-review/minio/env_review new file mode 100644 index 0000000..0d12a7f --- /dev/null +++ b/docker-swarm-review/minio/env_review @@ -0,0 +1,5 @@ +NAMESPACE=review +NODE_PORT=9000 +NODE_PORT_1=9001 +MINIO_ROOT_USER=minioadmin +MINIO_ROOT_PASSWORD=gkxl###650 \ No newline at end of file diff --git a/docker-swarm-review/mongodb/README b/docker-swarm-review/mongodb/README new file mode 100644 index 0000000..29eac1f --- /dev/null +++ b/docker-swarm-review/mongodb/README @@ -0,0 +1,12 @@ + +# crm1环境下 部署集群mongodb + +env $(cat ./env_crm1 | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - crm1_mongodb + + + +# crm1环境下 部署集群mongodb rs + + + +docker stack deploy --compose-file docker-stack-rs.yml review_mongodb --with-registry-auth diff --git a/docker-swarm-review/mongodb/docker-compose.yml b/docker-swarm-review/mongodb/docker-compose.yml new file mode 100644 index 0000000..f93ba3f --- /dev/null +++ b/docker-swarm-review/mongodb/docker-compose.yml @@ -0,0 +1,25 @@ +version: '3.8' + +networks: + default: + name: ${NAMESPACE} + external: true +services: + db: + image: docker.io/bitnami/mongodb:6.0 + ports: + - '${NODE_PORT}:27017' + environment: + - TZ=Asia/Shanghai + - MONGODB_ROOT_USER=root + - MONGODB_ROOT_PASSWORD=123456 + - MONGODB_DATABASE=${MONGODB_DATABASE} + volumes: + - '/mnt/data/volumes/mongodb:/bitnami/mongodb' + deploy: + update_config: + order: start-first + placement: + constraints: + - node.labels.${NAMESPACE}_mongodb==1 + diff --git a/docker-swarm-review/mongodb/docker-stack-rs.yml b/docker-swarm-review/mongodb/docker-stack-rs.yml new file mode 100644 index 0000000..d3884e8 --- /dev/null +++ b/docker-swarm-review/mongodb/docker-stack-rs.yml @@ -0,0 +1,64 @@ +# Copyright Broadcom, Inc. All Rights Reserved. +# SPDX-License-Identifier: APACHE-2.0 + +version: '3.8' +networks: + default: + name: review + external: true +services: + primary: + image: docker.io/bitnami/mongodb:7.0 + hostname: mongodb-primary + ports: + - 27015:27017 + environment: + - TZ=Asia/Shanghai + - MONGODB_ADVERTISED_HOSTNAME=mongodb-primary + - MONGODB_REPLICA_SET_MODE=primary + - MONGODB_ROOT_PASSWORD=123456 + - MONGODB_REPLICA_SET_KEY=replicasetkey123 + volumes: + - '/mnt/data/volumes/mongodb/primary:/bitnami/mongodb' + deploy: + mode: replicated + replicas: 1 + placement: + constraints: [node.hostname == ZD-CRM5] + secondary: + image: docker.io/bitnami/mongodb:7.0 + hostname: mongodb-secondary + ports: + - 27016:27017 + depends_on: + - mongodb-primary + environment: + - TZ=Asia/Shanghai + - MONGODB_ADVERTISED_HOSTNAME=mongodb-secondary + - MONGODB_REPLICA_SET_MODE=secondary + - MONGODB_INITIAL_PRIMARY_HOST=mongodb-primary + - MONGODB_INITIAL_PRIMARY_ROOT_PASSWORD=123456 + - MONGODB_REPLICA_SET_KEY=replicasetkey123 + volumes: + - '/mnt/data/volumes/mongodb/secondary:/bitnami/mongodb' + deploy: + mode: replicated + replicas: 1 + placement: + constraints: [node.hostname == ZD-CRM6] + arbiter: + image: docker.io/bitnami/mongodb:7.0 + depends_on: + - mongodb-primary + environment: + - TZ=Asia/Shanghai + - MONGODB_ADVERTISED_HOSTNAME=mongodb-arbiter + - MONGODB_REPLICA_SET_MODE=arbiter + - MONGODB_INITIAL_PRIMARY_HOST=mongodb-primary + - MONGODB_INITIAL_PRIMARY_ROOT_PASSWORD=password123 + - MONGODB_REPLICA_SET_KEY=replicasetkey123 + deploy: + mode: replicated + replicas: 1 + placement: + constraints: [node.hostname == ZD-CRM3] diff --git a/docker-swarm-review/mongodb/env_crm1 b/docker-swarm-review/mongodb/env_crm1 new file mode 100644 index 0000000..b0755ef --- /dev/null +++ b/docker-swarm-review/mongodb/env_crm1 @@ -0,0 +1,3 @@ +NAMESPACE=crm1 +NODE_PORT=27017 +MONGODB_DATABASE=gps_data \ No newline at end of file diff --git a/docker-swarm-review/mongodb/env_review b/docker-swarm-review/mongodb/env_review new file mode 100644 index 0000000..21f5e02 --- /dev/null +++ b/docker-swarm-review/mongodb/env_review @@ -0,0 +1,3 @@ +NAMESPACE=review +NODE_PORT=27017 +MONGODB_DATABASE=gps_data diff --git a/docker-swarm-review/monitor/README b/docker-swarm-review/monitor/README new file mode 100644 index 0000000..cdabd93 --- /dev/null +++ b/docker-swarm-review/monitor/README @@ -0,0 +1,19 @@ + +env $(cat ./env_review | xargs) envsubst < ./docker-stack.yml | docker stack deploy --compose-file - monitor + +docker stack deploy --compose-file docker-compose.yml monitor --with-registry-auth + + +docker run \ + -p 9090:9090 \ + -v /opt/support/prometheus.yml:/etc/prometheus/prometheus.yml \ + prom/prometheus:v2.52.0 + +docker service create --name cadvisor -l prometheus-job=cadvisor \ + --mode=global --publish target=8080,mode=host \ + --mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock,ro \ + --mount type=bind,src=/,dst=/rootfs,ro \ + --mount type=bind,src=/var/run,dst=/var/run \ + --mount type=bind,src=/sys,dst=/sys,ro \ + --mount type=bind,src=/var/lib/docker,dst=/var/lib/docker,ro \ + spcodes/cadvisor:v0.49.1 -docker_only \ No newline at end of file diff --git a/docker-swarm-review/monitor/docker-compose.yml b/docker-swarm-review/monitor/docker-compose.yml new file mode 100644 index 0000000..63039ab --- /dev/null +++ b/docker-swarm-review/monitor/docker-compose.yml @@ -0,0 +1,70 @@ +version: '3.8' + +services: + prometheus: + image: prom/prometheus:v2.52.0 + ports: + - "9090:9090" + configs: + - source: prometheus_conf + target: /etc/prometheus/prometheus.yml + command: + - --config.file=/etc/prometheus/prometheus.yml + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + deploy: + mode: replicated + placement: + constraints: [node.hostname == ZD-CRM2] + replicas: 1 + + # alertmanager: + # image: prom/alertmanager:v0.27.0 + # ports: + # - "9093:9093" + # volumes: + # - ./alertmanager.yml:/etc/alertmanager/alertmanager.yml + # command: + # - --config.file=/etc/alertmanager/alertmanager.yml + # deploy: + # mode: replicated + # replicas: 1 + + # node-exporter: + # image: prom/node-exporter:v1.8.1 + # volumes: + # - /proc:/host/proc:ro + # - /sys:/host/sys:ro + # - /:/rootfs:ro + # deploy: + # mode: global + # placement: + # constraints: [node.role == manager] + cadvisor: + image: spcodes/cadvisor:v0.49.1 + ports: + - 8180:8080 + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - /:/rootfs:ro + - /var/run:/var/run:rw + - /sys:/sys:ro + - /var/lib/docker/:/var/lib/docker:ro + deploy: + mode: global + grafana: + image: grafana/grafana:11.0.0 + ports: + - 23000:3000 + volumes: + - /opt/data/grafana/:/var/lib/grafana:ro + deploy: + mode: replicated + placement: + constraints: [node.hostname == ZD-CRM2] + replicas: 1 +configs: + prometheus_conf: +# file: ./prometheus.yml + external: true + name: monitor_prometheus_conf_v2 diff --git a/docker-swarm-review/monitor/docker-stack.yml b/docker-swarm-review/monitor/docker-stack.yml new file mode 100644 index 0000000..bcb071f --- /dev/null +++ b/docker-swarm-review/monitor/docker-stack.yml @@ -0,0 +1,125 @@ +version: "3.8" + +services: + grafana: + image: portainer/template-swarm-monitoring:grafana-9.5.2 + ports: + - target: 3000 + published: 3000 + protocol: tcp + mode: ingress + deploy: + replicas: 1 + restart_policy: + condition: on-failure + placement: + constraints: + - node.role == manager + - node.labels.monitoring == true + volumes: + - type: volume + source: grafana-data + target: /var/lib/grafana + environment: + - GF_SECURITY_ADMIN_USER=${GRAFANA_USER} + - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD} + - GF_USERS_ALLOW_SIGN_UP=false + networks: + - net + + prometheus: + image: portainer/template-swarm-monitoring:prometheus-v2.44.0 + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--log.level=error' + - '--storage.tsdb.path=/prometheus' + - '--storage.tsdb.retention.time=7d' + deploy: + replicas: 1 + restart_policy: + condition: on-failure + placement: + constraints: + - node.role == manager + - node.labels.monitoring == true + volumes: + - type: volume + source: prometheus-data + target: /prometheus + networks: + - net + + cadvisor: + image: spcodes/cadvisor:v0.49.1 + command: -logtostderr -docker_only + deploy: + mode: global + resources: + limits: + memory: 128M + reservations: + memory: 64M + volumes: + - type: bind + source: / + target: /rootfs + read_only: true + - type: bind + source: /var/run + target: /var/run + read_only: true + - type: bind + source: /sys + target: /sys + read_only: true + - type: bind + source: /var/lib/docker + target: /var/lib/docker + read_only: true + - type: bind + source: /dev/disk + target: /dev/disk + read_only: true + networks: + - net + + node-exporter: + image: prom/node-exporter:v1.5.0 + command: + - '--path.sysfs=/host/sys' + - '--path.procfs=/host/proc' + - '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)' + - '--no-collector.ipvs' + deploy: + mode: global + resources: + limits: + memory: 128M + reservations: + memory: 64M + volumes: + - type: bind + source: / + target: /rootfs + read_only: true + - type: bind + source: /proc + target: /host/proc + read_only: true + - type: bind + source: /sys + target: /host/sys + read_only: true + networks: + - net + +volumes: + grafana-data: + prometheus-data: + +networks: + net: + driver: host + + + diff --git a/docker-swarm-review/monitor/prometheus.yml b/docker-swarm-review/monitor/prometheus.yml new file mode 100644 index 0000000..48db71f --- /dev/null +++ b/docker-swarm-review/monitor/prometheus.yml @@ -0,0 +1,30 @@ +scrape_configs: + # Make Prometheus scrape itself for metrics. + - job_name: 'prometheus' + static_configs: + - targets: ['localhost:9090'] + + # Create a job for Docker Swarm containers. + - job_name: 'dockerswarm' + dockerswarm_sd_configs: + - host: unix:///var/run/docker.sock + role: nodes + relabel_configs: + # Fetch metrics on port 9323. + - source_labels: [__meta_dockerswarm_node_address] + target_label: __address__ + replacement: $1:9323 + # Set hostname as instance label + - source_labels: [__meta_dockerswarm_node_hostname] + target_label: instance + # Only keep containers that should be running. + - source_labels: [__meta_dockerswarm_task_desired_state] + regex: running + action: keep + # Only keep containers that have a `prometheus-job` label. + - source_labels: [__meta_dockerswarm_service_label_prometheus_job] + regex: .+ + action: keep + # Use the prometheus-job Swarm label as Prometheus job label. + - source_labels: [__meta_dockerswarm_service_label_prometheus_job] + target_label: job \ No newline at end of file diff --git a/docker-swarm-review/mysql-repl-tool/README.md b/docker-swarm-review/mysql-repl-tool/README.md new file mode 100644 index 0000000..f97d6df --- /dev/null +++ b/docker-swarm-review/mysql-repl-tool/README.md @@ -0,0 +1,50 @@ +生产环境用于nacos xxl-job的专用数据库 + + + +# review环境下 部署 附属工具类服务使用的mysql + +env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_tool_mysql + +[mysqladmin] +user= + +[mysqld] +skip_name_resolve +explicit_defaults_for_timestamp +basedir=/opt/bitnami/mysql +port=3306 +tmpdir=/opt/bitnami/mysql/tmp +socket=/opt/bitnami/mysql/tmp/mysql.sock +pid_file=/opt/bitnami/mysql/tmp/mysqld.pid +max_allowed_packet=16M +bind_address=0.0.0.0 +log_error=/opt/bitnami/mysql/logs/mysqld.log +slow_query_log=0 +slow_query_log_file=/opt/bitnami/mysql/logs/mysqld.log +long_query_time=10 +character_set_server=utf8mb4 +collation_server=utf8mb4_unicode_ci +plugin_dir=/opt/bitnami/mysql/lib/plugin + +[client] +port=3306 +socket=/opt/bitnami/mysql/tmp/mysql.sock +default_character_set=utf8mb4 +plugin_dir=/opt/bitnami/mysql/lib/plugin + +[manager] +port=3306 +socket=/opt/bitnami/mysql/tmp/mysql.sock +pid_file=/opt/bitnami/mysql/tmp/mysqld.pid + + +---- 实际版本 + +[mysqld] +max_connections=500 +max_allowed_packet=64M +sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES +log-bin=mysql-bin # 开启 binlog +binlog-format=ROW # 选择 ROW 模式 +server_id=209 \ No newline at end of file diff --git a/docker-swarm-review/mysql-repl-tool/docker-compose.yml b/docker-swarm-review/mysql-repl-tool/docker-compose.yml new file mode 100644 index 0000000..e71ff6b --- /dev/null +++ b/docker-swarm-review/mysql-repl-tool/docker-compose.yml @@ -0,0 +1,92 @@ +version: '3.8' +networks: + default: + name: ${NAMESPACE} + external: true +services: + mysql-master: + image: docker.io/bitnami/mysql:8.0 + hostname: ${NAMESPACE}-tool-mysql-master + ports: + - '${NODE_PORT_MASTER}:3306' + volumes: + - 'mysql_repl_master_data:/bitnami/mysql/data' + environment: + - MYSQL_REPLICATION_MODE=master + - MYSQL_REPLICATION_USER=repl_user + - MYSQL_REPLICATION_PASSWORD=${MYSQL_REPLICATION_PASSWORD} + # - MYSQL_DATABASE=my_database + # ALLOW_EMPTY_PASSWORD is recommended only for development. + - MYSQL_USER=${MYSQL_USER} + - MYSQL_PASSWORD=${MYSQL_PASSWORD} + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} + - MYSQL_AUTHENTICATION_PLUGIN=mysql_native_password + - MYSQL_ENABLE_SLOW_QUERY=0 + - MYSQL_LONG_QUERY_TIME=10 + configs: + - source: custome_config_master + target: /opt/bitnami/mysql/conf/my_custom.cnf + healthcheck: + test: ['CMD', '/opt/bitnami/scripts/mysql/healthcheck.sh'] + interval: 15s + timeout: 5s + retries: 6 + deploy: + mode: replicated + replicas: 1 + placement: + constraints: [node.hostname == ZD-BAK-APP1] + mysql-slave: + image: docker.io/bitnami/mysql:8.0 + hostname: ${NAMESPACE}-tool-mysql-slave + ports: + - '${NODE_PORT_SLAVE}:3306' + volumes: + - 'mysql_repl_slave_data:/bitnami/mysql/data' + depends_on: + - mysql-master + environment: + - MYSQL_REPLICATION_MODE=slave + - MYSQL_REPLICATION_USER=repl_user + - MYSQL_REPLICATION_PASSWORD=${MYSQL_REPLICATION_PASSWORD} + # - MYSQL_DATABASE=my_database + - MYSQL_MASTER_HOST=mysql-master + - MYSQL_MASTER_PORT_NUMBER=3306 + - MYSQL_MASTER_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} + - MYSQL_USER=${MYSQL_USER} + - MYSQL_PASSWORD=${MYSQL_PASSWORD} + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} + - MYSQL_AUTHENTICATION_PLUGIN=mysql_native_password + - MYSQL_ENABLE_SLOW_QUERY=0 + - MYSQL_LONG_QUERY_TIME=10 + # ALLOW_EMPTY_PASSWORD is recommended only for development. + # - ALLOW_EMPTY_PASSWORD=yes + # In case of missing binary files on master, use `true` to reset those binary files. Creating a previous backup is recommended. + - MYSQL_REPLICATION_SLAVE_DUMP=false + healthcheck: + test: ['CMD', '/opt/bitnami/scripts/mysql/healthcheck.sh'] + interval: 30s + timeout: 10s + retries: 6 + start_period: 180s + configs: + - source: custome_config_slave + target: /opt/bitnami/mysql/conf/my_custom.cnf + deploy: + mode: replicated + replicas: 1 + placement: + constraints: [node.hostname == ZD-BAK-APP2] +volumes: + mysql_repl_master_data: + driver: local + mysql_repl_slave_data: + driver: local + +configs: + custome_config_master: + external: true + name: ${CUSTOME_CONFIG_MASTER} + custome_config_slave: + external: true + name: ${CUSTOME_CONFIG_SLAVE} \ No newline at end of file diff --git a/docker-swarm-review/mysql-repl-tool/env_review b/docker-swarm-review/mysql-repl-tool/env_review new file mode 100644 index 0000000..2b7b0d1 --- /dev/null +++ b/docker-swarm-review/mysql-repl-tool/env_review @@ -0,0 +1,9 @@ +NAMESPACE=review +NODE_PORT_MASTER=25306 +NODE_PORT_SLAVE=25307 +MYSQL_USER=zd_tool +MYSQL_PASSWORD=gkxl2024#@ +MYSQL_ROOT_PASSWORD=gkxl2024#@ +MYSQL_REPLICATION_PASSWORD=gkxl2024#@ +CUSTOME_CONFIG_MASTER=review_tool_mysql_master_conf_v1 +CUSTOME_CONFIG_SLAVE=review_tool_mysql_slave_conf_v1 \ No newline at end of file diff --git a/docker-swarm-review/mysql/README b/docker-swarm-review/mysql/README new file mode 100644 index 0000000..1063a4d --- /dev/null +++ b/docker-swarm-review/mysql/README @@ -0,0 +1,36 @@ + +# crm1环境下 部署 mysql + +env $(cat ./env_crm1 | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - crm1_mysql + +[mysqladmin] +user= + +[mysqld] +skip_name_resolve +explicit_defaults_for_timestamp +basedir=/opt/bitnami/mysql +port=3306 +tmpdir=/opt/bitnami/mysql/tmp +socket=/opt/bitnami/mysql/tmp/mysql.sock +pid_file=/opt/bitnami/mysql/tmp/mysqld.pid +max_allowed_packet=16M +bind_address=0.0.0.0 +log_error=/opt/bitnami/mysql/logs/mysqld.log +slow_query_log=0 +slow_query_log_file=/opt/bitnami/mysql/logs/mysqld.log +long_query_time=10 +character_set_server=utf8mb4 +collation_server=utf8mb4_unicode_ci +plugin_dir=/opt/bitnami/mysql/lib/plugin + +[client] +port=3306 +socket=/opt/bitnami/mysql/tmp/mysql.sock +default_character_set=utf8mb4 +plugin_dir=/opt/bitnami/mysql/lib/plugin + +[manager] +port=3306 +socket=/opt/bitnami/mysql/tmp/mysql.sock +pid_file=/opt/bitnami/mysql/tmp/mysqld.pid \ No newline at end of file diff --git a/docker-swarm-review/mysql/docker-compose.yml b/docker-swarm-review/mysql/docker-compose.yml new file mode 100644 index 0000000..525dbfd --- /dev/null +++ b/docker-swarm-review/mysql/docker-compose.yml @@ -0,0 +1,42 @@ +version: '3.8' +networks: + default: + name: ${NAMESPACE} + external: true +services: + db: + image: docker.io/bitnami/mysql:8.0 + ports: + - '${NODE_PORT}:3306' + environment: + - TZ=Asia/Shanghai + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} + - MYSQL_DATABASE=${MYSQL_DATABASE} + - MYSQL_ENABLE_SLOW_QUERY=0 + - MYSQL_LONG_QUERY_TIME=10 + - MYSQL_USER=${MYSQL_USER} + - MYSQL_PASSWORD=${MYSQL_PASSWORD} + - MYSQL_AUTHENTICATION_PLUGIN=mysql_native_password + volumes: + - 'data_db:/bitnami/mysql/data' + healthcheck: + test: ['CMD', '/opt/bitnami/scripts/mysql/healthcheck.sh'] + interval: 15s + timeout: 5s + retries: 6 + configs: + - source: custome_config + target: /opt/bitnami/mysql/conf/my_custom.cnf + deploy: + placement: + constraints: + - node.labels.${NAMESPACE}_mysql==1 +volumes: + data_db: + driver: local +configs: + custome_config: + external: true + name: ${CUSTOME_CONFIG} + + diff --git a/docker-swarm-review/mysql/env_review b/docker-swarm-review/mysql/env_review new file mode 100644 index 0000000..a4c1b22 --- /dev/null +++ b/docker-swarm-review/mysql/env_review @@ -0,0 +1,7 @@ +NAMESPACE=review +NODE_PORT=3306 +MYSQL_USER=gkxl650 +MYSQL_PASSWORD=gkxl650 +MYSQL_ROOT_PASSWORD=gkxl650 +MYSQL_DATABASE=zd_rescue +CUSTOME_CONFIG=review_mysql_conf_v1 diff --git a/docker-swarm-review/nacos-cluser/README b/docker-swarm-review/nacos-cluser/README new file mode 100644 index 0000000..51828a2 --- /dev/null +++ b/docker-swarm-review/nacos-cluser/README @@ -0,0 +1,10 @@ + +# crm1环境下 部署单机nacos + +env $(cat ./env_crm1 | xargs) envsubst < ./standalone-derby.yml | docker stack deploy --compose-file - crm1_nacos + + + +# review环境下 部署单机nacos + +env $(cat ./env_review | xargs) envsubst < ./cluster-docker-compose.yml | docker stack deploy --compose-file - review_nacos diff --git a/docker-swarm-review/nacos-cluser/cluster-docker-compose.yml b/docker-swarm-review/nacos-cluser/cluster-docker-compose.yml new file mode 100644 index 0000000..0363c35 --- /dev/null +++ b/docker-swarm-review/nacos-cluser/cluster-docker-compose.yml @@ -0,0 +1,103 @@ +version: '3.8' +networks: + default: + name: ${NAMESPACE} + external: true +services: + nacos1: + image: nacos/nacos-server:${NACOS_VERSION} + hostname: ${NAMESPACE}-nacos1 + ports: + - ${NODE_PORT_11}:8848 + - ${NODE_PORT_12}:9848 + - ${NODE_PORT_13}:9849 + volumes: + - nacos_cluster_log:/home/nacos/logs #配置docker存储日志的卷 + environment: + MODE: cluster + PREFER_HOST_MODE: hostname + NACOS_SERVERS: ${NAMESPACE}-nacos1:8848 ${NAMESPACE}-nacos2:8848 ${NAMESPACE}-nacos3:8848 + NACOS_SERVER_PORT: 8848 + NACOS_AUTH_ENABLE: 'true' #1.2.0版本默认关闭登陆界面 + SPRING_DATASOURCE_PLATFORM: mysql + MYSQL_SERVICE_HOST: ${MYSQL_SERVICE_HOST} + MYSQL_SERVICE_DB_NAME: nacos + MYSQL_SERVICE_PORT: 3306 + MYSQL_SERVICE_USER: ${MYSQL_SERVICE_USER} + MYSQL_SERVICE_PASSWORD: ${MYSQL_SERVICE_PASSWORD} + NACOS_AUTH_IDENTITY_KEY: ${NACOS_AUTH_IDENTITY_KEY} + NACOS_AUTH_IDENTITY_VALUE: ${NACOS_AUTH_IDENTITY_VALUE} + NACOS_AUTH_TOKEN: ${NACOS_AUTH_TOKEN} + deploy: + replicas: 1 #部署时,指定部署一个副本 + placement: + constraints: [node.hostname == ZD-BAK-APP1] + restart_policy: + condition: on-failure + nacos2: + image: nacos/nacos-server:${NACOS_VERSION} + hostname: ${NAMESPACE}-nacos2 + ports: + - ${NODE_PORT_21}:8848 + - ${NODE_PORT_22}:9848 + - ${NODE_PORT_23}:9849 + volumes: + - nacos_cluster_log:/home/nacos/logs #配置docker存储日志的卷 + environment: + MODE: cluster + PREFER_HOST_MODE: hostname + NACOS_SERVERS: ${NAMESPACE}-nacos1:8848 ${NAMESPACE}-nacos2:8848 ${NAMESPACE}-nacos3:8848 + NACOS_SERVER_PORT: 8848 + NACOS_AUTH_ENABLE: 'true' #1.2.0版本默认关闭登陆界面 + SPRING_DATASOURCE_PLATFORM: mysql + MYSQL_SERVICE_HOST: ${MYSQL_SERVICE_HOST} + MYSQL_SERVICE_DB_NAME: nacos + MYSQL_SERVICE_PORT: 3306 + MYSQL_SERVICE_USER: ${MYSQL_SERVICE_USER} + MYSQL_SERVICE_PASSWORD: ${MYSQL_SERVICE_PASSWORD} + NACOS_AUTH_IDENTITY_KEY: ${NACOS_AUTH_IDENTITY_KEY} + NACOS_AUTH_IDENTITY_VALUE: ${NACOS_AUTH_IDENTITY_VALUE} + NACOS_AUTH_TOKEN: ${NACOS_AUTH_TOKEN} + deploy: + replicas: 1 #部署时,指定部署一个副本 + placement: + constraints: [node.hostname == ZD-BAK-APP2] + restart_policy: + condition: on-failure + + nacos3: + image: nacos/nacos-server:${NACOS_VERSION} + hostname: ${NAMESPACE}-nacos3 + ports: + - ${NODE_PORT_31}:8848 + - ${NODE_PORT_32}:9848 + - ${NODE_PORT_33}:9849 + volumes: + - nacos_cluster_log:/home/nacos/logs #配置docker存储日志的卷 + environment: + MODE: cluster + PREFER_HOST_MODE: hostname + NACOS_SERVERS: ${NAMESPACE}-nacos1:8848 ${NAMESPACE}-nacos2:8848 ${NAMESPACE}-nacos3:8848 + NACOS_SERVER_PORT: 8848 + NACOS_AUTH_ENABLE: 'true' #1.2.0版本默认关闭登陆界面 + SPRING_DATASOURCE_PLATFORM: mysql + MYSQL_SERVICE_HOST: ${MYSQL_SERVICE_HOST} + MYSQL_SERVICE_DB_NAME: nacos + MYSQL_SERVICE_PORT: 3306 + MYSQL_SERVICE_USER: ${MYSQL_SERVICE_USER} + MYSQL_SERVICE_PASSWORD: ${MYSQL_SERVICE_PASSWORD} + NACOS_AUTH_IDENTITY_KEY: ${NACOS_AUTH_IDENTITY_KEY} + NACOS_AUTH_IDENTITY_VALUE: ${NACOS_AUTH_IDENTITY_VALUE} + NACOS_AUTH_TOKEN: ${NACOS_AUTH_TOKEN} + deploy: + replicas: 1 #部署时,指定部署一个副本 + placement: + constraints: [node.hostname == zd-bak-app3] + restart_policy: + condition: on-failure + +volumes: + nacos_cluster_log: + driver: local + + diff --git a/docker-swarm-review/nacos-cluser/env_crm1 b/docker-swarm-review/nacos-cluser/env_crm1 new file mode 100644 index 0000000..9db7fd8 --- /dev/null +++ b/docker-swarm-review/nacos-cluser/env_crm1 @@ -0,0 +1,5 @@ +NAMESPACE=crm1 +NACOS_VERSION=v2.2.2 +NODE_PORT=8848 +NODE_PORT_2=9848 +NACOS_SERVER_IP=192.168.1.209 \ No newline at end of file diff --git a/docker-swarm-review/nacos-cluser/env_review b/docker-swarm-review/nacos-cluser/env_review new file mode 100644 index 0000000..34c08b7 --- /dev/null +++ b/docker-swarm-review/nacos-cluser/env_review @@ -0,0 +1,17 @@ +NAMESPACE=review +NACOS_VERSION=v2.3.0 +NODE_PORT_11=21848 +NODE_PORT_12=22848 +NODE_PORT_13=22849 +NODE_PORT_21=23848 +NODE_PORT_22=24848 +NODE_PORT_23=24849 +NODE_PORT_31=25848 +NODE_PORT_32=26848 +NODE_PORT_33=26849 +MYSQL_SERVICE_HOST=review-tool-mysql-master +MYSQL_SERVICE_USER=zd_tool +MYSQL_SERVICE_PASSWORD=gkxl2024#@ +NACOS_AUTH_IDENTITY_KEY=nacos +NACOS_AUTH_IDENTITY_VALUE=gkxl2024#@ +NACOS_AUTH_TOKEN=OTg1NjRzZnJ0Z2RmZzIwMjQ1NTU1NTExZWZnZGVmZGVz \ No newline at end of file diff --git a/docker-swarm-review/nacos-cluser/mysql-schema.sql b/docker-swarm-review/nacos-cluser/mysql-schema.sql new file mode 100644 index 0000000..1aec30a --- /dev/null +++ b/docker-swarm-review/nacos-cluser/mysql-schema.sql @@ -0,0 +1,213 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/******************************************/ +/* 表名称 = config_info */ +/******************************************/ +CREATE TABLE `config_info` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) DEFAULT NULL COMMENT 'group_id', + `content` longtext NOT NULL COMMENT 'content', + `md5` varchar(32) DEFAULT NULL COMMENT 'md5', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + `src_user` text COMMENT 'source user', + `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', + `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', + `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', + `c_desc` varchar(256) DEFAULT NULL COMMENT 'configuration description', + `c_use` varchar(64) DEFAULT NULL COMMENT 'configuration usage', + `effect` varchar(64) DEFAULT NULL COMMENT '配置生效的描述', + `type` varchar(64) DEFAULT NULL COMMENT '配置的类型', + `c_schema` text COMMENT '配置的模式', + `encrypted_data_key` text NOT NULL COMMENT '密钥', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info'; + +/******************************************/ +/* 表名称 = config_info_aggr */ +/******************************************/ +CREATE TABLE `config_info_aggr` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) NOT NULL COMMENT 'group_id', + `datum_id` varchar(255) NOT NULL COMMENT 'datum_id', + `content` longtext NOT NULL COMMENT '内容', + `gmt_modified` datetime NOT NULL COMMENT '修改时间', + `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', + `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租户字段'; + + +/******************************************/ +/* 表名称 = config_info_beta */ +/******************************************/ +CREATE TABLE `config_info_beta` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) NOT NULL COMMENT 'group_id', + `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', + `content` longtext NOT NULL COMMENT 'content', + `beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps', + `md5` varchar(32) DEFAULT NULL COMMENT 'md5', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + `src_user` text COMMENT 'source user', + `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', + `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', + `encrypted_data_key` text NOT NULL COMMENT '密钥', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta'; + +/******************************************/ +/* 表名称 = config_info_tag */ +/******************************************/ +CREATE TABLE `config_info_tag` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) NOT NULL COMMENT 'group_id', + `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id', + `tag_id` varchar(128) NOT NULL COMMENT 'tag_id', + `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', + `content` longtext NOT NULL COMMENT 'content', + `md5` varchar(32) DEFAULT NULL COMMENT 'md5', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + `src_user` text COMMENT 'source user', + `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag'; + +/******************************************/ +/* 表名称 = config_tags_relation */ +/******************************************/ +CREATE TABLE `config_tags_relation` ( + `id` bigint(20) NOT NULL COMMENT 'id', + `tag_name` varchar(128) NOT NULL COMMENT 'tag_name', + `tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) NOT NULL COMMENT 'group_id', + `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id', + `nid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增长标识', + PRIMARY KEY (`nid`), + UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`), + KEY `idx_tenant_id` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation'; + +/******************************************/ +/* 表名称 = group_capacity */ +/******************************************/ +CREATE TABLE `group_capacity` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群', + `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值', + `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量', + `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值', + `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值', + `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值', + `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_group_id` (`group_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表'; + +/******************************************/ +/* 表名称 = his_config_info */ +/******************************************/ +CREATE TABLE `his_config_info` ( + `id` bigint(20) unsigned NOT NULL COMMENT 'id', + `nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增标识', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) NOT NULL COMMENT 'group_id', + `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', + `content` longtext NOT NULL COMMENT 'content', + `md5` varchar(32) DEFAULT NULL COMMENT 'md5', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + `src_user` text COMMENT 'source user', + `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', + `op_type` char(10) DEFAULT NULL COMMENT 'operation type', + `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', + `encrypted_data_key` text NOT NULL COMMENT '密钥', + PRIMARY KEY (`nid`), + KEY `idx_gmt_create` (`gmt_create`), + KEY `idx_gmt_modified` (`gmt_modified`), + KEY `idx_did` (`data_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造'; + + +/******************************************/ +/* 表名称 = tenant_capacity */ +/******************************************/ +CREATE TABLE `tenant_capacity` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID', + `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值', + `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量', + `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值', + `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数', + `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值', + `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_tenant_id` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表'; + + +CREATE TABLE `tenant_info` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `kp` varchar(128) NOT NULL COMMENT 'kp', + `tenant_id` varchar(128) default '' COMMENT 'tenant_id', + `tenant_name` varchar(128) default '' COMMENT 'tenant_name', + `tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc', + `create_source` varchar(32) DEFAULT NULL COMMENT 'create_source', + `gmt_create` bigint(20) NOT NULL COMMENT '创建时间', + `gmt_modified` bigint(20) NOT NULL COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`), + KEY `idx_tenant_id` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info'; + +CREATE TABLE `users` ( + `username` varchar(50) NOT NULL PRIMARY KEY COMMENT 'username', + `password` varchar(500) NOT NULL COMMENT 'password', + `enabled` boolean NOT NULL COMMENT 'enabled' +); + +CREATE TABLE `roles` ( + `username` varchar(50) NOT NULL COMMENT 'username', + `role` varchar(50) NOT NULL COMMENT 'role', + UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE +); + +CREATE TABLE `permissions` ( + `role` varchar(50) NOT NULL COMMENT 'role', + `resource` varchar(255) NOT NULL COMMENT 'resource', + `action` varchar(8) NOT NULL COMMENT 'action', + UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE +); + +INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE); + +INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN'); diff --git a/docker-swarm-review/nacos-cluser/standalone-derby.yml b/docker-swarm-review/nacos-cluser/standalone-derby.yml new file mode 100644 index 0000000..807c732 --- /dev/null +++ b/docker-swarm-review/nacos-cluser/standalone-derby.yml @@ -0,0 +1,38 @@ +version: '3.8' + +networks: + default: + name: ${NAMESPACE} + external: true +services: + server: + hostname: ${NAMESPACE}_nacos_server + image: nacos/nacos-server:${NACOS_VERSION} + environment: + - PREFER_HOST_MODE=hostname + - NACOS_SERVER_IP=${NACOS_SERVER_IP} + - MODE=standalone + - NACOS_AUTH_ENABLE=true + - NACOS_AUTH_IDENTITY_KEY=bndmsdsad + - NACOS_AUTH_IDENTITY_VALUE=wepqweq#dasld + - NACOS_AUTH_TOKEN=SecretKey012345678901234567890123456587012345678901234567890123456789 + ports: + - target: 8848 + published: ${NODE_PORT} + protocol: tcp + mode: host # 解析:默认是ingress就是通过swarm的负载均衡模式,无论通过集群节点的映射端口都能访问到业务容器,此种方式类似于k8s的NodePort的svc服务暴露方式,而host则属于,业务容器运行在哪个节点,则就通过节点地址+映射端口访问对应的业务容器。 + - target: 9848 + published: ${NODE_PORT_2} + protocol: tcp + mode: host + volumes: + - data_server:/home/nacos/ + deploy: + update_config: + order: stop-first + placement: + constraints: + - node.labels.${NAMESPACE}_nacos_server==1 +volumes: + data_server: + driver: local diff --git a/docker-swarm-review/nacos/README b/docker-swarm-review/nacos/README new file mode 100644 index 0000000..64e1c7c --- /dev/null +++ b/docker-swarm-review/nacos/README @@ -0,0 +1,5 @@ + +# crm1环境下 部署单机nacos + +env $(cat ./env_crm1 | xargs) envsubst < ./standalone-derby.yml | docker stack deploy --compose-file - crm1_nacos + diff --git a/docker-swarm-review/nacos/cluser.yml b/docker-swarm-review/nacos/cluser.yml new file mode 100644 index 0000000..92704a9 --- /dev/null +++ b/docker-swarm-review/nacos/cluser.yml @@ -0,0 +1,155 @@ +version: '3.8' + +services: + + + + nacos1: + container_name: nacos1 + image: nacos/nacos-server:latest + hostname: nacos1 + restart: always + ports: + - target: 8848 + published: 8848 + protocol: tcp + mode: host #采用host模式(默认为ingress,配置较灵活,根据自己的需求也可调整为ingress,本案例防止nacos 采用 swarm集群调度,所以改为host模式,两台服务器之间通过内网及nacos端口访问,通过nginx配置对外服务) + volumes: + - cluster1_logs:/home/nacos/logs #配置docker存储日志的卷 + environment: + MODE: cluster + PREFER_HOST_MODE: hostname + NACOS_SERVERS: 192.168.3.75:8848 192.168.3.94:8848 192.168.3.142:8848 + NACOS_SERVER_IP: 192.168.3.75 + NACOS_SERVER_PORT: 8848 + NACOS_AUTH_ENABLE: 'true' #1.2.0版本默认关闭登陆界面 + MYSQL_SERVICE_HOST: mysql + MYSQL_SERVICE_DB_NAME: nacos_devtest + MYSQL_SERVICE_PORT: 3306 + MYSQL_SERVICE_USER: nacos + MYSQL_SERVICE_PASSWORD: 123456 + deploy: + replicas: 1 #部署时,指定部署一个副本 + placement: + constraints: + - node.labels.env==docker-server-1 + restart_policy: + condition: on-failure + depends_on: + - mysql + networks: + - srm + + + + nacos2: + container_name: nacos2 + image: nacos/nacos-server:latest + restart: always + hostname: nacos2 + ports: + - target: 8848 + published: 8848 + protocol: tcp + mode: host + volumes: + - cluster2_logs:/home/nacos/logs + environment: + MODE: cluster + PREFER_HOST_MODE: hostname + NACOS_SERVERS: 192.168.3.75:8848 192.168.3.94:8848 192.168.3.142:8848 + NACOS_SERVER_IP: 192.168.3.94 + NACOS_SERVER_PORT: 8848 + NACOS_AUTH_ENABLE: 'true' + MYSQL_SERVICE_HOST: mysql + MYSQL_SERVICE_DB_NAME: nacos_devtest + MYSQL_SERVICE_PORT: 3306 + MYSQL_SERVICE_USER: nacos + MYSQL_SERVICE_PASSWORD: 123456 + deploy: + replicas: 1 + placement: + constraints: + - node.labels.env==docker-server-2 + restart_policy: + condition: on-failure + depends_on: + - mysql + networks: + - srm + + + + nacos3: + container_name: nacos3 + image: nacos/nacos-server:latest + restart: always + hostname: nacos3 + ports: + - target: 8848 + published: 8848 + protocol: tcp + mode: host + volumes: + - cluster3_logs:/home/nacos/logs + environment: + MODE: cluster + PREFER_HOST_MODE: hostname + NACOS_SERVERS: 192.168.3.75:8848 192.168.3.94:8848 192.168.3.142:8848 + NACOS_SERVER_IP: 192.168.3.142 + NACOS_SERVER_PORT: 8848 + NACOS_AUTH_ENABLE: 'true' + MYSQL_SERVICE_HOST: mysql + MYSQL_SERVICE_DB_NAME: nacos_devtest + MYSQL_SERVICE_PORT: 3306 + MYSQL_SERVICE_USER: nacos + MYSQL_SERVICE_PASSWORD: 123456 + deploy: + replicas: 1 + placement: + constraints: + - node.labels.env==docker-server-3 + restart_policy: + condition: on-failure + depends_on: + - mysql + networks: + - srm + + + mysql: + image: mysql:5.7.33 + restart: always + container_name: mysql + hostname: mysql + ports: + - 3306:3306 + volumes: + - /data/software/nacos/mysql/data:/var/lib/mysql + - /etc/localtime:/etc/localtime:ro + - /etc/my.cnf:/etc/mysql/mysql.conf.d/my.cnf + environment: + TZ: Asia/Shanghai + MYSQL_ROOT_PASSWORD: sonar + MYSQL_DATABASE: nacos_devtest + deploy: + replicas: 1 + placement: + constraints: + - node.labels.env==docker-server-1 + restart_policy: + condition: on-failure + networks: + - srm + +volumes: + cluster1_logs: + cluster2_logs: + cluster3_logs: + + +networks: + srm: + external: true + +#https://blog.51cto.com/u_12898848/4054447 \ No newline at end of file diff --git a/docker-swarm-review/nacos/env_crm1 b/docker-swarm-review/nacos/env_crm1 new file mode 100644 index 0000000..9db7fd8 --- /dev/null +++ b/docker-swarm-review/nacos/env_crm1 @@ -0,0 +1,5 @@ +NAMESPACE=crm1 +NACOS_VERSION=v2.2.2 +NODE_PORT=8848 +NODE_PORT_2=9848 +NACOS_SERVER_IP=192.168.1.209 \ No newline at end of file diff --git a/docker-swarm-review/nacos/standalone-derby.yml b/docker-swarm-review/nacos/standalone-derby.yml new file mode 100644 index 0000000..807c732 --- /dev/null +++ b/docker-swarm-review/nacos/standalone-derby.yml @@ -0,0 +1,38 @@ +version: '3.8' + +networks: + default: + name: ${NAMESPACE} + external: true +services: + server: + hostname: ${NAMESPACE}_nacos_server + image: nacos/nacos-server:${NACOS_VERSION} + environment: + - PREFER_HOST_MODE=hostname + - NACOS_SERVER_IP=${NACOS_SERVER_IP} + - MODE=standalone + - NACOS_AUTH_ENABLE=true + - NACOS_AUTH_IDENTITY_KEY=bndmsdsad + - NACOS_AUTH_IDENTITY_VALUE=wepqweq#dasld + - NACOS_AUTH_TOKEN=SecretKey012345678901234567890123456587012345678901234567890123456789 + ports: + - target: 8848 + published: ${NODE_PORT} + protocol: tcp + mode: host # 解析:默认是ingress就是通过swarm的负载均衡模式,无论通过集群节点的映射端口都能访问到业务容器,此种方式类似于k8s的NodePort的svc服务暴露方式,而host则属于,业务容器运行在哪个节点,则就通过节点地址+映射端口访问对应的业务容器。 + - target: 9848 + published: ${NODE_PORT_2} + protocol: tcp + mode: host + volumes: + - data_server:/home/nacos/ + deploy: + update_config: + order: stop-first + placement: + constraints: + - node.labels.${NAMESPACE}_nacos_server==1 +volumes: + data_server: + driver: local diff --git a/docker-swarm-review/nginx-proxy-simple/crm1.conf b/docker-swarm-review/nginx-proxy-simple/crm1.conf new file mode 100644 index 0000000..5a249b9 --- /dev/null +++ b/docker-swarm-review/nginx-proxy-simple/crm1.conf @@ -0,0 +1,68 @@ + +server { + listen 8080; + server_name api1.sino-assist.com api2.sino-assist.com; + + location / { + proxy_pass http://ss208_sa-gateway_svc:8080/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location ~ .*actuator.* { + deny all; + } + +} + + +server { + listen 8080; + server_name crm1.sino-assist.com crm2.sino-assist.com; + + location / { + proxy_pass http://crm1_ss_sa-cc_svc:8080/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + } + + location /nacos/ { + proxy_pass http://crm1_nacos_server:8848/nacos/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location /xxl-job-admin { + proxy_pass http://crm1_xxl_job_server:8080/xxl-job-admin; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + } + + location /boot-admin/ { + proxy_pass http://ss209_boot-admin_svc:8080/boot-admin/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + + } + location /mq/ { + proxy_pass http://crm1_rabbitmq_stats:15672/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + } +} \ No newline at end of file diff --git a/docker-swarm-review/nginx-proxy-simple/deploy.sh b/docker-swarm-review/nginx-proxy-simple/deploy.sh new file mode 100644 index 0000000..24de61e --- /dev/null +++ b/docker-swarm-review/nginx-proxy-simple/deploy.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +# 在 192.168.3.132 上执行 + +# 1. 创建配置目录 +mkdir -p /data/nginx-proxy + +# 2. 写入 nginx 配置 +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; + + 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; + } + + # 后端网关 + 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 + +# 3. 启动 nginx 容器 +docker run -d \ + --name nginx-proxy \ + --restart always \ + -p 8080:8080 \ + -v /data/nginx-proxy/nginx.conf:/etc/nginx/nginx.conf:ro \ + nginx:alpine diff --git a/docker-swarm-review/nginx-proxy-simple/docker-compose.yml b/docker-swarm-review/nginx-proxy-simple/docker-compose.yml new file mode 100644 index 0000000..47205a7 --- /dev/null +++ b/docker-swarm-review/nginx-proxy-simple/docker-compose.yml @@ -0,0 +1,26 @@ +version: '3.8' + +services: + nginx-proxy: + image: nginx:alpine + ports: + - '8080:8080' + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + environment: + - TZ=Asia/Shanghai + deploy: + mode: replicated + replicas: 1 + restart_policy: + condition: on-failure + delay: 5s + max_attempts: 3 + placement: + constraints: + - node.labels.review_nginx_proxy==1 + +networks: + default: + name: review + external: true diff --git a/docker-swarm-review/nginx-proxy-simple/nginx.conf b/docker-swarm-review/nginx-proxy-simple/nginx.conf new file mode 100644 index 0000000..b4e163f --- /dev/null +++ b/docker-swarm-review/nginx-proxy-simple/nginx.conf @@ -0,0 +1,35 @@ +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; + + 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"; + } + } +} diff --git a/docker-swarm-review/nginx-proxy-simple/review.conf b/docker-swarm-review/nginx-proxy-simple/review.conf new file mode 100644 index 0000000..617b61f --- /dev/null +++ b/docker-swarm-review/nginx-proxy-simple/review.conf @@ -0,0 +1,68 @@ + +server { + listen 8080; + server_name apireview.sino-assist.com; + + location / { + proxy_pass http://ss132_sa-gateway_svc:8080/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location ~ .*actuator.* { + deny all; + } + +} + + +server { + listen 8080; + server_name ccreview.sino-assist.com; + + location / { + proxy_pass http://review_ss_sa-cc_svc:8080/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + } + + location /nacos/ { + proxy_pass http://review_nacos_nacos1:8848/nacos/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location /xxl-job-admin { + proxy_pass http://review_xxl_job_server:8080/xxl-job-admin; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + } + + location /boot-admin/ { + proxy_pass http://ss132_boot-admin_svc:8080/boot-admin/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + + } + location /mq/ { + proxy_pass http://review_rabbitmq_stats:15672/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + } +} diff --git a/docker-swarm-review/nginx-proxy-simple/review_temp.conf b/docker-swarm-review/nginx-proxy-simple/review_temp.conf new file mode 100644 index 0000000..eca930f --- /dev/null +++ b/docker-swarm-review/nginx-proxy-simple/review_temp.conf @@ -0,0 +1,126 @@ +server { + listen 80; + server_name crm1.sino-assist.com api1.sino-assist.com api-sit.sino-assist.com; + + include ssl.sino_assist.conf; + + + # 中道汽车救援公众号 + location /MP_verify_TyW3WkUF0gacMB4m.txt { + default_type text/html; + return 200 "TyW3WkUF0gacMB4m"; + } + + # 中道汽车服务 服务号 + location /MP_verify_WjQInvWDvPvfZvL0.txt { + default_type text/html; + return 200 "WjQInvWDvPvfZvL0"; + } + + + + + location / { + + proxy_pass http://192.168.1.209:8080/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For + $proxy_add_x_forwarded_for; + + if ($request_filename ~ .*\.(htm|html)$) + { + add_header Cache-Control no-cache; + } + } + + location /h5/supplier/dispatch { + proxy_pass http://192.168.1.209:8031/h5/supplier/dispatch; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For + $proxy_add_x_forwarded_for; + client_max_body_size 200m; + } + + location ^~ /dev/h5/rescue { + rewrite ^/dev/h5/rescue/(.*)$ /h5/client/$1 break; # [2,5](@ref) + + proxy_pass http://192.168.1.209:8032; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For + $proxy_add_x_forwarded_for; + client_max_body_size 200m; + } + + + + location /h5/client/ { + + proxy_pass http://192.168.1.209:8032/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For + $proxy_add_x_forwarded_for; + client_max_body_size 200m; + } + location /dev/h5/rentCar { + + proxy_pass http://192.168.1.209:8034/dev/h5/rentCar; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For + $proxy_add_x_forwarded_for; + client_max_body_size 200m; + } + + +} + +server { + listen 80; + server_name portainer.sino-assist.com; + + include ssl.sino_assist.conf; + + location / { + + proxy_pass http://192.168.1.209:9000/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For + $proxy_add_x_forwarded_for; + if ($request_filename ~ .*\.(htm|html)$) + { + add_header Cache-Control no-cache; + } + } +} + +server { + listen 80; + server_name oem-jlr.sino-assist.com; + + include ssl.sino_assist.conf; + + location / { + + proxy_pass http://192.168.1.226:5868/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For + $proxy_add_x_forwarded_for; + if ($request_filename ~ .*\.(htm|html)$) + { + add_header Cache-Control no-cache; + } + } +} diff --git a/docker-swarm-review/nginx-review-132/README b/docker-swarm-review/nginx-review-132/README new file mode 100644 index 0000000..298139b --- /dev/null +++ b/docker-swarm-review/nginx-review-132/README @@ -0,0 +1,10 @@ + +# crm1环境下 部署nginx + +env $(cat ./env_crm1 | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - crm1_nginx + + + +# review环境下 部署nginx + +env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_nginx diff --git a/docker-swarm-review/nginx-review-132/docker-compose.yml b/docker-swarm-review/nginx-review-132/docker-compose.yml new file mode 100644 index 0000000..d979b2e --- /dev/null +++ b/docker-swarm-review/nginx-review-132/docker-compose.yml @@ -0,0 +1,58 @@ +version: '3.8' + +networks: + default: + name: ${NAMESPACE} + external: true +services: + server1: + image: 'docker.io/bitnami/nginx:1.24' + ports: + - mode: host + protocol: tcp + published: 8180 + target: 8080 + - mode: host + protocol: tcp + published: 8143 + target: 8443 + environment: + - TZ=Asia/Shanghai + volumes: + - /opt/logs/nginx/:/opt/bitnami/nginx/logs/ + configs: + - source: nginx_conf + target: /opt/bitnami/nginx/conf/nginx.conf + - source: nginx_ssl_sinoassist_config + target: /opt/bitnami/nginx/conf/ssl.sinoassist.conf + - source: ssl_sinoassist_key + target: /opt/bitnami/nginx/conf/server_blocks/sinoassist.com.key + - source: ssl_sinoassist_pem + target: /opt/bitnami/nginx/conf/server_blocks/sinoassist.com.pem + - source: nginx_review_config + target: /opt/bitnami/nginx/conf/server_blocks/review_temp.conf + + deploy: + mode: replicated + replicas: 1 + update_config: + order: start-first + placement: + constraints: + - node.hostname==ZD-BAK-APP2 +configs: + nginx_conf: + external: true + name: nginx_conf_v1 + nginx_ssl_sinoassist_config: + external: true + name: nginx_ssl_sinoassist_conf_v1 + nginx_review_config: + external: true + name: nginx_review_config_v2 + ssl_sinoassist_key: + external: true + name: ssl_sinoassist_key_2024 + ssl_sinoassist_pem: + external: true + name: ssl_sinoassist_pem_2024 diff --git a/docker-swarm-review/nginx-review-132/env_crm1 b/docker-swarm-review/nginx-review-132/env_crm1 new file mode 100644 index 0000000..c192c5a --- /dev/null +++ b/docker-swarm-review/nginx-review-132/env_crm1 @@ -0,0 +1,4 @@ +NAMESPACE=crm1 +NODE_PORT=8080 +CUSTOME_CONFIG=nginx_conf_v1 +CUSTOME_CONFIG=nginx_conf_v1 \ No newline at end of file diff --git a/docker-swarm-review/nginx-review-132/env_review b/docker-swarm-review/nginx-review-132/env_review new file mode 100644 index 0000000..7b5a858 --- /dev/null +++ b/docker-swarm-review/nginx-review-132/env_review @@ -0,0 +1 @@ +NAMESPACE=review diff --git a/docker-swarm-review/nginx-review-132/nginx.conf b/docker-swarm-review/nginx-review-132/nginx.conf new file mode 100644 index 0000000..a56bf62 --- /dev/null +++ b/docker-swarm-review/nginx-review-132/nginx.conf @@ -0,0 +1,60 @@ +# Based on https://www.nginx.com/resources/wiki/start/topics/examples/full/#nginx-conf +user www www; ## Default: nobody + +worker_processes auto; +error_log "/opt/bitnami/nginx/logs/error.log"; +pid "/opt/bitnami/nginx/tmp/nginx.pid"; + +events { + worker_connections 1024; +} + +http { + include mime.types; + default_type application/octet-stream; + log_format main '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log "/opt/bitnami/nginx/logs/access.log" main; + add_header X-Frame-Options SAMEORIGIN; + + client_body_temp_path "/opt/bitnami/nginx/tmp/client_body" 1 2; + proxy_temp_path "/opt/bitnami/nginx/tmp/proxy" 1 2; + fastcgi_temp_path "/opt/bitnami/nginx/tmp/fastcgi" 1 2; + scgi_temp_path "/opt/bitnami/nginx/tmp/scgi" 1 2; + uwsgi_temp_path "/opt/bitnami/nginx/tmp/uwsgi" 1 2; + + sendfile on; + tcp_nopush on; + tcp_nodelay off; + gzip on; + gzip_http_version 1.0; + gzip_comp_level 2; + gzip_proxied any; + gzip_types text/plain text/css application/javascript text/xml application/xml+rss; + keepalive_timeout 65; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5:!DES:!3DES; + client_max_body_size 105M; + server_tokens off; + + absolute_redirect off; + port_in_redirect off; + + include "/opt/bitnami/nginx/conf/server_blocks/*.conf"; + + # # HTTP Server + # server { + # # Port to listen on, can also be set in IP:PORT format + # listen 80; + + # include "/opt/bitnami/nginx/conf/bitnami/*.conf"; + + # location /status { + # stub_status on; + # access_log off; + # allow 127.0.0.1; + # deny all; + # } + # } +} \ No newline at end of file diff --git a/docker-swarm-review/nginx-review-132/nginx_review_config_v1 b/docker-swarm-review/nginx-review-132/nginx_review_config_v1 new file mode 100644 index 0000000..129c9db --- /dev/null +++ b/docker-swarm-review/nginx-review-132/nginx_review_config_v1 @@ -0,0 +1,282 @@ + +#### 中道review环境开始 #### + +upstream api.zhongdao { + server ss52_sa-gateway_svc:8080; + server ss53_sa-gateway_svc:8080; +} + +# 中道外部接口 +server { + listen 8080; + server_name api.sinoassist.net api.sinoassist.com xcx-api.sinoassist.com interface.review.sino-assist.com; + include /opt/bitnami/nginx/conf/ssl.sinoassist.conf; + + + location / { + proxy_pass http://api.zhongdao; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location ~ .*actuator.* { + deny all; + } +} + + +# 接口 +server { + listen 8080; + server_name api-nj.do-dec.com api-cd.do-dec.com api-wh.do-dec.com api-hz.do-dec.com api-sh.do-dec.com; + + location / { + proxy_pass http://api.zhongdao; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location ~ .*actuator.* { + deny all; + } +} + + +server { + server_name site.sinoassist.com; + include /opt/bitnami/nginx/conf/ssl.sinoassist.conf; + + + location / { + root /zd/cc-site/dist/; + index index.html index.htm; + try_files $uri $uri/ /index.html; + if ($request_filename ~ .*\.(htm|html)$) + { + add_header Cache-Control no-cache; + } + } +} + + +server { + server_name www.sinoassist.com; + include /opt/bitnami/nginx/conf/ssl.sinoassist.conf; + + location /h5/rescue { + alias /zd/rescue-h5/dist/; + try_files $uri $uri/ /h5/rescue/index.html; + index index.html; + if ($request_filename ~ .*\.(htm|html)$) + { + add_header Cache-Control no-cache; + } + } + + location /dev/h5/rescue { + proxy_pass http://192.168.1.209:8030/dev/h5/rescue; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For + $proxy_add_x_forwarded_for; + client_max_body_size 200m; + } + + location /h5/client/ { + alias /zd/sino-client-h5/dist/build/h5/; + index index.html; + if ($request_filename ~ .*\.(htm|html)$) + { + add_header Cache-Control no-cache; + } + } + + + + location /h5/supplier/dispatch { + alias /zd/supplier-dispatch-h5/dist/; + try_files $uri $uri/ /h5/supplier/dispatch/index.html; + index index.html; + if ($request_filename ~ .*\.(htm|html)$) + { + add_header Cache-Control no-cache; + } + } + + + # 太科app 海豚湾 微信验证配置 + + location /FowqINu4W1.txt { + default_type text/html; + return 200 "90d7811c9e948fe95df1fd46ca3c1984"; + } + + location /HQgOV1DbaM.txt { + default_type text/html; + return 200 "91aad82c4fadf3b6b4843771561dac64"; + } + location /pay/gateway/ { + proxy_pass http://192.168.3.121:9226/pay/gateway/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For + $proxy_add_x_forwarded_for; + client_max_body_size 200m; + } + + + location /pay/gateway/api/ { + proxy_next_upstream http_502 http_504 error timeout invalid_header; + 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_pass http://192.168.3.121:9216/api/; + # 启用支持websocket连接 + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + location / { + proxy_pass http://review_ss_sa-cc_svc:8080/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location ~ ^/(export-app|common|order|supplier|contract|base) { + proxy_pass http://api.zhongdao; + } +} + + + +## 前端http强制转https +server { + listen 8080; + + server_name www.sinoassist.com; + add_header Strict-Transport-Security max-age=15768000; + return 301 https://www.sinoassist.com$request_uri; + +} + +#### 中道review环境结束 #### + +## 公司其他域名的切换 + +# 4s店微信 +server { + listen 8080; + server_name wx4s.sinoassist.com; + + location / { + proxy_pass http://192.168.10.7:8777; + } +} + + +# 微信供应商 +server { + listen 8080; + server_name wxdd.sinoassist.com; + + location / { + proxy_pass http://192.168.10.7:8568; + } +} + +# 呼叫中心接口 +server { + listen 8080; + server_name apicc.sinoassist.com; + include /opt/bitnami/nginx/conf/ssl.sinoassist.conf; + + location / { + proxy_pass http://192.168.5.201:8080; + } +} + +# 呼叫中心接口websocket +server { + listen 8080; + server_name apiccws.sinoassist.com; + include /opt/bitnami/nginx/conf/ssl.sinoassist.conf; + + location / { + proxy_pass http://192.168.5.201:1884; + } +} + + +## 救援生产环境其他服务域名切换 + + +## rabbitmq stomp +upstream stomp.zhongdao { + server review_rabbitmq_queue1:15674; + server review_rabbitmq_queue2:15674; + server review_rabbitmq_stats:15674; +} + +server { + listen 8080; + include /opt/bitnami/nginx/conf/ssl.sinoassist.conf; + server_name stomp.sinoassist.com; + + location / { + proxy_pass http://stomp.zhongdao; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} + + +# 备份文件服务器 +server { + listen 8080; + server_name file.sinoassist.com; + include /opt/bitnami/nginx/conf/ssl.sinoassist.conf; + + # 录音文件服务器 + location /ly/ { + proxy_pass http://192.168.5.204:8088/; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For + $proxy_add_x_forwarded_for; + if ($request_filename ~ .*\.(htm|html)$) + { + add_header Cache-Control no-cache; + } + + } + + # 备份文件服务器 + location / { + proxy_pass http://192.168.10.18:8888; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For + $proxy_add_x_forwarded_for; + if ($request_filename ~ .*\.(htm|html)$) + { + add_header Cache-Control no-cache; + } + + } +} diff --git a/docker-swarm-review/nginx-review-132/nginx_ssl_sinoassist_conf_v1 b/docker-swarm-review/nginx-review-132/nginx_ssl_sinoassist_conf_v1 new file mode 100644 index 0000000..3db0e3c --- /dev/null +++ b/docker-swarm-review/nginx-review-132/nginx_ssl_sinoassist_conf_v1 @@ -0,0 +1,11 @@ +listen 8443 ssl; + +ssl_certificate /opt/bitnami/nginx/conf/server_blocks/sinoassist.com.pem; + +ssl_certificate_key /opt/bitnami/nginx/conf/server_blocks/sinoassist.com.key; + +ssl_protocols TLSv1 TLSv1.1 TLSv1.2; +ssl_ciphers HIGH:!aNULL:!MD5:!DES:!3DES; +ssl_prefer_server_ciphers on; +ssl_session_cache shared:SSL:10m; +ssl_session_timeout 10m; \ No newline at end of file diff --git a/docker-swarm-review/nginx-review-132/swarm/docker-compose.yml b/docker-swarm-review/nginx-review-132/swarm/docker-compose.yml new file mode 100644 index 0000000..d4201f4 --- /dev/null +++ b/docker-swarm-review/nginx-review-132/swarm/docker-compose.yml @@ -0,0 +1,59 @@ +version: "3.8" + +services: + nginx-review: + image: nginx:alpine + ports: + - target: 80 + published: 80 + protocol: tcp + mode: host + - target: 443 + published: 443 + protocol: tcp + mode: host + - target: 18888 + published: 18888 + protocol: tcp + mode: host + - target: 18889 + published: 18889 + protocol: tcp + mode: host + - target: 1180 + published: 1180 + protocol: tcp + mode: host + - target: 8888 + published: 8888 + protocol: tcp + mode: host + - target: 38888 + published: 38888 + protocol: tcp + mode: host + environment: + - TZ=Asia/Shanghai + volumes: + - /data/nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - /data/nginx/sites:/etc/nginx/sites:ro + - /data/nginx/ssl.sino_assist.conf:/etc/nginx/ssl.sino_assist.conf:ro + - /data/nginx/ssl.conf:/etc/nginx/ssl.conf:ro + - /data/nginx/ssl:/etc/nginx/ssl:ro + - /opt/logs/nginx:/var/log/nginx + deploy: + replicas: 1 + placement: + constraints: + - node.hostname == ZD-BAK-APP2 + restart_policy: + condition: on-failure + update_config: + parallelism: 1 + delay: 10s + networks: + - review + +networks: + review: + external: true diff --git a/docker-swarm-review/nginx-review-132/swarm/logrotate-nginx b/docker-swarm-review/nginx-review-132/swarm/logrotate-nginx new file mode 100644 index 0000000..90611d0 --- /dev/null +++ b/docker-swarm-review/nginx-review-132/swarm/logrotate-nginx @@ -0,0 +1,14 @@ +/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 +} diff --git a/docker-swarm-review/nginx-review-132/swarm/nginx.conf b/docker-swarm-review/nginx-review-132/swarm/nginx.conf new file mode 100644 index 0000000..8d7531f --- /dev/null +++ b/docker-swarm-review/nginx-review-132/swarm/nginx.conf @@ -0,0 +1,73 @@ +worker_processes auto; + +events { + worker_connections 10240; +} + +http { + include mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + error_log /var/log/nginx/error.log warn; + + client_header_buffer_size 512k; + large_client_header_buffers 4 512k; + client_max_body_size 500m; + + sendfile on; + server_tokens off; + + gzip on; + gzip_min_length 1k; + gzip_comp_level 3; + gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css; + gzip_disable "MSIE [1-6]\."; + gzip_vary on; + + keepalive_timeout 6500; + + proxy_connect_timeout 6000; + proxy_read_timeout 6000; + proxy_send_timeout 6000; + + 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:拦截无匹配请求 + server { + listen 80 default_server; + location / { + return 404; + } + } + + include /etc/nginx/sites/*.conf; +} + +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; + } +} diff --git a/docker-swarm-review/nginx-review-132/swarm/sites/crm1.conf b/docker-swarm-review/nginx-review-132/swarm/sites/crm1.conf new file mode 100644 index 0000000..131ff28 --- /dev/null +++ b/docker-swarm-review/nginx-review-132/swarm/sites/crm1.conf @@ -0,0 +1,95 @@ +server { + listen 80; + server_name crm1.sino-assist.com api1.sino-assist.com api-sit.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + # 中道汽车救援公众号 + location /MP_verify_TyW3WkUF0gacMB4m.txt { + default_type text/html; + return 200 "TyW3WkUF0gacMB4m"; + } + + # 中道汽车服务服务号 + location /MP_verify_WjQInvWDvPvfZvL0.txt { + default_type text/html; + return 200 "WjQInvWDvPvfZvL0"; + } + + location / { + proxy_pass http://192.168.1.209:8080/; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } + + location /h5/supplier/dispatch { + proxy_pass http://192.168.1.209:8031/h5/supplier/dispatch; + proxy_set_header X-Forwarded-Host $server_name; + client_max_body_size 200m; + } + + location ^~ /dev/h5/rescue { + rewrite ^/dev/h5/rescue/(.*)$ /h5/client/$1 break; + proxy_pass http://192.168.1.209:8032; + proxy_set_header X-Forwarded-Host $server_name; + client_max_body_size 200m; + } + + location ^~ /dev/h5/rvc { + rewrite ^/dev/h5/rvc/(.*)$ /h5/rvc/$1 break; + proxy_pass http://192.168.1.209:8035; + proxy_set_header X-Forwarded-Host $server_name; + client_max_body_size 200m; + } + + location /h5/rvc/ { + proxy_pass http://192.168.1.209:8035/; + proxy_set_header X-Forwarded-Host $server_name; + client_max_body_size 200m; + } + + location /h5/client/ { + proxy_pass http://192.168.1.209:8032/; + proxy_set_header X-Forwarded-Host $server_name; + client_max_body_size 200m; + } + + location /dev/h5/rentCar { + proxy_pass http://192.168.1.209:8034/dev/h5/rentCar; + proxy_set_header X-Forwarded-Host $server_name; + client_max_body_size 200m; + } +} + +server { + listen 80; + server_name portainer.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location / { + proxy_pass http://192.168.1.209:9000/; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} + +server { + listen 80; + server_name oem-jlr.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location / { + proxy_pass http://192.168.1.226:5868/; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} diff --git a/docker-swarm-review/nginx-review-132/swarm/sites/crm2.conf b/docker-swarm-review/nginx-review-132/swarm/sites/crm2.conf new file mode 100644 index 0000000..6d92cba --- /dev/null +++ b/docker-swarm-review/nginx-review-132/swarm/sites/crm2.conf @@ -0,0 +1,75 @@ +server { + listen 80; + server_name api2.sino-assist.com interface.crm2.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location /oss/minio/ { + proxy_set_header Host $http_host; + proxy_set_header REMOTE-HOST $remote_addr; + proxy_pass http://192.168.3.132:28773/oss/minio/; + } + + location ~ .*actuator.* { + deny all; + } + + location / { + proxy_pass http://192.168.1.209:8080; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} + +server { + listen 80; + server_name test-api-sh.do-dec.com test-api-nj.do-dec.com test-api-cd.do-dec.com test-api-wh.do-dec.com test-api-hz.do-dec.com; + + location / { + proxy_pass http://192.168.1.209:8080; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} + +# cc.crm2 前端(https) +server { + include /etc/nginx/ssl.sino_assist.conf; + server_name cc.crm2.sino-assist.com crm2.sino-assist.com; + + location / { + proxy_pass http://192.168.1.209:8081; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} + +# http → https 跳转 +server { + listen 80; + server_name cc.crm2.sino-assist.com crm2.sino-assist.com; + return 301 https://$server_name$request_uri; +} + +server { + listen 80; + server_name stomp2.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location / { + proxy_pass http://192.168.1.209:15674; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_http_version 1.1; + } +} diff --git a/docker-swarm-review/nginx-review-132/swarm/sites/fastdfs.conf b/docker-swarm-review/nginx-review-132/swarm/sites/fastdfs.conf new file mode 100644 index 0000000..cc91e27 --- /dev/null +++ b/docker-swarm-review/nginx-review-132/swarm/sites/fastdfs.conf @@ -0,0 +1,27 @@ +server { + listen 18888; + listen 18889 ssl; + server_name file-gk.sino-assist.com; + + include /etc/nginx/ssl.conf; + + location /n1/ { + proxy_pass http://192.168.3.125:8080/; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } + + location / { + proxy_pass http://192.168.3.119:8888; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; + add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization'; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} diff --git a/docker-swarm-review/nginx-review-132/swarm/sites/git.conf b/docker-swarm-review/nginx-review-132/swarm/sites/git.conf new file mode 100644 index 0000000..3815380 --- /dev/null +++ b/docker-swarm-review/nginx-review-132/swarm/sites/git.conf @@ -0,0 +1,242 @@ +server { + listen 80; + server_name jira.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location / { + proxy_pass http://192.168.3.129:8018/; + add_header Access-Control-Allow-Origin *; + add_header Access-Control-Allow-Headers X-Requested-With; + add_header Access-Control-Allow-Methods GET,POST,OPTIONS; + proxy_redirect off; + client_max_body_size 10m; + client_body_buffer_size 128k; + proxy_buffers 32 4k; + proxy_connect_timeout 600; + proxy_send_timeout 600; + proxy_read_timeout 600; + } +} + +server { + listen 80; + server_name itsm.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location /WW_verify_TWp375Kzd79EfP0l.txt { + default_type text/html; + return 200 "TWp375Kzd79EfP0l"; + } + + location / { + proxy_pass http://192.168.3.200:8013/; + add_header Access-Control-Allow-Origin *; + add_header Access-Control-Allow-Headers X-Requested-With; + add_header Access-Control-Allow-Methods GET,POST,OPTIONS; + proxy_redirect off; + client_max_body_size 10m; + client_body_buffer_size 128k; + proxy_buffers 32 4k; + proxy_connect_timeout 600; + proxy_send_timeout 600; + proxy_read_timeout 600; + } +} + +server { + listen 80; + server_name wiki.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location / { + proxy_pass http://192.168.1.101:8081; + proxy_set_header X-Forwarded-Host $server_name; + client_max_body_size 200m; + proxy_read_timeout 86400s; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_buffering off; + proxy_cache off; + } +} + +server { + listen 80; + server_name vote.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location / { + proxy_pass http://192.168.3.124:8089; + proxy_set_header X-Forwarded-Host $server_name; + client_max_body_size 200m; + proxy_read_timeout 86400s; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_buffering off; + proxy_cache off; + } +} + +server { + listen 80; + server_name jira.sinoassist.com; + + location / { + proxy_pass http://192.168.3.129:10880/; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} + +server { + listen 1180; + + location / { + proxy_pass http://192.168.3.140:8018/; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} + +server { + listen 80; + server_name harbor.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location / { + proxy_pass http://192.168.3.129:8082/; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} + +server { + listen 80; + server_name docker-mirror.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location / { + proxy_pass http://192.168.3.129:5000/; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} + +server { + listen 80; + server_name git.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location / { + proxy_pass http://192.168.3.129:3000/; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} + +server { + listen 80; + server_name maven.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location / { + proxy_pass http://192.168.3.129:8081/; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} + +server { + listen 80; + server_name api-doc.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location / { + proxy_pass http://192.168.3.129:7700/; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} + +server { + listen 80; + server_name hr.sinoassist.com; + + include /etc/nginx/ssl.conf; + + location / { + proxy_pass http://192.168.3.200:8012; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} + +server { + listen 80; + server_name training.sino-assist.com; + + include /etc/nginx/ssl.conf; + + location / { + proxy_pass http://192.168.1.226:8090; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} + +server { + listen 80; + server_name csc.sino-assist.com; + + location / { + proxy_pass http://192.168.1.161:8011; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} + +server { + listen 80; + server_name file.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location / { + proxy_pass http://192.168.1.171:8088/; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} diff --git a/docker-swarm-review/nginx-review-132/swarm/sites/oss.conf b/docker-swarm-review/nginx-review-132/swarm/sites/oss.conf new file mode 100644 index 0000000..e4895fd --- /dev/null +++ b/docker-swarm-review/nginx-review-132/swarm/sites/oss.conf @@ -0,0 +1,14 @@ +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; + } + } +} diff --git a/docker-swarm-review/nginx-review-132/swarm/sites/pay.conf b/docker-swarm-review/nginx-review-132/swarm/sites/pay.conf new file mode 100644 index 0000000..3ca5ec2 --- /dev/null +++ b/docker-swarm-review/nginx-review-132/swarm/sites/pay.conf @@ -0,0 +1,70 @@ +server { + listen 80; + server_name pay.sinoassist.com; + + include /etc/nginx/ssl.conf; + + location /MP_verify_WjQInvWDvPvfZvL0.txt { + default_type text/html; + return 200 "WjQInvWDvPvfZvL0"; + } + + location /api/ { + proxy_next_upstream http_502 http_504 error timeout invalid_header; + proxy_pass http://192.168.3.125:9216; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + location / { + proxy_pass http://192.168.3.125:9226; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} + +server { + listen 80; + server_name pay-manager.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location /api/ { + proxy_next_upstream http_502 http_504 error timeout invalid_header; + proxy_pass http://192.168.3.125:9217; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + location / { + proxy_pass http://192.168.3.125:9227; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} + +server { + listen 80; + server_name pay-client.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location /api/ { + proxy_next_upstream http_502 http_504 error timeout invalid_header; + proxy_pass http://192.168.3.125:9218; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + location / { + proxy_pass http://192.168.3.125:9228; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} diff --git a/docker-swarm-review/nginx-review-132/swarm/sites/review.conf b/docker-swarm-review/nginx-review-132/swarm/sites/review.conf new file mode 100644 index 0000000..d3cd585 --- /dev/null +++ b/docker-swarm-review/nginx-review-132/swarm/sites/review.conf @@ -0,0 +1,80 @@ +# review 后端接口(原经 3.110 → 3.132:18092 中转,现直连网关) +server { + listen 80; + server_name interface.review.sino-assist.com apireview.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location / { + proxy_pass http://192.168.3.132:28092; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } + + location ~ .*actuator.* { + deny all; + } +} + +# review 前端 +server { + include /etc/nginx/ssl.sino_assist.conf; + server_name cc.review.sino-assist.com ccreview.sino-assist.com; + + location /MP_verify_TyW3WkUF0gacMB4m.txt { + default_type text/html; + return 200 "TyW3WkUF0gacMB4m"; + } + + location /MP_verify_WjQInvWDvPvfZvL0.txt { + default_type text/html; + return 200 "WjQInvWDvPvfZvL0"; + } + + location /dev/h5/rescue/ { + proxy_pass http://192.168.1.209:8032/; + proxy_set_header X-Forwarded-Host $server_name; + client_max_body_size 200m; + } + + location /m/ { + return 301 https://api1.sino-assist.com/base/shortUrlMappings/$request_uri; + } + + location /m/dev- { + return 301 https://api.sinoassist.com/base/shortUrlMappings/$request_uri; + } + + location / { + proxy_pass http://192.168.3.132:8081; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} + +# http → https 跳转 +server { + listen 80; + server_name cc.review.sino-assist.com ccreview.sino-assist.com; + add_header Strict-Transport-Security max-age=15768000; + return 301 https://ccreview.sino-assist.com$request_uri; +} + +server { + listen 80; + server_name jenkins.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location / { + proxy_pass http://192.168.3.120:8081; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } +} diff --git a/docker-swarm-review/nginx-review-132/swarm/sites/uat.conf b/docker-swarm-review/nginx-review-132/swarm/sites/uat.conf new file mode 100644 index 0000000..3a16fd5 --- /dev/null +++ b/docker-swarm-review/nginx-review-132/swarm/sites/uat.conf @@ -0,0 +1,43 @@ +# http → https 跳转 +server { + listen 80; + server_name uat.sino-assist.com api-uat.sino-assist.com api-pre.sino-assist.com; + return 301 https://$host$request_uri; +} + +server { + server_name uat.sino-assist.com api-uat.sino-assist.com api-pre.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location / { + proxy_cookie_path / "/; HttpOnly; Secure; SameSite=Strict"; + proxy_pass http://192.168.3.126:8080/; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $server_name; + if ($request_filename ~ .*\.(htm|html)$) { + add_header Cache-Control no-cache; + } + } + + location /h5/supplier/dispatch { + proxy_pass http://192.168.3.126:8031/h5/supplier/dispatch; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $server_name; + client_max_body_size 200m; + } + + location /h5/client { + proxy_pass http://192.168.3.126:8032/h5/client; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $server_name; + client_max_body_size 200m; + } + + location /dev/h5/rentCar { + proxy_pass http://192.168.3.126:8034/dev/h5/rentCar; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host $server_name; + client_max_body_size 200m; + } +} diff --git a/docker-swarm-review/nginx-review-132/swarm/sites/wx.conf b/docker-swarm-review/nginx-review-132/swarm/sites/wx.conf new file mode 100644 index 0000000..45eef12 --- /dev/null +++ b/docker-swarm-review/nginx-review-132/swarm/sites/wx.conf @@ -0,0 +1,32 @@ +server { + listen 80; + server_name supplierwxtest.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location /ws { + proxy_pass http://192.168.3.111:13000/ws; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + } + + location / { + proxy_pass http://192.168.13.24:8081; + proxy_set_header X-Forwarded-Host $server_name; + client_max_body_size 200m; + } +} + +server { + listen 80; + server_name site.sinoassist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location / { + proxy_pass http://192.168.10.2:8090; + proxy_set_header X-Forwarded-Host $server_name; + client_max_body_size 200m; + } +} diff --git a/docker-swarm-review/nginx-review-132/swarm/sites/zd_report.conf b/docker-swarm-review/nginx-review-132/swarm/sites/zd_report.conf new file mode 100644 index 0000000..6d72c6a --- /dev/null +++ b/docker-swarm-review/nginx-review-132/swarm/sites/zd_report.conf @@ -0,0 +1,60 @@ +server { + listen 80; + server_name report.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location /SinoGYS { + proxy_pass http://192.168.14.47/SinoGYS; + proxy_connect_timeout 600s; + proxy_send_timeout 600s; + proxy_read_timeout 600s; + } + + location /SinoORG { + proxy_pass http://192.168.14.47/SinoORG; + proxy_connect_timeout 600s; + proxy_send_timeout 600s; + proxy_read_timeout 600s; + } + + location / { + proxy_pass http://192.168.3.123:8080; + proxy_connect_timeout 600s; + proxy_send_timeout 600s; + proxy_read_timeout 600s; + } +} + +server { + listen 80; + server_name bi.sino-assist.com; + + include /etc/nginx/ssl.sino_assist.conf; + + location / { + proxy_pass http://192.168.3.123:4280; + proxy_connect_timeout 600s; + proxy_send_timeout 600s; + proxy_read_timeout 600s; + } +} + +server { + listen 80; + server_name bi.sinoassist.com; + + include /etc/nginx/ssl.conf; + + location /WW_verify_TWp375Kzd79EfP0l.txt { + default_type text/html; + return 200 "TWp375Kzd79EfP0l"; + } + + location / { + proxy_pass http://192.168.3.131:80; + proxy_connect_timeout 600s; + proxy_send_timeout 600s; + proxy_read_timeout 600s; + } +} diff --git a/docker-swarm-review/nginx-review-132/swarm/ssl.conf b/docker-swarm-review/nginx-review-132/swarm/ssl.conf new file mode 100644 index 0000000..c73eb20 --- /dev/null +++ b/docker-swarm-review/nginx-review-132/swarm/ssl.conf @@ -0,0 +1,8 @@ +listen 443 ssl; +ssl_certificate /etc/nginx/ssl/2026/sinoassist.com.pem; +ssl_certificate_key /etc/nginx/ssl/2026/sinoassist.com.key; +ssl_protocols TLSv1.1 TLSv1.2; +ssl_ciphers HIGH:!aNULL:!MD5:!DES:!3DES; +ssl_prefer_server_ciphers on; +ssl_session_cache shared:SSL:10m; +ssl_session_timeout 10m; diff --git a/docker-swarm-review/nginx-review-132/swarm/ssl.sino_assist.conf b/docker-swarm-review/nginx-review-132/swarm/ssl.sino_assist.conf new file mode 100644 index 0000000..566048e --- /dev/null +++ b/docker-swarm-review/nginx-review-132/swarm/ssl.sino_assist.conf @@ -0,0 +1,8 @@ +listen 443 ssl; +ssl_certificate /etc/nginx/ssl/23368363_sino-assist.com_nginx/sino-assist.com.pem; +ssl_certificate_key /etc/nginx/ssl/23368363_sino-assist.com_nginx/sino-assist.com.key; +ssl_protocols TLSv1.2 TLSv1.3; +ssl_ciphers HIGH:!aNULL:!MD5:!DES:!3DES; +ssl_prefer_server_ciphers on; +ssl_session_cache shared:SSL:10m; +ssl_session_timeout 10m; diff --git a/docker-swarm-review/nginx/README b/docker-swarm-review/nginx/README new file mode 100644 index 0000000..ed0b417 --- /dev/null +++ b/docker-swarm-review/nginx/README @@ -0,0 +1,4 @@ + +# crm1环境下 部署nginx + +env $(cat ./env_crm1 | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - crm1_nginx \ No newline at end of file diff --git a/docker-swarm-review/nginx/docker-compose.yml b/docker-swarm-review/nginx/docker-compose.yml new file mode 100644 index 0000000..8bfaaab --- /dev/null +++ b/docker-swarm-review/nginx/docker-compose.yml @@ -0,0 +1,26 @@ +version: '3.8' + +networks: + default: + name: ${NAMESPACE} + external: true +services: + server: + image: 'docker.io/bitnami/nginx:1.24' + ports: + - '${NODE_PORT}:8080' + environment: + - TZ=Asia/Shanghai + configs: + - source: custome_config + target: /opt/bitnami/nginx/conf/server_blocks/crm1.conf + deploy: + update_config: + order: start-first + placement: + constraints: + - node.labels.${NAMESPACE}_nginx==1 +configs: + custome_config: + external: true + name: ${CUSTOME_CONFIG} \ No newline at end of file diff --git a/docker-swarm-review/nginx/env_crm1 b/docker-swarm-review/nginx/env_crm1 new file mode 100644 index 0000000..395a6d5 --- /dev/null +++ b/docker-swarm-review/nginx/env_crm1 @@ -0,0 +1,3 @@ +NAMESPACE=crm1 +NODE_PORT=8080 +CUSTOME_CONFIG=crm1_nginx_config \ No newline at end of file diff --git a/docker-swarm-review/pipeline-script b/docker-swarm-review/pipeline-script new file mode 100644 index 0000000..ea1d6a2 --- /dev/null +++ b/docker-swarm-review/pipeline-script @@ -0,0 +1,211 @@ +#!/usr/bin/env groovy +import groovy.json.JsonSlurperClassic + +//properties(projectProperties) +def jsonOption = new JsonSlurperClassic().parseText(params.modulesOption) +echo "jsonOption ${jsonOption}" + +jsonOption.harbor = "harbor.sino-assist.com" +jsonOption.deploy_server = "192.168.3.132" +jsonOption.profile = "review" +jsonOption.nacos_address = "review-nacos1:8848,review-nacos2:8848,review-nacos3:8848" +jsonOption.namespace = "review" + +def branch = params.branch +def DOCKER_CREDENTIAL_ID = 'harbor' +def REGISTRY_URL = jsonOption.harbor +def IMAGE_TAG = params.branch +def deploy_modules = jsonOption.deploy_modules +def deploy_server = jsonOption.deploy_server +def deploy_step = jsonOption.deploy_step +// 根据传入的部署模块配置build的内容 +def deploy_project_names = "" + + +for (module in deploy_modules) { + if (module.o == true) { + deploy_project_names += " ${module.module}:jib " + } +} + + +node { + + def gradleHome = tool 'gradle' + def gradle = "${gradleHome}/bin/gradle" + + + stage('checkout') { + git branch: branch, credentialsId: 'gitlab', url: 'https://git.sino-assist.com/server/sa-server.git' + } + + stage('docker-build-push') { + if (deploy_step.contains("打包镜像")) { + withCredentials([usernamePassword(passwordVariable: 'DOCKER_PASSWORD', usernameVariable: 'DOCKER_USERNAME', credentialsId: "${DOCKER_CREDENTIAL_ID}",)]) { + sh "docker login $REGISTRY_URL -u '$DOCKER_USERNAME' -p '$DOCKER_PASSWORD'" + } + sh "$gradle $deploy_project_names -x test --parallel --build-cache -Pdocker_hub='$REGISTRY_URL' -Pdocker_version=$IMAGE_TAG -Djib.console=plain" + } + } + 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] + module.imageTag = IMAGE_TAG + + echo "deploy module ${module.module}" + + def services = docker_service_param(module, jsonOption) + echo "部署服务" + for (final def svc in services) { + String yml = makeYML(svc) + String serverName = svc.get("serviceName") + String ymlFile = "/data/swarm/${serverName}.yml" + // 添加 SSH 选项: -o StrictHostKeyChecking=no 跳过 host key 检查 + 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 + } + } + } + } + } +} + +def makeYML(params) { + return """ +version: \\"3.8\\" +services: + svc: + image: ${params.IMAGE} + environment: + - active_profile=${params.profile} + - nacos_address=${params.nacos_address} + - nacos_password=gkxl2024#@ + - namespace=${params.namespace} + - project_name=${params.projectName} + - params=${params.params} + - nativeIp=${params.nativeIp} + - reservationsMemory=${params.reservationsMemory} + - limitMemory=${params.limitMemory} + - TZ=Asia/Shanghai + ports: + - '${params.port}:8080' + healthcheck: + test: \\"curl --fail --silent localhost:8080/actuator/health/ping | grep UP || exit 1\\" + interval: 15s + timeout: 5s + retries: 20 + volumes: + - ${params.namespace}_logs:/logs + logging: + driver: json-file + options: + max-size: "1G" + max-file: "3" + extra_hosts: + - "hostname:127.0.0.1" + - "open.property.cic.cn:59.46.218.8" + + deploy: + mode: replicated + replicas: 1 + restart_policy: + condition: on-failure + delay: 5s + max_attempts: 3 + update_config: + order: stop-first + + resources: + limits: + cpus: \\"${params.limitCpu}\\" + memory: ${params.limitMemory} + reservations: + cpus: \\"${params.reservationsCpu}\\" + memory: ${params.reservationsMemory} + placement: + constraints: + - "node.hostname==${params.hostname}" +networks: + default: + name: ${params.namespace} + external: true +volumes: + ${params.namespace}_logs: + external: true +""" +} + +// 转换内存格式:0.1G -> 102M, 0.5G -> 512M, 1G -> 1G +def convertMemory(String mem) { + if (mem == null || mem.trim().isEmpty()) { + return "512M" + } + mem = mem.trim().toUpperCase() + + // 如果是小数G格式,转换为M + if (mem.matches(/^\d+\.\d+G$/)) { + def value = mem.replace("G", "").toDouble() + def mbValue = (value * 1024).intValue() + return "${mbValue}M" + } + return mem +} + +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', + ] + + + def projectName = module.projectName + def node = module.node + def cpu = module.cpu.split("-") + def memory = module.memory.split("-") + // 转换内存格式,确保 Java 堆参数有效 + def reservationsMemory = convertMemory(memory[0]) + def limitMemory = convertMemory(memory[1]) + def address = module.address.split("\n") + + + def services = [] + for (final def add in address) { + def addSplit = add.split(":") + def ip = addSplit[0] + def port = addSplit[1] + def hostname = ipHostnameMap.get(ip) + def serviceName = """ss${ip.split("\\.")[3]}_${projectName}""" + def par = """-Dspring.cloud.inetutils.preferredNetworks=10.18""" + + services.add([ + nacos_address : jsonOption.nacos_address, + namespace : jsonOption.namespace, + projectName : projectName, + IMAGE : "$jsonOption.harbor/sa-server/$projectName:$module.imageTag", + profile : jsonOption.profile, + node : node, // 副本数量 + reservationsCpu : cpu[0], // 保留cpu + limitCpu : cpu[1], // 最大cpu + reservationsMemory: reservationsMemory, // 保留内存(已转换格式) + limitMemory : limitMemory, // 最大内存(已转换格式) + serviceName : serviceName, + hostname : hostname, + port : port, + nativeIp : ip, + params : par + ]) + } + echo "params ${params}" + + + return services +} + +// vim: ft=groovy diff --git a/docker-swarm-review/pipeline-script-cc b/docker-swarm-review/pipeline-script-cc new file mode 100644 index 0000000..9aed599 --- /dev/null +++ b/docker-swarm-review/pipeline-script-cc @@ -0,0 +1,101 @@ +#!/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 + } +} \ No newline at end of file diff --git a/docker-swarm-review/portainer/README.md b/docker-swarm-review/portainer/README.md new file mode 100644 index 0000000..99b0a14 --- /dev/null +++ b/docker-swarm-review/portainer/README.md @@ -0,0 +1 @@ +docker stack deploy --compose-file docker-compose.yml - portainer \ No newline at end of file diff --git a/docker-swarm-review/portainer/docker-compose.yml b/docker-swarm-review/portainer/docker-compose.yml new file mode 100644 index 0000000..c135ed2 --- /dev/null +++ b/docker-swarm-review/portainer/docker-compose.yml @@ -0,0 +1,39 @@ +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: diff --git a/docker-swarm-review/rabbitmq/README b/docker-swarm-review/rabbitmq/README new file mode 100644 index 0000000..150bc18 --- /dev/null +++ b/docker-swarm-review/rabbitmq/README @@ -0,0 +1,38 @@ + +# crm1环境下 部署rabbitmq集群 + +env $(cat ./env_crm1 | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - crm1_rabbitmq + +rabbitmqctl add_user root gkxl650 +rabbitmqctl set_user_tags root administrator + +rabbitmqctl set_permissions -p / root ".*" ".*" ".*" + + + +rabbitmqctl add_user admin gkxl650 +rabbitmqctl set_user_tags admin administrator + +rabbitmqctl set_permissions -p / admin ".*" ".*" ".*" + + + + +# review环境下 部署rabbitmq集群 + +env $(cat ./env_review | xargs) envsubst < ./docker-compose-review.yml | docker stack deploy --compose-file - review_rabbitmq + + +docker.nju.edu.cn + + +{ + "registry-mirrors": [ + "https://.mirror.aliyuncs.com", + "https://dockerproxy.com", + "https://mirror.baidubce.com", + "https://docker.m.daocloud.io", + "https://docker.nju.edu.cn", + "https://docker.mirrors.sjtug.sjtu.edu.cn" + ] +} diff --git a/docker-swarm-review/rabbitmq/docker-compose-review.yml b/docker-swarm-review/rabbitmq/docker-compose-review.yml new file mode 100644 index 0000000..19bce8c --- /dev/null +++ b/docker-swarm-review/rabbitmq/docker-compose-review.yml @@ -0,0 +1,88 @@ +version: '3.8' + +networks: + default: + name: ${NAMESPACE} + external: true +services: + stats: + image: harbor.sino-assist.com/bitnami/rabbitmq:3.11 + hostname: ${NAMESPACE}-rabbitmq-stats + environment: + - TZ=Asia/Shanghai + - RABBITMQ_NODE_TYPE=stats + - RABBITMQ_NODE_NAME=rabbit@stats + - RABBITMQ_ERL_COOKIE=s3cr3tc00ki3 + - RABBITMQ_SECURE_PASSWORD=yes + - RABBITMQ_VHOSTS=/${NAMESPACE} + - RABBITMQ_USERNAME=root + - RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD} + - RABBITMQ_MANAGEMENT_ALLOW_WEB_ACCESS=true + - RABBITMQ_PLUGINS=rabbitmq_management,rabbitmq_stomp,rabbitmq_web_stomp + - RABBITMQ_LOGS=- + ports: + - '${NODE_PORT}:15672' + volumes: + - 'data_stats:/bitnami/rabbitmq/mnesia' + deploy: + update_config: + order: start-first + placement: + constraints: + - node.labels.rabbit_stats==1 + queue1: + image: harbor.sino-assist.com/bitnami/rabbitmq:3.11 + hostname: ${NAMESPACE}-rabbitmq-queue1 + environment: + - TZ=Asia/Shanghai + - RABBITMQ_NODE_TYPE=queue-disc + - RABBITMQ_NODE_NAME=rabbit@queue1 + - RABBITMQ_CLUSTER_NODE_NAME=rabbit@stats + - RABBITMQ_ERL_COOKIE=s3cr3tc00ki3 + - RABBITMQ_SECURE_PASSWORD=yes + - RABBITMQ_VHOSTS=/${NAMESPACE} + - RABBITMQ_USERNAME=root + - RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD} + - RABBITMQ_MANAGEMENT_ALLOW_WEB_ACCESS=true + - RABBITMQ_PLUGINS=rabbitmq_stomp,rabbitmq_web_stomp + - RABBITMQ_LOGS=- + volumes: + - 'data_queue1:/bitnami/rabbitmq/mnesia' + deploy: + update_config: + order: start-first + placement: + constraints: + - node.labels.rabbit_queue1==1 + queue2: + image: harbor.sino-assist.com/bitnami/rabbitmq:3.11 + hostname: ${NAMESPACE}-rabbitmq-queue2 + environment: + - TZ=Asia/Shanghai + - RABBITMQ_NODE_TYPE=queue-disc + - RABBITMQ_NODE_NAME=rabbit@queue2 + - RABBITMQ_CLUSTER_NODE_NAME=rabbit@stats + - RABBITMQ_ERL_COOKIE=s3cr3tc00ki3 + - RABBITMQ_SECURE_PASSWORD=yes + - RABBITMQ_VHOSTS=/${NAMESPACE} + - RABBITMQ_USERNAME=root + - RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD} + - RABBITMQ_MANAGEMENT_ALLOW_WEB_ACCESS=true + - RABBITMQ_PLUGINS=rabbitmq_stomp,rabbitmq_web_stomp + - RABBITMQ_LOGS=- + volumes: + - 'data_queue2:/bitnami/rabbitmq/mnesia' + deploy: + update_config: + order: start-first + placement: + constraints: + - node.labels.rabbit_queue2==1 +volumes: + data_stats: + driver: local + data_queue1: + driver: local + data_queue2: + driver: local + diff --git a/docker-swarm-review/rabbitmq/docker-compose.yml b/docker-swarm-review/rabbitmq/docker-compose.yml new file mode 100644 index 0000000..efd0c9e --- /dev/null +++ b/docker-swarm-review/rabbitmq/docker-compose.yml @@ -0,0 +1,81 @@ +version: '3.8' + +networks: + default: + name: ${NAMESPACE} + external: true +services: + stats: + image: docker.io/bitnami/rabbitmq:3.11 + environment: + - TZ=Asia/Shanghai + - RABBITMQ_NODE_TYPE=stats + - RABBITMQ_NODE_NAME=rabbit@stats + - RABBITMQ_ERL_COOKIE=s3cr3tc00ki3 + - RABBITMQ_SECURE_PASSWORD=yes + - RABBITMQ_VHOSTS=/ ${NAMESPACE} + - RABBITMQ_USERNAME=root + - RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD} + - RABBITMQ_PLUGINS=rabbitmq_management,rabbitmq_stomp,rabbitmq_web_stomp + - RABBITMQ_LOGS=- + ports: + - '${NODE_PORT}:15672' + volumes: + - 'data_stats:/bitnami/rabbitmq/mnesia' + deploy: + update_config: + order: start-first + placement: + constraints: + - node.labels.${NAMESPACE}_rabbit_stats==1 + queue-disc1: + image: docker.io/bitnami/rabbitmq:3.11 + environment: + - TZ=Asia/Shanghai + - RABBITMQ_NODE_TYPE=queue-disc + - RABBITMQ_NODE_NAME=rabbit@queue-disc1 + - RABBITMQ_CLUSTER_NODE_NAME=rabbit@stats + - RABBITMQ_ERL_COOKIE=s3cr3tc00ki3 + - RABBITMQ_SECURE_PASSWORD=yes + - RABBITMQ_VHOSTS=/ ${NAMESPACE} + - RABBITMQ_USERNAME=root + - RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD} + - RABBITMQ_PLUGINS=rabbitmq_stomp,rabbitmq_web_stomp + - RABBITMQ_LOGS=- + volumes: + - 'data_disc1:/bitnami/rabbitmq/mnesia' + deploy: + update_config: + order: start-first + placement: + constraints: + - node.labels.${NAMESPACE}_rabbit_queue-disc1==1 + queue-ram1: + image: docker.io/bitnami/rabbitmq:3.11 + environment: + - TZ=Asia/Shanghai + - RABBITMQ_NODE_TYPE=queue-ram + - RABBITMQ_NODE_NAME=rabbit@queue-ram1 + - RABBITMQ_CLUSTER_NODE_NAME=rabbit@stats + - RABBITMQ_ERL_COOKIE=s3cr3tc00ki3 + - RABBITMQ_SECURE_PASSWORD=yes + - RABBITMQ_VHOSTS=/ ${NAMESPACE} + - RABBITMQ_USERNAME=root + - RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD} + - RABBITMQ_PLUGINS=rabbitmq_stomp,rabbitmq_web_stomp + - RABBITMQ_LOGS=- + volumes: + - 'data_ram1:/bitnami/rabbitmq/mnesia' + deploy: + update_config: + order: start-first + placement: + constraints: + - node.labels.${NAMESPACE}_rabbit_queue-ram1==1 +volumes: + data_stats: + driver: local + data_disc1: + driver: local + data_ram1: + driver: local diff --git a/docker-swarm-review/rabbitmq/env_crm1 b/docker-swarm-review/rabbitmq/env_crm1 new file mode 100644 index 0000000..9a477ef --- /dev/null +++ b/docker-swarm-review/rabbitmq/env_crm1 @@ -0,0 +1,3 @@ +NAMESPACE=crm1 +NODE_PORT=15672 +RABBITMQ_PASSWORD=gkxl650 \ No newline at end of file diff --git a/docker-swarm-review/rabbitmq/env_review b/docker-swarm-review/rabbitmq/env_review new file mode 100644 index 0000000..00fa3ce --- /dev/null +++ b/docker-swarm-review/rabbitmq/env_review @@ -0,0 +1,3 @@ +NAMESPACE=review +NODE_PORT=15672 +RABBITMQ_PASSWORD=gkxl650 \ No newline at end of file diff --git a/docker-swarm-review/redis-review-132/README b/docker-swarm-review/redis-review-132/README new file mode 100644 index 0000000..dd76004 --- /dev/null +++ b/docker-swarm-review/redis-review-132/README @@ -0,0 +1,8 @@ + +# crm1环境下 部署redis sentinel + +env $(cat ./env_crm1 | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - crm1_redis + + +env $(cat ./env_prod | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - prod_redis + diff --git a/docker-swarm-review/redis-review-132/docker-compose-sentinel.yml b/docker-swarm-review/redis-review-132/docker-compose-sentinel.yml new file mode 100644 index 0000000..58f8b57 --- /dev/null +++ b/docker-swarm-review/redis-review-132/docker-compose-sentinel.yml @@ -0,0 +1,100 @@ +version: '3.8' + +# Review Sentinel 配置 +# 仅在故障切换后部署,用于 Review 环境的高可用 +# 部署前确保 132 已提升为 Master (SLAVEOF NO ONE) + +networks: + default: + name: ${NAMESPACE} + external: true + +services: + sentinel-1: + image: 'bitnami/redis-sentinel:7.0.11' + hostname: ${NAMESPACE}-sentinel-1 + environment: + - TZ=Asia/Shanghai + - REDIS_MASTER_HOST=192.168.3.132 + - REDIS_MASTER_PORT_NUMBER=6379 + - REDIS_MASTER_PASSWORD=${REDIS_PASSWORD} + - REDIS_SENTINEL_PASSWORD=${REDIS_SENTINEL_PASSWORD} + - REDIS_SENTINEL_ANNOUNCE_IP=192.168.3.132 + - REDIS_MASTER_SET=reviewmaster + - REDIS_SENTINEL_QUORUM=2 + - REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS=5000 + ports: + - mode: host + protocol: tcp + published: 26379 + target: 26379 + volumes: + - sentinel_data_1:/bitnami + deploy: + update_config: + order: stop-first + placement: + constraints: + - node.hostname==ZD-BAK-APP2 + + sentinel-2: + image: 'bitnami/redis-sentinel:7.0.11' + hostname: ${NAMESPACE}-sentinel-2 + environment: + - TZ=Asia/Shanghai + - REDIS_MASTER_HOST=192.168.3.132 + - REDIS_MASTER_PORT_NUMBER=6379 + - REDIS_MASTER_PASSWORD=${REDIS_PASSWORD} + - REDIS_SENTINEL_PASSWORD=${REDIS_SENTINEL_PASSWORD} + - REDIS_SENTINEL_ANNOUNCE_IP=192.168.3.133 + - REDIS_MASTER_SET=reviewmaster + - REDIS_SENTINEL_QUORUM=2 + - REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS=5000 + ports: + - mode: host + protocol: tcp + published: 26379 + target: 26379 + volumes: + - sentinel_data_2:/bitnami + deploy: + update_config: + order: stop-first + placement: + constraints: + - node.hostname==zd-bak-app3 + + sentinel-3: + image: 'bitnami/redis-sentinel:7.0.11' + hostname: ${NAMESPACE}-sentinel-3 + environment: + - TZ=Asia/Shanghai + - REDIS_MASTER_HOST=192.168.3.132 + - REDIS_MASTER_PORT_NUMBER=6379 + - REDIS_MASTER_PASSWORD=${REDIS_PASSWORD} + - REDIS_SENTINEL_PASSWORD=${REDIS_SENTINEL_PASSWORD} + - REDIS_SENTINEL_ANNOUNCE_IP=192.168.3.134 + - REDIS_MASTER_SET=reviewmaster + - REDIS_SENTINEL_QUORUM=2 + - REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS=5000 + ports: + - mode: host + protocol: tcp + published: 26379 + target: 26379 + volumes: + - sentinel_data_3:/bitnami + deploy: + update_config: + order: stop-first + placement: + constraints: + - node.hostname==ZD-BAK-APP1 + +volumes: + sentinel_data_1: + driver: local + sentinel_data_2: + driver: local + sentinel_data_3: + driver: local diff --git a/docker-swarm-review/redis-review-132/docker-compose.yml b/docker-swarm-review/redis-review-132/docker-compose.yml new file mode 100644 index 0000000..b667e4a --- /dev/null +++ b/docker-swarm-review/redis-review-132/docker-compose.yml @@ -0,0 +1,96 @@ +version: '3.8' + +networks: + default: + name: ${NAMESPACE} + external: true + +services: + # 132: 主同步节点,从生产 10.56 同步数据 + # 使用 replica-announced no 对生产 Sentinel 隐藏 + master: + image: 'bitnami/redis:7.0.11' + hostname: ${NAMESPACE}-redis-master + environment: + - TZ=Asia/Shanghai + - REDIS_REPLICATION_MODE=slave + - REDIS_MASTER_HOST=${REDIS_PROD_MASTER_HOST} + - REDIS_MASTER_PORT_NUMBER=${REDIS_PROD_MASTER_PORT} + - REDIS_MASTER_PASSWORD=${REDIS_PASSWORD} + - REDIS_PASSWORD=${REDIS_PASSWORD} + - REDIS_EXTRA_FLAGS=--replica-announced no --replica-priority 0 + ports: + - mode: host + protocol: tcp + published: 6379 + target: 6379 + volumes: + - data_master:/bitnami + deploy: + update_config: + order: stop-first + placement: + constraints: + - node.hostname==ZD-BAK-APP2 + + # 133: 从 132 同步数据(级联复制) + # 不需要 replica-announced no,因为 master 是 132,生产 Sentinel 看不到 + # 故障切换后需要被 Review Sentinel 发现 + slave-1: + image: 'bitnami/redis:7.0.11' + hostname: ${NAMESPACE}-redis-slave-1 + environment: + - TZ=Asia/Shanghai + - REDIS_REPLICATION_MODE=slave + - REDIS_MASTER_HOST=${REDIS_REVIEW_PRIMARY_HOST} + - REDIS_MASTER_PORT_NUMBER=6379 + - REDIS_MASTER_PASSWORD=${REDIS_PASSWORD} + - REDIS_PASSWORD=${REDIS_PASSWORD} + ports: + - mode: host + protocol: tcp + published: 6379 + target: 6379 + volumes: + - data_slave_1:/bitnami + deploy: + update_config: + order: stop-first + placement: + constraints: + - node.hostname==zd-bak-app3 + + # 134: 从 132 同步数据(级联复制) + # 不需要 replica-announced no,因为 master 是 132,生产 Sentinel 看不到 + # 故障切换后需要被 Review Sentinel 发现 + slave-2: + image: 'bitnami/redis:7.0.11' + hostname: ${NAMESPACE}-redis-slave-2 + environment: + - TZ=Asia/Shanghai + - REDIS_REPLICATION_MODE=slave + - REDIS_MASTER_HOST=${REDIS_REVIEW_PRIMARY_HOST} + - REDIS_MASTER_PORT_NUMBER=6379 + - REDIS_MASTER_PASSWORD=${REDIS_PASSWORD} + - REDIS_PASSWORD=${REDIS_PASSWORD} + ports: + - mode: host + protocol: tcp + published: 6379 + target: 6379 + volumes: + - data_slave_2:/bitnami + deploy: + update_config: + order: stop-first + placement: + constraints: + - node.hostname==ZD-BAK-APP1 + +volumes: + data_master: + driver: local + data_slave_1: + driver: local + data_slave_2: + driver: local diff --git a/docker-swarm-review/redis-review-132/env_crm1 b/docker-swarm-review/redis-review-132/env_crm1 new file mode 100644 index 0000000..0ea239c --- /dev/null +++ b/docker-swarm-review/redis-review-132/env_crm1 @@ -0,0 +1,4 @@ +NAMESPACE=crm1 +NODE_PORT=6379 +REDIS_PASSWORD=gkxl650 +REDIS_SENTINEL_PASSWORD=gkxl650 \ No newline at end of file diff --git a/docker-swarm-review/redis-review-132/env_review b/docker-swarm-review/redis-review-132/env_review new file mode 100644 index 0000000..2987ca1 --- /dev/null +++ b/docker-swarm-review/redis-review-132/env_review @@ -0,0 +1,6 @@ +NAMESPACE=review +REDIS_PASSWORD=sino#650 +REDIS_SENTINEL_PASSWORD=sino#650 +REDIS_PROD_MASTER_HOST=192.168.10.56 +REDIS_PROD_MASTER_PORT=6379 +REDIS_REVIEW_PRIMARY_HOST=192.168.3.132 diff --git a/docker-swarm-review/redis-review-132/failover-to-review.sh b/docker-swarm-review/redis-review-132/failover-to-review.sh new file mode 100644 index 0000000..2b716d0 --- /dev/null +++ b/docker-swarm-review/redis-review-132/failover-to-review.sh @@ -0,0 +1,869 @@ +#!/bin/bash +# +# Review Redis 故障切换脚本 +# 当生产环境 Redis 故障时,使用此脚本将 Review 环境切换为主服务 +# +# 使用方法: +# ./failover-to-review.sh status - 查看当前状态 +# ./failover-to-review.sh failover - 执行故障切换(提升 132 为 Master) +# ./failover-to-review.sh sentinel - 仅部署 Review Sentinel +# ./failover-to-review.sh full - 完整切换(failover + sentinel) +# ./failover-to-review.sh rollback - 安全回滚(先同步数据到生产,再切换) +# +# 安全回滚流程: +# 前置条件:【重要】必须先停止或暂停生产 Sentinel,防止自动故障转移 +# 1. 让 10.56 先作为 132 的从节点同步数据(反向同步) +# 2. 等待数据同步完成 +# 3. 将 132 设为只读,防止切换期间数据丢失 +# 4. 提升 10.56 为主节点 +# 5. 132 重新指向 10.56,恢复级联复制结构 +# 6. 重新启动生产 Sentinel +# +# 数据一致性保证: +# - 回滚前:通过反向同步确保故障期间的数据同步到生产 +# - 回滚中:将 132 设为只读(min-replicas-to-write=99)防止数据丢失 +# - 回滚后:验证偏移量差异,确保数据完全同步 +# + +set -e + +# ==================== 全局参数 ==================== +# -y 或 --yes: 跳过确认提示(危险操作仍需确认) +# --force: 强制执行,跳过所有确认 +AUTO_CONFIRM=false +FORCE_MODE=false + +# 解析命令行参数 +parse_global_args() { + for arg in "$@"; do + case $arg in + -y|--yes) + AUTO_CONFIRM=true + ;; + --force) + AUTO_CONFIRM=true + FORCE_MODE=true + ;; + esac + done +} + +# 确认提示(如果 AUTO_CONFIRM=true 则自动确认) +confirm_prompt() { + local message=$1 + local default=${2:-n} # 默认值 n + + if [ "$AUTO_CONFIRM" = true ]; then + log_info "自动确认: $message" + return 0 + fi + + read -p "$message (y/n): " confirm + if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then + return 0 + fi + return 1 +} + +# 危险操作确认(即使 AUTO_CONFIRM=true 也需要确认,除非 FORCE_MODE) +confirm_dangerous() { + local message=$1 + + if [ "$FORCE_MODE" = true ]; then + log_warn "强制模式: 跳过危险操作确认" + return 0 + fi + + read -p "$message (y/n): " confirm + if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then + return 0 + fi + return 1 +} + +# ==================== 配置 ==================== +REDIS_PASSWORD="sino#650" +SENTINEL_PASSWORD="sino#650" + +# Review 节点 +NODE_132="192.168.3.132" +NODE_133="192.168.3.133" +NODE_134="192.168.3.134" + +# 生产节点 +PROD_MASTER="192.168.10.56" +PROD_PORT=6379 + +REDIS_PORT=6379 +SENTINEL_PORT=26379 + +# 脚本目录 +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +# ==================== 颜色输出 ==================== +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +log_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# ==================== Docker/Redis 命令封装 ==================== + +# Redis 镜像(用于临时容器执行命令) +REDIS_IMAGE="bitnami/redis:7.0.11" + +# 通过 Docker 容器执行 Redis 命令 +# 优先使用已运行的 review_redis_master 容器,否则使用临时容器 +redis_cmd() { + local host=$1 + local port=$2 + shift 2 + + # 尝试获取运行中的 redis master 容器 + local container_id=$(docker ps -q -f name=review_redis_master 2>/dev/null | head -1) + + if [ -n "$container_id" ]; then + # 使用已运行的容器 + docker exec "$container_id" redis-cli -h "$host" -p "$port" -a "$REDIS_PASSWORD" --no-auth-warning "$@" 2>/dev/null + else + # 使用临时容器执行命令 + docker run --rm --network host "$REDIS_IMAGE" redis-cli -h "$host" -p "$port" -a "$REDIS_PASSWORD" --no-auth-warning "$@" 2>/dev/null + fi +} + +# 检查 Redis 镜像是否存在,不存在则提示 +check_redis_image() { + if ! docker image inspect "$REDIS_IMAGE" &>/dev/null; then + log_warn "Redis 镜像 $REDIS_IMAGE 不存在" + log_info "请先拉取镜像: docker pull $REDIS_IMAGE" + return 1 + fi + return 0 +} + +# ==================== 功能函数 ==================== + +# 检查节点状态 +check_status() { + echo -e "${BLUE}========================================${NC}" + echo -e "${BLUE} 当前 Redis 状态 ${NC}" + echo -e "${BLUE}========================================${NC}" + + for node in $NODE_132 $NODE_133 $NODE_134; do + echo "" + echo -e "${GREEN}[$node]${NC}" + + local info=$(redis_cmd "$node" $REDIS_PORT INFO replication 2>/dev/null) + if [ -z "$info" ]; then + echo -e " ${RED}无法连接${NC}" + continue + fi + + local role=$(echo "$info" | grep "^role:" | cut -d: -f2 | tr -d '\r\n') + local master_host=$(echo "$info" | grep "^master_host:" | cut -d: -f2 | tr -d '\r\n') + local master_port=$(echo "$info" | grep "^master_port:" | cut -d: -f2 | tr -d '\r\n') + local master_link=$(echo "$info" | grep "^master_link_status:" | cut -d: -f2 | tr -d '\r\n') + local slaves=$(echo "$info" | grep "^connected_slaves:" | cut -d: -f2 | tr -d '\r\n') + + echo -e " 角色: ${YELLOW}$role${NC}" + if [ "$role" = "slave" ]; then + echo -e " 主节点: $master_host:$master_port" + echo -e " 同步状态: $master_link" + else + echo -e " 从节点数: $slaves" + fi + done + + echo "" +} + +# 检查生产环境状态 +check_prod_status() { + echo -e "${BLUE}========================================${NC}" + echo -e "${BLUE} 生产环境 Redis 状态 ${NC}" + echo -e "${BLUE}========================================${NC}" + echo "" + + local info=$(redis_cmd "$PROD_MASTER" $PROD_PORT INFO replication 2>/dev/null) + if [ -z "$info" ]; then + echo -e "${RED}生产环境 $PROD_MASTER:$PROD_PORT 无法连接${NC}" + return 1 + fi + + local role=$(echo "$info" | grep "^role:" | cut -d: -f2 | tr -d '\r\n') + echo -e "生产主节点 $PROD_MASTER: ${GREEN}$role${NC}" + return 0 +} + +# 提升 132 为 Master +promote_132() { + echo "" + log_info "正在提升 $NODE_132 为 Master..." + + # 执行 SLAVEOF NO ONE + local result=$(redis_cmd "$NODE_132" $REDIS_PORT SLAVEOF NO ONE) + if [ "$result" != "OK" ]; then + log_error "SLAVEOF NO ONE 执行失败: $result" + return 1 + fi + + sleep 2 + + # 验证角色 + local role=$(redis_cmd "$NODE_132" $REDIS_PORT INFO replication | grep "^role:" | cut -d: -f2 | tr -d '\r\n') + if [ "$role" = "master" ]; then + log_info "$NODE_132 已成功提升为 Master" + else + log_error "$NODE_132 提升失败,当前角色: $role" + return 1 + fi +} + +# 配置从节点指向新 Master +configure_slaves() { + echo "" + log_info "正在配置从节点指向 $NODE_132..." + + for node in $NODE_133 $NODE_134; do + local master_host=$(redis_cmd "$node" $REDIS_PORT INFO replication | grep "^master_host:" | cut -d: -f2 | tr -d '\r\n') + + if [ "$master_host" = "$NODE_132" ]; then + log_info "[$node] 已指向 $NODE_132" + else + log_warn "[$node] 当前指向 $master_host,正在重新配置..." + redis_cmd "$node" $REDIS_PORT SLAVEOF "$NODE_132" $REDIS_PORT + sleep 1 + + # 验证 + master_host=$(redis_cmd "$node" $REDIS_PORT INFO replication | grep "^master_host:" | cut -d: -f2 | tr -d '\r\n') + if [ "$master_host" = "$NODE_132" ]; then + log_info "[$node] 配置成功" + else + log_error "[$node] 配置失败" + fi + fi + done +} + +# 部署 Sentinel +deploy_sentinel() { + echo "" + log_info "正在部署 Review Sentinel..." + + cd "$SCRIPT_DIR" + + if [ ! -f "./docker-compose-sentinel.yml" ]; then + log_error "未找到 docker-compose-sentinel.yml" + return 1 + fi + + if [ ! -f "./env_review" ]; then + log_error "未找到 env_review" + return 1 + fi + + # 部署 + env $(cat ./env_review | xargs) envsubst < ./docker-compose-sentinel.yml | docker stack deploy --compose-file - review_sentinel + + log_info "Sentinel 部署命令已执行,等待服务启动..." + sleep 10 + + # 检查 Sentinel 状态 + log_info "检查 Sentinel 状态..." + for node in $NODE_132 $NODE_133 $NODE_134; do + local master=$(redis_cmd "$node" $SENTINEL_PORT SENTINEL get-master-addr-by-name reviewmaster 2>/dev/null) + if [ -n "$master" ]; then + log_info "[$node:$SENTINEL_PORT] Sentinel 运行正常,Master: $master" + else + log_warn "[$node:$SENTINEL_PORT] Sentinel 未就绪或未响应" + fi + done +} + +# 移除 Sentinel +remove_sentinel() { + echo "" + log_info "正在移除 Review Sentinel..." + docker stack rm review_sentinel 2>/dev/null || true + sleep 5 + log_info "Sentinel 已移除" +} + +# ==================== 安全回滚相关函数 ==================== + +# 检查生产节点是否可连接 +check_prod_connectable() { + local info=$(redis_cmd "$PROD_MASTER" $PROD_PORT PING 2>/dev/null) + if [ "$info" = "PONG" ]; then + return 0 + else + return 1 + fi +} + +# 获取节点的复制偏移量 +get_repl_offset() { + local host=$1 + local info=$(redis_cmd "$host" $REDIS_PORT INFO replication 2>/dev/null) + local offset=$(echo "$info" | grep "master_repl_offset:" | cut -d: -f2 | tr -d '\r\n') + if [ -z "$offset" ]; then + offset=$(echo "$info" | grep "slave_repl_offset:" | cut -d: -f2 | tr -d '\r\n') + fi + echo "$offset" +} + +# 获取节点角色 +get_node_role() { + local host=$1 + local info=$(redis_cmd "$host" $REDIS_PORT INFO replication 2>/dev/null) + echo "$info" | grep "^role:" | cut -d: -f2 | tr -d '\r\n' +} + +# 获取从节点同步状态 +get_slave_sync_status() { + local host=$1 + local info=$(redis_cmd "$host" $REDIS_PORT INFO replication 2>/dev/null) + echo "$info" | grep "master_link_status:" | cut -d: -f2 | tr -d '\r\n' +} + +# 远程执行生产节点命令(尝试自动执行,失败则提示手动) +exec_prod_redis_cmd() { + local redis_args="$@" + local result="" + + # 尝试通过网络直接执行命令 + log_info "尝试远程执行 Redis 命令..." + result=$(redis_cmd "$PROD_MASTER" $PROD_PORT $redis_args 2>/dev/null) + + if [ -n "$result" ]; then + echo "$result" + return 0 + fi + + # 无法远程执行,提示手动操作 + log_warn "无法远程执行命令,请在生产服务器上手动执行" + return 1 +} + +# 步骤 1: 让生产 10.56 作为 132 的从节点 +rollback_step1_make_prod_slave() { + echo "" + echo -e "${BLUE}========================================${NC}" + echo -e "${BLUE} 步骤 1: 配置生产节点为 132 的从节点 ${NC}" + echo -e "${BLUE}========================================${NC}" + + # 检查 132 当前是否为 Master + local role_132=$(get_node_role "$NODE_132") + if [ "$role_132" != "master" ]; then + log_error "132 当前不是 Master (角色: $role_132),无法执行回滚" + log_error "请先确认 132 已通过 failover 提升为 Master" + return 1 + fi + log_info "确认 132 当前为 Master" + + # 检查生产节点是否可连接 + if ! check_prod_connectable; then + log_error "生产节点 $PROD_MASTER 无法连接,请先确保生产 Redis 已恢复" + return 1 + fi + log_info "生产节点 $PROD_MASTER 可连接" + + # 配置 10.56 作为 132 的从节点 + log_info "正在配置 $PROD_MASTER 作为 $NODE_132 的从节点..." + + # 尝试直接远程执行 + local result=$(redis_cmd "$PROD_MASTER" $PROD_PORT SLAVEOF "$NODE_132" $REDIS_PORT 2>/dev/null) + + if [ "$result" = "OK" ]; then + log_info "远程执行成功: SLAVEOF $NODE_132 $REDIS_PORT" + else + # 无法远程执行,提示手动操作 + echo "" + echo -e "${YELLOW}========================================${NC}" + echo -e "${YELLOW} 无法远程执行,请在生产服务器 ($PROD_MASTER) 上执行: ${NC}" + echo -e "${YELLOW}========================================${NC}" + echo "" + echo "# 方法1: 进入 Redis 容器执行" + echo "docker exec -it \$(docker ps -q -f name=redis) redis-cli -a '$REDIS_PASSWORD' --no-auth-warning SLAVEOF $NODE_132 $REDIS_PORT" + echo "" + echo "# 方法2: 使用临时容器执行" + echo "docker run --rm --network host $REDIS_IMAGE redis-cli -h $PROD_MASTER -p $PROD_PORT -a '$REDIS_PASSWORD' --no-auth-warning SLAVEOF $NODE_132 $REDIS_PORT" + echo "" + + if ! confirm_dangerous "已在生产服务器执行上述命令?"; then + log_warn "操作已取消" + return 1 + fi + fi + + # 验证配置 + sleep 2 + local prod_role=$(get_node_role "$PROD_MASTER") + local prod_sync=$(get_slave_sync_status "$PROD_MASTER") + + if [ "$prod_role" = "slave" ]; then + log_info "生产节点已配置为从节点" + log_info "同步状态: $prod_sync" + if [ "$prod_sync" = "up" ]; then + log_info "数据同步连接正常" + else + log_warn "同步连接状态: $prod_sync,请等待连接建立" + fi + else + log_error "配置失败,生产节点角色: $prod_role" + return 1 + fi +} + +# 步骤 2: 等待数据同步完成 +rollback_step2_wait_sync() { + echo "" + echo -e "${BLUE}========================================${NC}" + echo -e "${BLUE} 步骤 2: 等待数据同步完成 ${NC}" + echo -e "${BLUE}========================================${NC}" + + local max_wait=300 # 最大等待 5 分钟 + local wait_time=0 + local check_interval=5 + + while [ $wait_time -lt $max_wait ]; do + local master_offset=$(get_repl_offset "$NODE_132") + local slave_offset=$(redis_cmd "$PROD_MASTER" $REDIS_PORT INFO replication 2>/dev/null | grep "slave_repl_offset:" | cut -d: -f2 | tr -d '\r\n') + local sync_status=$(get_slave_sync_status "$PROD_MASTER") + + echo -e " Master(132) 偏移量: ${GREEN}$master_offset${NC}" + echo -e " Slave(10.56) 偏移量: ${YELLOW}$slave_offset${NC}" + echo -e " 同步状态: $sync_status" + echo -e " 偏移量差异: $((master_offset - slave_offset))" + + if [ "$sync_status" != "up" ]; then + log_warn "同步连接未建立,等待中..." + sleep $check_interval + wait_time=$((wait_time + check_interval)) + continue + fi + + # 检查偏移量差异 + local offset_diff=$((master_offset - slave_offset)) + if [ $offset_diff -lt 1000 ]; then + log_info "数据同步基本完成(偏移量差异: $offset_diff)" + + # 停止 Review 写入后再次确认 + echo "" + log_warn "建议:在执行下一步前,先停止 Review 应用对 Redis 的写入" + if confirm_prompt "是否已停止写入并继续?"; then + sleep 2 + master_offset=$(get_repl_offset "$NODE_132") + slave_offset=$(redis_cmd "$PROD_MASTER" $REDIS_PORT INFO replication 2>/dev/null | grep "slave_repl_offset:" | cut -d: -f2 | tr -d '\r\n') + offset_diff=$((master_offset - slave_offset)) + + if [ $offset_diff -eq 0 ]; then + log_info "数据完全同步(偏移量差异: 0)" + return 0 + else + log_info "当前偏移量差异: $offset_diff" + # 偏移量差异小于 100 字节时自动继续 + if [ $offset_diff -lt 100 ] || confirm_prompt "偏移量差异 $offset_diff 字节,是否继续执行切换?"; then + return 0 + fi + fi + fi + fi + + echo "等待同步... ($wait_time/$max_wait 秒)" + sleep $check_interval + wait_time=$((wait_time + check_interval)) + echo "" + done + + log_error "同步超时,请检查网络和 Redis 状态" + if confirm_dangerous "是否强制继续?"; then + return 0 + fi + return 1 +} + +# 步骤 3: 切换主从关系 +rollback_step3_switch_master() { + echo "" + echo -e "${BLUE}========================================${NC}" + echo -e "${BLUE} 步骤 3: 切换主从关系 ${NC}" + echo -e "${BLUE}========================================${NC}" + + # 3.0 先将 132 设为只读,防止切换期间有新数据写入 + log_info "将 132 设为只读模式,防止切换期间数据写入..." + redis_cmd "$NODE_132" $REDIS_PORT CONFIG SET min-replicas-to-write 99 + log_info "132 已设为只读(min-replicas-to-write=99)" + + # 等待最后的数据同步到 10.56 + log_info "等待最后的数据同步..." + sleep 3 + + # 再次确认偏移量 + local master_offset=$(get_repl_offset "$NODE_132") + local slave_offset=$(redis_cmd "$PROD_MASTER" $REDIS_PORT INFO replication 2>/dev/null | grep "slave_repl_offset:" | cut -d: -f2 | tr -d '\r\n') + local offset_diff=$((master_offset - slave_offset)) + log_info "最终偏移量差异: $offset_diff" + + # 偏移量差异小于 100 字节时自动继续,否则需要确认 + if [ $offset_diff -gt 100 ]; then + log_warn "仍有 $offset_diff 字节数据未同步" + if ! confirm_dangerous "是否继续切换?"; then + # 恢复 132 可写 + redis_cmd "$NODE_132" $REDIS_PORT CONFIG SET min-replicas-to-write 0 + log_warn "已取消,132 已恢复可写" + return 1 + fi + elif [ $offset_diff -gt 0 ]; then + log_info "偏移量差异 $offset_diff 字节(<100),自动继续" + fi + + # 3.1 提升 10.56 为主节点 + log_info "正在提升 $PROD_MASTER 为主节点..." + + # 尝试直接远程执行 + local result=$(redis_cmd "$PROD_MASTER" $PROD_PORT SLAVEOF NO ONE 2>/dev/null) + + if [ "$result" = "OK" ]; then + log_info "远程执行成功: SLAVEOF NO ONE" + else + # 无法远程执行,提示手动操作 + echo "" + echo -e "${YELLOW}========================================${NC}" + echo -e "${YELLOW} 无法远程执行,请在生产服务器 ($PROD_MASTER) 上执行: ${NC}" + echo -e "${YELLOW}========================================${NC}" + echo "" + echo "# 方法1: 进入 Redis 容器执行" + echo "docker exec -it \$(docker ps -q -f name=redis) redis-cli -a '$REDIS_PASSWORD' --no-auth-warning SLAVEOF NO ONE" + echo "" + echo "# 方法2: 使用临时容器执行" + echo "docker run --rm --network host $REDIS_IMAGE redis-cli -h $PROD_MASTER -p $PROD_PORT -a '$REDIS_PASSWORD' --no-auth-warning SLAVEOF NO ONE" + echo "" + + if ! confirm_dangerous "已执行上述命令?"; then + # 恢复 132 可写 + redis_cmd "$NODE_132" $REDIS_PORT CONFIG SET min-replicas-to-write 0 + log_warn "已取消,132 已恢复可写" + return 1 + fi + fi + + sleep 2 + local prod_role=$(get_node_role "$PROD_MASTER") + if [ "$prod_role" = "master" ]; then + log_info "$PROD_MASTER 已提升为 Master" + else + log_error "提升失败,当前角色: $prod_role" + # 恢复 132 可写 + redis_cmd "$NODE_132" $REDIS_PORT CONFIG SET min-replicas-to-write 0 + return 1 + fi + + # 3.2 配置 132 指向生产(立即执行,减少时间窗口) + log_info "正在配置 $NODE_132 指向 $PROD_MASTER..." + redis_cmd "$NODE_132" $REDIS_PORT SLAVEOF "$PROD_MASTER" $PROD_PORT + + # 恢复 132 可写设置(作为从节点后这个设置不影响,但保持配置干净) + redis_cmd "$NODE_132" $REDIS_PORT CONFIG SET min-replicas-to-write 0 + + # 恢复隐藏配置 + log_info "恢复 132 的隐藏从节点配置..." + redis_cmd "$NODE_132" $REDIS_PORT CONFIG SET replica-announced no + redis_cmd "$NODE_132" $REDIS_PORT CONFIG SET replica-priority 0 + + sleep 2 + local role_132=$(get_node_role "$NODE_132") + local sync_132=$(get_slave_sync_status "$NODE_132") + if [ "$role_132" = "slave" ] && [ "$sync_132" = "up" ]; then + log_info "$NODE_132 已配置为从节点,同步状态: $sync_132" + else + log_warn "$NODE_132 配置完成,角色: $role_132, 同步状态: $sync_132" + fi + + # 3.3 配置 133/134 指向 132 + log_info "正在配置从节点指向 $NODE_132..." + for node in $NODE_133 $NODE_134; do + redis_cmd "$node" $REDIS_PORT SLAVEOF "$NODE_132" $REDIS_PORT + sleep 1 + local role=$(get_node_role "$node") + local sync=$(get_slave_sync_status "$node") + log_info "[$node] 角色: $role, 同步状态: $sync" + done +} + +# 安全回滚主函数 +safe_rollback() { + echo "" + echo -e "${RED}╔════════════════════════════════════════════════════════════╗${NC}" + echo -e "${RED}║ 安全回滚流程 ║${NC}" + echo -e "${RED}║ ║${NC}" + echo -e "${RED}║ 此流程会先将故障期间 132 的新数据同步到生产 10.56, ║${NC}" + echo -e "${RED}║ 确保数据不丢失后再切换回原有架构。 ║${NC}" + echo -e "${RED}╚════════════════════════════════════════════════════════════╝${NC}" + echo "" + + echo "回滚流程说明:" + echo " 1. 让 10.56 先作为 132 的从节点(反向同步)" + echo " 2. 等待数据完全同步" + echo " 3. 提升 10.56 为主节点,132 重新作为隐藏从节点" + echo " 4. 恢复 133/134 级联复制" + echo "" + echo "前置条件:" + echo " - 132 当前是 Master(故障切换状态)" + echo " - 10.56 已恢复并可连接" + echo -e " - ${YELLOW}【重要】所有应用已切换到 Review Sentinel${NC}" + echo -e " - ${YELLOW}【重要】生产 Sentinel 已停止${NC}" + echo " - 建议先停止 Review 应用的写入" + echo "" + + echo -e "${YELLOW}========================================${NC}" + echo -e "${YELLOW} ⚠️ 生产 Sentinel 处理提醒 ${NC}" + echo -e "${YELLOW}========================================${NC}" + echo "" + echo "在执行回滚前,必须先停止生产 Sentinel!" + echo "" + echo "原因:" + echo " 1. 回滚步骤1会让 10.56 变成 132 的从节点" + echo " 2. 如果 Sentinel 还在运行,会检测到 master 下线并触发故障转移" + echo " 3. 此外,任何仍通过生产 Sentinel 连接的应用会收到 READONLY 错误" + echo "" + echo -e "${RED}【重要】请确认以下条件:${NC}" + echo " 1. 所有应用都已切换到 Review Sentinel (reviewmaster)" + echo " 2. 生产 Sentinel 已停止" + echo "" + echo "停止生产 Sentinel 命令(在生产服务器执行):" + echo " docker stack rm prod_sentinel" + echo " # 或者根据实际部署名称调整" + echo "" + + # 这是危险操作,必须确认(除非 --force) + if ! confirm_dangerous "已确认所有应用已切换且生产 Sentinel 已停止?"; then + log_warn "请先完成上述操作后再执行回滚" + return 1 + fi + + check_prod_status || { + log_error "生产环境不可用,无法执行回滚" + return 1 + } + check_status + + if ! confirm_prompt "确认开始安全回滚?"; then + echo "已取消" + return 0 + fi + + # 移除 Review Sentinel + remove_sentinel + + # 执行三个步骤 + rollback_step1_make_prod_slave || return 1 + rollback_step2_wait_sync || return 1 + rollback_step3_switch_master || return 1 + + echo "" + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN} 安全回滚完成 ${NC}" + echo -e "${GREEN}========================================${NC}" + check_status + + echo "" + echo "后续操作:" + echo " 1. 重新启动生产 Sentinel(如果之前停止了)" + echo " 2. 验证生产 Sentinel 是否正常识别 10.56 为主节点" + echo " 3. 修改应用配置,连接回生产 Sentinel" + echo " 4. 确认 132/133/134 对生产 Sentinel 不可见" +} + +# 旧的不安全回滚(保留但标记警告) +unsafe_rollback() { + echo "" + echo -e "${RED}╔════════════════════════════════════════════════════════════╗${NC}" + echo -e "${RED}║ ⚠️ 警告 ⚠️ ║${NC}" + echo -e "${RED}║ ║${NC}" + echo -e "${RED}║ 此操作会直接让 132 重新指向 10.56,可能导致数据丢失! ║${NC}" + echo -e "${RED}║ 故障期间 132 上的所有写入数据将被覆盖! ║${NC}" + echo -e "${RED}║ ║${NC}" + echo -e "${RED}║ 推荐使用: ./failover-to-review.sh rollback ║${NC}" + echo -e "${RED}╚════════════════════════════════════════════════════════════╝${NC}" + echo "" + + read -p "确认执行不安全回滚(数据可能丢失)? 输入 'YES' 确认: " confirm + if [ "$confirm" != "YES" ]; then + echo "已取消" + return 0 + fi + + log_warn "正在执行不安全回滚..." + + # 先移除 Sentinel + remove_sentinel + + # 重新配置 132 指向生产 + log_info "配置 $NODE_132 指向生产 $PROD_MASTER..." + redis_cmd "$NODE_132" $REDIS_PORT SLAVEOF "$PROD_MASTER" $PROD_PORT + sleep 2 + + # 重新配置 133/134 指向 132 + for node in $NODE_133 $NODE_134; do + log_info "配置 $node 指向 $NODE_132..." + redis_cmd "$node" $REDIS_PORT SLAVEOF "$NODE_132" $REDIS_PORT + sleep 1 + done + + log_warn "不安全回滚完成,请检查数据一致性" +} + +# 显示最终状态和连接信息 +show_final_info() { + echo "" + echo -e "${BLUE}========================================${NC}" + echo -e "${BLUE} 切换完成 ${NC}" + echo -e "${BLUE}========================================${NC}" + + check_status + + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN} Review 应用连接配置 ${NC}" + echo -e "${GREEN}========================================${NC}" + echo "" + echo "直连模式:" + echo " host: $NODE_132" + echo " port: $REDIS_PORT" + echo " password: $REDIS_PASSWORD" + echo "" + echo "Sentinel 模式 (Spring Boot):" + echo " spring:" + echo " redis:" + echo " sentinel:" + echo " master: reviewmaster" + echo " nodes: $NODE_132:$SENTINEL_PORT,$NODE_133:$SENTINEL_PORT,$NODE_134:$SENTINEL_PORT" + echo " password: $SENTINEL_PASSWORD" + echo " password: $REDIS_PASSWORD" + echo "" +} + +# ==================== 主函数 ==================== +main() { + # 解析全局参数 + parse_global_args "$@" + + echo -e "${GREEN}" + echo "╔════════════════════════════════════════════╗" + echo "║ Review Redis 故障切换脚本 ║" + echo "╚════════════════════════════════════════════╝" + echo -e "${NC}" + + if [ "$AUTO_CONFIRM" = true ]; then + log_info "已启用自动确认模式 (-y)" + fi + if [ "$FORCE_MODE" = true ]; then + log_warn "已启用强制模式 (--force),将跳过所有确认" + fi + + # 对于需要执行 Redis 命令的操作,检查镜像是否存在 + case "${1:-help}" in + status|failover|full|rollback|unsafe-rollback) + check_redis_image || exit 1 + ;; + esac + + case "${1:-help}" in + status) + check_prod_status || true + check_status + ;; + failover) + check_status + echo "" + if confirm_prompt "确认执行故障切换(提升 132 为 Master)?"; then + promote_132 + configure_slaves + check_status + else + echo "已取消" + fi + ;; + sentinel) + deploy_sentinel + ;; + full) + check_prod_status || true + check_status + echo "" + if confirm_prompt "确认执行完整故障切换(包含 Sentinel 部署)?"; then + promote_132 + configure_slaves + deploy_sentinel + show_final_info + else + echo "已取消" + fi + ;; + rollback) + safe_rollback + ;; + unsafe-rollback) + unsafe_rollback + check_status + ;; + help|*) + echo "用法: $0 {status|failover|sentinel|full|rollback|unsafe-rollback} [-y|--yes] [--force]" + echo "" + echo "命令说明:" + echo " status - 查看当前 Redis 状态" + echo " failover - 执行故障切换(提升 132 为 Master)" + echo " sentinel - 仅部署 Review Sentinel" + echo " full - 完整切换(failover + sentinel)" + echo " rollback - 安全回滚(先同步数据到生产,再切换)" + echo " unsafe-rollback - 不安全回滚(直接切换,可能丢数据)" + echo "" + echo "可选参数:" + echo " -y, --yes - 自动确认普通提示(危险操作仍需手动确认)" + echo " --force - 强制模式,跳过所有确认(慎用!)" + echo "" + echo "示例:" + echo " $0 full -y # 自动确认执行完整切换" + echo " $0 rollback # 交互式安全回滚" + echo " $0 rollback --force # 强制执行回滚(跳过所有确认)" + echo "" + echo "故障切换流程:" + echo " 1. 执行 ./failover-to-review.sh status 检查状态" + echo " 2. 执行 ./failover-to-review.sh full 完整切换" + echo " 3. 修改应用配置,连接 Review Sentinel" + echo "" + echo "安全恢复流程(推荐):" + echo " 1. 确认生产环境 Redis 已恢复" + echo " 2. 【重要】确认所有应用已切换到 Review Sentinel" + echo " 3. 【重要】停止生产 Sentinel: docker stack rm prod_sentinel" + echo " 4. 建议先停止 Review 应用对 Redis 的写入" + echo " 5. 执行 ./failover-to-review.sh rollback" + echo " - 步骤1: 让 10.56 作为 132 的从节点(反向同步)" + echo " - 步骤2: 等待数据完全同步" + echo " - 步骤3: 将 132 设为只读,提升 10.56 为主节点,恢复原架构" + echo " 6. 重新启动生产 Sentinel" + echo " 7. 修改应用配置,连接生产 Sentinel" + echo "" + echo "注意:" + echo " - 所有 Redis 操作通过 Docker 容器执行,需要 $REDIS_IMAGE 镜像" + echo " - 如果能连接生产 Redis,命令会自动远程执行" + echo " - 如果无法连接,会提示手动在生产服务器执行" + echo " - 如果 review_redis_master 容器运行中,优先使用该容器" + echo " - 否则使用临时容器执行命令" + ;; + esac +} + +main "$@" diff --git a/docker-swarm-review/redis-review-132/shake.toml b/docker-swarm-review/redis-review-132/shake.toml new file mode 100644 index 0000000..346a8c7 --- /dev/null +++ b/docker-swarm-review/redis-review-132/shake.toml @@ -0,0 +1,30 @@ +# redis-shake 配置文件 +# 从 132:6379 (数据源) 同步到 133:6380 (Review Master) + +[sync_reader] +# 源 Redis 地址 (132 - 从生产同步的数据源) +address = "192.168.3.132:6379" +password = "sino#650" +tls = false + +[redis_writer] +# 目标 Redis 地址 (133 - Review Master) +address = "192.168.3.133:6380" +password = "sino#650" +tls = false + +[advanced] +# 日志级别: debug, info, warn, error +log_level = "info" + +# 源库选择 (空表示同步所有库) +# db_filter_list = [0, 1, 2] + +# key 过滤 (支持正则) +# key_filter_list = [] + +# 大 key 阈值 (字节) +big_key_threshold = 524288000 + +# 并发 RDB 恢复线程数 +rdb_restore_command_behavior = "rewrite" diff --git a/docker-swarm-review/redis/README b/docker-swarm-review/redis/README new file mode 100644 index 0000000..5ec10fd --- /dev/null +++ b/docker-swarm-review/redis/README @@ -0,0 +1,4 @@ + +# crm1环境下 部署redis sentinel + +env $(cat ./env_crm1 | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - crm1_redis \ No newline at end of file diff --git a/docker-swarm-review/redis/docker-compose copy.yml b/docker-swarm-review/redis/docker-compose copy.yml new file mode 100644 index 0000000..484502a --- /dev/null +++ b/docker-swarm-review/redis/docker-compose copy.yml @@ -0,0 +1,89 @@ +version: '3.8' + +networks: + default: + name: ${NAMESPACE} + external: true +services: + master: + image: 'bitnami/redis:7.0.11' + environment: + - TZ=Asia/Shanghai + - REDIS_MASTER_HOST=192.168.1.207 + - REDIS_REPLICATION_MODE=master + - REDIS_MASTER_PASSWORD=${REDIS_PASSWORD} + - REDIS_PASSWORD=${REDIS_PASSWORD} + - REDIS_REPLICA_IP=192.168.1.207 + ports: + - mode: host + protocol: tcp + published: 6379 + target: 6379 + volumes: + - data_master:/bitnami + deploy: + update_config: + order: stop-first + placement: + constraints: + - node.hostname==okd7 + slave: + image: 'bitnami/redis:7.0.11' + environment: + - TZ=Asia/Shanghai + - REDIS_REPLICATION_MODE=slave + - REDIS_MASTER_HOST=192.168.1.207 + - REDIS_MASTER_PASSWORD=${REDIS_PASSWORD} + - REDIS_PASSWORD=${REDIS_PASSWORD} + - REDIS_REPLICA_IP=192.168.1.208 + ports: + - mode: host + protocol: tcp + published: 6379 + target: 6379 + depends_on: + - master + volumes: + - data_slave:/bitnami + deploy: + update_config: + order: stop-first + placement: + constraints: + - node.hostname==zd-dev-208 + redis-sentinel: + image: 'bitnami/redis-sentinel:7.0.11' + environment: + - TZ=Asia/Shanghai + - REDIS_MASTER_HOST=192.168.1.207 + - REDIS_MASTER_PASSWORD=${REDIS_PASSWORD} + - REDIS_SENTINEL_ANNOUNCE_IP=192.168.1.209 + - REDIS_PASSWORD=${REDIS_PASSWORD} + - REDIS_SENTINEL_PASSWORD=${REDIS_SENTINEL_PASSWORD} + depends_on: + - master + - slave + ports: + - mode: host + protocol: tcp + published: 26379 + target: 26379 + deploy: + update_config: + order: stop-first + mode: replicated + replicas: 1 # replicas模式, 副本数目为1 + placement: + constraints: + - node.hostname==zd-dev-209 + volumes: + - data_sentinel_1:/bitnami +volumes: + data_sentinel_1: + driver: local + data_sentinel_2: + driver: local + data_master: + driver: local + data_slave: + driver: local \ No newline at end of file diff --git a/docker-swarm-review/redis/docker-compose.yml b/docker-swarm-review/redis/docker-compose.yml new file mode 100644 index 0000000..942c434 --- /dev/null +++ b/docker-swarm-review/redis/docker-compose.yml @@ -0,0 +1,71 @@ +version: '3.8' + +networks: + default: + name: ${NAMESPACE} + external: true +services: + master: + image: 'bitnami/redis:7.0.11' + environment: + - TZ=Asia/Shanghai + - REDIS_REPLICATION_MODE=master + - REDIS_PASSWORD=${REDIS_PASSWORD} + ports: + - '${NODE_PORT}:6379' + volumes: + - data_master:/bitnami + deploy: + update_config: + order: start-first + placement: + constraints: + - node.labels.${NAMESPACE}_redis_master==1 + slave: + image: 'bitnami/redis:7.0.11' + environment: + - TZ=Asia/Shanghai + - REDIS_REPLICATION_MODE=slave + - REDIS_MASTER_HOST=${NAMESPACE}_redis_master + - REDIS_MASTER_PASSWORD=${REDIS_PASSWORD} + - REDIS_PASSWORD=${REDIS_PASSWORD} + depends_on: + - master + volumes: + - data_slave:/bitnami + deploy: + update_config: + order: start-first + placement: + constraints: + - node.labels.${NAMESPACE}_redis_slave==1 + redis-sentinel: + image: 'bitnami/redis-sentinel:7.0.11' + environment: + - TZ=Asia/Shanghai + - REDIS_MASTER_HOST=${NAMESPACE}_redis_master + - REDIS_MASTER_PASSWORD=${REDIS_PASSWORD} + - REDIS_SENTINEL_PASSWORD=${REDIS_SENTINEL_PASSWORD} + depends_on: + - master + - slave + deploy: + update_config: + order: start-first + mode: replicated + replicas: 3 # replicas模式, 副本数目为1 + placement: + constraints: + - node.labels.${NAMESPACE}_redis_sentinel==1 + volumes: + - data_sentinel:/bitnami +volumes: + data_sentinel: + driver: local + data_master: + driver: local + data_slave: + driver: local + + + diff --git a/docker-swarm-review/redis/env_crm1 b/docker-swarm-review/redis/env_crm1 new file mode 100644 index 0000000..0ea239c --- /dev/null +++ b/docker-swarm-review/redis/env_crm1 @@ -0,0 +1,4 @@ +NAMESPACE=crm1 +NODE_PORT=6379 +REDIS_PASSWORD=gkxl650 +REDIS_SENTINEL_PASSWORD=gkxl650 \ No newline at end of file diff --git a/docker-swarm-review/skywalking/README b/docker-swarm-review/skywalking/README new file mode 100644 index 0000000..51ee799 --- /dev/null +++ b/docker-swarm-review/skywalking/README @@ -0,0 +1,13 @@ + +### - SW_STORAGE_ES_ADVANCED={"index.lifecycle.name":"sw-policy"} +# 此处为配置索引的生命周期,需要在es中添加此项 + + +env $(cat ./env_crm1 | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - crm1_skywalking --with-registry-auth + +# + +env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_skywalking --with-registry-auth + + +env $(cat ./env | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - skywalking --with-registry-auth diff --git a/docker-swarm-review/skywalking/docker-compose.yml b/docker-swarm-review/skywalking/docker-compose.yml new file mode 100644 index 0000000..de687b2 --- /dev/null +++ b/docker-swarm-review/skywalking/docker-compose.yml @@ -0,0 +1,48 @@ +version: '3.8' +networks: + default: + name: ${NAMESPACE} + external: true +services: + oap: + image: apache/skywalking-oap-server:10.0.0 + hostname: ${NAMESPACE}-skywalking-oap + ports: + - '${NODE_PORT}:11800' + - '${NODE_PORT_2}:12800' + environment: + - TZ=Asia/Shanghai + - SW_STORAGE=elasticsearch + - SW_STORAGE_ES_CLUSTER_NODES=${NAMESPACE}-es-elasticsearch:9200 + - SW_HEALTH_CHECKER=default + - SW_TELEMETRY=prometheus + - SW_STORAGE_ES_ADVANCED={"index.lifecycle.name":"sw-policy"} + - JAVA_OPTS=-Xms2048m -Xmx2048m + volumes: + - 'ext_config:/skywalking/ext-config' + deploy: + placement: + constraints: + - node.labels.${NAMESPACE}_skywalking==1 + ui: + image: apache/skywalking-ui:10.0.0 + ports: + - "${NODE_PORT_UI}:8080" + environment: + - TZ=Asia/Shanghai + - SW_OAP_ADDRESS=http://${NAMESPACE}-skywalking-oap:12800 + - SW_ZIPKIN_ADDRESS=http://${NAMESPACE}-skywalking-oap:9412 + depends_on: + - oap + deploy: + update_config: + order: start-first + placement: + constraints: + - node.labels.${NAMESPACE}_skywalking==1 +volumes: + ext_config: + driver: local + + + diff --git a/docker-swarm-review/skywalking/env_crm1 b/docker-swarm-review/skywalking/env_crm1 new file mode 100644 index 0000000..c44e855 --- /dev/null +++ b/docker-swarm-review/skywalking/env_crm1 @@ -0,0 +1,4 @@ +NAMESPACE=crm1 +NODE_PORT=11800 +NODE_PORT_2=12800 +NODE_PORT_UI=18080 diff --git a/docker-swarm-review/skywalking/env_review b/docker-swarm-review/skywalking/env_review new file mode 100644 index 0000000..1e43eef --- /dev/null +++ b/docker-swarm-review/skywalking/env_review @@ -0,0 +1,4 @@ +NAMESPACE=review +NODE_PORT=11800 +NODE_PORT_2=12800 +NODE_PORT_UI=18080 diff --git a/docker-swarm-review/xxl-job-admin/README b/docker-swarm-review/xxl-job-admin/README new file mode 100644 index 0000000..5fe05bf --- /dev/null +++ b/docker-swarm-review/xxl-job-admin/README @@ -0,0 +1,8 @@ + +# crm1环境下 部署xxl-job + +env $(cat ./env_crm1 | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - crm1_xxl_job + + +env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - review_xxl_job + diff --git a/docker-swarm-review/xxl-job-admin/docker-compose.yml b/docker-swarm-review/xxl-job-admin/docker-compose.yml new file mode 100644 index 0000000..962a346 --- /dev/null +++ b/docker-swarm-review/xxl-job-admin/docker-compose.yml @@ -0,0 +1,24 @@ +version: '3.8' + +networks: + default: + name: ${NAMESPACE} + external: true +services: + server: + image: 'xuxueli/xxl-job-admin:2.4.1' + hostname: ${NAMESPACE}-xxl-job-admin + ports: + - '${NODE_PORT}:8080' + environment: + - TZ=Asia/Shanghai + - PARAMS=--xxl.job.logretentiondays=90 --xxl.job.accessToken= --spring.datasource.url=jdbc:mysql://${DATASOURCE_URL}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai --spring.datasource.username=${DATASOURCE_USERNAME} --spring.datasource.password=${DATASOURCE_PASSWORD} + deploy: + mode: replicated + replicas: ${REPLICAS} + update_config: + order: start-first + placement: + constraints: + - node.labels.xxl_job_admin==1 + max_replicas_per_node: 1 \ No newline at end of file diff --git a/docker-swarm-review/xxl-job-admin/env_crm1 b/docker-swarm-review/xxl-job-admin/env_crm1 new file mode 100644 index 0000000..94f8985 --- /dev/null +++ b/docker-swarm-review/xxl-job-admin/env_crm1 @@ -0,0 +1,6 @@ +NAMESPACE=crm1 +NODE_PORT=9991 +DATASOURCE_URL=crm1_mysql_db:3306/xxl_job +DATASOURCE_USERNAME=root +DATASOURCE_PASSWORD=gkxl650 +REPLICAS=1 \ No newline at end of file diff --git a/docker-swarm-review/xxl-job-admin/env_review b/docker-swarm-review/xxl-job-admin/env_review new file mode 100644 index 0000000..7bd5bc0 --- /dev/null +++ b/docker-swarm-review/xxl-job-admin/env_review @@ -0,0 +1,6 @@ +NAMESPACE=review +NODE_PORT=9991 +DATASOURCE_URL=review-tool-mysql-master:3306/xxl_job +DATASOURCE_USERNAME=zd_tool +DATASOURCE_PASSWORD=gkxl2024#@ +REPLICAS=2 \ No newline at end of file diff --git a/docker-swarm-review/部署手册.md b/docker-swarm-review/部署手册.md new file mode 100644 index 0000000..80f6c4e --- /dev/null +++ b/docker-swarm-review/部署手册.md @@ -0,0 +1,6621 @@ +# 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 <文件路径> + +# 示例 +docker config create review_mysql_conf_v1 /tmp/mysql_custom.cnf +``` + +### 8.3 查看 Config + +```bash +docker config ls +docker config inspect +``` + +### 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。 + +--- + +## 九、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/ +``` + +**示例:** + +```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 --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 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 +sleep 15 +docker volume rm +# 重新部署 +``` + +### 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 --pretty | grep -A 30 "Env" + +# 查看初始化日志是否有复制配置 +docker service logs 2>&1 | grep -iE "repl|slave|master|configur" +``` + +**解决:** + +```bash +# 删除 stack 和 slave 数据卷 +docker stack rm +sleep 15 +docker volume rm + +# 确保 Master 已启动并可连接 +docker run --rm --network <网络名> mysql -h -uroot -p'<密码>' -e "SELECT 1" + +# 重新部署 +env $(cat ./env_review | xargs) envsubst < ./docker-compose.yml | docker stack deploy --compose-file - +``` + +### 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 +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://: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://:9200/_cat/plugins?v + +# 预期输出应包含 analysis-ik + +# 测试 IK 分词器(ik_max_word 细粒度分词) +curl -X POST "http://:9200/_analyze?pretty" -H 'Content-Type: application/json' -d' +{ + "analyzer": "ik_max_word", + "text": "中华人民共和国国歌" +}' + +# 测试 IK 分词器(ik_smart 粗粒度分词) +curl -X POST "http://: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 +curl http://: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://:9200/_cluster/health?pretty + +# 检查节点信息 +curl http://: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://: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://: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://: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://: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 + + org.apache.skywalking + apm-toolkit-logback-1.x + +``` + +**关联查询:** +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://: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://: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://: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://: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://:9200/sw_*_20241201*" + +# 3. 查看 SkyWalking 索引 +curl "http://: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 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 - +``` + +### 19.2 常用管理命令 + +```bash +# 查看所有 Stack +docker stack ls + +# 查看 Stack 中的服务 +docker stack ps + +# 查看服务日志 +docker service logs <服务名> + +# 删除 Stack +docker stack rm + +# 更新服务 +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 +- [ ] 验证应用功能正常