一、架构说明
| 角色 | 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反向代理故障

