ZrLog 高可用部署

一、架构说明

角色 IP 部署内容
数据库服务器 172.25.254.20 MariaDB
应用服务器 1 172.25.254.21 ZrLog(Maven 构建) + Tomcat + 阿里云镜像优化
应用服务器 2 172.25.254.22 ZrLog(Maven 构建) + Tomcat + 阿里云镜像优化
Nginx Master 1 172.25.254.23 Nginx + Keepalived + 邮件通知 + 阿里云镜像优化
Nginx Master 2 172.25.254.24 Nginx + Keepalived + 邮件通知 + 阿里云镜像优化
VIP 1 172.25.254.100 Keepalived 虚拟 IP
VIP 2 172.25.254.200 Keepalived 虚拟 IP

二、基础环境配置(所有主机)

2.1 挂载本地软件仓库

复制代码
[root@node1 ~]# mount /dev/sr0   /mnt
mount: /mnt: WARNING: source write-protected, mounted read-only.

2.2 安装基础依赖(所有主机)

复制代码
[root@node1 ~]# dnf install -y wget vim   tree net-tools   zip

三、部署 MariaDB(172.25.254.20)

复制代码
#安装MariaDB(阿里云源)
[root@node1 ~]# dnf install -y mariadb-server

#启动并设置开机自启
[root@node1 ~]# systemctl start mariadb && systemctl enable mariadb

#初始化数据库并配置ZrLog权限
[root@node1 ~]# mysql -u root -e "
CREATE DATABASE IF NOT EXISTS zrlog DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS 'zrlog'@'%' IDENTIFIED BY 'zrlog123456';
GRANT ALL PRIVILEGES ON zrlog.* TO 'zrlog'@'%';
FLUSH PRIVILEGES;
"
# 开放数据库端口
[root@node1 ~]# firewall-cmd --add-port=3306/tcp --permanent
[root@node1 ~]# firewall-cmd --reload

四、部署 ZrLog 应用服务器(172.25.254.21 和 172.25.254.22)

4.1 安装 JDK 11

复制代码
# 1.上传jdk-11.tar.gz到/root目录,执行解压
[root@node2 ~]# tar -zxf jdk-11_linux-x64_bin.tar.gz  
[root@node2 ~]# mv  jdk-11  jdk11

# 2.配置全局环境变量(永久生效)
[root@node2 ~]# cat > /etc/profile.d/jdk.sh << 'EOF'
export JAVA_HOME=/root/jdk11
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib
EOF

# 3.生效并验证
[root@node2 ~]# source /etc/profile.d/jdk.sh
[root@node2 ~]# java -version  # 输出JDK版本即成功

4.2 Maven(配置阿里云镜像)

复制代码
# 1. 下载 Maven 3.9.6(适配JDK 11),解压并重命名
[root@node2 ~]# tar -zxf   apache-maven-3.9.14-bin.tar.gz 
[root@node2 ~]# mv apache-maven-3.9.14 maven

# 2. 配置全局环境变量
[root@node2 ~]# cat > /etc/profile.d/maven.sh << 'EOF'
export MAVEN_HOME=/root/maven
export PATH=$MAVEN_HOME/bin:$PATH
EOF

[root@node2 ~]# source /etc/profile.d/maven.sh
[root@node2 ~]# mvn -version  # 输出Maven版本即成功

[root@node2 ~]# vim /root/maven/conf/settings.xml
160     <mirror>
161       <id>maven-default-http-blocker</id>
162       <mirrorOf>external:http:*</mirrorOf>
163       <name>Pseudo repository to mirror external repositories initially using HTTP.</name>
164       <url>http://0.0.0.0/</url>
165       <blocked>true</blocked>
166     </mirror>
167 
168     <mirror>
169     <id>tencentmaven</id>
170     <mirrorOf>central</mirrorOf>
171     <url>https://maven.aliyun.com/repository/public/</url>
172 </mirror>
173   </mirrors>

4.3 下载 ZrLog 源码并通过 Maven 构建

复制代码
# 1.解压到指定目录,避免文件混乱
[root@node2 ~]# unzip -q zrlog-3.3.0-1811fd7-release.zip -d zrlog-src
[root@node2 ~]# cd zrlog-src

# 2.进入pom.xml所在目录
[root@node2 ~]# find   /root/zrlog-src/   -name  pom.xml   
/root/zrlog-src/META-INF/maven/com.hibegin/package/pom.xml

# 3.配置阿里云镜像(解决依赖下载慢/失败)
[root@node2 ~]# mv /root/zrlog-src/META-INF/maven/com.hibegin/package/pom.xml /root/zrlog-src/META-INF/maven/com.hibegin/package/pom.xml.bak

# 4.写入全新的独立pom.xml(无需父POM,直接构建War包)
[root@node2 ~]# cat > /root/zrlog-src/META-INF/maven/com.hibegin/package/pom.xml << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- 手动定义项目基本信息(替代父POM) -->
    <groupId>com.hibegin</groupId>
    <artifactId>zrlog-web</artifactId>
    <version>3.3.0</version>
    <packaging>war</packaging>
    <name>ZrLog Web</name>
    <description>ZrLog Blog System</description>

    <!-- 核心依赖(覆盖父POM的关键依赖) -->
    <dependencies>
        <!-- Servlet & JSP 核心 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
            <scope>provided</scope>
        </dependency>
        <!-- Java EE 基础 -->
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>8.0.1</version>
            <scope>provided</scope>
        </dependency>
        <!-- 数据库驱动(ZrLog核心依赖) -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
        <!-- 日志依赖 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.36</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.13</version>
        </dependency>
    </dependencies>

    <!-- 构建配置(确保能打出可运行的War包) -->
    <build>
        <finalName>zrlog</finalName>
        <plugins>
            <!-- 编译插件(适配JDK11) -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <!-- War包插件(忽略缺失web.xml) -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.2</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                    <!-- 指向ZrLog的源码/资源目录(关键) -->
                    <webResources>
                        <resource>
                            <directory>/root/zrlog-src</directory>
                            <includes>
                                <include>WEB-INF/**/*</include>
                                <include>META-INF/**/*</include>
                                <include>**/*.jsp</include>
                                <include>**/*.html</include>
                                <include>**/*.css</include>
                                <include>**/*.js</include>
                            </includes>
                        </resource>
                    </webResources>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
EOF

[root@node2 ~]# cd  /root/zrlog-src/META-INF/maven/com.hibegin/package/

# 5.执行Maven打包(-DskipTests跳过测试,加快构建速度)
[root@node2 package]# mvn clean package -DskipTests

4.4 复制 War 包并清理旧数据

复制代码
[root@node2 ~]# tar -xaf   apache-tomcat-10.1.52.tar.gz 
[root@node2 ~]# mv apache-tomcat-10.1.52   tomcat10

#复制War包到Tomcat webapps目录,重命名为ROOT.war(直接访问根路径)
[root@node2 ~]# cd  /root/zrlog-src/META-INF/maven/com.hibegin/package/
[root@node2 package]# cp target/zrlog.war   /root/tomcat10/webapps/ROOT.war


#清理Tomcat旧解压文件(避免冲突)
[root@node2 package]# rm -rf /root/tomcat10/webapps/ROOT

4.5 启动 Tomcat 并验证

复制代码
#启动Tomcat
[root@node2 ~]# /root/tomcat10/bin/startup.sh

#查看启动日志(确认War包解压、服务无报错)
[root@node2 ~]# tail -f /root/tomcat10/logs/catalina.out

启动成功标识:


五、部署 Nginx + Keepalived(172.25.254.23 和 172.25.254.24)

5.1 安装 Nginx 和 Keepalived(阿里云源)

复制代码
[root@node4 ~]# dnf install -y nginx keepalived

5.2 配置 Nginx 负载均衡

复制代码
[root@node4 ~]# cat > /etc/nginx/conf.d/zrlog.conf << 'EOF'
upstream zrlog_backend {
    server 172.25.254.21:8080;
    server 172.25.254.22:8080;
}

server {
    listen 80;
    server_name _;

    location / {
        proxy_pass http://zrlog_backend;
        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_connect_timeout 5;
        proxy_send_timeout 10;
        proxy_read_timeout 10;
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 4 32k;
    }
}
EOF

#启动Nginx并设置开机自启
[root@node4 ~]# systemctl start nginx  &&  systemctl enable nginx
[root@node4 ~]# nginx -t

5.3 编写Nginx健康检查脚本

复制代码
[root@node4 ~]# cat > /usr/local/bin/nginx_check.sh << 'EOF'
#!/bin/bash
# Nginx健康检查脚本:检测进程+端口,双重验证
NGINX_PID=$(ps -ef | grep nginx | grep -v grep | grep -v "nginx_check.sh" | wc -l)
NGINX_PORT=$(netstat -tnlp | grep :80 | grep nginx | wc -l)

# 若进程数为0或80端口未监听,判定Nginx故障,返回非0(触发Keepalived故障)
if [ $NGINX_PID -eq 0 ] || [ $NGINX_PORT -eq 0 ]; then
    # 尝试重启Nginx(可选,失败则彻底触发故障)
    systemctl restart nginx
    sleep 3
    # 再次检测,仍失败则返回1
    NGINX_PID=$(ps -ef | grep nginx | grep -v grep | wc -l)
    NGINX_PORT=$(netstat -tnlp | grep :80 | grep nginx | wc -l)
    if [ $NGINX_PID -eq 0 ] || [ $NGINX_PORT -eq 0 ]; then
        exit 1
    fi
fi
# 检测成功,返回0
exit 0
EOF

# 赋予执行权限(关键,否则Keepalived无法调用)
[root@node4 ~]# chmod +x /usr/local/bin/nginx_check.sh

# 测试脚本:正常运行Nginx时返回0,停止Nginx时返回1
[root@node4 ~]# /usr/local/bin/nginx_check.sh
[root@node4 ~]# echo $?  # 输出0=正常,1=故障

5.4 创建 Shell 邮件发送脚本

复制代码
#安装邮件,并提供可执行权限
[root@node4 ~]# yum install s-nail   -y 
[root@node4 ~]# chmod  755 /etc/s-nail.rc

#编辑 mailx 主配置文件
[root@node4 ~]# vim /etc/s-nail.rc
#在文件末尾添加以下内容(替换为你的网易邮箱信息)
set from="xxxxxxxxx@163.com"  # 发件人邮箱
set smtp="smtps://smtp.163.com:465"  #网易邮箱 SMTP 地址+端口
set smtp-auth-user="xxxxxxxxxxx@163.com"  # 邮箱账号
set smtp-auth-password="你的网易邮箱授权码"  # 替换为新生成的授权码(关键!)
set smtp-auth=login  # 认证方式
set ssl-verify=ignore  # 忽略 SSL 验证(避免版本问题)
set nss-config-dir=/etc/pki/nssdb/  # SSL 证书路径(系统默认)

#执行以下命令,测试基础发送
[root@node4 ~]# echo "这是 Keepalived 邮件通知测试内容" | mailx -s "测试邮件" xxxxxxxxxxx@163.com

5.5 创建 Keepalived 通知脚本

复制代码
[root@node4 ~]# cat > /usr/local/bin/keepalived_mail.sh << 'EOF'
#!/bin/bash
# 参数:$1 = 状态(master/backup/fault)

# 配置参数
VIP="172.25.254.100"  # 你的 VIP 地址
TO_EMAIL="xxxxxxxxx@163.com"  # 收件邮箱
SUBJECT="【Keepalived 告警】节点状态变更"

# 拼接邮件内容
MESSAGE="
=====================================
Keepalived 节点状态变更通知
=====================================
节点状态:$1
VIP 地址:${VIP}
发生时间:$(date +'%Y-%m-%d %H:%M:%S')
=====================================
"

# 用 mailx 发送邮件
echo "${MESSAGE}" | mailx -s "${SUBJECT}" ${TO_EMAIL}

# 记录日志(可选,便于排查)
echo "$(date +'%Y-%m-%d %H:%M:%S') - 发送 Keepalived 状态邮件,状态:$1" >> /var/log/keepalived_mail.log
EOF

5.6 配置 Keepalived(双主模式)

  • 172.25.254.100(VI_1):当前服务器作为 MASTER(主节点) 持有这个 VIP;
  • 172.25.254.200(VI_2):当前服务器作为 BACKUP(备节点),仅当持有该 VIP 的主节点故障时才接管;
  • 配合 notify 脚本,在节点角色切换(主 / 备 / 故障)时执行自定义操作(比如切换 Nginx 配置、发送告警)。

172.25.254.23配置:

复制代码
[root@node4 ~]# cat > /etc/keepalived/keepalived.conf << 'EOF'
global_defs {
   router_id LVS_20
}

vrrp_script check_nginx {
    script "/usr/local/bin/nginx_check.sh"  # 调用健康检查脚本
    interval 2  
    weight -20  
    fall 3
    rise 2   
}

vrrp_instance VI_1 {
    state MASTER
    interface ens160
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        172.25.254.100
    }
    track_script {
        check_nginx
    }
    # 状态切换通知脚本:节点角色变化时执行指定脚本,并传递参数(master/backup/fault)
    # notify_master:节点成为主节点时执行
    # notify_backup:节点成为备节点时执行
    # notify_fault:节点故障时执行
    notify_master "/usr/local/bin/keepalived_mail.sh master"
    notify_backup "/usr/local/bin/keepalived_mail.sh backup"
    notify_fault "/usr/local/bin/keepalived_mail.sh fault"
}

vrrp_instance VI_2 {
    state BACKUP
    interface ens160
    virtual_router_id 52
    priority 90
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 2222
    }
    virtual_ipaddress {
        172.25.254.200
    }
    track_script {
        check_nginx
    }
    notify_master "/usr/local/bin/keepalived_mail.sh master"
    notify_backup "/usr/local/bin/keepalived_mail.sh backup"
    notify_fault "/usr/local/bin/keepalived_mail.sh fault"
}
EOF

172.25.254.24配置:

复制代码
[root@node5 ~]# cat > /etc/keepalived/keepalived.conf << 'EOF'
global_defs {
   router_id LVS_23
}

vrrp_script check_nginx {
    script "/usr/local/bin/nginx_check.sh"  # 调用健康检查脚本
    interval 2  
    weight -20  
    fall 3
    rise 2   
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens160
    virtual_router_id 51
    priority 90
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        172.25.254.100
    }
    track_script {
        check_nginx
    }
    notify_master "/usr/local/bin/keepalived_mail.sh master"
    notify_backup "/usr/local/bin/keepalived_mail.sh backup"
    notify_fault "/usr/local/bin/keepalived_mail.sh fault"
}

vrrp_instance VI_2 {
    state MASTER
    interface ens160
    virtual_router_id 52
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 2222
    }
    virtual_ipaddress {
        172.25.254.200
    }
    track_script {
        check_nginx
    }
    notify_master "/usr/local/bin/keepalived_mail.sh master"
    notify_backup "/usr/local/bin/keepalived_mail.sh backup"
    notify_fault "/usr/local/bin/keepalived_mail.sh fault"
}
EOF

5.7 启动 Keepalived

复制代码
[root@node4 ~]# systemctl start keepalived
[root@node4 ~]# systemctl enable keepalived

六、验证结果

6.1 检查VIP是否生效(在负载均衡器上)

6.2 测试Maven构建产物访问

6.3 测试反向代理访问

6.4 VIP漂移

6.5 Nginx反向代理故障

相关推荐
Elastic 中国社区官方博客2 小时前
Elasticsearch:使用 Agent Builder 的 A2A 实现 - 开发者的圣诞颂歌
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
2301_816660212 小时前
PHP怎么处理Eloquent Attribute Inference属性推断_Laravel从数据自动推导类型【操作】
jvm·数据库·python
qq_372154233 小时前
Go 中自定义类型与基础类型的显式转换规则详解
jvm·数据库·python
_下雨天.4 小时前
NoSQL之Redis配置与优化
数据库·redis·nosql
热爱专研AI的学妹4 小时前
Seedance 2.0(即梦 2.0)深度解析:AI 视频正式迈入导演级精准可控时代
大数据·人工智能·阿里云·音视频
LiAo_1996_Y4 小时前
CSS如何实现文字渐变效果_通过background-clip实现艺术字
jvm·数据库·python
2401_887724504 小时前
CSS如何让表单在手机端友好展示_利用Flexbox实现堆叠排版
jvm·数据库·python
数据库小组4 小时前
MySQL 删库后怎么恢复?binlog2sql 之外,NineData 还能做什么
数据库·sql·mysql·安全·数据·ninedata·删库
zhangchaoxies4 小时前
Layui轮播图(carousel)怎么设置自动播放间隔
jvm·数据库·python
切糕师学AI5 小时前
HBase:一文搞懂分布式宽列数据库(原理 + 架构 + 实战)
数据库·分布式·nosql·hbase·分布式宽列数据库·wide column db