● 部署实施手册 · Two-Node Cluster

RHEL 10 双节点 Pacemaker
高可用集群部署配置文档

从裸机到可用集群的完整落地路径。每一步都给出可直接复制执行的命令、执行位置、预期输出与验证方法,并针对双节点场景特有的脑裂与 fencing 竞态问题给出明确对策。

目标版本 RHEL 10 节点数 2 + 1 仲裁 示例业务 Apache 主备 工具 pcs 配套 核心模块培训文档

00文档说明与实施路线

0.1 命令执行位置约定

本文每段命令的标题栏右侧都标注了执行位置,务必按标注执行:

两节点均执行

node1 和 node2 都要跑一遍。

仅在 node1 执行

集群配置类命令只需在任一节点执行一次,会自动同步全集群。

仲裁主机执行

在 qnetd 第三方主机上执行。

0.2 实施路线总览

阶段章节产出关卡(必须通过才进入下一步)
① 规划01IP 表、主机名表、fence 设备清单网络互通、fence 设备凭据已确认可用
② 准备02–03软件安装完成、防火墙放行、pcsd 运行两节点互相能解析主机名、时间同步
③ 建集群04集群创建并启动pcs status 显示两节点 Online
④ 保完整性05–07qdevice + fencing 就绪pcs stonith fence 测试成功
⑤ 上业务08–09存储、资源、约束配置完成业务可通过 VIP 访问
⑥ 验收10故障演练报告全部测试用例通过
⑦ 交接11–13运维手册、配置备份交付检查表全绿
最重要的一条实施纪律

不要跳过第 ④ 阶段直接上业务。没有可用 fencing 的双节点集群在故障时不会切换(会一直等待 fence 结果),或者更糟 —— 发生脑裂损坏数据。Red Hat 对未配置 fencing 的集群不提供生产支持

01规划:拓扑 / 网络 / 命名

1.1 参考拓扑

客户端 / 上游负载 访问 VIP 192.168.10.100 业务网 192.168.10.0/24 (承载 VIP 与服务流量) node1.example.com 业务 IP192.168.10.11 心跳 link0192.168.20.11 心跳 link1192.168.30.11 BMC / iDRAC10.10.10.11 RHEL 10 · pacemaker · corosync · pcsd node2.example.com 业务 IP192.168.10.12 心跳 link0192.168.20.12 心跳 link1192.168.30.12 BMC / iDRAC10.10.10.12 RHEL 10 · pacemaker · corosync · pcsd 仲裁主机 qnetd 192.168.10.20 TCP 5403 corosync 双心跳链路(192.168.20.0/24 + 192.168.30.0/24,物理隔离) 共享存储(SAN / iSCSI)— /dev/sdb HA-LVM 独占激活,同一时刻仅一个节点挂载
图 1-1 双节点 + 仲裁主机参考拓扑:业务网 / 双心跳网 / BMC 带外网三网分离

1.2 规划表(实施前填写)

项目node1node2说明
主机名(FQDN)node1.example.comnode2.example.com必须与 hostname -f 一致,且两边可互相解析
业务 IP192.168.10.11192.168.10.12
心跳 link0192.168.20.11192.168.20.12独立网卡/VLAN
心跳 link1192.168.30.11192.168.30.12强烈建议,避免单链路故障误判
BMC / iDRAC / iLO10.10.10.1110.10.10.12fencing 用;必须与集群心跳网物理隔离
VIP(浮动 IP)192.168.10.100/24不能与任何物理网卡 IP 冲突
集群名my_cluster
仲裁主机192.168.10.20 / qnetd.example.com第三方主机,不装 pacemaker
共享存储/dev/sdb(两节点看到同一 LUN)按需;无共享存储可跳过第 8 章

1.3 硬件与环境前置条件

规划阶段最容易被忽略的一点

fence 设备的网络路径不能与心跳网共用。如果心跳网交换机故障导致节点失联,而 BMC 也走同一台交换机,那么 fence 也会失败 → 集群卡死。带外管理网必须独立。

02订阅与软件仓库

1

启用 High Availability 仓库

两个节点都要执行。x86_64 架构的仓库 id 如下;其他架构请替换。

启用 HA 仓库两节点均执行
# 确认已注册
subscription-manager status

# 启用高可用附加仓库(RHEL 10 / x86_64)
subscription-manager repos --enable=rhel-10-for-x86_64-highavailability-rpms

# 确认仓库已生效
dnf repolist | grep -i highavailability
其他仓库

若使用 GFS2 等共享集群文件系统,还需 Resilient Storage 仓库:rhel-10-for-x86_64-resilientstorage-rpms。本文的主备(active/passive)方案使用 HA-LVM,不需要该仓库。

2

安装集群软件

官方推荐一次性安装全部 fence agent,也可只装需要的型号以减小攻击面。

安装软件包两节点均执行
# 方式 A:安装全部 fence agents(官方默认建议)
dnf install -y pcs pacemaker fence-agents-all

# 方式 B:只装所需 fence agent(把 model 换成实际型号,如 ipmilan)
# dnf install -y pcs pacemaker fence-agents-ipmilan

# 可选:性能数据采集,便于事后分析集群抖动(官方建议)
dnf install -y pcp-zeroconf

# 查看已安装的 fence agents
rpm -q -a | grep fence

# 确认版本
rpm -q pcs pacemaker corosync

03系统准备(两节点)

3

主机名与解析

集群使用节点名进行通信与 fencing 映射,解析必须稳定可靠。

主机名与 /etc/hosts两节点均执行
# 在 node1 上
hostnamectl set-hostname node1.example.com
# 在 node2 上
# hostnamectl set-hostname node2.example.com

# 两节点都写入 /etc/hosts(即使有 DNS 也建议写,避免 DNS 故障影响集群)
cat >> /etc/hosts <<'EOF'
192.168.10.11   node1.example.com node1
192.168.10.12   node2.example.com node2
192.168.10.20   qnetd.example.com qnetd
EOF

# 验证:两节点互 ping FQDN
ping -c2 node1.example.com
ping -c2 node2.example.com
hostname -f
4

时间同步

时间不一致会导致日志难以关联、部分资源代理行为异常。

chrony 时间同步两节点均执行
dnf install -y chrony
systemctl enable --now chronyd
chronyc sources -v
chronyc tracking      # 确认 System time 偏差很小
timedatectl           # 确认时区一致、NTP 已同步
5

防火墙放行

firewalld 内置 high-availability 服务,一条命令覆盖所有必需端口。

firewalld两节点均执行
firewall-cmd --permanent --add-service=high-availability
firewall-cmd --add-service=high-availability
firewall-cmd --list-services

# 如使用 qdevice,仲裁主机与节点间还需 5403/tcp(high-availability 已含)
# 如后续使用 Pacemaker Remote:3121/tcp;GFS2/DLM:21064/tcp;Booth:9929
端口协议用途本方案是否需要
2224TCPpcsd,节点间通信与认证必需
5404–5412UDPcorosync 节点间通信必需
5403TCPcorosync-qnetd 仲裁设备使用 qdevice 时必需
3121TCPPacemaker Remote 节点本方案不需要
21064TCPDLM(GFS2)本方案不需要
9929TCP/UDPBooth 多站点本方案不需要
6

设置 hacluster 密码并启动 pcsd

hacluster 是安装 pcs 时自动创建的管理账户,两节点密码建议保持一致。

hacluster + pcsd两节点均执行
# 设置 hacluster 密码(两节点使用相同密码)
passwd hacluster

# 启动并设置开机自启
systemctl start  pcsd.service
systemctl enable pcsd.service

# 验证
systemctl status pcsd.service --no-pager
ss -lntp | grep 2224
SELinux

保持 enforcing。RHEL 的集群组件与常用资源代理均已适配 SELinux 策略。若某个自定义应用被拦截,正确做法是用 ausearch -m avc -ts recent 分析后添加策略模块,而不是关闭 SELinux

04节点认证与集群创建

7

节点认证

RHEL 8 起使用 pcs host auth(取代了旧版的 pcs cluster auth)。只需在一个节点执行。

pcs host auth仅 node1 执行
pcs host auth node1.example.com node2.example.com
# Username: hacluster
# Password: ********
# 预期输出:
#   node1.example.com: Authorized
#   node2.example.com: Authorized

# 非交互方式(脚本化部署时使用,注意密码会进 history)
# pcs host auth node1.example.com node2.example.com -u hacluster -p 'YourPassword'
8

创建集群

推荐直接使用双心跳链路创建。多个 addr= 按出现顺序对应 link0、link1。

创建双链路双节点集群仅 node1 执行
# —— 推荐:双心跳链路 ——
pcs cluster setup my_cluster --start \
  node1.example.com addr=192.168.20.11 addr=192.168.30.11 \
  node2.example.com addr=192.168.20.12 addr=192.168.30.12

# —— 最简形式(单链路,仅实验用)——
# pcs cluster setup my_cluster --start node1.example.com node2.example.com

# 设置开机自启(否则节点重启后需手工 pcs cluster start)
pcs cluster enable --all
9

验证集群已就绪

这是第一个关卡,必须两节点均为 Online 才能继续。

集群状态验证仅 node1 执行
pcs cluster status          # 集群服务状态
pcs status                  # 总览:节点 / 资源 / 守护进程
pcs status --full           # 含 failcount 等详细信息
pcs cluster config          # 查看生成的 corosync 配置
pcs quorum status           # 票数与 quorum
corosync-cfgtool -s         # 两条 link 都应为 connected
# pcs status 期望输出(节选)
Cluster name: my_cluster

Cluster Summary:
  * Stack: corosync
  * Current DC: node1.example.com (version ...) - partition with quorum
  * 2 nodes configured
  * 0 resource instances configured

Node List:
  * Online: [ node1.example.com node2.example.com ]      <== 两节点均 Online

Daemon Status:
  corosync: active/enabled
  pacemaker: active/enabled
  pcsd: active/enabled
10

确认 two_node 已自动写入

双节点集群,pcs cluster setup 会自动在 corosync 配置中写入 two_node: 1

检查 two_node仅 node1 执行
grep -A5 '^quorum' /etc/corosync/corosync.conf

# 期望看到:
# quorum {
#     provider: corosync_votequorum
#     two_node: 1
# }

pcs quorum config
corosync-quorumtool -s

05双节点特有风险与对策

双节点是最常见、也是最危险的集群规模。本章的内容决定了这套集群在真实故障中是"优雅切换"还是"数据损坏"。

5.1 two_node 与 wait_for_all

参数行为解决什么引入什么风险
two_node: 1quorum 计算改为"有 1 票即算有 quorum"否则任一节点故障就整体停摆,HA 形同虚设网络分区时两边都认为自己有 quorum → 脑裂
wait_for_all集群首次启动时必须看到全部节点才获得 quorum;two_node: 1 时自动隐式启用社区 votequorum(5)防止冷启动时两台机器各自成群两台都断电后,必须两台都起来集群才恢复服务
核心结论

two_node: 1 把"防脑裂"的责任全部转移给了 fencing。这就是为什么双节点集群必须配置可靠的 fencing,并且强烈建议再加一个 qdevice 把票数变成 3。

5.2 脑裂场景推演

场景 A:无 qdevice(危险) node1 1 票 → 有 quorum node2 1 票 → 有 quorum 两边都认为对方死了,同时尝试接管 → 双方同时发起 fence(fencing race) → 可能互相打死,或延迟不确定 对策:pcmk_delay_base 差异化 + priority-fencing-delay 让一方稍慢动手,确定性地决出胜者 场景 B:有 qdevice(推荐) qnetd(1 票) node1 1+1=2 票 → 存活 node2 1 票 → 失去 quorum 仲裁明确:只有一侧有 quorum node1 fence 掉 node2 并接管,node2 停止资源 无竞态、无歧义 —— 生产环境首选
图 5-1 有无 qdevice 的脑裂处置差异

5.3 Fencing 竞态的两种对策

对策一:给两个 stonith 资源设置不同的固定延迟仅 node1 执行
# 负责打 node1 的设备立即动手;负责打 node2 的设备等 10 秒
# 结果:脑裂时 node2 总是先被打掉,node1 胜出(确定性)
pcs stonith update fence-node1 pcmk_delay_base=0s
pcs stonith update fence-node2 pcmk_delay_base=10s

# 或使用随机延迟打破对称(不确定谁赢,但一定不会互杀)
# pcs stonith update fence-node1 pcmk_delay_max=15s
# pcs stonith update fence-node2 pcmk_delay_max=15s
对策二:priority-fencing-delay(推荐,按资源价值决定谁活)仅 node1 执行
# 给重要资源设置 priority,集群会让"承载高优先级资源较少"的节点延迟 fence,
# 从而更可能被对方先干掉 —— 保住业务更重的一侧。
pcs resource update Website meta priority=10
pcs property set priority-fencing-delay=15s

pcs property config priority-fencing-delay
延迟值怎么定

延迟必须大于 fence 操作本身的耗时,否则起不到错开作用。IPMI 通常 5–10 秒可完成,因此 priority-fencing-delay 建议设为 15s 或更高。可用 time pcs stonith fence <node> 实测再定。

06Quorum Device 配置

qdevice 让双节点集群拥有第 3 票,从根本上消除对半分裂的歧义。生产双节点集群强烈建议部署。RHEL10 官方 Ch.28

11

准备仲裁主机

一台独立的 RHEL 主机即可,不安装 pacemaker,不参与资源运行;可被多个集群共用。

仲裁主机安装与初始化仲裁主机执行
dnf install -y pcs corosync-qnetd

systemctl start  pcsd.service
systemctl enable pcsd.service

# 设置 hacluster 密码(与集群节点保持一致更便于管理)
passwd hacluster

# 初始化并启动 qnetd 服务
pcs qdevice setup model net --enable --start

# 放行端口
firewall-cmd --permanent --add-service=high-availability
firewall-cmd --add-service=high-availability

# 验证
pcs qdevice status net --full
ss -lntp | grep 5403
12

集群节点安装 qdevice 客户端

安装客户端两节点均执行
dnf install -y corosync-qdevice
13

认证仲裁主机并加入集群

添加 quorum device仅 node1 执行
# 先认证仲裁主机
pcs host auth qnetd.example.com
# Username: hacluster / Password: ********

# 加入 quorum device(ffsplit 为默认算法)
pcs quorum device add model net \
    host=qnetd.example.com \
    algorithm=ffsplit

# 验证
pcs quorum config
pcs quorum status
pcs quorum device status
# pcs quorum status 期望输出(节选)
Quorum information
------------------
Nodes:            2
Node ID:          1
Quorate:          Yes

Votequorum information
----------------------
Expected votes:   3          <== 变成 3 票了
Highest expected: 3
Total votes:      3
Quorum:           2
Flags:            Quorate Qdevice

Membership information
----------------------
    Nodeid      Votes    Qdevice Name
         1          1    A,V,NMW node1.example.com (local)
         2          1    A,V,NMW node2.example.com
         0          1            Qdevice
算法行为适用
ffsplit(默认)对半分裂时,qnetd 只把票投给一个分区(按启发式规则裁决)双节点标准选择
lms(last man standing)只要还能连上 qnetd,即使只剩最后一个节点也保持 quorum希望"最后一台也能撑住"的场景,但需确保 fencing 绝对可靠
算法切换 / 移除仅 node1 执行
pcs quorum device update model algorithm=lms
pcs quorum device remove          # 从集群移除 qdevice
# 在仲裁主机上销毁:pcs qdevice destroy net

07Fencing / STONITH 实操

本章是整个部署的核心关卡

fencing 未验证通过之前,不要配置任何业务资源

14

确认可用的 fence agent

列出与查看 fence agent仅 node1 执行
pcs stonith list                     # 全部可用 agent
pcs stonith list ipmi                # 关键字过滤
pcs stonith describe fence_ipmilan   # 查看参数说明(配置前必看)

# 先用命令行直接测试 fence agent 能否连通设备(不经过集群)
fence_ipmilan -a 10.10.10.12 -l fenceuser -p 'FencePass' \
              --lanplus -o status

7.1 方案 A:fence_ipmilan(物理服务器,最常见)

为两个节点分别创建 stonith 资源仅 node1 执行
# 负责隔离 node1 的设备(指向 node1 的 BMC)
pcs stonith create fence-node1 fence_ipmilan \
    ip=10.10.10.11 \
    username=fenceuser password='FencePass' \
    lanplus=1 \
    pcmk_host_list="node1.example.com" \
    pcmk_delay_base=0s \
    op monitor interval=60s

# 负责隔离 node2 的设备(指向 node2 的 BMC)
pcs stonith create fence-node2 fence_ipmilan \
    ip=10.10.10.12 \
    username=fenceuser password='FencePass' \
    lanplus=1 \
    pcmk_host_list="node2.example.com" \
    pcmk_delay_base=10s \
    op monitor interval=60s

# 避免 fence 设备资源运行在它要隔离的那个节点上
pcs constraint location fence-node1 avoids node1.example.com=INFINITY
pcs constraint location fence-node2 avoids node2.example.com=INFINITY

pcs stonith config
pcs stonith status

7.2 方案 B:fence_vmware_rest(VMware 虚机)

VMware 环境 fencing仅 node1 执行
# 先确认能列出虚机(验证凭据与连通性)
fence_vmware_rest -a vcenter.example.com \
    -l 'fenceuser@vsphere.local' -p 'FencePass' \
    --ssl-insecure -o list | grep -Ei 'node1|node2'

# 单个 stonith 资源即可管理两台虚机(用 pcmk_host_map 做映射)
pcs stonith create fence-vmware fence_vmware_rest \
    ip=vcenter.example.com \
    username='fenceuser@vsphere.local' password='FencePass' \
    ssl=1 ssl_insecure=1 \
    pcmk_host_map="node1.example.com:RHEL10-node1;node2.example.com:RHEL10-node2" \
    pcmk_delay_max=15s \
    op monitor interval=60s
pcmk_host_map 的含义

格式为 集群节点名:设备上的标识,多个用分号分隔。VMware 场景里"设备上的标识"是 vCenter 中的虚机名称;APC PDU 场景里是插座编号;刀片场景里是槽位号。名字对不上是 fencing 失败最常见的原因。

7.3 方案 C:SBD + Watchdog(无带外管理时)

SBD poison-pill 模式两节点均执行(标注部分除外)
# ① 两节点安装
dnf install -y sbd fence-agents-sbd

# ② 确认 watchdog 设备存在(硬件 watchdog 优先;虚机可加载 softdog)
ls -l /dev/watchdog*
# modprobe softdog   # 仅测试环境

# ③ 在共享存储上创建 SBD 分区(例如 /dev/sdc1,8MB 即可),仅需在一个节点初始化
sbd -d /dev/sdc1 create
sbd -d /dev/sdc1 dump
sbd -d /dev/sdc1 list

# ④ 两节点配置 /etc/sysconfig/sbd
#   SBD_DEVICE="/dev/sdc1"
#   SBD_WATCHDOG_DEV=/dev/watchdog
#   SBD_STARTMODE=always

# ⑤ 启用(需要先停集群)
pcs cluster stop --all
pcs stonith sbd enable --device=/dev/sdc1
pcs cluster start --all
pcs stonith sbd status

# ⑥ 创建 fence_sbd 资源(poison-pill 模式需要)
pcs stonith create fence-sbd fence_sbd devices=/dev/sdc1
SBD 注意

watchdog-only 模式(无共享存储)依赖节点在失去 quorum 时自我复位,因此必须配合 qdevice 或三节点才有意义 —— 双节点无仲裁时两边都不会主动认输。另外软件 watchdog(softdog)在内核 hang 死时可能失效,生产环境应使用硬件 watchdog。

15

启用 stonith 并测试(关键验收点)

fencing 验收测试仅 node1 执行
# 确认已启用(默认即为 true)
pcs property set stonith-enabled=true
pcs property config stonith-enabled

# ⚠ 真实测试:这会真的重启 node2!请在业务上线前完成
pcs stonith fence node2.example.com

# 预期:node2 被重启,pcs status 中短暂显示 OFFLINE,随后自动恢复 Online
watch -n2 pcs status

# 查看 fencing 历史
pcs stonith history show
stonith_admin --history='*'
验收标准

pcs stonith fence node2.example.com 返回成功;② node2 确实被重启;③ 反向测试(从 node2 fence node1)同样成功;④ pcs stonith status 中两个 stonith 资源均为 Started。四条全部满足,才可进入下一章。

7.4 可选:多级 fencing

fencing topology仅 node1 执行
# 第 1 级 IPMI,失败后第 2 级切 PDU 电源
pcs stonith level add 1 node1.example.com fence-node1
pcs stonith level add 2 node1.example.com fence-pdu-node1
pcs stonith level add 1 node2.example.com fence-node2
pcs stonith level add 2 node2.example.com fence-pdu-node2

pcs stonith level config

08存储与 LVM 准备

本方案采用 HA-LVM(system_id 模式):同一时刻只有一个节点激活并挂载卷组。相比 GFS2 更简单、性能更好,是主备场景的标准做法。

操作前确认

下列命令会销毁磁盘数据。执行前务必确认 /dev/sdb 是规划中的共享盘,且两节点看到的是同一个 LUN(用 lsblk -o NAME,SIZE,WWNmultipath -ll 核对 WWN)。

16

配置 LVM system_id

让 VG 打上"归属节点"标记,防止另一节点误激活。

/etc/lvm/lvm.conf两节点均执行
# 将 system_id_source 设为 uname
sed -i 's/^\s*#\?\s*system_id_source\s*=.*/\tsystem_id_source = "uname"/' /etc/lvm/lvm.conf

# 验证:应返回本机 hostname
lvm systemid
grep -E '^\s*system_id_source' /etc/lvm/lvm.conf
17

创建 VG / LV / 文件系统

只在 node1 执行一次。--setautoactivation n 关键:禁止开机自动激活,必须由集群控制。

创建存储仅 node1 执行
# 分区(也可直接用整盘)
parted -s /dev/sdb mklabel gpt
parted -s /dev/sdb mkpart primary 0% 100%
partprobe /dev/sdb

# 加入 LVM devices 文件(RHEL 9+ 默认启用 devices file)
lvmdevices --adddev /dev/sdb1

# 创建 VG:禁止自动激活,交给集群管理
vgcreate --setautoactivation n my_vg /dev/sdb1

# 创建 LV 与文件系统
lvcreate -L 10G -n my_lv my_vg
mkfs.xfs /dev/my_vg/my_lv

# 确认 VG 的 system id 已打上 node1
vgs -o+systemid
node2 识别共享存储node2 执行
lvmdevices --adddev /dev/sdb1
pvscan --cache
vgs -o+systemid       # 应能看到 my_vg,system id 为 node1(当前归属)
绝对禁止

不要把共享 LV 写入 /etc/fstab!挂载必须完全由集群的 Filesystem 资源控制。写入 fstab 会导致两节点开机同时挂载 → XFS 文件系统损坏

09资源与约束配置

以官方的 Apache 主动/被动示例为模板。RHEL10 官方 Ch.4 其他业务(NFS、数据库、自研应用)可套用同样的结构。

18

准备 Apache

注意:不要 enable httpd,服务由集群拉起。

安装并配置 Apache两节点均执行
dnf install -y httpd wget

# apache 资源代理需要通过 server-status 探测健康,必须配置
cat > /etc/httpd/conf.d/status.conf <<'EOF'
<Location /server-status>
    SetHandler server-status
    Require local
</Location>
EOF

# ⚠ 关键:禁止 systemd 自启,避免与集群抢管理权
systemctl disable --now httpd

# 防火墙放行 http
firewall-cmd --permanent --add-service=http
firewall-cmd --add-service=http
在共享存储上准备测试页面仅 node1 执行
# 临时激活并挂载(配置完资源后会由集群接管)
lvchange -ay my_vg/my_lv
mount /dev/my_vg/my_lv /var/www
mkdir -p /var/www/html /var/www/cgi-bin /var/www/error
restorecon -R /var/www

echo '<html><body>Hello from RHEL 10 HA Cluster</body></html>' \
     > /var/www/html/index.html

umount /var/www
lvchange -an my_vg/my_lv
19

创建资源组(沙箱方式,推荐)

pcs -f 离线编辑再一次性提交,避免中间态引起资源抖动。

创建 apachegroup 资源组仅 node1 执行
# ① 导出当前 CIB 到本地文件
pcs cluster cib my_cfg

# ② 在沙箱里创建资源(组内按声明顺序启动,逆序停止,且必定同节点)
pcs -f my_cfg resource create my_lvm ocf:heartbeat:LVM-activate \
      vgname=my_vg vg_access_mode=system_id \
      --group apachegroup

pcs -f my_cfg resource create my_fs ocf:heartbeat:Filesystem \
      device="/dev/my_vg/my_lv" directory="/var/www" fstype="xfs" \
      --group apachegroup

pcs -f my_cfg resource create VirtualIP ocf:heartbeat:IPaddr2 \
      ip=192.168.10.100 cidr_netmask=24 \
      op monitor interval=30s \
      --group apachegroup

pcs -f my_cfg resource create Website ocf:heartbeat:apache \
      configfile="/etc/httpd/conf/httpd.conf" \
      statusurl="http://127.0.0.1/server-status" \
      op monitor interval=1min \
      --group apachegroup

# ③ 设置默认资源黏性,避免故障节点恢复后资源自动漂回(二次中断)
pcs -f my_cfg resource defaults update resource-stickiness=100

# ④ 校验后一次性提交
crm_verify -x my_cfg -V
pcs cluster cib-push my_cfg --config

# ⑤ 查看
pcs status
pcs resource config apachegroup
为什么用资源组而不是手写约束

资源组自动等价于"组内所有资源 colocation INFINITY + 按顺序 order"。四个资源如果手写约束需要 3 条 order + 3 条 colocation,既繁琐又易错。凡是"必须在同一节点、有严格先后顺序"的一组资源,一律用 group。

9.1 如需手工约束(跨组场景)

约束示例仅 node1 执行
# 位置倾向:正常情况下优先跑在 node1
pcs constraint location apachegroup prefers node1.example.com=100

# 共置:Website 必须和 VirtualIP 在一起(A 跟着 B 走)
pcs constraint colocation add Website with VirtualIP INFINITY

# 顺序:先起 VirtualIP 再起 Website
pcs constraint order start VirtualIP then start Website

# 查看 / 删除
pcs constraint --full
pcs constraint delete <constraint_id>
20

业务连通性验证

验证仅 node1 执行
pcs status
ip a | grep 192.168.10.100        # VIP 应出现在承载资源的节点上
mount | grep /var/www             # 文件系统已挂载
curl http://192.168.10.100        # 应返回 Hello from RHEL 10 HA Cluster
curl http://192.168.10.100/server-status | head

10验证与故障演练

上线前必须完成下列全部测试用例,并记录实际结果。建议在演练时开一个终端持续运行 watch -n1 pcs status 观察。

#测试项操作命令预期结果
T1计划内切换pcs node standby node1.example.com资源组整体迁到 node2,curl VIP 中断 < 30s;pcs node unstandby 后因 stickiness=100 不自动漂回
T2手工迁移pcs resource move apachegroup node2.example.com迁移成功;随后必须执行 pcs resource clear apachegroup 清除临时约束
T3单资源故障killall httpdmonitor 检测到失败,集群在原节点重启 Website;pcs status --full 中 failcount +1
T4反复故障触发迁移连续 kill 超过 migration-threshold资源被迁到另一节点;pcs resource cleanup 可清零计数
T5节点内核崩溃echo c > /proc/sysrq-trigger(在承载资源的节点)另一节点 fence 掉它并接管全部资源
T6强制 fencingpcs stonith fence node2.example.comnode2 重启并自动重新加入集群
T7单心跳链路故障断开 link0 网卡集群不切换,link1 接管;corosync-cfgtool -s 显示 link0 faulty
T8全部心跳断开(脑裂)同时断开 link0 与 link1有 qdevice:一侧保留 quorum 并 fence 对方;无 qdevice:观察 delay 是否有效避免互杀
T9仲裁主机故障停掉 qnetd集群退化为 two_node 模式仍可运行;pcs quorum status 显示 Qdevice 不可用
T10断电恢复两节点同时断电后依次上电wait_for_all,需两节点都启动后集群才恢复服务 —— 这是预期行为,需向业务方说明
T11集群整体重启pcs cluster stop --allpcs cluster start --all资源自动恢复到正常状态
演练常用命令仅 node1 执行
# 实时观察
watch -n1 'pcs status --full'
crm_mon -f                      # 交互式,含 failcount

# 客户端侧观察中断时长
while true; do curl -s -o /dev/null -w "%{http_code} " \
  --max-time 2 http://192.168.10.100; sleep 1; done

# standby / unstandby
pcs node standby   node1.example.com
pcs node unstandby node1.example.com

# 迁移后务必清理临时约束(否则资源被"钉死")
pcs resource move  apachegroup node2.example.com
pcs constraint --full | grep -i "cli-"
pcs resource clear apachegroup

# 清理失败计数
pcs resource failcount show --full
pcs resource cleanup Website
T2 的经典坑

pcs resource move 会创建一条 cli-prefer-*-INFINITY location 约束把资源"钉"在目标节点。如果不执行 pcs resource clear,这条约束会永久存在,导致后续故障时资源无法回到原节点。每次 move 之后立刻 clear,养成习惯。

11日常运维与排错

11.1 巡检命令

日常巡检任一节点
pcs status --full            # 总览 + failcount
pcs cluster status
pcs quorum status
pcs stonith status
pcs constraint --full
pcs config                   # 完整配置快照
crm_mon -1 -A                # 含节点属性
crm_verify -L -V             # 校验配置合法性(应无输出)
corosync-cfgtool -s          # 心跳链路健康

11.2 计划内维护

维护操作任一节点
# —— 单节点维护(打补丁、换硬件)——
pcs node standby node2.example.com     # 资源迁走
# ... 维护操作、重启 ...
pcs node unstandby node2.example.com
pcs status                              # 确认重新 Online

# —— 全集群维护模式(集群只监控不动作)——
pcs property set maintenance-mode=true
# ... 手工操作资源 ...
pcs resource refresh                    # 重新探测真实状态
pcs property set maintenance-mode=false

# —— 单个资源不受管 ——
pcs resource unmanage Website
pcs resource manage   Website

# —— 停用/启用资源 ——
pcs resource disable apachegroup
pcs resource enable  apachegroup

11.3 配置备份与恢复

备份任一节点
# 官方备份/恢复(打包 CIB + corosync 配置)
pcs config backup  /root/cluster_backup_$(date +%F)
pcs config restore /root/cluster_backup_2026-08-09.tar.bz2

# 额外导出为可复现的命令脚本(做变更前强烈建议)
pcs cluster cib > /root/cib_$(date +%F).xml
pcs resource config --output-format=cmd   > /root/resources.sh
pcs constraint config --output-format=cmd > /root/constraints.sh

11.4 排错决策树

现象先查什么常见根因
节点显示 UNCLEAN (offline),资源不切换pcs stonith status / pcs stonith history showfencing 失败:BMC 不可达、凭据过期、pcmk_host_map 名字对不上
资源 Stopped 且不启动pcs status --full 看 Failed Actions;pcs resource debug-start <rsc>RA 参数错误、依赖资源未起、存储不可用、SELinux 拦截
资源在两节点反复漂移pcs resource failcount show --fullmonitor 超时太短、应用本身不稳定、resource-stickiness=0
资源被"钉"在某节点回不去pcs constraint --full | grep cli-之前 pcs resource move 后忘了 clear
集群失去 quorum,资源全停pcs quorum status心跳全断、qdevice 不可达、no-quorum-policy=stop(默认行为)
节点重启后不自动加入systemctl is-enabled corosync pacemaker忘了执行 pcs cluster enable --all
两节点都挂载了共享 FS立即 umount 一侧并检查文件系统写了 /etc/fstab、或 fencing 未生效导致脑裂
找不到原因pcs cluster report --from "YYYY-MM-DD H:M" /tmp/report打包全集群日志提交 Red Hat Support
日志位置按需
journalctl -u pacemaker -u corosync --since "30 min ago"
tail -f /var/log/pacemaker/pacemaker.log
tail -f /var/log/cluster/corosync.log
grep -Ei 'fence|stonith|error|Transition' /var/log/messages | tail -50

# 一键打包全集群日志与配置(提交支持时使用)
pcs cluster report --from "2026-08-09 08:00" /tmp/cluster_report

12避坑清单与最佳实践

❌ 禁止

生产环境关闭 fencing

stonith-enabled=false 意味着放弃数据完整性保护,Red Hat 不提供支持。只在无共享存储的纯功能演示环境临时使用。

❌ 禁止

共享 LV 写入 /etc/fstab

会导致开机双挂载,XFS 直接损坏。挂载必须由 Filesystem 资源独占控制。

❌ 禁止

集群管理的服务 systemctl enable

httpd、nfs-server 等交给集群后必须 systemctl disable,否则与集群抢管理权。

❌ 禁止

手工编辑 corosync.conf / cib.xml

官方明确要求通过 pcs / Web 控制台 / ha_cluster 系统角色修改,手改会被覆盖或造成不一致。

❌ 禁止

用 NetworkManager 配置 VIP

浮动 IP 必须由 IPaddr2 资源管理。静态配置会与集群冲突导致 IP 冲突。

✅ 必做

每个资源都配 monitor

没有 monitor 操作 = 集群看不到故障 = 假 HA。op monitor interval=30s

✅ 必做

设置正的 resource-stickiness

建议 100。避免故障节点恢复后资源自动漂回造成二次中断。

✅ 必做

双心跳链路 + 独立带外网

单链路故障不应触发切换;fence 路径不能与心跳共用交换机。

✅ 必做

部署 qdevice

把双节点变成 3 票模型,彻底消除脑裂歧义。仲裁主机成本极低。

✅ 必做

move 之后立刻 clear

pcs resource move 会留下 cli-prefer 约束,不清理会影响后续故障切换。

✅ 必做

变更用 pcs -f 沙箱

多条命令的配置变更先导出 CIB 离线编辑,crm_verify 校验后一次性 cib-push

✅ 必做

上线前跑完全部演练用例

特别是 T5(内核崩溃)与 T8(脑裂)。没演练过的切换等于没有 HA。

12.1 关键参数推荐值

参数推荐值理由
stonith-enabledtrue生产必须
no-quorum-policystop(默认)失去 quorum 时停资源最安全;GFS2 场景才用 freeze
resource-stickiness(默认)100避免资源自动漂回
priority-fencing-delay15s必须大于一次 fence 操作耗时
pcmk_delay_basenode1=0s / node2=10s确定性打破 fencing 竞态
qdevice algorithmffsplit双节点标准选择
心跳链路数2knet 支持最多 8 条,2 条已能覆盖绝大多数故障
migration-threshold视应用而定,常用 3避免单次抖动就迁移

13附录:全量命令 / 交付表

13.1 从零到可用 —— 完整命令序列

完整部署脚本(按注释区分执行位置,请勿盲目整段执行)
#=========== [两节点] 1. 仓库与安装 ===========
subscription-manager repos --enable=rhel-10-for-x86_64-highavailability-rpms
dnf install -y pcs pacemaker fence-agents-all pcp-zeroconf corosync-qdevice

#=========== [两节点] 2. 系统准备 ===========
hostnamectl set-hostname node1.example.com          # node2 上改成 node2
cat >> /etc/hosts <<'EOF'
192.168.10.11   node1.example.com node1
192.168.10.12   node2.example.com node2
192.168.10.20   qnetd.example.com qnetd
EOF
dnf install -y chrony && systemctl enable --now chronyd
firewall-cmd --permanent --add-service=high-availability
firewall-cmd --add-service=high-availability
echo 'YourPassword' | passwd --stdin hacluster
systemctl enable --now pcsd.service

#=========== [node1] 3. 建集群 ===========
pcs host auth node1.example.com node2.example.com -u hacluster -p 'YourPassword'
pcs cluster setup my_cluster --start \
  node1.example.com addr=192.168.20.11 addr=192.168.30.11 \
  node2.example.com addr=192.168.20.12 addr=192.168.30.12
pcs cluster enable --all
pcs status

#=========== [仲裁主机] 4. qnetd ===========
# dnf install -y pcs corosync-qnetd
# systemctl enable --now pcsd.service; echo 'YourPassword' | passwd --stdin hacluster
# pcs qdevice setup model net --enable --start
# firewall-cmd --permanent --add-service=high-availability; firewall-cmd --reload

#=========== [node1] 5. 加入 qdevice ===========
pcs host auth qnetd.example.com -u hacluster -p 'YourPassword'
pcs quorum device add model net host=qnetd.example.com algorithm=ffsplit
pcs quorum status

#=========== [node1] 6. Fencing ===========
pcs stonith create fence-node1 fence_ipmilan ip=10.10.10.11 \
    username=fenceuser password='FencePass' lanplus=1 \
    pcmk_host_list="node1.example.com" pcmk_delay_base=0s op monitor interval=60s
pcs stonith create fence-node2 fence_ipmilan ip=10.10.10.12 \
    username=fenceuser password='FencePass' lanplus=1 \
    pcmk_host_list="node2.example.com" pcmk_delay_base=10s op monitor interval=60s
pcs constraint location fence-node1 avoids node1.example.com=INFINITY
pcs constraint location fence-node2 avoids node2.example.com=INFINITY
pcs property set stonith-enabled=true
pcs property set priority-fencing-delay=15s
pcs stonith fence node2.example.com          # ⚠ 验收测试

#=========== [两节点] 7. LVM + Apache 准备 ===========
sed -i 's/^\s*#\?\s*system_id_source\s*=.*/\tsystem_id_source = "uname"/' /etc/lvm/lvm.conf
dnf install -y httpd
printf '<Location /server-status>\n SetHandler server-status\n Require local\n</Location>\n' \
   > /etc/httpd/conf.d/status.conf
systemctl disable --now httpd
firewall-cmd --permanent --add-service=http && firewall-cmd --add-service=http

#=========== [node1] 8. 存储 ===========
lvmdevices --adddev /dev/sdb1
vgcreate --setautoactivation n my_vg /dev/sdb1
lvcreate -L 10G -n my_lv my_vg
mkfs.xfs /dev/my_vg/my_lv

#=========== [node1] 9. 资源 ===========
pcs cluster cib my_cfg
pcs -f my_cfg resource create my_lvm ocf:heartbeat:LVM-activate \
      vgname=my_vg vg_access_mode=system_id --group apachegroup
pcs -f my_cfg resource create my_fs ocf:heartbeat:Filesystem \
      device="/dev/my_vg/my_lv" directory="/var/www" fstype="xfs" --group apachegroup
pcs -f my_cfg resource create VirtualIP ocf:heartbeat:IPaddr2 \
      ip=192.168.10.100 cidr_netmask=24 op monitor interval=30s --group apachegroup
pcs -f my_cfg resource create Website ocf:heartbeat:apache \
      configfile="/etc/httpd/conf/httpd.conf" \
      statusurl="http://127.0.0.1/server-status" op monitor interval=1min --group apachegroup
pcs -f my_cfg resource defaults update resource-stickiness=100
crm_verify -x my_cfg -V
pcs cluster cib-push my_cfg --config

#=========== [node1] 10. 验收 ===========
pcs status --full
curl http://192.168.10.100
pcs config backup /root/cluster_backup_initial

13.2 交付检查表

13.3 参考资料

  1. RHEL 10《Configuring and managing high availability clusters》
  2. Ch.3 Creating a Red Hat High-Availability cluster with Pacemaker(安装 / 端口表 / pcs cluster setup
  3. Ch.4 Configuring an active/passive Apache HTTP server(本文资源配置模板来源)
  4. Ch.9 Configuring fencing
  5. Ch.10 Configuring SBD fencing
  6. Ch.23 Controlling cluster behavior
  7. Ch.27 Configuring cluster quorum
  8. Ch.28 Configuring quorum devices
  9. Ch.32 Performing cluster maintenance
  10. RHEL 9 同名文档(单页版,用于语法对照)RHEL9 官方
  11. man 5 votequorum / man 5 corosync.conf / man 8 pcs社区
配套文档

如需理解各组件的内部原理(Corosync、CIB、各守护进程、调度决策流程),请参阅 《RHEL 10 Pacemaker 高可用集群 · 核心模块培训文档》pacemaker-core-modules-training.html)。