原文地址: https://www.tony-yin.site/201...
之前写过一篇文章【Ctdb Rados方式导致All Banned的问题】,谈到了当ctdb
将recovery lock
设置成rados
的方式后,断网master
节点会造成所有ctdb
节点All Banned
,主要原因是master
意外断网没有释放锁,其他节点无法获取到锁,当时的解决方案是每5
分钟检查一次ctdb
状态,如果连续两次发生了All Banned
的情况,则手动删除lock
,这种做法在最近的测试中遇到了一些问题,本文对这些问题进行剖析并对相应的解决方案进行分享。
完整代码地址: https://github.com/tony-yin/C...
场景一
如果基于原来的做法,ctdb
发生All Banned
的情况,需要十分钟的监控时间加上两分钟左右的recovery
时间,也就是说大概需要十二分钟才能恢复ctdb
服务,这样看来高可用有点名实其副了,这个也会明显地影响存储业务的正常运行。后来,我们讨论出新的方案:每5s
检查一次ctdb
的状态,All Banned
的次数累计到5
次才确定为该故障场景,然后手动删除lock
,最终要保证ctdb
能够在2min
内完成恢复。
问题1
cron tab
最短周期只支持分钟级别,所以如何5s
检查一次便是一个问题。
代码是死的,人是活的,虽然cron tab
只支持分钟级别,但是我们可以每分钟调用一个脚本,然后在这个脚本中遍历12
次,每次调用ctdb monitor
脚本,然后sleep 5s
,这样就可以达到每5s
检查一次ctdb
的效果了。
# ctdb_monitor
* * * * * root /etc/ctdb/cron-seconds
# cron-seconds
#!/bin/bash
for((i=1;i<=12;i++));do
../monitor_ctdb
sleep 5
done
这样检查到ctdb
发生All Banned
情况,只需要花费25s
,剩下的就是recovery
的时间了。
问题2
当ctdb master
节点的network
服务断掉,其他两个节点(我的开发环境是三节点的虚拟机环境)便会选举一个为master
节点,然后去获取lock
,因为原master
没有释放锁,导致所有节点All Banned
,即使我们手动删除了锁,但是这时候其他两个节点仍然处于Banned
的情况,需要等到Ban Timeout
才会再次尝试获取锁并开始恢复过程,这个timeout
的时间是300s
,即5min
,这显然是我们不能接受的,所以我们要在删除lock
后,重启所有节点的ctdb
服务。
不过该如何触发该重启操作呢?
我们在删除lock
后将ctdb
所有节点的ip
作为对象存进rados
中,然后在每5s
监控的脚本中,查看rados
中是否存在本节点的ip
对象,如果有,则尝试重启ctdb
操作,重启后便删除该对象。
function save_nodes_ip() {
nodes=$(ctdb listnodes)
for node in $nodes; do
echo "$node" > $node
rados -p rbd put $node $node
rm -f $node
done
}
function get_current_node_ips() {
ips=$(/usr/sbin/ip addr | grep "inet " | awk '{print $2}')
echo $ips
}
function monitor_nodes_ip_in_rados() {
ips=$(get_current_node_ips)
for ipinfo in $ips; do
ip=${ipinfo%/*}
if $(timeout 10 rados -p rbd ls | grep "$ip" -qw); then
systemctl restart ctdb
rados -p rbd rm $ip
fi
done
}
至于为什么三个节点的ip
都要存入rados
,这个是因为原master
节点恢复网络后,ctdb
服务的状态为failed
,同样需要重启ctdb
服务才能正常恢复原master
节点。
注意:
这边有两个问题,当时浪费了我不少时间,问题不是多么深奥,但是不易发现。。。
第一个问题便是ips=$(/usr/sbin/ip addr | grep "inet " | awk '{print $2}')
这行代码,原来的写法是ips=$(ip addr | grep "inet " | awk '{print $2}')
,当时发现ip
总是获取不到,然后无论是命令行还是脚本运行都可以正常获取到,后来还是同事提醒才发现在crontab
脚本中,shell
命令默认是/usr/bin/
下的,而ip
命令则是/usr/sbin/
下,所以这里的命令我们需要全路径。(这个需要格外注意!!!被坑的不要不要的。。。)
第二个问题便是rados -p rbd ls | grep "$ip" -qw
这行代码,当时没注意写成了rados -p rbd ls | grep "$ip" -w
,发现if
判断时常有问题,一开始还以为不能grep
数字什么的,后来才发现没有加q
,q
表示安静模式,不打印任何标准输出,如果有匹配的内容则立即返回状态值0。
场景二
“断网”这个词不够具体,在实际生产环境中,一个集群中,一般都会有多个网络,就拿本人的ceph
集群环境来说(物理机环境,并非前文提及的虚拟机开发环境),ceph
有个public network
和cluster network
,而ctdb
也有它的node network
和public network
,ceph
的public
和ctdb
的public
是同一网段,ceph
的cluster
是单独网段,ctdb
的node
是单独的网段。所以ctdb master
断网可以分为三种情况:
- 拔掉
ctdb master node
网段网线 - 拔掉
ctdb master public
网段网线 - 断掉
ctdb master network
服务
当拔掉ctdb master public
网段网线,这没有什么好说的,ctdb master
节点服务还存在,只是master
节点上的public address
不可用了,会漂移到其他节点上。
问题1
当拔掉ctdb master node
网段网线后,master
节点仍然有public
网卡,(这里注意)它仍然可以获取其他ctdb
节点的状态,而其他节点却不可以获取它的状态,因为master
的node
节点ip
不存在。所以造成的结果就是原master
节点还默认自己是master
节点,而其他的节点却又选举出了新的master
,我们的脚本因为All Banned
手动删除了lock
,这时候其他节点可以正常恢复ctdb
服务,但是当ctdb master
节点断网再恢复后,它还以为自己是master
,会不断去获取锁,而原来的锁已经被我们手动删除,这时候新的锁被新的master
掌握,所以此时产生脑裂,我们要牺牲原master
节点,也就是断网节点,所以需要重启它。这个重启触发机制我们是通过在每次删除lock
之后在rados
中存入ctdb
所有节点的ip
作为object
(这就是为什么要存入所有节点的ip
),然后只要发现有这个object
便执行ctdb
重启操作,然后便删除这个对象。至于为什么要存所有对象是因为除了原master
需要重启之外,另外两个正常节点发生All Banned
的情况,默认timeout
时间是300s
(这个上面也提到过),我们为了减少恢复时间,直接在删除lock
后重启ctdb
;
问题2
由于现在ctdb
的锁是放在rados
中,而不是以前的cephfs
的方式了。所以当master
断网再恢复时,它会不断地去rados
获取他原来的锁,这是获取锁的进程越来越多,会阻塞住rados
服务,我们可以通过ps -ef | grep rados_helper
看到进程不断变多,那么rados
服务不能正常读写就影响到我们上一条的机制,不能读rados
中是否含有本节点ip
的对象,就没办法进行重启操作,那么这样它就会不断地继续获取lock
,所以我们在这里又加了一个机制,如果ps -ef | grep rados_helper
的数目超过6
个,就默认启动重启ctdb
服务。
function monitor_get_lock_timeout() {
count=$(ps -ef | grep rados_helper | wc -l)
if [ $count -ge $RADOS_HELPER_PROCESS_MAX ]; then
systemctl restart ctdb
update_last_ctdb_restart_time
fi
}
问题3
ctdb
目前重启的机制有点多,有自身自带的故障重启,也有我们监控脚本的异常情况,很容易发生重复重启,还有可能rados_helper
堆积的进程很多,比如20
个,我们的脚本是5s
一次,也许20
个的时候重启了,过5s
,进程释放也需要时间,可能此时还有10
个,那么大于我们规定的6
个,就会继续重启,这种重复重启没有必要,所以我们要加上ctdb
重启的周期限定2min
。
function get_ctdb_restart_interval() {
last_time=$(get_ctdb_restart_last_time)
if [ -z "$last_time" ]; then
interval=$(expr $RESTART_CTDB_INTERVAL_MAX + 1)
else
current_time=$(date +%s)
interval=$(expr $current_time - $last_time)
fi
echo $interval
}
考虑并解决以上提到的问题,基本上可以覆盖以上三种断网的场景了,在监控和管理ctdb
的过程中,一定要小心,不能影响到业务正常运行。
总结
生产环境网络结构错综复杂,往往在虚拟机上开发的功能当时好好的,到了物理机上面测试会发生各种问题,此时,我们首先要搞清楚网络拓扑结构,熟悉硬件配置,各网段的作用和相互之间的关联,这样遇到问题我们可以顺藤摸瓜,同样ctdb
的原理也需要掌握才能了解它各种行为的触发机制,才能更好的定制化监控和管理。之后我会花点时间好好地研究一下ctdb
,然后再单独做分享。
完整代码地址: https://github.com/tony-yin/C...