一、为什么做这个项目 / 和之前项目的区别
这次引入了 LVS(Linux Virtual Server) 做四层流量转发,实现:
-
统一的流量入口:客户端只认一个 VIP,后端随便换
-
负载均衡:请求被平均 / 加权地分到多台后端
-
高可用:Keepalived 主备 + 健康检查,后端挂了自动剔除,主备切换 VIP 漂移
这次项目的关键设计点:
-
转发类型全部用 LVS-DR(直接路由)
-
调度算法用了两种 :Web 集群用 wrr (加权轮询),DNS 集群用 rr(纯轮询)
-
NFS 做共享存储(网页内容一份,所有 Web 共用)
-
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 模式下,后端服务器要做三件事(记忆口诀:藏家门、挂门牌、别抢答):
-
挂门牌:把 VIP 绑到回环口
ip addr add 192.168.211.100/32 dev lo挂在
lo(回环)而不是ens160,是为了让 VIP 只用于"收包",不参与物理网卡对外。 -
别抢答(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 -parp_ignore=1:只回答"目标 IP 是本接口 IP"的 ARP → 后端不会回应 VIP 的查询,外界只认 Director。arp_announce=2:对外通告时只用自己接口的 IP → 不主动宣告 VIP。 -
永久挂载(防重启丢失):用 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、web02weight 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 51、Unknown keyword 'track_scrip' 根因:
-
两个
vrrp_instance用了同一个virtual_router_id 51(同接口上 VRID 必须唯一)→ 致命 -
track_scrip {拼写错误(应为track_script) 解决 :VI_DNS 改成 VRID 52,track_script写对。改完记得keepalived -t验证 +systemctl restart。
坑2:wRR / TCP_CHECK 关键字写错
现象 :keepalived -t 报 Unknown keyword 日志 :nb_get_retry、connect_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 ipvsadm 报 Failed 日志 :/etc/sysconfig/ipvsadm: 没有那个文件或目录 根因 :规则从没保存到配置文件 解决 :ipvsadm-save > /etc/sysconfig/ipvsadm
坑5:ipvsadm 配置里混入 Director 自身 IP(.71)
现象 :ipvsadm -Ln 出现 192.168.211.71:80 这种规则 根因 :历史手动 ipvsadm -A 残留 + ipvsadm.service 的 ExecStop=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 根因(两个):
-
脚本语法错误 :
dig ... >和/dev/null被断成两行 → 应合并为> /dev/null -
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_nginx、vrrp_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 里上线,坏了就剔出去