原文地址: https://www.tony-yin.site/201...

ping

之前写过一篇文章【Ctdb Rados方式导致All Banned的问题】,谈到了当ctdbrecovery 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数字什么的,后来才发现没有加qq表示安静模式,不打印任何标准输出,如果有匹配的内容则立即返回状态值0。

场景二

“断网”这个词不够具体,在实际生产环境中,一个集群中,一般都会有多个网络,就拿本人的ceph集群环境来说(物理机环境,并非前文提及的虚拟机开发环境),ceph有个public networkcluster network,而ctdb也有它的node networkpublic networkcephpublicctdbpublic是同一网段,cephcluster是单独网段,ctdbnode是单独的网段。所以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节点的状态,而其他节点却不可以获取它的状态,因为masternode节点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...

Tony_Zby
7.1k 声望154 粉丝

世界太大,没事瞄一瞄


引用和评论

0 条评论