LVS-DR 高可用集群实战:NFS + Web + DNS + Keepalived 全套架构

一、为什么做这个项目 / 和之前项目的区别

这次引入了 LVS(Linux Virtual Server)四层流量转发,实现:

  • 统一的流量入口:客户端只认一个 VIP,后端随便换

  • 负载均衡:请求被平均 / 加权地分到多台后端

  • 高可用:Keepalived 主备 + 健康检查,后端挂了自动剔除,主备切换 VIP 漂移

这次项目的关键设计点:

  1. 转发类型全部用 LVS-DR(直接路由)

  2. 调度算法用了两种 :Web 集群用 wrr (加权轮询),DNS 集群用 rr(纯轮询)

  3. NFS 做共享存储(网页内容一份,所有 Web 共用)

  4. BIND 主从 DNS(dns01 master / dns02 slave)解析域名


二、整体架构图

复制代码
                    ┌──────────────────────────────┐
                    │     客户端 (如 nfs01 模拟)     │
                    │   resolv.conf: nameserver     │
                    │       192.168.211.200         │
                    └──────────────┬───────────────┘
                                   │
        DNS VIP .200               │                 Web VIP .100
┌──────────────────────────────────┼──────────────────────────────────┐
│  lb-master (.71)  ←── keepalived──→  lb-backup (.72)                 │
│   ├─ DNS 虚拟服务  192.168.211.200:53 (rr)                            
│   │     └──→ dns01 (.61)      ←── BIND 主从 ──→  dns02 (.62)         │
│   └─ Web 虚拟服务  192.168.211.100:80 (wrr)     ← NFS /share 共享     │
│         └──→ web01 (.81) / web02 (.82)  (lo:0 绑 .100)               │
└──────────────────────────────────────────────────────────────────────┘

角色分配表:

主机 IP 角色
lb-master 192.168.211.71 LVS Director(Keepalived MASTER)
lb-backup 192.168.211.72 LVS Director 备(Keepalived BACKUP)
web01 192.168.211.81 Web 后端 RS(挂 NFS /share)
web02 192.168.211.82 Web 后端 RS(挂 NFS /share)
dns01 192.168.211.61 DNS master(BIND)
dns02 192.168.211.62 DNS slave(BIND)
nfs01 192.168.211.51 NFS 共享存储 + 模拟外部客户端

三、核心原理(讲透才记得住)

1. LVS 的三种模式:为什么选 DR

模式 原理 数据包修改 回包路径 特点
NAT Director 改源/目的 IP 必须经 Director 回 适合小规模,Director 是瓶颈
DR(Direct Routing) Director 只改 MAC,不懂 IP 只改二层 MAC 后端直接回客户端 性能最好,后端要求有 VIP + 关 ARP
TUN IP 隧道封装 加 IP 头 后端隧道回 跨网段,复杂

选 DR 的原因:回包不经过 Director,Director 只做转发入口,吞吐最高。这是生产最常用的方案。

2. LVS-DR 的三件套(后端 RS 必须配)

DR 模式下,后端服务器要做三件事(记忆口诀:藏家门、挂门牌、别抢答):

  1. 挂门牌:把 VIP 绑到回环口

    复制代码
    ip addr add 192.168.211.100/32 dev lo

    挂在 lo(回环)而不是 ens160,是为了让 VIP 只用于"收包",不参与物理网卡对外。

  2. 别抢答(ARP 抑制):防止后端对外应答 VIP 的 ARP 请求

    复制代码
    vim /etc/sysctl.conf
    # 加入
    net.ipv4.conf.all.arp_ignore=1
    net.ipv4.conf.all.arp_announce=2
    net.ipv4.conf.lo.arp_ignore=1
    net.ipv4.conf.lo.arp_announce=2
    sysctl -p

    arp_ignore=1:只回答"目标 IP 是本接口 IP"的 ARP → 后端不会回应 VIP 的查询,外界只认 Director。 arp_announce=2:对外通告时只用自己接口的 IP → 不主动宣告 VIP。

  3. 永久挂载(防重启丢失):用 systemd 服务做开机自启(后面讲)

判断后端配置对了没(一句话): 客户端 arp -n 看 VIP 对应的 MAC,必须是指向 Director 而不是后端。

3. 调度算法 rr vs wrr

算法 全名 原理 本次用在
rr Round Robin 轮询 请求依次轮流分配到后端,一台一个 DNS 集群 (.200:53)
wrr Weighted Round Robin 加权轮询 按权重比例分配,权重高的分得多 Web 集群 (.100:80)

本次实践里的差别:

  • Web 用 wrr:web01 weight 2、web02 weight 1 → 大约 2:1 分配(web01 能力强就多扛一点)

  • DNS 用 rr:两台 DNS 均等,weight 1 → 轮流响应

4. Keepalived 主备 + VIP 漂移

  • Keepalived 用 VRRP 协议在 master/backup 之间同步状态

  • **VIP(虚拟 IP)**由 Keepalived 管理:MASTER 持有,BACKUP 待命

  • master 挂了 → backup 收不到 VRRP 通告 → 自动升 MASTER → VIP 漂移到 backup → 对外无感

  • 每个 vrrp_instance独立的 virtual_router_id(VRID),同一接口上必须唯一,否则脑裂

两个 VIP:

VIP 归属实例 用途
192.168.211.100 VI_WEB (VRID 51) Web 网站入口
192.168.211.200 VI_DNS (VRID 52) DNS 查询入口

Keepalived 既管 VIP(VRRP),又管 LVS 规则(把 virtual_server 写进去就能动态加载 ipvsadm)。


四、部署步骤(按顺序)

Step 1:保持网络与主机名

全部 RHEL9,/etc/hosts 里写清各主机名 ↔ IP 映射(DNS 也用它)。

Step 2:NFS 共享(nfs01, .51)

复制代码
# nfs01 上
dnf install -y nfs-utils
mkdir -p /share
echo "this is nfs share" > /share/index.html
# 导出
cat > /etc/exports <<'EOF'
/share 192.168.211.0/24(rw,sync)
EOF
exportfs -arv
systemctl enable --now nfs-server rpcbind
showmount -e 192.168.211.51   # 验证共享

Step 3:Web 后端(web01 .81 / web02 .82)

复制代码
# 1. 装 nginx
dnf install -y nginx
​
# 2. 挂 NFS 到 nginx 的 html 目录
mkdir -p /usr/share/nginx/html
mount -t nfs4 192.168.211.51:/share /usr/share/nginx/html
# 永久挂载(坑:_netdev 必须加,否则开机网卡没起就挂会卡住)
echo "192.168.211.51:/share /usr/share/nginx/html nfs4 defaults,_netdev,vers=4 0 0" >> /etc/fstab
​
# 3. 配 LVS-DR 三件套(VIP + ARP 抑制),见上文
# 4. nginx 的 root 目录 = /usr/share/nginx/html
systemctl enable --now nginx

⚠️ :nginx 默认 html 目录是 /usr/share/nginx/html,不是 /usr/bin/nginx/html。挂载点要对准。

Step 4:DNS 主从(dns01 .61 / dns02 .62)

复制代码
# dns01 (master)
dnf install -y bind
vim /etc/named.conf
#   listen-on port 53 { 192.168.211.61;192.168.211.200; };
#   directory "/var/named";
#   zone "chengke.com" { type master; file "chengke.zone"; allow-transfer { 192.168.211.62; }; };
cat > /var/named/chengke.zone <<'EOF'
$TTL 1D
@  IN SOA dns1.chengke.com. root.chengke.com. ( 3 1D 1H 1W 3H )
   IN NS dns1
   IN NS dns2
dns1 IN A 192.168.211.61
dns2 IN A 192.168.211.62
www  IN A 192.168.211.100
txt  IN TXT "AaBbCcDdEeFf"
EOF
systemctl enable --now named
​
# dns02 (slave)
# zone "chengke.com" { type slave; masters { 192.168.211.61; }; file "slaves/chengke.zone"; };
systemctl enable --now named

Step 5:Keepalived + LVS(lb-master .71 / lb-backup .72,两台都要)

复制代码
dnf install -y keepalived ipvsadm bind-utils
# 两台都要装 bind-utils!(提供 dig,健康检查脚本要用,见排错)

lb-master 的 keepalived.conf 关键片段:

复制代码
global_defs { router_id lvs-master; }
​
# Web VIP 实例
vrrp_instance VI_WEB {
    state MASTER            # backup 上是 BACKUP
    interface ens160
    virtual_router_id 51    # 唯一!
    priority 100            # backup 设 80
    virtual_ipaddress { 192.168.211.100 }
}
​
# DNS VIP 实例
vrrp_instance VI_DNS {
    state MASTER
    interface ens160
    virtual_router_id 52    # 唯一!不能和 51 重复
    priority 100
    virtual_ipaddress { 192.168.211.200 }
}
​
# Web 虚拟服务(wrr)
virtual_server 192.168.211.100 80 {
    delay_loop 6
    lb_algo wrr             # 加权轮询
    lb_kind DR              # 直接路由
    protocol TCP
    real_server 192.168.211.81 80 { weight 2; TCP_CHECK { connect_timeout 3; retry 3; delay_before_retry 3; connect_port 80; } }
    real_server 192.168.211.82 80 { weight 1; TCP_CHECK { connect_timeout 3; retry 3; delay_before_retry 3; connect_port 80; } }
}
​
# DNS 虚拟服务(rr)
virtual_server 192.168.211.200 53 {
    delay_loop 6
    lb_algo rr              # 纯轮询
    lb_kind DR
    protocol UDP
    real_server 192.168.211.61 53 { weight 1; MISC_CHECK { misc_timeout 3; misc_path "/etc/keepalived/script/checkdns.sh -h 192.168.211.61 txt.chengke.com"; } }
    real_server 192.168.211.62 53 { weight 1; MISC_CHECK { misc_timeout 3; misc_path "/etc/keepalived/script/checkdns.sh -h 192.168.211.62 txt.chengke.com"; } }
}

Step 6:健康检查脚本 checkdns.sh(两台 LVS 都要)

复制代码
cat > /etc/keepalived/script/checkdns.sh <<'EOF'
#!/bin/bash
[ $# -le 2 ] && { echo "usage: $0 -h <ip>"; exit 2; }
domain=$3
while getopts "h:" OPT; do
  case $OPT in
    h) host=$OPTARG ;;
    *) echo "usage: $0 -h <ip>" && exit 1 ;;
  esac
done
dig @${host} txt ${domain} +time=1 | grep "\<AaBbCcDdEeFf\>" > /dev/null
exit $?
EOF
chmod +x /etc/keepalived/script/checkdns.sh

Step 7:确认 ipvsadm 开机自启

复制代码
# 把当前规则保存到 /etc/sysconfig/ipvsadm(ipvsadm.service 启动时 restore)
ipvsadm-save > /etc/sysconfig/ipvsadm
systemctl enable --now ipvsadm

五、避坑清单 / 排错记录

按"现象 → 根因 → 解决"记录,面试能讲出真实排错故事。

坑1:keepalived 启动失败,退出码 2

现象systemctl status keepalived → failed 日志VI_WEB and VI_DNS both use VRID 51Unknown keyword 'track_scrip' 根因

  1. 两个 vrrp_instance 用了同一个 virtual_router_id 51(同接口上 VRID 必须唯一)→ 致命

  2. track_scrip { 拼写错误(应为 track_script解决 :VI_DNS 改成 VRID 52,track_script 写对。改完记得 keepalived -t 验证 + systemctl restart

坑2:wRR / TCP_CHECK 关键字写错

现象keepalived -tUnknown keyword 日志nb_get_retryconnect_timeout(在错误位置) 根因

  • TCP_CHECK 里没有 nb_get_retry,正确是 retry

  • MISC_CHECK 里没有 connect_timeout,正确是 misc_timeout 解决:改成正确关键字。

坑3:web 后端两台都有 VIP,是不是脑裂?

现象 :web01 和 web02 的 lo:0 都绑着 192.168.211.100 关键认知这是对的! LVS-DR 后端本来就该每台都绑 VIP(要收包)。真正的脑裂指 Keepalived 主备同时持有同一 VIP 并对外宣告判断 :只要 ARP 抑制配好了,后端即使有 VIP 也不对外应答,流量只会进 Director。arp -n 看 VIP 的 MAC 是 Director 就对了。

坑4:ipvsadm 开机自启失败

现象systemctl enable --now ipvsadmFailed 日志/etc/sysconfig/ipvsadm: 没有那个文件或目录 根因 :规则从没保存到配置文件 解决ipvsadm-save > /etc/sysconfig/ipvsadm

坑5:ipvsadm 配置里混入 Director 自身 IP(.71)

现象ipvsadm -Ln 出现 192.168.211.71:80 这种规则 根因 :历史手动 ipvsadm -A 残留 + ipvsadm.serviceExecStop=ipvsadm-save 机制把脏规则固化 解决 :清空重写正确规则(只留 .100.200)再 ipvsadm-restore + ipvsadm-save

坑6:curl <www.chengke.com> 打不开 / 打开的是公网页面

现象 :域名能访问 200,但内容不是 NFS 的 "this is nfs share" 根因 :web 机的 resolv.conf 写的是 VIP .200 → web 作为 LVS 后端,通过 VIP 访问自己集群 DNS 会超时 → 自动 fallback 到 8.8.8.8(公网)→ 解析到公网 chengke.com 解决web/dns 这类 LVS 后端机器,resolv.conf 用 DNS 的"真实 IP"(.61/.62)互相指,不要用 VIP

重要原理"集群外的客户端"用 VIP .200(享受完整 LVS 高可用);"集群内的 RS/Director"不能用 VIP(DR 限制),用真实 IP failover 就够。

坑7:DNS 主从 zone 不同步

现象 :从 dns02 查 <www.chengke.com> 返回 .200,应该 .100 根因 :dns01 的 zone 改成 .100 后,dns02(slave)没重新拉取,还是旧的 .200 解决 :dns01 rndc reload chengke.com(并递增 serial);dns02 rndc retransfer chengke.com

坑8:checkdns.sh 健康检查失败 → DNS 后端被剔除

现象 :外部客户端用 .200 查 DNS 超时 / 无法解析 日志Misc check ... checkdns.sh failed ... Removing service [.61]:udp:53 根因(两个)

  1. 脚本语法错误dig ... > /dev/null 被断成两行 → 应合并为 > /dev/null

  2. dig 命令不存在 :LVS 主机没装 bind-utils!→ dnf install -y bind-utils 解决 :修好脚本 + 装 bind-utils → 健康检查成功 → LVS 自动恢复后端(add service

坑9:NetworkManager 覆盖 resolv.conf

现象 :改了 /etc/resolv.conf,重启/重连就被还原 根因 :RHEL 用 NetworkManager 管理 DNS 解决(永久):

复制代码
nmcli con mod ens160 ipv4.dns "192.168.211.61 192.168.211.62" ipv4.ignore-auto-dns yes
nmcli con up ens160

坑10:Director 上"检查本机服务"的 vrrp_script 是死配置

现象 :keepalived.conf 里定义了 vrrp_script monitor_nginx / vrrp_script check_named,日志一直报 failed (exited with status 4),但主备还正常。 根因vrrp_script 是检查 keepalived 所在主机(Director)本机的服务健康度,用来触发主备切换。但本架构里:

  • nginx 跑在 web01/web02 后端,不在 lb-master/lb-backup 上

  • named 跑在 dns01/dns02 后端,也不在 Director 上 所以这两个脚本在 Director 上永远失败monitor_nginx 永远找不到 nginx、check_named 里的 systemctl status named 返回 4 = 没装 named),是纯 dead code,甚至还会给 VI_DNS 永久扣掉 2 分优先级(weight 2),属于"假降级"隐患。 关键认知LVS-DR 架构下,业务服务(nginx/DNS)都在后端 RS 上,Director 不跑业务服务 → Director 上不需要用 vrrp_script 做本机服务检查。 服务健康检查的正确姿势(分两层):

  • 后端 RS 是否还"活着" → 用 real_server 里的 TCP_CHECK / MISC_CHECK(keepalived 对整个 .100/.200 虚拟服务做健康检查,坏的后端自动从 LVS 剔除)

  • Director 之间的主备 → 只靠 VRRP 本身判断(收不到对端通告就切),不需要业务脚本 解决 :删掉 vrrp_script monitor_nginxvrrp_script check_named 及其 track_script,只保留 real_server 里的 CHECK。


六、最终验收(端到端全链路)

1. 各层验证命令

复制代码
# LVS 规则(.100 wrr + .200 rr)
ipvsadm -Ln

# Keepalived 主备(master 持 VIP、backup 不持)
ip addr | grep 192.168.211.1   # master 上应看到 .100/.200
systemctl status keepalived

# 后端 lo 上的 VIP + ARP 抑制
ip addr show lo | grep 211
cat /proc/sys/net/ipv4/conf/lo/arp_ignore

# NFS 挂载
mount | grep nginx/html

# DNS 解析(外部客户端用 VIP .200)
dig www.chengke.com @192.168.211.200

2. 端到端访问测试

复制代码
# 从 web01 / web02(内网后端,走真实 IP 解析)
curl http://www.chengke.com/     # → this is nfs share

# 从 nfs01(模拟外部客户端,走 VIP .200 解析 DNS)
# 域名解析成 .100 后访问 web VIP
curl http://192.168.211.100/     # → this is nfs share

3. 负载均衡效果验证

复制代码
# 打多次请求后看 ipvsadm 统计,确认 wrr 按 2:1 分发
ipvsadm -Ln --stats
# TCP 192.168.211.100:80  Conns 6
#   -> 192.168.211.81   4   (weight 2 → 约2/3)
#   -> 192.168.211.82   2   (weight 1 → 约1/3)

4. 本次项目最终状态(全绿)

环节 状态
Keepalived 主备 ✅ master 持 VIP、backup 待命、无脑裂
LVS Web 分发 ✅ .100 wrr → web01:w=2 / web02:w=1
LVS DNS 分发 ✅ .200 rr → dns01/dns02
DNS 主从 ✅ dns01 master / dns02 slave,都返回 .100
NFS 共享 ✅ web01/02 挂载 /share
外部客户端 DNS ✅ nfs01 用 .200 解析 www → .100
健康检查 checkdns.sh + dig 正常,后端在线

七、一句话记忆总结

  • LVS-DR:Director 只该 MAC 不改 IP,后端直接回包,吞吐最高

  • 三件套:挂 VIP(lo)+ ARP 抑制(藏起来)+ 持久化(防重启丢)

  • rr vs wrr:轮询吃果果 vs 谁壮谁多吃

  • VIP 使用规则:集群外客户端用 VIP;集群内的 RS/Director 用真实 IP(DR 限制)

  • 健康检查:后端要被 keepalived 认为"活着"才在 LVS 里上线,坏了就剔出去

相关推荐
搬砖的小码农_Sky1 小时前
LANDrop 在 Ubuntu 上的安装方法
linux·运维·计算机网络·ubuntu
raindayinrain2 小时前
深入理解Linux内核--内存架构,页框分配器,性能优化
linux·numa·水位·区域·页框分配
Mortalbreeze2 小时前
深入理解 TCP 协议(二):TCP 可靠传输与高效通信机制详解
linux·服务器·网络·网络协议·tcp/ip
sel_92 小时前
【Linux】Linux 常用命令手册
linux·服务器
bksczm2 小时前
细谈传输层TCP协议API接口的底层原理
服务器·网络·tcp/ip
Zguigo2 小时前
lesson27-29计算机网络第三章精讲:交换机与VLAN的工作原理、区别与实战应用
网络·计算机网络·负载均衡
网络设计ensp2 小时前
商超网络设计
网络·学习·安全·智能路由器·ensp·华为网络设计
2401_868534782 小时前
企业信息安全建设
linux·网络协议·beautifulsoup
小玮看世界2 小时前
[Python]从40分到100分:一场关于编程思维的“辩论赛”
运维·服务器