分类目录归档:其他

Redhat cluster 的心跳机制

Redhat cluster软件的架构分为两层:

  • 底层的cluster messaging layer(集群信息层)负责在所有节点之间传递集群信息包括心跳信息
    RHEL6及之前是CMAN,从RHEL7开始是Corosync。
  • 上层的cluster resource manager(集群资源管理器)管理划归集群所属的资源,包括IP地址、逻辑卷、文件系统等
    RHEL6及之前是RGManager,从RHEL7开始是Pacemaker。

心跳机制使用的是Totem协议,Totem是一个巨复杂的协议,心跳只是其中一个很小的部分,可以这么理解:Totem协议负责处理集群内部所有节点之间的通信问题,包含四个主要组件:Total Ordering Protocol, Membership Protocol, Recovery Protocol, Flow Control Mechanism。这里只讲与心跳有关的部分。

Totem的通信是两种方式互相配合进行的:multicast(多播)和token(令牌)。[注意multicast与broadcast(广播)不同,它相当于限定范围的广播,只有分组内的成员能收到。] 一个节点只有在持有token的时候才能发言,通过multicast给集群内所有节点发送信息,每一条multicast信息都附有顺序号,发送完成之后就把顺序号记录到token中,然后把token传给下一个节点,token的传递方式是一对一的,就像首尾相连的环形接力,从IP地址最小的节点依次传给下一个IP地址更大的节点,每个节点收到token之后,根据token里记录的顺序号检查自己是否漏收了multicast信息,如果漏收了,就在token里添加重传申请,然后把token传给下一个节点,下一个节点除了检查自己有没有漏收multicast之外,还会根据token里记录的重传申请把相应的multicast信息重新发送一次(当然前提是该节点已经收到了这条multicast信息)。

totem

集群通过两种机制检测节点的健康状态:

  • Token超时

如果token中断的时间超过了指定的期限,节点就会触发membership protocol,重组cluster,这个期限通过以下参数设置:<totem token=”XXX”/>
注:XXX以毫秒为单位。

在上述期限内,token会尝试重传,重传的次数是以下参数指定的,缺省值是4:
<totem token_retransmits_before_loss_const=”X”/>

  • Multicast retransmit(重传)超过阈值

如果某个节点能收到token,但收不到multicast,那么经过若干次token循环之后,也会触发membership protocol,重组cluster,这个次数可以通过参数 fail_recv_const 设置,缺省值是2500次。

Totem协议有4种状态,代表集群运行的不同阶段,不同的状态下运行的子协议也不同:

  • GATHER
  • COMMIT
    Gather和Commit状态出现在集群组建的阶段,可以比喻为集合、报数的过程。运行的是membership protocol。
  • RECOVERY
    Recovery状态出现在集群发生问题、进行恢复的阶段。运行的是recovery protocol。
  • OPERATIONAL
    Operational状态表示集群正常工作的阶段。运行的是Totem Ordering Protocol。

totem_status

有了这些基本概念,我们就可以大致看懂集群的日志了:

 

怎样避免mysqld被oom-killer杀死?

前几天我的小小的WordPress服务器被攻击了,被五六台机器从不同的IP发起xmlrpc攻击,大约每秒80次请求的样子,虽然不算猛烈,但我的虚拟机本身太小了,单CPU仅1GB内存,于是频频出现内存紧张的情况,oom-killer会自动选择合适的进程牺牲掉,但它怎么就那么不开眼,每次都选中了最重要的mysqld进程。解决的思路很简单,就是减小mysqld进程的oom_score_adj值,因为oom-killer通过比较每个进程的oom_score来挑选要出局的进程,数值越大就越容易被选中,而手工调整oom_score是通过oom_score_adj来实现的,命令如下:

但是每次reboot或者mysql重启都需要重新设置一遍实在麻烦,怎样才能实现自动化呢?我的做法如下,虽然道理很简单,但实现过程中有好几个坑,包含了几个有用的知识点,所以有点共享价值。

要点一:如何修改systemd的服务脚本

首先,我想在启动mysql服务的时候就自动把 oom_score_adj 调整好,理论上应该通过修改启动脚本完成,问题是我用了CentOS 7,systemd的启动脚本与以前有很大的不同。还是让我们先找到它吧:

显然/usr/lib/systemd/system/mariadb.service就是我们感兴趣的,可以直接修改,但不建议这么做,因为将来升级mariadb有可能会覆盖掉你的改动。更好的方式是这样的:创建一个新目录 “/etc/systemd/system/mariadb.service.d/”,把需要改动的内容放在该目录下的”.conf”文件里,文件名可以随便,但必须以”.conf”结尾。这个文件起的作用是对systemd的service文件做出补充,systemd的service文件包括三个主要部分:[Unit], [Service]和[Install],其中[Service]段定义了服务启停的方法,[Service]段中以下三个字段是最常用的:
ExecStart 用于定义启动服务的命令,
ExecStartPre 是启动服务之前执行的命令,
ExecStartPost 是启动服务之后执行的命令。
我们修改oom_score_adj的任务应该放在ExecStartPost部分,需要注意的是:命令必须使用全路径,因为systemd不提供设置好的PATH环境变量。(注:更多关于systemd的细节可参考Red Hat的系统管理指南

我们只增加了一行有实质内容,即让mariadb.service在启动之后调用自定义的脚本oom_mysql.sh,我们在这个脚本中调整mysqld进程的oom_score_adj值,脚本如下 (因为mariadb会把mysqld的PID记录在/var/run/mariadb/mariadb.pid之中,所以在脚本中我们寻找mysqld进程号便简单了):

要点二:如何设置不用输入密码而且没有tty的sudo

为什么我要通过sudo来执行以上脚本呢?
ExecStartPost=/bin/sudo   /usr/local/bin/oom_mysql.sh
为什么不象下面这样直接执行呢?
ExecStartPost=/usr/local/bin/oom_mysql.sh

这是因为mariadb.service是以mysql用户的身份启动的,mysql用户不具备写入/proc的权限,而在oom_mysql.sh脚本中我们需要写入 /proc/<pid>oom_score_adj

为了使mysql用户能够写入/proc/<pid>/oom_score_adj,我们利用了sudo。修改sudo配置的命令是visudo,如果不加 -f 参数,它默认修改配置文件/etc/sudoers,但直接改动/etc/sudoers不太稳妥,万一改坏了什么地方就有麻烦,所以推荐的方法是:把你要增加的sudoers配置放在/etc/sudoers.d/ 目录下的文件中,文件名可以随意。

我们把要增加的配置放进了4mysql文件里,内容只有两行,需要解释一下:因为mysql用户很特别,它是不能登录的,因为它的shell是/sbin/nologin,而且在自动启动mariadb.service的时候显然也不能依赖人工输入口令去执行sudo,所以它的sudoers配置需要一种特殊的权限–即无需输入口令就可以执行sudo命令,这就是上述第二行 “mysql ALL=(ALL) NOPASSWD:” 的意思;第一行表示mysql用户执行sudo可以无需tty(终端),因为RHEL和CentOS默认情况下sudo需要tty才能执行,否则会发生下列错误:

至此,我们的工作就完成了,最后重启mariadb.service即可:

再检查mysqld,发现它的oom_score_adj已经设置为-100了,正是我们所期望的: