更新信息
原先一直使用的是 heartbeat 2.x 来搭建 HA 系统, 后来 heartbeat 升级到了 3.x, 它将原本的模块拆分成好几个子项目. 原先的 heartbeat 只用来做心跳, 所以索性就抛弃 heartbeat 改成主流的 pacemaker
+ corosync
HA 方案.
Ruby on Rails 程序本身设计就能很好的支持集群部署, 所以就算坏一台应用服务器也不只至于造成整体无法对外提供服务.
但数据层面一般只会有一台服务器, 所以数据库服务器的可用性是整个系统的短板. 如果可以提高数据库层面服务器的可用性的话, 也就大大提高了整体系统的可用性.
数据库的可用性说白了就是多部署一台服务器, 当一台出问题的时候另外一台可以接手继续工作. 这方面 linux 有很多现成的工具. 这里只是整理一下自己的安装步骤, 目标是提供一套简单的高可用方案. (大规模复杂的就是专业 SA 的事情了)
结构规划
图中 app N
就是应用服务器(比如 rails, tomcat 之类的). 他们通过一个 虚拟的IP地址(简称: vip
) 连接到数据库服务器.
而数据库服务器则分为两台, 其中只有一台 A 会设置成 VIP 的地址.
他们之间通过心跳网络来判断是否正常, 当 A 故障后, B 服务器 就会自动的设置成 VIP的地址 对外进行服务. 当故障的 A 修复好以后, 对外服务的仍然还是 B 服务器. 直到 B 故障才会重新变为 A.
作为 vip
服务器需要自动的将修改的内容同步给另一台服务器, 以保证万一出故障另一台可以马上继续服务.
IP地址
每台服务器都配有两块网卡, 一块用于对外服务区, 一块用于内部保持心跳和通讯. 规划如下:
name | eth0 | eth1 |
---|---|---|
db1 |
192.168.37.51 | 192.168.0.1 |
db2 |
192.168.37.52 | 192.168.0.2 |
vip |
192.168.37.50 |
其中 vip
的 192.168.37.50
就是 app
服务器配置的比如: nfs
, postgresql
的地址.
基础设置
这里是基于 ubuntu server 12.04 lts 设置的, 如果是其他的服务器配置需要进行相应的修改.
设置 db1 数据库
-
修改服务器名
/etc/hostname
为db1
-
设置 IP 地址
/etc/network/interfaces
为auto eth0 iface eth0 inet static address 192.168.37.51 netmask 255.255.255.0 gateway 192.168.37.1 dns-nameservers 192.168.37.1 auto eth1 iface eth1 inet static address 192.168.0.1 netmask 255.255.255.0
db2
以此类推, 另外这里不用设置 vip
的地址.
配置 drbd
drbd 是用于在多台服务器之间自动同步硬盘数据. 一般用户上传的附件之类的不会存数据库, 所以需要通过 drbd 来同步, 如果有的数据库并没有提供复制功能, 那也可以通过 drbd 来同步数据.
1. [db1, db2]
准备 drbd 硬盘分区
我这里使用的是通过 LVM 划分出的虚拟分区 /dev/mapper/ubuntu/drbd0
另外 drbd 也可以使用真实的物理分区, 详细的分区步骤这里就不详述了.
2. 安装 drbd
root@db1:~# apt-get install -y drbd8-utils
root@db2:~# apt-get install -y drbd8-utils
安装完成后编辑 /etc/drbd.d/r0.res
加入
resource r0 {
startup {
wfc-timeout 15;
degr-wfc-timeout 60;
}
net {
after-sb-0pri discard-zero-changes;
after-sb-1pri discard-secondary;
after-sb-2pri disconnect;
}
syncer {
rate 20M;
}
on db1 {
device /dev/drbd0;
disk /dev/mapper/ubuntu-drbd0;
address 192.168.0.1:7788;
meta-disk internal;
}
on db2 {
device /dev/drbd0;
disk /dev/mapper/ubuntu-drbd0;
address 192.168.0.2:7788;
meta-disk internal;
}
}
这个配置文件中 disk
就是真实分区的位置, 还在 net
加入了对自动冲突(split brain) 的处理.
完成后将配置同步到 db2
上.
root@db1:~# scp /etc/drbd.d/r0.res db2:/etc/drbd.d/
3. 初始化磁盘
终端中输入
root@db1:~# service drbd stop
root@db2:~# service drbd stop
root@db1:~# drbdadm create-md r0
root@db2:~# drbdadm create-md r0
接着启动 drbd 服务
root@db1:~# service drbd start
root@db2:~# service drbd start
修复 ubuntu 12.04 lts 下 drbd 无法启动问题
在 ubuntu 12.04 下运行 service drbd start
很可能会出现如下错误.
node already registered
* Starting DRBD resources
DRBD module version: 8.4.2
userland version: 8.3.11
you should upgrade your drbd tools!
这是由于 linux 内核版本升级到 3.8, 而于自带的 drbd
软件不兼容导致的. 可以通过 ppa 来安装第三方提供的 8.4 版本的 drbd 解决, 命令如下:
apt-get install -y python-software-properties
add-apt-repository ppa:icamargo/drbd
apt-get update
apt-get install -y drbd8-utils
之后应该就可以启动 drbd
了
service drbd start
启动后可以输入
root@db1:~# service drbd status
drbd driver loaded OK; device status:
version: 8.4.2 (api:1/proto:86-101)
srcversion: 18C7EBE1B3F8CCCB5CF512C
m:res cs ro ds p mounted fstype
0:r0 Connected Secondary/Secondary Inconsistent/Inconsistent C
看到当前两个服务器磁盘都属于 Secondary
模式. 接着将 db1
的磁盘设置成主磁盘
root@db1:~# drbdadm -- --overwrite-data-of-peer primary r0
这时可以通过
root@db1:~# cat /proc/drbd
version: 8.4.2 (api:1/proto:86-101)
srcversion: 18C7EBE1B3F8CCCB5CF512C
0: cs:SyncSource ro:Primary/Secondary ds:UpToDate/Inconsistent C r-----
ns:1915904 nr:0 dw:0 dr:1916568 al:0 bm:116 lo:0 pe:2 ua:0 ap:0 ep:1 wo:f oos:8570524
[==>.................] sync'ed: 18.3% (8368/10236)Mfinish: 0:01:29 speed: 95,692 (95,692) K/sec
看到目前正在慢慢的将数据同步到 db2
上. 不同等待同步结束可以直接输入
root@db1:~# mkfs.ext4 /dev/drbd0
将 drbd0 的磁盘格式化成 ext4 格式. 接着需要在服务器上建被 mount 的目录
root@db1:~# mkdir /srv/drbd0
root@db2:~# mkdir /srv/drbd0
在 db1
上可以测试下能否被正确的 mount
mount /dev/drbd0 /srv/drbd0
ls -al /srv/drbd0
测试没问题后把它 umount 掉
umount /srv/drbd0
至此虽然 drbd 配置完成了, 但还需要配合 pacemaker
才能实现自动转移.
Corosync
corosync
是用于处理心跳和上报节点信息的软件.
1. 安装配置 corosync
root@db1:~# apt-get install -y corosync
root@db2:~# apt-get install -y corosync
安装完成以后修改配置文件 /etc/corosync/corosync.conf
找到
interface {
# The following values need to be set based on your environment
ringnumber: 0
bindnetaddr: 127.0.0.1
mcastaddr: 226.94.1.1
mcastport: 5405
}
其中的 bindnetaddr
需要根据当前网络修改, 通过命令 (其中 eth1
根据实际的网络修改)
ip addr | grep "inet " | grep eth1 | awk '{print $4}' | sed s/255/0/
可以看到这里我们需要修成的值是 192.168.0.0
. 于是我们把这段配置修改成
interface {
# The following values need to be set based on your environment
ringnumber: 0
bindnetaddr: 192.168.0.0
mcastaddr: 226.94.1.1
mcastport: 5405
}
接着找到
service {
# Load the Pacemaker Cluster Resource Manager
ver: 0
name: pacemaker
}
把其中的 ver: 0
修改成 1
也就是修改成:
service {
# Load the Pacemaker Cluster Resource Manager
ver: 1
name: pacemaker
}
2. 启动 corosync
完成以上配置后还需要修改 /etc/default/corosync
将
START=no
修改成
START=yes
接着通过命令, 启动 corosync
root@db1:~# service corosync start
root@db2:~# service corosync start
Pacemaker
pacemaker
是一个群集资源管理器, 用于管理 corosync 的节点.
1. 安装和运行 pacemaker
root@db1:~# apt-get install -y pacemaker
root@db1:~# apt-get install -y pacemaker
root@db1:~# service pacemaker start
root@db2:~# service pacemaker start
完成后就可以通过命令 crm status
查看节点状态了.
root@db1:~# crm status
============
Last updated: Mon Dec 2 16:45:30 2013
Last change: Mon Dec 2 16:41:35 2013 via crmd on db2
Stack: openais
Current DC: NONE
2 Nodes configured, 2 expected votes
0 Resources configured.
============
Node db1: UNCLEAN (offline)
Node db2: UNCLEAN (offline)
可以看到目前的2个节点 db1 和 db2 都是离线的. 接着就需要配置 pacemaker 了
2. 配置 pacemaker
首先在 db1
输入
crm configure property stonith-enabled=false
crm configure property no-quorum-policy=ignore
crm configure rsc_defaults resource-stickiness=100
这几条命令保证, 当 db1 宕机切换到 db2 后, db1 修复好重新上线也不会自动的把主机转移回 db1, 造成不必要的切换.
3. 配置 drbd
继续再终端输入 crm configure
进入配置模式, 接着输入
primitive drbd0 ocf:linbit:drbd \
params drbd_resource="r0" \
op monitor interval="29s" role="Master" \
op monitor interval="31s" role="Slave"
ms ms-drbd0 drbd0 \
meta master-max="1" master-node-max="1" \
clone-max="2" clone-node-max="1" \
notify="true"
primitive db-fs ocf:heartbeat:Filesystem \
params fstype=ext4 directory=/srv/drbd0 \
device=/dev/drbd0
group db-ha-group db-fs
colocation db-ha-group-on-ms-drbd0 inf: db-ha-group ms-drbd0:Master
order db-ha-group-after-ms-drbd0 inf: ms-drbd0:promote db-ha-group:start
commit
exit
这时可以通过 crm status
查看到
Online: [ db1 db2 ]
Master/Slave Set: ms-drbd0 [drbd0]
Masters: [ db1 ]
Slaves: [ db2 ]
Resource Group: db-ha-group
db-fs (ocf::heartbeat:Filesystem): Started db1
当前的 drbd 已经在 db1
上启动并挂载到 /srv/drbd0
下了.
4. 配置 vip
crm configure primitive db-ip ocf:heartbeat:IPaddr2 \
params ip="192.168.37.50" nic="eth0" meta target-role=stopped
echo $(crm configure show db-ha-group) db-ip | crm configure load update -
crm resource start db-ip
以上命令就是新建了一个叫 db-ip
的资源, 并把他加入 db-ha-group
组并启动. 为了验证是否成功我们可以输入
root@db1:~# crm status
Online: [ db1 db2 ]
Master/Slave Set: ms-drbd0 [drbd0]
Masters: [ db1 ]
Slaves: [ db2 ]
Resource Group: db-ha-group
db-fs (ocf::heartbeat:Filesystem): Started db1
db-ip (ocf::heartbeat:IPaddr2): Started db1
看到 db-ip
已经在 db1 启动了. 接着验证这个 ip 的设置可以输入
root@db1:~# ip addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:0c:0d:69 brd ff:ff:ff:ff:ff:ff
inet 192.168.37.51/24 brd 192.168.37.255 scope global eth0
inet 192.168.37.50/32 brd 192.168.37.178 scope global eth0
inet6 fe80::20c:29ff:fe0c:d69/64 scope link
valid_lft forever preferred_lft forever
可以看到这个 192.168.37.50
已经被配置给了 eth0
5. 配置 Postgresql
这里 postgresql 将使用 drbd 来做 HA, 没有使用 postgresql 自带的 replication, 主要是因为 replication 配置太复杂, 也没啥必要.
首先安装数据库
root@db1:~# apt-get install -y postgresql
root@db2:~# apt-get install -y postgresql
安装完成后, 取消 postgresql 的默认启动
root@db1:~# service postgresql stop
root@db2:~# service postgresql stop
root@db1:~# update-rc.d -f postgresql remove
root@db2:~# update-rc.d -f postgresql remove
接着在 db1
上将 postgresql 相关文件移动到 drbd 上
mkdir /srv/drbd0/postgresql
ln -s /srv/drbd0/postgresql /srv/postgresql
mv /etc/postgresql/9.1 /srv/postgresql/conf
ln -s /srv/postgresql/conf /etc/postgresql/9.1
chown postgres:postgres -h /etc/postgresql/9.1
mv /var/lib/postgresql/9.1 /srv/postgresql/data
ln -s /srv/postgresql/data /var/lib/postgresql/9.1
chown postgres:postgres -h /var/lib/postgresql/9.1
db1
这样处理好以后, 把 db2
的同样目录也指向 drbd, 输入
ln -s /srv/drbd0/postgresql /srv/postgresql
rm -rf /etc/postgresql/9.1
ln -s /srv/postgresql/conf /etc/postgresql/9.1
chown postgres:postgres -h /etc/postgresql/9.1
rm -rf /var/lib/postgresql/9.1
ln -s /srv/postgresql/data /var/lib/postgresql/9.1
chown postgres:postgres -h /var/lib/postgresql/9.1
之后就可以用 packmaker 配置启动了, 再 db1
上输入
crm configure primitive db-pg ocf:heartbeat:pgsql \
params pgctl="/usr/lib/postgresql/9.1/bin/pg_ctl" \
psql="/usr/bin/psql" pgdata="/var/lib/postgresql/9.1/main" \
config="/etc/postgresql/9.1/main/postgresql.conf" \
logfile="/var/log/postgresql/postgresql-9.1-main.log" \
op start interval="0" timeout="120s" \
op stop interval="0" timeout="120s" \
op monitor interval="30s" meta target-role=stopped
echo $(crm configure show db-ha-group) db-pg | crm configure load update -
crm resource start db-pg
之后通过 crm status
或者 sudo -u postgres psql
应该可以看到已经成功启动了数据库了.
6. 配置 NFS
对于集群的应用服务器来说, 对于像用户上传文件之类的资源, 一般都都会通过 NFS 挂在到应用服务器上. 而这里还需要将 nfs 的目录映射到 drbd 的目录.
首先安装 nfs
root@db1:~# apt-get install -y nfs-kernel-server
root@db2:~# apt-get install -y nfs-kernel-server
接着和 postgresql 一样 停止默认启动
root@db1:~# service nfs-kernel-server stop
root@db2:~# service nfs-kernel-server stop
root@db1:~# update-rc.d -f nfs-kernel-server remove
root@db2:~# update-rc.d -f nfs-kernel-server remove
接着将原有配置复制到 drbd 中
root@db1:~# mkdir /srv/drbd0/nfs
root@db1:~# ln -sf /srv/drbd0/nfs /srv/nfs
root@db1:~# mv /etc/exports /srv/nfs/
root@db1:~# ln -s /srv/nfs/exports /etc/exports
root@db1:~# mkdir /srv/nfs/data
root@db2:~# ln -sf /srv/drbd0/nfs /srv/nfs
root@db2:~# ln -sf /srv/nfs/exports /etc/exports
然后编辑 db1
的 /etc/exports
最后加入
/srv/nfs/data *(rw,no_subtree_check,no_all_squash,no_root_squash)
最后配置 pacemaker 的资源
crm configure primitive db-nfs lsb:nfs-kernel-server \
op monitor interval="30s" meta target-role=stopped
echo $(crm configure show db-ha-group) db-nfs | crm configure load update -
crm resource start db-nfs
最终输入 crm status
Online: [ db1 db2 ]
Master/Slave Set: ms-drbd0 [drbd0]
Masters: [ db1 ]
Slaves: [ db2 ]
Resource Group: db-ha-group
db-fs (ocf::heartbeat:Filesystem): Started db1
db-ip (ocf::heartbeat:IPaddr2): Started db1
db-pg (ocf::heartbeat:pgsql): Started db1
db-nfs (lsb:nfs-kernel-server): Started db1
可以看到所有的服务都已启动.
7. 测试迁移
完成之前配置后, 可以通过 crm status
看到所有资源都在 db1
上启动了, 现在我们测试能否将资源转移到 db2
上并且根据设计他不应该自动迁移回 db1
, 我们输入
crm resource migrate db-ha-group db2
crm resource unmigrate db-ha-group
先资源强制分配到 db2
, 再取消强制分配. 然后输入 crm_mon
, 应该就可以看到资源再慢慢的迁移到 db2
, 等一会最终可以看到.
Online: [ db1 db2 ]
Master/Slave Set: ms-drbd0 [drbd0]
Masters: [ db2 ]
Slaves: [ db1 ]
Resource Group: db-ha-group
db-fs (ocf::heartbeat:Filesystem): Started db2
db-ip (ocf::heartbeat:IPaddr2): Started db2
db-pg (ocf::heartbeat:pgsql): Started db2
db-nfs (lsb:nfs-kernel-server): Started db2
所有的资源都运行于 db2
了. 然后把资源再迁回 db1
可以输入之前的迁移命令也可以简单的重启 pacemaker
service pacemaker restart
不一会儿就可以通过 crm status
看到所有的资源又被迁移回了 db1
最后
至此基本的 HA 都已经配置完成了. 基本就是让 pacemaker
来负责程序的启动和调度. 另外还可以通过下列命令来设置 crm.
crm resource show # 显示所有的 resource
crm resource cleanup ${id} # 清除失败的 action 日志
crm resource stop ${id} # 停止某个 resource
crm configure delete ${id} # 删除某个配置
crm configure edit # 编辑现有的配置文件
参考资料
- http://www.drbd.org/users-guide-8.4/s-pacemaker-crm-drbd-backed-service.html
- http://clusterlabs.org/doc/zh-CN/Pacemaker/1.1-plugin/html/Clusters_from_Scratch/index.html
更新历史
- 2013-12-03 更新 使用 pacemaker + corosync 代替 heartbeat
- 2012-08-13 初始 使用 heartbeat 2.x 搭建