LVS+Nginx+Keepalived+Tomcat+MySQL高可用负载均衡集群
实验环境机器信息总览
所有机器规划在同一个内网网段 192.168.10.0/24,虚拟IP(VIP) 设定为 192.168.10.100,作为用户访问的统一入口。
整体操作流程概览
整个部署过程分为 6个阶段,按顺序执行:
Step 1: 基础环境准备(所有6台机器)
↓
Step 2: MySQL主从复制(mysql-master + mysql-slave)
↓
Step 3: Tomcat集群部署 + Session共享配置(web-1 + web-2)
↓
Step 4: Nginx反向代理部署(web-1 + web-2)
↓
Step 5: LVS + Keepalived 部署(lvs-master + lvs-backup)
↓
Step 6: 整体功能验证基础环境准备
# 1.关闭防火墙(所有6台机器)
sudo ufw disable
sudo systemctl stop ufw
sudo systemctl disable ufw
# 2.更新系统包(所有6台机器)
sudo apt update && sudo apt upgrade -y
# 3.安装常用工具(所有6台机器)
sudo apt install -y vim curl wget net-tools iputils-pingMySQL主从复制(mysql-master + mysql-slave)
# 安装MySQL(两台数据库机器) mysql-master 和 mysql-slave 上分别执行
sudo apt install mysql-server -y
sudo systemctl start mysql
sudo systemctl enable mysql配置MySQL主库(mysql-master)
# 编辑配置文件
vim /etc/mysql/mysql.conf.d/mysqld.cnf
# 在 [mysqld] 段落中添加或修改
[mysqld]
# ... 其他配置保持不变 ...
# 取消注释并修改以下三项:
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_do_db = testdb # 要同步的数据库名,可按需修改
# 确保 bind-address 改为 0.0.0.0 允许远程连接(主库必须)
bind-address = 0.0.0.0
# 重启MySQL
sudo systemctl restart mysql
# 登录MySQL并创建复制账号
sudo mysql
# 创建指令(记下输出中的 File 和 Position 值,配置从库时需要用到)
CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'Repl@123456';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
# 查看当前的二进制日志位置信息
SHOW MASTER STATUS;
# 显示内容
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 | 827 | testdb | | |
+------------------+----------+--------------+------------------+-------------------+配置MySQL从库(mysql-slave)
# 编辑配置文件
vim /etc/mysql/mysql.conf.d/mysqld.cnf
# 在 [mysqld] 段落中添加或修改
[mysqld]
server-id = 2 # 修改
read_only = 1 # 添加
# 重启MySQL
sudo systemctl restart mysql
# 登录MySQL
sudo mysql
# 配置主从关系
CHANGE MASTER TO
MASTER_HOST='192.168.10.31',
MASTER_USER='repl',
MASTER_PASSWORD='Repl@123456',
MASTER_LOG_FILE='mysql-bin.000001', -- 替换为上面记下的实际值
MASTER_LOG_POS=827; -- 替换为上面记下的实际值
# 启动复制
START SLAVE;
# 查看状态
SHOW SLAVE STATUS\G;Tomcat集群部署(web-1 + web-2)
# web-1 和 web-2 上分别执行
sudo apt install openjdk-11-jdk -y
java -version
# 下载并安装Tomcat(web-1 和 web-2 上分别执行)
# 1. 进入 /opt 目录
cd /opt
sudo wget https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.120/bin/apache-tomcat-9.0.120.tar.gz
sudo tar -xzf apache-tomcat-9.0.120.tar.gz
sudo mv apache-tomcat-9.0.120 tomcat
sudo chmod +x /opt/tomcat/bin/*.sh
/opt/tomcat/bin/startup.sh
# 配置Tomcat Session共享
vim /opt/tomcat/conf/server.xml
# 在 <Engine name="Catalina" defaultHost="localhost"> 标签内部(143h)
# 添加以下 <Cluster> 配置
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
channelSendOptions="6">
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4"
port="45564"
frequency="500"
dropTime="3000"/>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="192.168.10.21" # Web1 真实IP;Web2 改成 192.168.10.22
port="4000"
autoBind="100"
selectorTimeout="5000"
maxThreads="6"/>
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
</Channel>
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
<!-- 注意:FarmWarDeployer 需放在 <Host> 下,放 <Cluster> 里会报 SEVERE,已移除 -->
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>
# 为应用启用Session复制支持
vim /opt/tomcat/webapps/ROOT/WEB-INF/web.xml
# 添加以下内容(大概在21行左右)
<display-name>Welcome to Tomcat</display-name>
<distributable/> # 添加一行这个内容即可
<description>
# 启动Tomcat web-1 和 web-2 上分别执行(可能存在端口占用问题记得查看)
# ⚠️ 重启 Tomcat 的正确方式(直接再跑 startup.sh 会因旧实例占端口报 BindException):
# 先 ./shutdown.sh -> sleep 3 -> 再 ./startup.sh
# 若 shutdown 停不掉:pkill -9 -f tomcat 后重新 ./startup.sh
cd /opt/tomcat/bin
./startup.sh
# 查看启动日志确认集群成员加入成功
tail -f /opt/tomcat/logs/catalina.outNginx 配置步骤(web-1 和 web-2 都要操作)
# 安装 Nginx
sudo apt install nginx -y
sudo systemctl start nginx
sudo systemctl enable nginx
# 配置 Nginx 反向代理
vim /etc/nginx/sites-available/default
# 替换为以下内容
upstream tomcat_server {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name _;
# 静态资源(由 Nginx 直接返回)
location ~* \.(jpg|jpeg|png|gif|ico|css|js|html|htm)$ {
root /opt/tomcat/webapps/ROOT;
expires 7d;
}
# 动态请求(转发给本机 Tomcat)
location / {
proxy_pass http://tomcat_server;
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 X-Forwarded-Proto $scheme;
}
}
# 测试并重启 Nginx
sudo nginx -t
sudo systemctl restart nginx配置 LVS + Keepalived
# 在 LVS 调度器上安装软件(在 lvs-master 和 lvs-backup 上分别执行)
sudo apt update
sudo apt install ipvsadm keepalived -y
# 创建 keepalived.conf
sudo vim /etc/keepalived/keepalived.conf
# 粘贴以下配置(lvs-master)
! Configuration File for keepalived
global_defs {
router_id LVS_MASTER
}
vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.100/24 dev ens33
}
}
virtual_server 192.168.10.100 80 {
delay_loop 6
lb_algo wrr
lb_kind DR
persistence_timeout 50
# 备注:50=会话粘滞(同一客户端50秒内固定到同一台RS,属正常现象,不会轮询)
# 想看严格轮询改成0:sed -i "s/persistence_timeout 50/persistence_timeout 0/" /etc/keepalived/keepalived.conf 后重启 keepalived
protocol TCP
real_server 192.168.10.21 80 {
weight 1
TCP_CHECK {
connect_port 80
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
}
}
real_server 192.168.10.22 80 {
weight 1
TCP_CHECK {
connect_port 80
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
}
}
}
# 粘贴以下配置(lvs-backup)
! Configuration File for keepalived
global_defs {
router_id LVS_BACKUP
}
vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 51
priority 90
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.100/24 dev ens33
}
}
virtual_server 192.168.10.100 80 {
delay_loop 6
lb_algo wrr
lb_kind DR
persistence_timeout 50
# 备注:50=会话粘滞(同一客户端50秒内固定到同一台RS,属正常现象,不会轮询)
# 想看严格轮询改成0:sed -i "s/persistence_timeout 50/persistence_timeout 0/" /etc/keepalived/keepalived.conf 后重启 keepalived
protocol TCP
real_server 192.168.10.21 80 {
weight 1
TCP_CHECK {
connect_port 80
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
}
}
real_server 192.168.10.22 80 {
weight 1
TCP_CHECK {
connect_port 80
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
}
}
}
# 配置 Web 服务器的 ARP 抑制 (在 web-1 和 web-2 上分别执行)且必须在启动 LVS 之前完成
# 将 VIP 配置到回环接口的子接口
sudo ip addr add 192.168.10.100/32 dev lo
# 配置 ARP 抑制(临时生效)
sudo sysctl -w net.ipv4.conf.lo.arp_ignore=1
sudo sysctl -w net.ipv4.conf.lo.arp_announce=2
sudo sysctl -w net.ipv4.conf.all.arp_ignore=1
sudo sysctl -w net.ipv4.conf.all.arp_announce=2
# 配置永久生效
echo "net.ipv4.conf.lo.arp_ignore=1" | sudo tee -a /etc/sysctl.conf
echo "net.ipv4.conf.lo.arp_announce=2" | sudo tee -a /etc/sysctl.conf
echo "net.ipv4.conf.all.arp_ignore=1" | sudo tee -a /etc/sysctl.conf
echo "net.ipv4.conf.all.arp_announce=2" | sudo tee -a /etc/sysctl.conf
# DR 模式必须关闭 rp_filter(否则回环 VIP 的包被丢弃)
echo "net.ipv4.conf.all.rp_filter=0" | sudo tee -a /etc/sysctl.conf
echo "net.ipv4.conf.ens33.rp_filter=0" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
# 验证 VIP 配置
ip addr show lo | grep 192.168.10.100
# 启动 LVS + Keepalived
# 在 lvs-master 上执行 先查看实际网卡名
ip a
# 启动 Keepalived
sudo systemctl start keepalived
sudo systemctl enable keepalived
# 开启 IP 转发(LVS 必须,否则收包后无法转发)
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
# 查看 VIP 是否在主节点出现(替换 ens33 为实际网卡名)
ip addr show ens33 | grep 192.168.10.100
# 查看 LVS 转发规则
sudo ipvsadm -Ln整体功能验证
⚠️ 重要:DR 模式下,不要在 LVS 本机访问自己的 VIP(curl http://192.168.10.100 在 LVS 上会超时/SYN_RECV,这是 DR 模式的已知怪癖,不是故障)。验证必须从外部客户端(如 MySQL 主/从、或任意一台没有 VIP 的机器)访问。
① 从外部客户端访问 VIP
# 在 MySQL 主库(192.168.10.31)或从库(192.168.10.32)上执行
# 注意:不能在 LVS 本机测,不能在 Web 服务器上测(Web 的 lo 有 VIP 会打到本地)
curl http://192.168.10.100 # 应返回 Tomcat 页面
curl http://192.168.10.100 # 多次访问验证轮询② 查看 LVS 分发情况(LVS 主上)
sudo ipvsadm -Ln --stats # 看两台 Web 的 Conns 是否都有增长
sudo ipvsadm -Lnc # 看连接分发到 .21 / .22③ MySQL 主从同步验证(MySQL 从库上)
sudo mysql -e "SHOW SLAVE STATUS\G" | grep -E "Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master"
# 必须:Slave_IO_Running: Yes、Slave_SQL_Running: Yes、Seconds_Behind_Master: 0④ keepalived 主备切换验证
# 1. 停掉主节点 keepalived(模拟故障)
sudo systemctl stop keepalived # 在 lvs-master 上
# 2. 备节点应自动接管 VIP
ip addr show ens33 | grep 192.168.10.100 # 在 lvs-backup 上查看
# 3. 外部客户端继续访问,应无感知
curl http://192.168.10.100 # 在 MySQL 主库上
# 4. 恢复主节点,VIP 自动漂移回来(priority 100 > 90)
sudo systemctl start keepalived # 在 lvs-master 上⑤ Nginx 静态资源权限检查
# 若页面能打开但 CSS/图标 404,日志报 Permission denied,执行:
sudo chmod -R o+r /opt/tomcat/webapps
# 或
sudo chown -R www-data:www-data /opt/tomcat/webapps/ROOT常见问题排查(实测踩坑)
关键注意事项
验证 VIP 一定要用外部客户端,不要在 LVS 本机或 Web 服务器上测(都有 VIP)
LVS 必须
ip_forward=1,否则收包后无法转发(OutPkts=0)Web 必须
rp_filter=0(all 和 ens33 都要),否则 DR 转发包被丢弃Web 必须 ARP 抑制(arp_ignore=1 / arp_announce=2),且要持久化
Nginx 静态资源目录权限要给 nginx 用户读权限
keepalived 主备都
systemctl enable keepalived开机自启wrr persistent 50会粘滞会话,生产看需求保留或去掉MySQL 主库必须开 binlog + 建 repl 账号;从库
read_only=1
原创
LVS+Nginx+Keepalived+Tomcat+MySQL高可用负载均衡集群
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
评论交流
欢迎留下你的想法