LVS+Keepalived+DNS+Web+NFS 高可用集群项目完整部署流程

LVS+Keepalived+DNS+Web+NFS 高可用集群项目完整部署流程

一、集群架构规划(共 8 台虚拟机)

ip段用自己的

当然完成这个案例并不是一定要8台虚拟机,集群是可以合并在一起或者一个功能集群少开一点虚拟机

节点角色主机名IP 地址核心职责
Web 节点 1web01192.168.72.201挂载 NFS 共享,提供 Web 服务
Web 节点 2web02192.168.72.202挂载 NFS 共享,提供 Web 服务
Web 节点 3web03192.168.72.203挂载 NFS 共享,提供 Web 服务
DNS 主节点dns-master192.168.72.107解析www.chengke.com到 Web VIP
DNS 从节点dns-slave192.168.72.108同步 DNS 主节点配置,备用
LB 主节点lb-master192.168.72.105LVS+Keepalived,承载 DNS/Web VIP
LB 备节点lb-backup192.168.72.106LB 故障时接管 VIP
NFS 存储节点nfs-server192.168.72.210提供 Web 内容共享存储
虚拟 IP(VIP)-192.168.72.100DNS 服务 VIP
虚拟 IP(VIP)-192.168.72.200Web 服务 VIP

按照上面的主机名和ip先把虚拟机初始化好,用下面的脚本非常方便

二、完整部署流程

阶段 1:NFS 节点初始化

1.1 脚本

脚本如下:

#!/bin/bash # useage: sudo ./init_sys.sh <hostname> <ip_address> [gateway] [dns] RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' log_info() { echo -e "${GREEN}[INFO]${NC} $(date +'%F %T') $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $(date +'%F %T') $1" } log_error() { echo -e "${RED}[ERROR]${NC} $(date +'%F %T') $1" } check_root() { if [[ $EUID -ne 0 ]]; then log_error "此脚本必须以root权限运行" exit 1 fi } usage() { echo "用法: $0 <hostname> <ip_address> [gateway] [dns_servers]" echo "" echo "参数说明:" echo " hostname 要设置的主机名" echo " ip_address 要设置的静态IP地址 (如: 192.168.72.100)" echo " gateway 网关地址 (如: 192.168.24.2)" echo " dns_servers DNS服务器,逗号分隔 (可选,默认: 223.5.5.5,8.8.8.8)" echo "" echo "示例:" echo " $0 myserver 192.168.72.100 192.168.72.2 8.8.8.8,223.5.5.5" echo " $0 webserver 10.0.0.50" exit 1 } get_interface() { # 尝试获取第一个活动的非环回接口(优化鲁棒性) local interface=$(ip -4 route show default | awk '/default/ {print $5}' 2>/dev/null) if [[ -z "$interface" ]]; then interface=$(ip link show | grep -v lo | grep -E 'state UP|state RUNNING' | head -1 | awk -F': ' '{print $2}' | sed 's/ //g') fi if [[ -z "$interface" ]]; then log_error "无法自动检测网络接口" read -p "请输入网络接口名称 (如: eth0, ens160): " interface # 二次校验输入 if [[ -z "$interface" ]]; then log_error "接口名称不能为空" exit 1 fi fi echo "$interface" } set_hostname() { local hostname=$1 log_info "正在设置主机名为: $hostname" hostnamectl set-hostname $hostname # 写入/etc/hosts避免解析问题 echo "$(hostname -I | awk '{print $1}') $hostname" >> /etc/hosts log_info "主机名设置完成" } close_selinux_firewalld() { log_info "关闭selinux" setenforce 0 sed -i.bak "s/^SELINUX=.*/SELINUX=disabled/" /etc/selinux/config # 验证selinux修改 if grep -q "SELINUX=disabled" /etc/selinux/config; then log_info "SELinux已设置为永久禁用" else log_error "SELinux配置修改失败" fi log_info "关闭防火墙" systemctl disable --now firewalld if [[ $(systemctl is-active firewalld) == "inactive" ]]; then log_info "防火墙已关闭" else log_warn "防火墙关闭失败,手动检查" fi } set_static_ip() { local interface=$(get_interface) local ip=$1 local gateway=${2:-"192.168.24.2"} #这里要根据自己的网关进行调整,这里代表有第二个参数就用第二参数的值不然就是用默认值 local dns=${3:-"223.5.5.5,8.8.8.8"} log_info "正在为接口 $interface 配置静态IP: $ip/24" # 先删除原有同名连接(避免冲突) nmcli connection delete $interface 2>/dev/null # 创建新连接 nmcli connection add con-name $interface ifname $interface type ethernet ipv4.method manual ipv4.addresses $ip/24 ipv4.gateway $gateway ipv4.dns $dns ipv4.dns-search chengke.com connection.autoconnect yes log_info "IP 配置完成,正在启动网卡(SSH 即将断开,请使用新 IP:$ip 重新连接)" log_info "系统初始化完成!" nmcli connection up $interface } main() { if [[ $# -lt 2 ]]; then usage fi check_root set_hostname "$1" close_selinux_firewalld set_static_ip "$2" "$3" "$4" } main "$@" 
1.2 NFS 节点初始化操作
  1. 克隆新虚拟机,命名为 nfs-server,执行优化后的初始化脚本:
# 上传脚本到nfs-server节点 scp init_sys.sh root@新虚拟机IP:/root/ # 执行脚本(主机名nfs-server,IP 192.168.72.210,网关192.168.72.2,DNS用集群VIP) chmod +x /root/init_sys.sh /root/init_sys.sh nfs-server 192.168.72.210 192.168.72.2 192.168.72.100 

当然你光改ip和主机名也是可行的

可以用以下方式稍加验证防火墙和selinux是否关闭

注意:这里是因为学习重点不在这为了方便才关的,一般要有针对的措施让其不会拦截你的数据,比如防火墙让服务或者端口通行,或者其他安全措施打标签之类的。

[root@nfs-serevr ~]# systemctl status firewalld.service ○ firewalld.service - firewalld - dynamic firewall daemon Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; preset: enabled) Active: inactive (dead) Docs: man:firewalld(1) [root@nfs-serevr ~]# cat /etc/selinux/config | grep SELINUX # SELINUX= can take one of these three values: # NOTE: Up to RHEL 8 release included, SELINUX=disabled would also SELINUX=disabled # SELINUXTYPE= can take one of these three values: SELINUXTYPE=targeted 

阶段 2:部署 NFS 服务(nfs-server 节点)

2.1 安装 NFS 相关包
log_info "安装NFS和rpcbind服务" dnf install -y nfs-utils rpcbind # 设置开机自启 systemctl enable --now rpcbind nfs-server # 验证服务状态 if [[ $(systemctl is-active nfs-server) == "active" ]]; then log_info "NFS服务启动成功" else log_error "NFS服务启动失败" exit 1 fi 

这里是为了练习脚本,不熟练应该好好练练,直接用命令当然也可行

疑点1:为什么要按照rpcbind

Linux 发行版的包管理器(dnf/yum)会维护软件包的依赖链,nfs-utils(NFS 核心工具包)的依赖清单中默认包含 rpcbind —— 执行dnf install nfs-utils时,包管理器会自动检测并安装 rpcbind(以及其他依赖),无需你手动指定。

简单来说就是不用指定安装也有,其作用如下:

NFS 是基于 RPC 协议实现的文件共享,RPC 协议的端口映射依赖 rpcbind,且这个依赖是 “双向的”:

  1. NFS 服务端:启动 nfs-server 时,会向本机 rpcbind 注册 “NFS 服务对应的 RPC 程序号 + 动态端口”;
  2. NFS 客户端(Web 节点):执行mount -t nfs挂载共享目录时,第一步会先访问 NFS 服务端的 rpcbind,查询 “NFS 服务的动态端口”,只有拿到端口后,客户端才能和服务端建立 NFS 通信、完成挂载。

2.2 配置 NFS 共享目录
  1. 创建 Web 内容共享目录:
# 创建共享目录,设置权限 mkdir -p /data/webroot chown -R nobody:nobody /data/webroot chmod -R 755 /data/webroot # 写入统一的Web测试内容 echo "<html> <head><title>chengke NFS Web</title></head> <body> <h1>Welcome to chengke.com - NFS Shared Content</h1> <p>Server IP: <span></span></p> <script> fetch('https://api.ipify.org?format=json') .then(response => response.json()) .then(data => document.getElementById('server-ip').textContent = data.ip); </script> </body> </html>" > /data/webroot/index.html 

注意:这里的测试内容也是自己顺便写就行

温故知识:NFS 默认开启root_squash机制 —— 将客户端的root用户映射为服务端的nobody用户(防止客户端 root 权限过大篡改服务端文件)。如果共享目录的所有者是root,客户端(Web 节点)以root身份挂载后,会被映射为nobody,导致无法写入文件(比如 nginx 生成日志、上传静态资源);

  1. 配置 NFS 共享规则(/etc/exports):
# 编辑exports文件 cat > /etc/exports << EOF /data/webroot 192.168.72.0/24(rw,sync,no_root_squash,no_all_squash,anonuid=0,anongid=0) EOF # 生效配置 exportfs -rv # -r:重新导出所有目录;-v:显示详细信息 # 验证共享 showmount -e localhost # 输出应包含:/data/webroot 192.168.72.0/24 

详细解释:

 rw 读写权限(read-write) 允许 Web 节点(客户端)对共享目录读 + 写(比如更新网页内容、生成日志);若设为ro则仅只读。 sync 同步写入(synchronous) 客户端写入数据时,NFS 服务端先将数据写入磁盘,再向客户端返回 “写入成功”; 对比async(异步):服务端先返回成功,再后台写磁盘,速度快但断电易丢数据,生产环境必用sync。 no_root_squash 不压缩 root 权限(核心) NFS 默认会把客户端的 root 用户映射为服务端的nobody(匿名用户),避免客户端 root 滥用权限; no_root_squash 表示:客户端 root 用户访问时,保留 root 权限(UID=0),能完全控制共享目录; 对你的场景:Web 节点以 root 挂载 / 操作目录时,有完整的读写权限,不会因权限不足无法修改文件。 no_all_squash 不压缩所有用户权限 与all_squash相反:不将所有客户端用户(包括普通用户)映射为匿名用户,保留客户端原用户的 UID/GID; 对你的场景:Web 节点的nginx用户(若有)访问目录时,能以自身身份操作,无需额外权限适配。 anonuid=0 匿名用户 UID 设为 0(root) 当需要映射匿名用户时,强制将匿名用户的 UID 设为 0(即 root); 配合no_root_squash,进一步保证客户端 root 的权限不丢失。 anongid=0 匿名用户 GID 设为 0(root) 与anonuid=0对应,匿名用户的组 ID 设为 root 组,权限完全匹配。
疑点1:755权限和以上冲突吗

无论 NFS exports 怎么配置,只要系统文件权限不允许的操作,NFS 远程访问一定也不允许。

2.3 NFS 服务优化(可选)

这是优化建议,不是案例强求设置

# 修改NFS配置,提升性能和稳定性 cat >> /etc/sysconfig/nfs << EOF RPCNFSDCOUNT=8 NFSD_V4_GRACE=90 NFSD_V4_LEASE=90 EOF # 重启NFS服务 systemctl restart nfs-server RPCNFSDCOUNT=8 设置 NFS 服务器的nfsd进程数量为 8 个 (nfsd是处理客户端 NFS 请求的核心进程) NFS 默认的nfsd进程数较少(通常 2-4 个),Web 集群有多个节点(web01/web02/web03)同时访问 NFS,增加到 8 个进程能提升并发处理能力,避免请求排队、响应慢; 建议值:通常设为服务器 CPU 核心数(比如 2 核设 4,4 核设 8),匹配硬件性能。 NFSD_V4_GRACE=90 NFS v4 的 “宽限期”(Grace Period),单位:秒 含义:NFS 服务器重启 / 故障恢复后,会等待 90 秒,让客户端重新上报自己的挂载状态、文件锁等信息,避免数据冲突 默认宽限期可能更长(比如 120 秒),设为 90 秒能缩短服务恢复时间:NFS 重启后,Web 集群无需等待太久就能重新挂载共享目录,减少业务中断时间; 不能太短(比如 < 60 秒),否则客户端来不及恢复,会出现文件锁丢失、挂载失败。 NFSD_V4_LEASE=90 NFS v4 的 “租约期”(Lease Period),单位:秒 含义:客户端与 NFS 服务端的会话有效期,客户端需在 90 秒内和服务端保持心跳,超时后服务端会释放该客户端的文件锁、资源 90 秒是 “稳定性 + 资源释放” 的平衡值: ✅ 对 Web 集群:短连接访问场景下,90 秒足够保证会话稳定,避免频繁重新建立连接; ✅ 对服务端:超时后及时释放僵尸会话的资源(比如某个 Web 节点宕机,90 秒后释放其占用的文件锁),避免资源泄漏。

阶段 3:Web 节点挂载 NFS 共享(web01/web02/web03),配置web节点,3个都要

3.1 统一配置 Web 节点
  1. 安装 nfs-utils(用于挂载):
dnf install -y nfs-utils 
  1. 备份原有 Web 目录,挂载 NFS:
# 停止nginx服务 systemctl stop nginx # 备份原有内容 mv /usr/share/nginx/html /usr/share/nginx/html.bak 这里mv相当于改名的作用 # 创建挂载点 mkdir -p /usr/share/nginx/html # 临时挂载NFS(测试) mount -t nfs 192.168.72.210:/data/webroot /usr/share/nginx/html # 验证挂载 df -h | grep /usr/share/nginx/html # 输出应包含:192.168.72.210:/data/webroot xxx xxx xxx xx% /usr/share/nginx/html 
  1. 设置开机自动挂载(/etc/fstab):

如果虚拟机好用来做别的案例怕麻烦也不用,手动挂载也行

# 写入fstab,添加软挂载参数避免开机卡壳 echo "192.168.72.210:/data/webroot /usr/share/nginx/html nfs defaults,_netdev,soft,timeo=10,retrans=3 0 0" >> /etc/fstab 
3.2 配置 Nginx 
# 编辑Web站点配置 cat > /etc/nginx/conf.d/chengke.conf << EOF server { listen 80; server_name www.chengke.com; include /usr/share/nginx/html/nginx_vars.conf; location / { root /usr/share/nginx/html; index index.html; add_header X-Server-IP \$remote_addr; add_header X-Server-Hostname \$hostname; } } EOF # 验证配置并重启 nginx -t systemctl restart nginx 
3.3 Web 节点 VIP+ARP 参数配置(脚本化)
#!/bin/bash # Web节点VIP配置脚本 VIP="192.168.72.200" LOG_INFO="\033[0;32m[INFO]\033[0m" # 配置LO接口VIP echo -e "$LOG_INFO 配置VIP: $VIP" ifconfig lo:1 $VIP netmask 255.255.255.255 up ip a show lo | grep $VIP # 配置ARP参数(避免冲突) echo -e "$LOG_INFO 配置ARP内核参数" cat >> /etc/sysctl.conf << EOF net.ipv4.conf.all.arp_ignore=1 net.ipv4.conf.lo.arp_ignore=1 net.ipv4.conf.all.arp_announce=2 net.ipv4.conf.lo.arp_announce=2 EOF sysctl -p echo -e "$LOG_INFO Web节点配置完成" 

执行:chmod +x web_vip.sh && ./web_vip.sh

阶段 4:部署 DNS 节点(dns-master/dns-slave)

可以查看主从配置文章

4.1 安装 BIND 服务
dnf install -y bind bind-utils 
4.2 配置 dns-master

有文章具体解释:

# 备份原有配置 cp /etc/named.conf /etc/named.conf.bak # 编辑主配置 cat > /etc/named.conf << EOF options { listen-on port 53 { 127.0.0.1; 192.168.72.107; 192.168.72.100; }; directory "/var/named"; allow-query { localhost; 192.168.72.0/24; }; recursion yes; dnssec-validation no; forwarders { 8.8.8.8; 223.5.5.5; }; }; logging { channel default_debug { file "data/named.run"; severity dynamic; }; }; zone "chengke.com" IN { type master; file "chengke.com.zone"; allow-transfer { 192.168.72.108; }; }; EOF # 配置区域数据文件(时间戳序列号) cp -p /var/named/named.localhost /var/named/chengke.com.zone cat > /var/named/chengke.com.zone << EOF \$TTL 1D @ IN SOA ns1 admin.chengke.com. ( 2026012001 ; serial 1D ; refresh 1H ; retry 1W ; expire 3H ) ; minimum NS ns1 NS ns2 ns1 A 192.168.72.107 ns2 A 192.168.72.108 www A 192.168.72.200 txt TXT "AaBbCcDdEeFf" EOF # 验证配置并启动 named-checkconf /etc/named.conf named-checkzone chengke.com /var/named/chengke.com.zone systemctl enable --now named 
4.3 配置 dns-slave
cp /etc/named.conf /etc/named.conf.bak cat > /etc/named.conf << EOF options { listen-on port 53 { 127.0.0.1; 192.168.72.108; 192.168.72.100; }; directory "/var/named"; allow-query { localhost; 192.168.72.0/24; }; recursion yes; dnssec-validation no; forwarders { 8.8.8.8; 223.5.5.5; }; }; logging { channel default_debug { file "data/named.run"; severity dynamic; }; }; zone "chengke.com" IN { type slave; masters { 192.168.72.107; }; file "slaves/chengke.com.zone"; transfer-timeout 60; }; EOF # 启动并同步数据(这个是原本有文件,修改后删除原本的文件,重启服务会产生最新的文件) 第一次启动没有这一步 rm -f /var/named/slaves/chengke.com.zone systemctl enable --now named dig -t A www.chengke.com @192.168.72.108 # 验证同步 

结果类似:

[root@dns-master ~]# dig -t A www.chengke.com @192.168.24.107 ; <<>> DiG 9.16.23-RH <<>> -t A www.chengke.com @192.168.24.107 ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 24861 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ; COOKIE: d2dbeb307d943d49010000006970eb7f596e4fa92eb5080d (good) ;; QUESTION SECTION: ;www.chengke.com. IN A ;; ANSWER SECTION: www.chengke.com. 86400 IN A 192.168.24.200 ;; Query time: 0 msec ;; SERVER: 192.168.24.107#53(192.168.24.107) ;; WHEN: Wed Jan 21 23:06:39 CST 2026 ;; MSG SIZE rcvd: 88 这里ip些许不同,主要是状态是noerror
4.4 DNS 节点 VIP 配置(脚本化)
#!/bin/bash # DNS节点VIP配置脚本 VIP="192.168.72.100" LOG_INFO="\033[0;32m[INFO]\033[0m" echo -e "$LOG_INFO 配置VIP: $VIP" ifconfig lo:1 $VIP netmask 255.255.255.255 up ip a show lo | grep $VIP echo -e "$LOG_INFO 配置ARP参数" cat >> /etc/sysctl.conf << EOF net.ipv4.conf.all.arp_ignore=1 net.ipv4.conf.lo.arp_ignore=1 net.ipv4.conf.all.arp_announce=2 net.ipv4.conf.lo.arp_announce=2 EOF sysctl -p echo -e "$LOG_INFO DNS节点配置完成" 

执行:chmod +x dns_vip.sh && ./dns_vip.sh

阶段 5:部署 LVS+Keepalived(lb-master/lb-backup)

四层网络负载均衡,内核级别,不解析http,只认ip+端口,做流量转发。

注意:lb主节点和备节点的dns都要写dns集群的vip,不然在用域名访问的时候没办法通过dns服务来查询到其对应的ip地址,如果直接用vip来访问,就不会经过dns这一步,通过负债均衡直接就可轮询web集群,用域名访问比用vip访问要多一步,后续步骤一样。

5.1 安装依赖
dnf install -y ipvsadm keepalived bind-utils 
5.2 初始化 ipvsadm
ipvsadm-save -n > /etc/sysconfig/ipvsadm systemctl enable --now ipvsadm ipvsadm -Ln # 验证 
5.3 配置 lb-master 的 Keepalived
! Configuration File for keepalived global_defs { router_id LVS_master # 节点标识,与backup区分 } # ========== NFS健康检查配置 ========== vrrp_script check_nfs { script "/etc/keepalived/check_nfs.sh" # NFS检查脚本路径 interval 5 # 每5秒检测一次 weight -20 # 检测失败则优先级减20(触发VIP漂移) fall 2 # 连续2次失败判定为真故障 rise 2 # 连续2次成功恢复正常 } # ========== Web VIP实例(lb-master为备节点) ========== vrrp_instance VI_web { state BACKUP # 备节点(lb-backup为MASTER) interface ens160 # 替换为你的实际网卡(如eth0/ens33) virtual_router_id 52 # 与lb-backup保持一致 priority 90 # 优先级低于lb-backup的100 advert_int 1 # VRRP通告间隔1秒 authentication { auth_type PASS auth_pass 1111 # 与lb-backup保持一致 } virtual_ipaddress { 192.168.72.200 # Web服务VIP } # 关联NFS健康检查:NFS故障时降低本节点优先级 track_script { check_nfs } } # ========== Web服务负载规则(替换为Nginx业务检查) ========== virtual_server 192.168.72.200 80 { delay_loop 6 # 健康检查间隔6秒 lb_algo rr # 轮询调度算法 lb_kind DR # DR模式(直接路由) persistence_timeout 50 # 会话持久化50秒 protocol TCP timeout 30 real_server 192.168.72.201 80 { weight 1 # 权重1 MISC_CHECK { # 替换为Nginx业务层检查 misc_path "/etc/keepalived/check_nginx.sh 192.168.24.201" connect_timeout 3 retry 3 delay_before_retry 3 } } real_server 192.168.72.202 80 { weight 1 MISC_CHECK { misc_path "/etc/keepalived/check_nginx.sh 192.168.24.202" connect_timeout 3 retry 3 delay_before_retry 3 } } real_server 192.168.72.203 80 { weight 1 MISC_CHECK { misc_path "/etc/keepalived/check_nginx.sh 192.168.24.203" connect_timeout 3 retry 3 delay_before_retry 3 } } } # ========== DNS VIP实例(lb-master为主节点) ========== vrrp_instance VI_dns { state MASTER # 主节点 interface ens160 virtual_router_id 51 # 与lb-backup保持一致 priority 100 # 优先级高于lb-backup的90 advert_int 1 authentication { auth_type PASS auth_pass 1111 # 与lb-backup保持一致 } virtual_ipaddress { 192.168.72.100 # DNS服务VIP } } # ========== DNS服务负载规则 ========== virtual_server 192.168.72.100 53 { delay_loop 6 lb_algo rr lb_kind DR persistence_timeout 50 protocol UDP # DNS默认使用UDP协议 timeout 30 real_server 192.168.72.107 53 { weight 1 MISC_CHECK { # DNS自定义解析检查 misc_path "/etc/keepalived/checkdns.sh -h 192.168.24.107 -d txt.chengke.com" connect_timeout 3 retry 3 delay_before_retry 3 } } real_server 192.168.72.108 53 { weight 1 MISC_CHECK { misc_path "/etc/keepalived/checkdns.sh -h 192.168.24.108 -d txt.chengke.com" connect_timeout 3 retry 3 delay_before_retry 3 } } }
5.4 配置 lb-backup 的 Keepalived
! Configuration File for keepalived global_defs { router_id LVS_backup # 节点标识,与master区分 } # ========== NFS健康检查配置 ========== vrrp_script check_nfs { script "/etc/keepalived/check_nfs.sh" interval 5 weight -20 fall 2 rise 2 } # ========== Web VIP实例(lb-backup为主节点) ========== vrrp_instance VI_web { state MASTER # 主节点 interface ens160 virtual_router_id 52 # 与lb-master保持一致 priority 100 # 优先级高于lb-master的90 advert_int 1 authentication { auth_type PASS auth_pass 1111 # 与lb-master保持一致 } virtual_ipaddress { 192.168.72.200 # Web服务VIP } track_script { check_nfs # 关联NFS健康检查 } } # ========== Web服务负载规则(替换为Nginx业务检查) ========== virtual_server 192.168.72.200 80 { delay_loop 6 lb_algo rr lb_kind DR persistence_timeout 50 protocol TCP timeout 30 real_server 192.168.72.201 80 { weight 1 MISC_CHECK { # 替换为Nginx业务层检查 misc_path "/etc/keepalived/check_nginx.sh 192.168.24.201" connect_timeout 3 retry 3 delay_before_retry 3 } } real_server 192.168.72.202 80 { weight 1 MISC_CHECK { misc_path "/etc/keepalived/check_nginx.sh 192.168.24.202" connect_timeout 3 retry 3 delay_before_retry 3 } } real_server 192.168.72.203 80 { weight 1 MISC_CHECK { misc_path "/etc/keepalived/check_nginx.sh 192.168.24.203" connect_timeout 3 retry 3 delay_before_retry 3 } } } # ========== DNS VIP实例(lb-backup为备节点) ========== vrrp_instance VI_dns { state BACKUP # 备节点 interface ens160 virtual_router_id 51 # 与lb-master保持一致 priority 90 # 优先级低于lb-master的100 advert_int 1 authentication { auth_type PASS auth_pass 1111 # 与lb-master保持一致 } virtual_ipaddress { 192.168.72.100 # DNS服务VIP } } # ========== DNS服务负载规则 ========== virtual_server 192.168.72.100 53 { delay_loop 6 lb_algo rr lb_kind DR persistence_timeout 50 protocol UDP timeout 30 real_server 192.168.72.107 53 { weight 1 MISC_CHECK { misc_path "/etc/keepalived/checkdns.sh -h 192.168.24.107 -d txt.chengke.com" connect_timeout 3 retry 3 delay_before_retry 3 } } real_server 192.168.72.108 53 { weight 1 MISC_CHECK { misc_path "/etc/keepalived/checkdns.sh -h 192.168.24.108 -d txt.chengke.com" connect_timeout 3 retry 3 delay_before_retry 3 } } }

5.5 创建健康检查脚本(lb-master/lb-backup 均需配置)

5.5.1 NFS 健康检查脚本(check_nfs.sh)

用于检测 NFS 服务器是否正常提供共享服务,异常时触发 Keepalived 优先级调整:

cat > /etc/keepalived/check_nfs.sh << EOF #!/bin/bash # NFS服务健康检查脚本:检测挂载可用性 NFS_SERVER="192.168.72.210" SHARE_DIR="/data/webroot" TEST_DIR="/tmp/nfs_test_mount" # 创建临时挂载目录 mkdir -p \$TEST_DIR # 尝试挂载NFS(超时5秒,避免阻塞) mount -t nfs -o timeo=5 \$NFS_SERVER:\$SHARE_DIR \$TEST_DIR &>/dev/null if [ \$? -eq 0 ]; then # 挂载成功,清理临时目录 umount \$TEST_DIR &>/dev/null rmdir \$TEST_DIR &>/dev/null exit 0 # 健康检查通过(返回0) else # 挂载失败,清理临时目录 rmdir \$TEST_DIR &>/dev/null exit 1 # 健康检查失败(返回非0) fi EOF # 赋予执行权限 chmod +x /etc/keepalived/check_nfs.sh 
5.5.2 DNS 健康检查脚本(checkdns.sh)

用于检测 DNS 节点是否能正常解析指定记录,确保 DNS 服务可用:

cat > /etc/keepalived/checkdns.sh << EOF #!/bin/bash # DNS健康检查脚本:检测指定DNS服务器的解析能力 while getopts "h:d:" opt; do case \$opt in h) DNS_IP=\$OPTARG ;; # DNS服务器IP d) DOMAIN=\$OPTARG ;; # 检测的域名 *) echo "用法: \$0 -h <DNS_IP> -d <检测域名>"; exit 1 ;; esac done # 校验参数完整性 if [ -z "\$DNS_IP" ] || [ -z "\$DOMAIN" ]; then echo "参数缺失!示例:\$0 -h 192.168.72.107 -d txt.chengke.com" exit 1 fi # 执行DNS解析(超时3秒,匹配指定TXT记录) dig -t TXT \$DOMAIN @\$DNS_IP +timeout=3 +short | grep -q "AaBbCcDdEeFf" if [ \$? -eq 0 ]; then exit 0 # 解析成功,健康检查通过 else exit 1 # 解析失败,健康检查不通过 fi EOF # 赋予执行权限 chmod +x /etc/keepalived/checkdns.sh 
5.5.3 nginx健康检查脚本(check_nginx.sh)
#!/bin/bash # Nginx业务层健康检查脚本(检测页面是否正常返回200) # 用法:./check_nginx.sh <Web节点IP> WEB_IP=$1 # 校验参数 if [ -z "$WEB_IP" ]; then echo "参数缺失!示例:$0 192.168.72.201" exit 1 fi # 检测Nginx是否能正常返回200状态码(超时3秒) curl -s -o /dev/null -w "%{http_code}" http://$WEB_IP/index.html --connect-timeout 3 | grep -q "200" if [ $? -eq 0 ]; then exit 0 # Nginx业务正常(页面返回200) else exit 1 # Nginx业务异常(即使80端口通也判定失败) fi

5.6 启动 Keepalived 服务(lb-master/lb-backup)

# 启动服务并设置开机自启 systemctl enable --now keepalived # 验证服务状态(无报错则正常) systemctl status keepalived --no-pager # 验证LVS规则是否生效(核心检查) ipvsadm -Ln # 预期输出示例:(这是我的例子,ip段不同) [root@lb-master keepalived]# ipvsadm -ln IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 192.168.24.200:80 rr persistent 50 -> 192.168.24.201:80 Route 1 0 0 -> 192.168.24.202:80 Route 1 0 0 -> 192.168.24.203:80 Route 1 0 0 UDP 192.168.24.100:53 rr persistent 50 -> 192.168.24.107:53 Route 1 0 0 -> 192.168.24.108:53 Route 1 0 0 

查看vip是否成功显示:

例如:

[root@lb-master keepalived]# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:0c:29:09:90:2b brd ff:ff:ff:ff:ff:ff altname enp3s0 inet 192.168.24.105/24 brd 192.168.24.255 scope global noprefixroute ens160 valid_lft forever preferred_lft forever inet 192.168.24.100/32 scope global ens160 valid_lft forever preferred_lft forever inet6 fe80::20c:29ff:fe09:902b/64 scope link noprefixroute valid_lft forever preferred_lft forever 

DNS 节点配置优化(可选)

在 dns-master 的区域数据文件中添加 NFS 节点解析(便于管理):

vim /var/named/chengke.com.zone # 添加以下行 nfs A 192.168.72.210 # 增加serial值(如改为1) @ IN SOA ns1 admin.chengke.com. ( 1 ; serial 【必须修改,否则从节点不同步】 1D ; refresh 1H ; retry 1W ; expire 3H ) ; minimum # 重启DNS服务 systemctl restart named # dns-slave验证同步 ls /var/named/slaves/chengke.com.zone dig -t A nfs.chengke.com @192.168.72.108 

测试(以下测试因为多种原因可能都需几十秒后验证结果,不能马上得到结果)

注意:而且原理上来说你nginx和keepalived配置不在一个虚拟机,停止nginx不会出现vip漂移等现象。原理就是通过脚本检测来停止keepalived来实现的漂移

将以下的24都改为72,做了多次,ip段不一样,有点混乱,不过应该没人有这样的疑问吧,太低级了

一、测试前准备

  1. 验证基础业务可用:
    • Web 访问:curl http://www.chengke.com(返回 200 状态码 + NFS 共享页面)
    • DNS 解析:dig www.chengke.com @192.168.24.100 +short(返回 192.168.24.200)
    • LVS 规则:ipvsadm -Ln(显示所有 Web/DNS 后端节点)

确认所有服务正常运行:

# 批量检查核心服务(lb-master执行,需配置免密登录) for node in 192.168.24.201 192.168.24.202 192.168.24.203 192.168.24.107 192.168.24.108 192.168.24.105 192.168.24.106 192.168.24.210; do echo -e "\n=== 节点 $node ===" ssh root@$node "systemctl status nginx named keepalived nfs-server --no-pager | grep Active" done 

二、分场景高可用测试

场景 1:单个 Web 节点故障(验证 LVS 自动剔除)

测试步骤:

再次访问,验证 web01 重新加入集群:

curl http://www.chengke.com | grep "web01" # 一段时间后可再次出现 

恢复 web01 服务:

ssh [email protected] "systemctl start nginx" 

验证 LVS 健康检查生效(web01 被剔除):

ipvsadm -Ln --stats # 查看web01的ActiveConn为0,且无新连接 

连续访问 Web VIP,验证请求不分发到 web01:

# 执行5次访问,观察返回结果 for i in {1..5}; do echo -e "第$i次访问:" curl -s http://www.chengke.com | grep "Server Hostname" done 

模拟 web01 故障(停止 Nginx 服务):

ssh [email protected] "systemctl stop nginx" 
预期结果:
  • 步骤 2 中仅返回 web02/web03 的页面内容,无 web01;
  • 步骤 3 中 web01 的连接数持续为 0;
  • 步骤 5 中 web01 恢复后,请求会重新分发到该节点。

场景 2:DNS 主节点故障(验证从节点接管)

测试步骤:

恢复 dns-master 服务:

ssh [email protected] "systemctl start named" 

查看 DNS 健康检查脚本状态(lb-master 执行):

systemctl status keepalived --no-pager | grep checkdns.sh 

测试 DNS VIP 解析是否正常:

# 执行3次解析,验证无失败 for i in {1..3}; do dig www.chengke.com @192.168.24.100 +short dig txt.chengke.com @192.168.24.100 +short # 验证健康检查记录 done 

模拟 dns-master 故障(停止 BIND 服务):

ssh [email protected] "systemctl stop named" 
预期结果:
  • 步骤 2 中解析正常返回(由 dns-slave 提供服务);
  • 步骤 3 中无 “checkdns.sh 检测失败” 的持续报错;
  • 恢复后,DNS 主从同步正常。

场景 3:LB 主备切换(验证 VIP 漂移)

测试步骤:

恢复 lb-master 服务,验证 VIP 回迁:

ssh [email protected] "systemctl start keepalived" ssh [email protected] "ip a show ens160 | grep 192.168.24.100" # 预期回迁 

验证业务不中断:

# Web访问 curl http://www.chengke.com # DNS解析 dig www.chengke.com @192.168.24.100 +short 

验证 VIP 漂移到 lb-backup:

ssh [email protected] "ip a show ens160 | grep -E '192.168.24.100|192.168.24.200'" 

模拟 lb-master 故障(停止 Keepalived 服务):

ssh [email protected] "systemctl stop keepalived" 

查看当前 VIP 持有状态(lb-master 为主节点,持有 DNS VIP):

# lb-master执行 ip a show ens160 | grep 192.168.24.100 # 预期显示DNS VIP # lb-backup执行 ip a show ens160 | grep 192.168.24.200 # 预期显示Web VIP 
预期结果:
  • 步骤 3 中 lb-backup 同时持有 DNS VIP(192.168.24.100)和 Web VIP(192.168.24.200);
  • 步骤 4 中 Web 访问和 DNS 解析均正常;
  • 步骤 5 中 lb-master 恢复后,DNS VIP 回迁(因优先级更高)。

场景 4:NFS 服务器故障(验证 LVS 优先级调整)

测试步骤:

验证业务恢复:

curl http://www.chengke.com # 正常返回NFS共享页面 

恢复 NFS 服务:

ssh [email protected] "systemctl start nfs-server" # Web节点重新挂载NFS for ip in 192.168.24.201 192.168.24.202 192.168.24.203; do ssh root@$ip "mount -a && systemctl restart nginx" done 

验证 Web 节点业务状态(页面无法加载,但 LVS 不分发新请求):

curl http://www.chengke.com # 预期返回500或超时(NFS挂载失效) 

查看 LVS 节点优先级变化(lb-backup 执行):

# 因check_nfs.sh检测失败,lb-backup优先级降低20(从100→80) systemctl status keepalived --no-pager | grep "priority" 

模拟 NFS 故障(停止 NFS 服务):

ssh [email protected] "systemctl stop nfs-server" 
预期结果:
  • 步骤 2 中 lb-backup 优先级降低,若存在其他备用 LVS 节点,VIP 会漂移;
  • 步骤 3 中 Web 页面暂时不可用,但 LVS 不会将新请求分发到无效节点;
  • 步骤 5 中 NFS 恢复后,Web 业务自动恢复。

场景 5:Web 节点 NFS 挂载丢失(验证脚本自动恢复)

测试步骤:

验证挂载恢复:

ssh [email protected] "df -h | grep /usr/share/nginx/html" # 显示NFS挂载 ssh [email protected] "curl -s localhost | grep 200" # 正常返回 

等待 5 分钟(或手动执行监控脚本)这个在下面的优化建议里面:

ssh [email protected] "/usr/local/bin/nfs_monitor.sh" 

验证 Nginx 业务异常(虽 80 端口通,但页面无法返回):

ssh [email protected] "curl -s localhost | grep 200" # 无输出 

模拟 web01 的 NFS 挂载丢失:

ssh [email protected] "umount /usr/share/nginx/html" 
预期结果:
  • 步骤 3 中监控脚本自动重新挂载 NFS;
  • 步骤 4 中 Web 节点业务恢复,无需手动干预。

三、测试后恢复与总结

  1. 最终验证集群状态:
    • VIP 分布:lb-master 持有 192.168.24.100,lb-backup 持有 192.168.24.200;
    • 业务可用:Web 访问和 DNS 解析均正常;
    • 健康检查:所有脚本执行返回 0(/etc/keepalived/check_*.sh && echo $?)。

恢复所有节点服务(若测试中未恢复):

# 批量启动所有核心服务 for node in 192.168.24.201 192.168.24.202 192.168.24.203; do ssh root@$node "systemctl start nginx && systemctl enable nginx" done for node in 192.168.24.107 192.168.24.108; do ssh root@$node "systemctl start named && systemctl enable named" done for node in 192.168.24.105 192.168.24.106; do ssh root@$node "systemctl start keepalived && systemctl enable keepalived" done ssh [email protected] "systemctl start nfs-server && systemctl enable nfs-server" 

三、优化改进建议

3.1 架构层面

  1. NFS 高可用:单 NFS 节点是单点故障,建议部署 NFS+DRBD+Heartbeat 或使用 GlusterFS/Ceph 分布式存储替代单机 NFS;
  2. 监控告警:部署 Zabbix/Prometheus+Grafana,监控各节点 CPU / 内存 / 磁盘、NFS 挂载状态、nginx 连接数、LB VIP 状态、DNS 解析成功率;
  3. 日志集中化:部署 ELK/EFK 栈,收集 nginx 访问日志、NFS 日志、LB/Keepalived 日志、DNS 日志,便于故障排查;
  4. 安全加固
    • NFS 共享限制仅 Web 节点访问(细化 exports 网段);
    • 配置 iptables/firewalld 白名单(仅允许集群内 IP 访问 53/80/111/2049 端口);
    • 给 DNS 添加 TSIG 密钥,防止主从同步被篡改;
    • 启用 nginx HTTPS,替换 80 端口为 443,LB 同步调整端口。

3.2 脚本层面

  1. 新增 NFS 挂载检测脚本(web 节点定时执行):

#!/bin/bash # /usr/local/bin/check_nfs_mount.sh MOUNT_POINT="/usr/share/nginx/html" NFS_SERVER="192.168.72.210:/data/webroot" LOG_FILE="/var/log/nfs_mount_check.log" log_info() { echo "$(date +'%F %T') [INFO] $1" >> $LOG_FILE } log_error() { echo "$(date +'%F %T') [ERROR] $1" >> $LOG_FILE # 发送告警(示例:邮件/钉钉/企业微信) # echo "NFS挂载失败: $MOUNT_POINT" | mail -s "NFS Mount Error" [email protected] } # 检查挂载状态 if ! mount | grep -q "$NFS_SERVER on $MOUNT_POINT"; then log_error "NFS挂载丢失,尝试重新挂载" mount -t nfs $NFS_SERVER $MOUNT_POINT # 验证重新挂载 if mount | grep -q "$NFS_SERVER on $MOUNT_POINT"; then log_info "NFS重新挂载成功" systemctl restart nginx else log_error "NFS重新挂载失败,手动处理" fi else log_info "NFS挂载正常" fi # 添加到crontab # */5 * * * * /usr/local/bin/check_nfs_mount.sh 
  1. 集群批量操作脚本(管理节点执行,需配置免密登录):
#!/bin/bash # /usr/local/bin/cluster_manage.sh # 集群节点列表 NODES=( "web01:192.168.72.201" "web02:192.168.72.202" "web03:192.168.72.203" "dns-master:192.168.72.107" "dns-slave:192.168.72.108" "lb-master:192.168.72.105" "lb-backup:192.168.72.106" "nfs-server:192.168.72.210" ) # 执行命令函数 exec_cmd() { local cmd=$1 for node in "${NODES[@]}"; do hostname=$(echo $node | cut -d':' -f1) ip=$(echo $node | cut -d':' -f2) echo "===== 执行命令到 $hostname ($ip) =====" ssh root@$ip "$cmd" echo "=======================================" echo done } # 重启指定服务函数 restart_service() { local service=$1 exec_cmd "systemctl restart $service" } # 显示帮助 usage() { echo "用法: $0 [选项] [参数]" echo "选项:" echo " exec <cmd> 批量执行命令" echo " restart <svc> 批量重启服务" echo " status <svc> 批量查看服务状态" exit 1 } case $1 in exec) if [[ -z $2 ]]; then usage fi exec_cmd "$2" ;; restart) if [[ -z $2 ]]; then usage fi restart_service "$2" ;; status) if [[ -z $2 ]]; then usage fi exec_cmd "systemctl status $2" ;; *) usage ;; esac 

部署方式:
 

 chmod +x /usr/local/bin/nfs_monitor.sh # 添加定时任务 echo "*/5 * * * * /usr/local/bin/nfs_monitor.sh" >> /var/spool/cron/root

3.3 运维层面

  1. 配置备份:定时备份各节点的关键配置(/etc/keepalived/、/etc/named/、/etc/exports、/etc/fstab)到 NFS 或远程存储;
  2. 自动化部署:使用 Ansible/Shell 脚本实现集群一键部署,替代手动操作;
  3. 文档标准化:整理集群拓扑图、IP 规划表、操作手册、故障排查手册;
  4. 性能优化
    • NFS 调整缓存参数(如 mount 添加 cache=none/loose);
    • nginx 开启缓存,调整 worker_processes 为 CPU 核心数;
    • LVS 调整调度算法(如根据 Web 节点性能调整 weight,或使用 wlc 算法)。

Read more

政安晨【零基础玩转开源AI项目】OpenClaw飞书通信端机器人配置指南(手把手配置OpenClaw飞书/Lark机器人,实现多渠道AI助手集成)(作者自己配置时留存使用,小伙伴们可酌情参考)

政安晨【零基础玩转开源AI项目】OpenClaw飞书通信端机器人配置指南(手把手配置OpenClaw飞书/Lark机器人,实现多渠道AI助手集成)(作者自己配置时留存使用,小伙伴们可酌情参考)

政安晨的个人主页:政安晨 欢迎 👍点赞✍评论⭐收藏 希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正! 目录 一、前言 1.1 为什么需要配置飞书机器人? 1.2 飞书机器人支持的功能 二、准备工作 2.1 环境要求 2.2 OpenClaw安装(本篇主要介绍飞书端的配置,这里可参考我上一篇博客) 2.3 飞书账号要求 三、飞书应用创建 3.1 创建企业应用 3.2 获取应用凭证 编辑3.3 开通权限 3.4 配置事件订阅 Webhook URL配置 订阅事件 3.5

LeRobot深度解析:5大核心模块构建下一代机器人学习系统

LeRobot深度解析:5大核心模块构建下一代机器人学习系统 【免费下载链接】lerobot🤗 LeRobot: State-of-the-art Machine Learning for Real-World Robotics in Pytorch 项目地址: https://gitcode.com/GitHub_Trending/le/lerobot 为什么LeRobot正在重新定义机器人开发范式 传统的机器人开发面临三大困境:算法与硬件脱节、数据格式不统一、部署流程复杂。LeRobot通过统一的数据-策略-执行闭环,将机器人学习从实验室推向真实世界应用。 LeRobot框架的核心优势在于其模块化设计,让开发者能够像搭积木一样构建复杂的机器人系统。无论你是想要实现一个简单的抓取任务,还是构建一个多机器人协同的复杂系统,LeRobot都提供了标准化的解决方案。 LeRobot架构全景:从多模态输入到精准控制 LeRobot采用先进的VLA(视觉-语言-动作)架构,实现从自然语言指令到机器人动作的端到端映射。整个系统由五个核心模块组成,每个模块都经过精心设计,确保系统

从‘看得见’到‘看得懂’:PaddleOCR-VL-WEB赋能智能OCR升级

从“看得见”到“看得懂”:PaddleOCR-VL-WEB赋能智能OCR升级 在银行票据处理中心、政务服务中心的档案科、电商商家后台,每天有数以万计的合同、发票、身份证、说明书、学术论文被扫描上传。过去,这些图像交由传统OCR系统处理——结果是一长串无序文字,像打翻的铅字盒:你能看见所有字符,却不知道哪一行是金额、哪个框是签章位置、表格里哪列对应税率、公式中哪个符号是求和变量。 而今天,一张PDF截图上传后3秒内,系统不仅返回清晰文本,还自动标注出“标题层级”“段落类型”“表格结构”“数学公式语义”“图表说明文字”,甚至能回答“这份采购合同的付款条件是什么?”——这不再是OCR,而是文档理解(Document Understanding)。 PaddleOCR-VL-WEB 镜像正是这一跃迁的关键载体。它不是对旧OCR的简单提速,而是用视觉-语言联合建模,把“图像识别”升级为“文档认知”。它不只告诉你“这里有一行字”,更告诉你“这行字是条款编号,属于第3条违约责任下的子项”