openEuler HA 存储+业务双节点集群部署
openEuler HA 存储+业务双节点集群部署
1 需求
- 支持存储+业务双活模式,存储双活采用DRBD。比如:ha01提供MySQL数据库服务,httpd业务系统服务;ha02提供FreeSWITCH通信服务。如果ha01宕机,则MySQL和httpd服务自动迁移至ha2上。
- 要求MySQL和FreeSWITCH都基于docker部署。文件系统使用GFS2(openEuler内核原生支持)。
- 对外提供统一的IP地址访问。
2 方案架构概述
| 节点 | 主业务 | 备业务 | 共享存储 |
| ha01 | MySQL+httpd | FreeSWITCH(备) | /dev/drbd0 → /data/shared (GFS2) |
| ha02 | FreeSWITCH | MySQL+httpd(备) | /dev/drbd0 → /data/shared (GFS2) |
资源组设计:
- Group A(MySQL+httpd+VIP):优先运行于ha01,故障时切换到ha02。
- Group B(FreeSWITCH+VIP):优先运行于ha02,故障时切换到ha01
资源自动链:DRBD->DLM->GFS2->业务资源组。
3 环境准备
| IP | 主机名 | 操作系统 |
| 10.32.0.90 | ha01 | openEuler 24.03 (LTS-SP4) |
| 10.32.0.91 | ha02 | openEuler 24.03 (LTS-SP4) |
| 10.32.0.92 | vip | ha01组业务虚拟IP |
| 10.32.0.93 | vip | ha02组业务虚拟IP |
两台节点均需执行
3.1 基础配置
# 设置主机名(ha01执行) hostnamectl set-hostname ha01 # (ha02执行) hostnamectl set-hostname ha02 # 编辑 /etc/hosts cat >> /etc/hosts << EOF 10.32.0.90 ha01 10.32.0.91 ha02 EOF # 关闭防火墙(测试环境) systemctl stop firewalld systemctl disable firewalld # 关闭 SELinux setenforce 0 sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
3.2 安装软件包
安装依赖库
dnf install -y corosync pacemaker pcs fence-agents drbd-utils docker haproxy httpd patch make gcc gcc-c++ kernel-devel flex perl automake libtool elfutils-libelf-devel
源码编译drbd
dnf install -y patch make gcc gcc-c++ kernel-devel flex perl \ automake libtool elfutils-libelf-devel # 下载源码 wget https://pkg.linbit.com/downloads/drbd/9/drbd-9.3.3.tar.gz tar -xzvf drbd-9.3.3.tar.gz # 编译并安装 make -j make install
安装依赖库
dnf install -y e2fsprogs-devel glib2-devel libtool autoconf automake \ python3-devel uuid-devel libaio-devel readline-devel ncurses-devel \ zlib-devel libxml2-devel
3.3 配置集群用户并启动服务
# 设置 hacluster 密码(两节点设为相同,本处密码为操作系统用户密码) passwd hacluster # 启动 pcsd 服务 systemctl start pcsd systemctl enable pcsd # 启动 Docker systemctl start docker systemctl enable docker
3.4 节点认证(仅在ha01上执行)
pcs host auth ha01 ha02 -u hacluster
3.5 创建集群(仅在ha01上执行)
pcs cluster setup hacluster ha01 addr=10.32.0.90 ha02 addr=10.32.0.91 --start pcs cluster enable --all pcs property set stonith-enabled=false pcs property set no-quorum-policy=ignore
4 配置DRBD双主模式
4.1 用LVM将多块硬盘聚合成一个大设备(所有节点)
# 1. 准备物理卷(PV) pvcreate /dev/sdb /dev/sdc /dev/sdd # 2. 创建卷组(VG),命名为 vg_drbd vgcreate vg_drbd /dev/sdb /dev/sdc /dev/sdd # 3. 创建逻辑卷(LV),命名为 lv_data(假设用全部空间) lvcreate -l 100%FREE -n lv_data vg_drbd # 4. 此时设备路径为 /dev/vg_drbd/lv_data,用于 DRBD
4.2 创建DRBD资源文件(所有节点)
编译/etc/drbd.d/r0.res
resource r0 { protocol C; startup { become-primary-on both; } net { allow-two-primaries yes; cram-hmac-alg sha1; shared-secret "Z8m2qP5tR7xW9nJ3kL1cV6fG4yB8oU0="; } on ha01 { device /dev/drbd0; disk /dev/vg_drbd/lv_data; # ← 替换为 LVM 逻辑卷路径 address 10.32.0.90:7789; meta-disk internal; } on ha02 { device /dev/drbd0; disk /dev/vg_drbd/lv_data; # ← 两台节点路径必须一致 address 10.32.0.91:7789; meta-disk internal; } }
4.3 初始化并启动DRBD(所有节点)
drbdadm create-md r0
drbdadm up r0
4.4 建立初步同步
在ha01执行:
drbdadm primary --force r0
等待同步完成后(用 drbdadm status r0 查看状态变为 UpToDate/UpToDate),再在 ha02 执行:
drbdadm primary r0
验证双主状态:
drbdadm status r0
# 应显示两个节点均为 Primary 角色
节点ha01:

节点ha02:

5 配置GFS2集群文件系统
5.1 安装GFS2工具
两个节点执行
dnf install -y gfs2-utils dlm lvm2-lockd
GFS2 的核心不是文件系统,而是:
GFS2 | DLM (Distributed Lock Manager) | Corosync | Pacemaker
所以需要两个节点:
systemctl enable dlm
但不要马上启动。生产环境应该让Pacemaker管理。
5.2 确认DRBD保持不变
即:

以及

5.3 创建LVM lock
两个节点启用
systemctl enable lvmlockd
systemctl start lvmlockd
5.4 创建GFS2
只执行一次(在ha01)
mkfs.gfs2 \ -p lock_dlm \ -j 2 \ -t ha_cluster:gfs2 \ /dev/drbd0
参数:
-p lock_dlm #使用集群锁:DLM -j 2 #必须为2,因为是2个节点

6 启动DLM
创建资源:
pcs resource create dlm ocf:pacemaker:controld \ op monitor interval=30s pcs resource update dlm allow_stonith_disabled=true pcs resource cleanup dlm pcs resource enable dlm pcs status

7 手动验证GFS2双节点挂载
不要马上交给Pacemaker。先测试。
两个节点执行:
mkdir -p /data/shared mount -t gfs2 /dev/drbd0 /data/shared mount | grep gfs2

测试:
ha01:
echo hello > /data/shared/test.txt
ha02:
cat /data/shared/test.txt
返回:

说明:DRBD+DLM+GFS2完成。
测试完需要手工卸载,然后完成后续工作
umount /data/shared
8 GFS2加入Pacemaker
创建资源:
pcs resource create gfs2_data Filesystem \ device="/dev/drbd0" \ directory="/data/shared" \ fstype="gfs2" \ options="noatime"
然后
pcs resource clone gfs2_data
查看状态
pcs status

9 配置VIP
创建虚拟IP
pcs resource create VirtualIP IPaddr2 ip=10.32.0.92 cidr_netmask=23 op monitor interval=30s
10 DRBD加入Pacemaker
下确认两个节点
drbdadm status r0
输出为
role:Primary
peer role:Primary
disk:UpToDate

由于这是前面手动维护的DRBD双主。所以这里需要进行调整:
10.1 进入维护模式,防止Pacemaker自动干预:
pcs property set maintenance-mode=true
确认:
pcs status

10.2 停止GFS2
pcs resource delete gfs2_data --force
两个节点执行
mount | grep gfs2
如果仍然挂载则执行:
umount /data/shared
10.3 删除DLM
pcs resource delete dlm --force
10.4 清理失败的DRBD资源(如果有的话)
pcs resource delete drbd_r0 --force
10.5 检查DRBD本身
drbdadm status r0
应该还是:
r0 role:Primary
disk:UpToDate
ha02 role:Primary
peer-disk:UpToDate
如果保持这个状态,说明存储没有受到影响
pcs resource cleanup
pcs resource refresh
pcs status

10.6 创建DRBD Pacemaker资源
pcs resource create drbd_r0 ocf:linbit:drbd \ drbd_resource=r0 \ op monitor interval=30s
然后马上转换为promotable:
pcs resource promotable drbd_r0 \ promoted-max=2 \ promoted-node-max=1 \ clone-max=2 \ clone-node-max=1 \ notify=true

10.7 退出maintenance模式
pcs property set maintenance-mode=false

验证DRBD
两个节点
drbdadm status r0
应该:
r0 role:Primary
disk:UpToDate
ha02 role:Primary
peer-disk:UpToDate
10.8 恢复DLM
pcs resource create dlm ocf:pacemaker:controld \ allow_stonith_disabled=true \ op monitor interval=30s
创建clone
pcs resource clone dlm meta interleave=true
查看状态
pcs status

10.9 恢复GFS2
创建资源
pcs resource create gfs2_data Filesystem \ device="/dev/drbd0" \ directory="/data/shared" \ fstype="gfs2" \ options="noatime" \ op monitor interval=20s
设置GFS2克隆,GFS2必须双节点同时挂载
pcs resource clone gfs2_data meta interleave=true
添加启动顺序,必须
DRBD Primary ↓ DLM ↓ GFS2 mount
添加约束:
pcs constraint order promote drbd_r0-clone then start dlm-clone
然后
pcs constraint order start dlm-clone then start gfs2_data-clone
至此,存储高可用层已经跑通。
11 部署MySQL
11.1 创建共享目录
因为GFS2已经挂载
/data/shared
所以在其中一个节点执行:
mkdir -p /data/shared/mysql
11.2 创建MySQL Dockers容器(不要启动)
注意:因为Pacemaker后面接管,所有这里用dockers create。
所有节点:
docker create \ --name mysql \ --network host \ -v /data/shared/mysql:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=bsoft-media \ -e MYSQL_DATABASE=wvp \ bsoft-media-mysql:ori
11.3 创建systemd服务
在所有节点都创建
文件:
vim /etc/systemd/system/mysql-container.service
内容:
[Unit] Description=Docker MySQL Container Requires=docker.service After=docker.service [Service] Type=oneshot RemainAfterExit=yes ExecStart=/usr/bin/docker start mysql ExecStop=/usr/bin/docker stop mysql TimeoutStartSec=120 TimeoutStopSec=120 [Install] WantedBy=multi-user.target
重启加载
systemctl daemon-reload
测试
systemctl start mysql-container docker ps systemctl stop mysql-container docker ps
输出如下图,表示正常

11.4 Pacemaker接管
先确认:
pcs status
现在应该类似:
VirtualIP Started ha01
DRBD Master ha01 ha02
DLM Started
GFS2 Started

创建资源:
pcs resource create mysql \ systemd:mysql-container \ op monitor interval=30s
pcs status显示如下结果,表示正常:
mysql-container Started ha01

11.5 绑定运行节点
让MySQL默认在ha01:
pcs constraint location mysql prefers ha01=100
查看
pcs constraint
出现如下提示,表示绑定成功
Location Constraints:
mysql prefers ha01

11.6 绑定VIP
VIP和MySQL必须在同一个节点
pcs constraint colocation add mysql with VirtualIP INFINITY
设置顺序:
pcs constraint order start VirtualIP then mysql
这时,启动顺序为:
ha01: VIP | MySQL
故障转移时启动顺手为:
ha01 DOWN VIP | ha02 | MySQL
确认VIP和MySQL在同一个节点,即ha01上

11.7 测试故障迁移
先模拟,测试Pacemaker是否自动拉起。
systemctl stop mysql-container
稍等2秒,查看状态,如下MySQL仍然运行在ha01上。说明Pacemaker工作正常:

测试节点故障(在ha01):
shutdown -h now
观察ha02(VIP MySQL成功跳转ha02)

ha01 修复后,VIP和MySQL又跳转回来了。
12 部署FreeSWITCH
12.1 创建共享目录
mkdir -p /data/shared/freeswitch/conf mkdir -p /data/shared/freeswitch/recordings
第一次启动时,把镜像里的默认配置复制出来
docker run -d \ --name freeswitch-init \ --network host \ bsoft-switch:v1.0.0
复制配置
docker cp freeswitch-init:/usr/local/freeswitch/conf/. \ /data/shared/freeswitch/conf/
删除临时容器
12.2 创建容器FreeSWITCH(不要启动)
所有节点:
docker create \ --name freeswitch \ --network host \ -v /data/shared/freeswitch/conf:/usr/local/freeswitch/conf \ -v /data/shared/freeswitch/recordings:/usr/local/freeswitch/recordings \ bsoft-switch:v1.0.0
12.3 创建systemd服务
所有节点:
cat >/etc/systemd/system/freeswitch-container.service <<EOF [Unit] Description=Docker FreeSWITCH Container Requires=docker.service After=docker.service [Service] Type=oneshot RemainAfterExit=yes ExecStart=/usr/bin/docker start freeswitch ExecStop=/usr/bin/docker stop freeswitch TimeoutStartSec=120 TimeoutStopSec=120 [Install] WantedBy=multi-user.target EOF
刷新:
systemctl daemon-reload
12.4 Pacemaker接管
创建资源:
pcs resource create freeswitch \ systemd:freeswitch-container \ op monitor interval=30s
指定运行节点:
pcs constraint location freeswitch prefers ha02=100
查看:
pcs constraint

freeswitch运行在正确的节点上:

添加资源约束:
GFS2启动后启动
pcs constraint order start gfs2_data-clone then start freeswitch
GFS2不存在则不能运行
pcs constraint colocation add freeswitch with gfs2_data-clone INFINITY
12.5 调整现有MySQL VIP,需改成资源组
pcs constraint --full
应该有:
mysql with VirtualIP
如图:

删除:
pcs constraint remove colocation-mysql-VirtualIP-INFINITY
创建Group A资源组
pcs resource group add group-db VirtualIP mysql
顺序非常重要
group-db: VirtualIP | mysql
Pacemaker自动保证:
启动:
VIP
↓
mysql
停止:
mysql
↓
VIP
检查:
pcs status
应该类似如下输出,表示操作成功
Resource Group: group-db
VirtualIP Started ha01
mysql Started ha01
如图:

固定资源组group-db在ha01上:
pcs constraint location group-db prefers ha01=100
12.6 绑定VIP
创建Group B(FreeSWITCH) VIP:
pcs resource create VirtualIP-media \ ocf:heartbeat:IPaddr2 \ ip=10.32.0.93 \ cidr_netmask=23 \ op monitor interval=30s
创建Group B业务组:
pcs resource group add group-media VirtualIP-media freeswitch
形成:
group-media: VirtualIP-media | freeswitch

固定FreeSWITCH默认运行ha02。
13 故障测试
13.1 测试1 ha01宕机
执行:
pcs node standby ha01
期望:
ha02:
group-db: VIP 10.32.0.92 mysql
同时:
group-media: VIP 10.32.0.93 freeswitch
即,所有业务运行在ha02节点:

13.2 测试2 恢复ha01
pcs node unstandby ha01
Group A资源组业务迁移回ha01

13.3 测试3 ha02宕机
期望:
ha01:
group-media: VIP 10.32.0.93 freeswitch
同时:
group-db: VIP 10.32.0.92 mysql

13.4 测试4 恢复ha02
pcs node unstandby ha02
恢复原有运行规则:

14 启用Pacemaker STONITH(IPMI/Fence)
因为DRBD Dual Primary 环境,STONITH 不是可选项
所以,生产环境必须STONITH/Fencing
如果是物理机则走路线:
fence_ipmilan
如果是VMware ESXi则:
fence_vmware_soap
如果是KVM,用:
fence_virt
如果是华为/浪潮/戴尔/超微服务器,执行:
dmidecode -s system-manufacturer
本文用VMware ESXi。所以使用fence_vmware_soap,通过vCenter API控制虚拟电源。
Pacemaker | | fence_vmware_soap | | vCenter Server | | VMware VM
14.1 安装fence agent
两个节点执行:
dnf install -y python3-requests fence-agents-vmware-soap
14.2 创建STONITH设备
记住VMware ESXi IP地址和登录信息:

将下列命令中的IP、用户名、密码换成上图登录ESXi时使用的信息。
创STONITH设备
pcs stonith create fence-esxi fence_vmware_rest \ ip="10.32.0.205" \ username="root" \ password="你的ESXi登录密码" \ ssl_insecure="1" \ pcmk_host_map="ha01:openEuler-ha01;ha02:openEuler-ha02"
可以看到fence-esxi在ha01或ha02其中一个节点上显示为Started,而不是Stopped。

14.3 开启全局STONITH开关
pcs property set stonith-enabled=true
14.4 调整集群仲裁策略(针对DRBD+GFS2)
由于只有2个节点,一旦心跳中断,两个节点都会失去多数派(Quorum)。未来防止GFS2备双主挂载损坏。执行以下命令,让失去仲裁的节点主动自杀(配合STONITH重启)
pcs property set no-quorum-policy=suicide
14.5 模拟围栏测试
pcs stonith fence ha02
此时在 ESXi 界面里看到 openEuler-ha02 虚拟机立刻重启(状态变灰再变绿)。如果这一步成功, STONITH 防线已经生效。
等ha02节点重启后,业务组又恢复原始状态:

未来防止开机时GFS2抢跑,必须避免开机时GFS2比DLM先启动,导致挂载失败。所以规定启动顺序:
# 1. DRBD 必须先提升为主,才能启动 DLM 锁服务 pcs constraint order promote drbd_r0-clone then start dlm-clone # 2. DLM 锁服务启动后,才能挂载 GFS2 pcs constraint order start dlm-clone then start gfs2_data-clone # 3. GFS2 挂载好了,才能启动 MySQL 和 FreeSWITCH 业务组 pcs constraint order start gfs2_data-clone then start group-db pcs constraint order start gfs2_data-clone then start group-media
配置DRBD的“脑裂自动裁决”机制,这是防止极端情况下数据损毁最后一道防线。编辑/etc/drbd.d/global_common.conf文件,net { ... } 部分中加入以下三行:
net { fencing resource-only; after-sb-0pri discard-zero-changes; after-sb-1pri discard-secondary; after-sb-2pri disconnect; }
执行
drbdadm adjust r0
验证:

15 新增服务
以添加httpd服务为例。
- 执行添加命令
pcs resource create httpd systemd:httpd --group group-db
- 确认资源和启动顺序
Resource Group: group-db VirtualIP (ocf::heartbeat:IPaddr2): Started ha01 mysql (systemd:mysql-container): Started ha01 httpd (systemd:httpd): Started ha01
调整顺序,如:
pcs resource group add group-db httpd --before mysql
浙公网安备 33010602011771号