原文地址: https://www.tony-yin.site/2018/04/20/Ctdb-Rados-All-Banned/
ctdb
最近专门为ceph
提供了一种raods object
作为文件锁的方式,lock file
可以放在对象存储中,而不是cephfs
,从而大大降低了系统宕机的延时。在此方案的实践中,我们发现master
节点宕机会导致严重的All Banned
的问题,本文则围绕该问题展开讨论和提供本人的解决方案。
很多系统都在用ctdb
做HA
,今天我们讨论的是基于cephfs
的ctdb HA
方案。ctdb
的作用是在一个共享文件系统中,当所有节点都访问同一个文件时,ctdb
会选举出一个master
节点获得lock
,我们之前的做法是把这个lock file
放在cephfs
的共享目录中,但是当其中某个节点down
了之后,会导致cephfs
这个目录卡死,进一步导致lock file
在其他节点都获取不到,只有等到锁超时了之后才能获取到,而这个超时时间默认是300s
,再加上ctdb
的监控检测和恢复的时间,切换的时间少则十几分钟,多则几十分钟,这对于高可用场景来说无疑是灾难级的。
具体场景
ctdb
的编译和安装我就不说了,大家可以参考磨渣的文章:CTDB使用rados object作为lock file。在ceph
集群中所有节点安装好ctdb
后,起服务后通过systemctl status ctdb
可以发现reclock
是通过ctdb_mutex_ceph_rados_helper
的方式,就说明ctdb rados
的方式配置成功了。
然后我们可以通过rados -p rbd ls
也可以看到自己配置的锁存在于rbd pool
中。这时我们断电一个slave
节点,一分钟左右后可以实现节点切换。但是我们的测试发现当断网master
节点的时候,就会造成长时间的卡住,且节点并不会切换。详细查看可以发现断网后,master
节点没有释放lock
,然后其他的集群节点选举出了master
节点后,试图获取锁,但是由于之前的master
节点一直没有释放,所以一直获取不到,然后就不停的去获取,ctdb
的机制是如果有不断的这种行为,就会让所有节点All Banned
。因为slave
节点并不拥有锁,所以不存在之前的问题。
这个问题是比较严重的,因为不存在超时机制,拥有锁的节点断网或者断电,所以不会因为超时就释放锁。所以就会一直就卡着,并且一直实现不了切换节点。这就意味着一旦这种情况发生,客户的业务就会发生中断,这是无法接受的。并且我们也发现了如果使用原来将lock file
放在cephfs
目录的方式,断网或者断电主节点并不会发生这种情况,后来大概看了下源码大概是因为cephfs
自己的机制会强制释放共享目录中文件的锁。
具体报错如下:
[root@tony ~]# ctdb status
Warning: All nodes are banned.
解决方案
我们的解决方案没有尝试着修改ctdb
的源码,而是通过定时监控ctdb
的状态。如果是主节点上面的ctdb
,并且如果是rados
方式的话,每3
分钟查看一下ctdb status
的状态,如果有连续两次的状态都是All Banned
的话,我们就认为目前主节点发生了不释放锁的问题,我们就主动地删除lock object
。部分代码如下:
#! /bin/bash
function check_if_master() {
MASTER_PNN=$(ctdb recmaster)
CURRENT_PNN=$(ctdb pnn)
if [ $MASTER_PNN -eq $CURRENT_PNN ]; then
echo true
else
echo false
fi
}
function get_lock_name() {
LOCK_INFO=$(grep rados $CTDB_CONFIG_FILE | awk '{print $5}')
LOCK_NAME=${LOCK_INFO:0:-1}
echo $LOCK_NAME
}
function monitor_lock() {
STATUS_FILE=/etc/ctdb/status.txt
CTDB_STATUS=$(ctdb status 2>&1)
ALL_BANNED="Warning: All nodes are banned."
if [ ! -f "$STATUS_FILE" ]; then
echo "$CTDB_STATUS" > $STATUS_FILE
else
if [ "$CTDB_STATUS" = "$ALL_BANNED" ]; then
LAST_CTDB_STATUS=$(cat $STATUS_FILE)
if [ "$LAST_CTDB_STATUS" = "$ALL_BANNED" ]; then
LOCKNAME=$(get_lock_name)
echo $(date)" Ctdb all nodes banned: Second time" >> /var/log/monitor_ctdb.log
echo $(date)" Remove ctdb rados lock: "$LOCKNAME >> /var/log/monitor_ctdb.log
rados -p rbd rm $LOCKNAME
echo -n "" > $STATUS_FILE
else
echo $(date)" Ctdb all nodes banned: First time" >> /var/log/monitor_ctdb.log
echo "$ALL_BANNED" > $STATUS_FILE
fi
else
echo -n "" > $STATUS_FILE
fi
fi
}
CTDB_CONFIG_FILE=/etc/sysconfig/ctdb
if $(grep rados $CTDB_CONFIG_FILE -q); then
if $(check_if_master); then
monitor_lock
fi
fi
完整代码地址:https://github.com/tony-yin/C...
总结
也许我的这种做法不是最优方案,希望遇到同样问题的同学可以一起讨论,拥有更好解决方案的可以一起分享。