解决 ClickHouse 高可用集群中 VRID 冲突问题:基于 chproxy 和 keepalived 的实践分析

Part1背景描述

近期,我们部署了两套 ClickHouse 生产集群,分别位于同城的两个数据中心。这两套集群的数据保持一致,以便在一个数据中心发生故障时,能够迅速切换应用至另一个数据中心的 ClickHouse 实例,确保服务连续性。

这两套 ClickHouse 集群采用高可用架构,由两台 ClickHouse 和三台 ZooKeeper 组成,前端通过两台服务器部署了 chproxy 与 keepalived,采用主备模式。当主节点发生异常时,VIP(虚拟 IP 地址)可以快速漂移至备份节点,继续对外提供服务。

这两套同城数据中心的 ClickHouse 集群的 chproxy + keepalived 配置基本相同,除了虚拟 IP 地址(virtual_ipaddress)和虚拟路由器 ID(virtual_router_id)不同。

keepalived.conf 配置文件内容如下:

! Configuration File for keepalived

global_defs {
   notification_email {
     xxxx@xxxx.com
   }
   notification_email_from xxxx@xxxx.com
   smtp_server 192.168.xxx.xxx
   smtp_connect_timeout 30
   router_id Chproxy_Router_Id
}

vrrp_script chk_http_port {
        script "/opt/check_chprpoxy.sh"
        interval 40
        weight 20
}

vrrp_instance VI_1 {
    state MASTER 
    interface ens192
    virtual_router_id xxx
    priority 110
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    track_script {
        chk_http_port
    }
    virtual_ipaddress {
        192.168.xxx.xxx
    }
    notify_master "/opt/keepalived_notify.sh"

}

chproxy和keepalived 服务启动后,virtual_ipaddress 会漂移到其中一个节点,如下所示。

昨日巡检发现其中一个机房的 chproxy-keepalived 的集群 /var/log/messages 产生大量如下信息。

[root@xxx-xxx-chproxy-keepalive-xxx ~]# tail -1f /var/log/messages
Oct 16 16:38:09 localhost Keepalived_vrrp[3712]: VRRP_Instance(VI_1) Dropping received VRRP packet...
Oct 16 16:38:16 localhost Keepalived_vrrp[3712]: (VI_1): ip address associated with VRID 53 not present in MASTER advert : 192.168.xxx.xxx
Oct 16 16:38:16 localhost Keepalived_vrrp[3712]: bogus VRRP packet received on ens33 !!!
Oct 16 16:38:16 localhost Keepalived_vrrp[3712]: VRRP_Instance(VI_1) Dropping received VRRP packet...
Oct 16 16:38:17 localhost Keepalived_vrrp[3712]: (VI_1): ip address associated with VRID 53 not present in MASTER advert : 192.168.xxx.xxx
Oct 16 16:38:17 localhost Keepalived_vrrp[3712]: bogus VRRP packet received on ens33 !!!
Oct 16 16:38:17 localhost Keepalived_vrrp[3712]: VRRP_Instance(VI_1) Dropping received VRRP packet...
Oct 16 16:38:20 localhost Keepalived_vrrp[3712]: (VI_1): ip address associated with VRID 53 not present in MASTER advert : 192.168.xxx.xxx

上面的日志信息里,192.168.xxx.xxx 是 virtual_ipaddress 地址信息, VRID 53 是 该 集群 virtual_router_id 的信息。

/var/log/messages 日志文件里上述信息刷新频率很高。

Party2 分析过程

通过上面的日志,可以看到 /var/log/messages 日志里反复出现如下三段内容:

localhost Keepalived_vrrp[3712]: (VI_1): ip address associated with VRID 53 not present in MASTER advert : 192.168.4.199
localhost Keepalived_vrrp[3712]: bogus VRRP packet received on ens33 !!!
localhost Keepalived_vrrp[3712]: VRRP_Instance(VI_1) Dropping received VRRP packet...

2.1 日志信息含义

上面这些信息代表什么意思呢,我查询了相关资料,对上面的文字内容解释如下:

1) Keepalived_vrrp[3712]: (VI_1): ip address associated with VRID 53 not present in MASTER advert : 192.168.xxx.xxx

  • 这条日志表示 Keepalived 的 VRRP 实例 VI_1 收到了与 VRID 53 相关的广告包,但在这个广告包中并没有找到指定的虚拟 IP 地址 ( 192.168.xxx.xxx)。

  • 可能意味着:

    这通常是主节点发送的广告包与当前节点的配置不一致所导致的。

    • 广告包中的虚拟 IP 配置与当前节点期望的配置不匹配,可能是由于配置不一致。

    • 有其他节点错误地发送了与 VRID 53 相关的广告包,且该广告包中并未包含 192.168.xxx.xxx

2) Keepalived_vrrp[3712]: bogus VRRP packet received on ens33 !!!:

  • 这条日志表示在网络接口 ens33 上接收到了一个"伪造的" VRRP 包。也就是说,这个 VRRP 包不符合节点对其所在集群的期望。

  • 这种情况可能是由于:

    • 网络中有其他设备错误地发送了 VRRP 广告包。

    • 主备节点之间的配置不一致,导致某个节点发送了无效或不可识别的 VRRP 广告包。

3)Keepalived_vrrp[3712]: VRRP_Instance(VI_1) Dropping received VRRP packet...:

  • 这条日志表示 Keepalived 丢弃了收到的 VRRP 包,因为它认为这些包是无效的或伪造的。

  • 由于接收到的 VRRP 包与期望不一致,因此将其丢弃以避免不正确的状态切换。

2.3 问题排查

针对以上可能存在的原因分析,目前极有可能是 网络中存在其他与 VRID 53 冲突的设备这条比较符合,那该如何排查分析原因,通过查询资料,显示可以使用 tcdump 命令来检测网络中的 VRRP 广播包,查看是否有冲突的 VRID 。

小贴士

-- tcpdump 是一个网络抓包工具,用于在指定的网络接口上捕获网络数据包并分析。

tcpdump -nn 的作用是让 tcpdump 直接显示 IP 地址和端口号,避免 DNS 查询和服务名解析。 

-nn 是 tcpdump 的常用选项之一,具体用法和含义如下:

-n:这个选项告诉 tcpdump 不解析主机名,直接显示 IP 地址,而不是将 IP 地址解析为主机名。这样可以提高性能,因为省去了 DNS 查询的过程。
例如,显示 192.168.1.1 而不是通过 DNS 查询显示类似 server.example.com。

-nn:当两个 n 连续使用时,除了不解析主机名外,还不解析端口号。这样 tcpdump 会直接显示端口号,而不是显示对应的服务名称。
例如,显示 192.168.1.1:80 而不是 192.168.1.1:http,显示 443 而不是 https。

-- 实例
# 捕获所有流量
tcpdump -nn

# 捕获特定接口的流量
tcpdump -nn -i ens33

# 捕获特定主机的流量
tcpdump -nn host 192.168.1.100

# 捕获特定端口的流量
tcpdump -nn port 80

# 捕获 VRRP 流量
tcpdump -nn -i any net 224.0.0.0/8 | grep "VRRP"

此处采用 tcpdump 来捕获 VRRP 流量,通过使用 tcpdump 使用捕获 VRRP 流量的命令,得到的信息如下。

[root@xxx-xxx-chproxy-keepalive-xxx ~]# tcpdump -nn -i any net 224.0.0.0/8
-- 得到的信息如下
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
16:33:07.480496 IP 192.168.xxx.63 > 224.0.0.18: VRRPv2, Advertisement, vrid 184, prio 102, authtype none, intvl 1s, length 20
16:33:07.519685 IP 192.168.xxx.35 > 224.0.0.18: VRRPv2, Advertisement, vrid 76, prio 100, authtype simple, intvl 1s, length 20
16:33:07.538291 IP 192.168.xxx.116 > 224.0.0.18: VRRPv2, Advertisement, vrid 118, prio 100, authtype simple, intvl 1s, length 20
16:33:07.545689 IP 192.168.xxx.10 > 224.0.0.18: VRRPv2, Advertisement, vrid 181, prio 102, authtype none, intvl 1s, length 20
...... 省略其它信息


-- 解释如下
此处以 16:33:07.480496 IP 192.168.xxx.63 > 224.0.0.18: VRRPv2, Advertisement, vrid 184, prio 102, authtype none, intvl 1s, length 20 为例来进行解释

1) 16:33:07.480496: 
   时间戳,表示捕获到此数据包的时间

2) IP 192.168.xxx.63 > 224.0.0.18:
  192.168.xxx.63:源 IP 地址,这个数据包是从 IP 地址 192.168.xxx.63 发送的。
  224.0.0.18:目标 IP 地址,这是一个组播地址,专用于 VRRP 的多播。224.0.0.18 被所有 VRRP 参与设备用于接收 VRRP 广告包。

3) VRRPv2, Advertisement:
  表示该数据包是一个 VRRP 第 2 版(VRRPv2)的广告包(Advertisement)。
  VRRP 广告包由主节点发送,用于告知备节点主节点的状态,以便实现高可用。

4) vrid 184:
   vrid 表示 VRRP 实例的虚拟路由器 ID。这是一个唯一的标识符,用于标识特定的 VRRP 组。在这一行中,vrid 的值是 184。
   不同的 VRID 对应不同的虚拟路由器,可以让多组 VRRP 实例在同一网络中运行而不会互相干扰。
   
5) prio 102:
   prio 表示 VRRP 广告包中的优先级。优先级越高,表示该节点越有可能成为主节点(MASTER)。
   在这一行中,优先级 (priority) 是 102。通常,主节点会配置一个较高的优先级,以确保自己能够保持主节点地位。
   
6) authtype none / authtype simple:
   authtype 表示 VRRP 包的认证类型:
   none:没有认证。
   simple:使用简单的明文密码认证。
   在 VRRP 中,认证可以用来防止未经授权的节点加入 VRRP 组。
   
7) intvl 1s:
   intvl 表示 VRRP 广告包的发送时间间隔,这里是 1 秒。这意味着该节点每隔 1 秒发送一个广告包,以告知其他节点其状态。
   通常情况下,VRRP 主节点会周期性发送广告包,以确保备节点知道主节点的健康状况。
   
   
8)length 20:
   length 表示数据包的长度(单位为字节)。在这一行中,VRRP 广告包的长度是 20 字节。

通过上面的命令可以查看到当前都有些哪些 IP及其对应的 vrid,由于信息太多,显然不利于查找当前 chproxy-keepalived 集群的 vrid 53 是否有冲突,我们可以使用 grep 进行过滤,如下所示。

[root@xxx-xxx-chproxy-keepalive-xxx ~]# tcpdump -nn -i any net 224.0.0.0/8 | grep "vrid 53"
-- 得到的信息如下
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
13:40:27.104788 IP 192.168.xxx.51 > 224.0.0.18: VRRPv2, Advertisement, vrid 53, prio 102, authtype simple, intvl 1s, length 20
13:40:28.105340 IP 192.168.xxx.51 > 224.0.0.18: VRRPv2, Advertisement, vrid 53, prio 102, authtype simple, intvl 1s, length 20
13:40:29.106068 IP 192.168.xxx.51 > 224.0.0.18: VRRPv2, Advertisement, vrid 53, prio 102, authtype simple, intvl 1s, length 2013:40:30.106548 IP 192.168.xxx.51 > 224.0.0.18: VRRPv2, Advertisement, vrid 53, prio 102, authtype simple, intvl 1s, length 20

通过上述命令信息,可以看到当前 chproxy-keepalived 集群的 vrid 已经被其它环境在使用,和当前这套集群有冲突。

2.4 解决办法

根据上面的分析,我们已经找到了问题根源,接下来就是如何调整,我将这套 chproxy-keepalived 主备节点的

vrid 进行调整,并通过 tcpdump -nn -i any net 224.0.0.0/8 | grep "vrid xxx", 查找哪个 vrid 未被占用,如下所示:

[root@xxx-xxx-chproxy-keepalive-xxx ~]# tcpdump -nn -i any net 224.0.0.0/8 | grep "vrid 59"
tcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes

比如,测试发现 vrid 59 没有被占用,于是我将 vrid 59 分别修改写入到 /etc/keepalived/keepalived.conf 文件,如下所示:

-- MASTER 节点 信息如下

! Configuration File for keepalived

global_defs {
   notification_email {
     xxx@xxx.com
   }
   notification_email_from xxx@xxx.com
   smtp_server 192.168.xxx.xxx
   smtp_connect_timeout 30
   router_id Chproxy_Router_Id
}

vrrp_script chk_http_port {
        script "/opt/check_chprpoxy.sh"
        interval 40
        weight 20
}

vrrp_instance VI_1 {
    state MASTER 
    interface ens33
    virtual_router_id 59
    priority 110
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    track_script {
        chk_http_port
    }
    virtual_ipaddress {
        192.168.xxx.xxx
    }
    notify_master "/opt/keepalived_notify.sh"

}

-- BACKUP 节点信息如下
! Configuration File for keepalived

global_defs {
   notification_email {
     xxx@xxx.com
   }
   notification_email_from xxx@xxx.com
   smtp_server 192.168.xxx.xxx
   smtp_connect_timeout 30
   router_id Chproxy_Router_Id
}

vrrp_script chk_http_port {
        script "/opt/check_chprpoxy.sh"
        interval 40
        weight 20
}

vrrp_instance VI_1 {
    state BACKUP 
    interface ens33
    virtual_router_id 59 
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    track_script {
        chk_http_port
    }
    virtual_ipaddress {
        192.168.xxx.xxx
    }
    notify_master "/opt/keepalived_notify.sh"

}

为了减少对应用的影响,我首先通过 ip addr 命令查看当前 VIP 漂移到哪个节点。当发现 VIP 位于 MASTER 节点时,我选择对 BACKUP 节点的 keepalived 服务进行重启。

# systemctl restart keepalived

重启后,再次检查 /var/log/messages,确认未再出现 VRID 被占用的相关信息。

随后,我们与应用团队协商,选择在业务低峰期对 MASTER 节点的 keepalived 也进行了重启,确认未再观察到 VRID 冲突的问题。

三、总结

这次问题的根源在于 VRID 冲突,导致主备节点的 VRRP 包不匹配。通过使用 tcpdump 工具排查问题并调整配置,我们成功解决了该问题。这一过程再次证明了配置一致性和唯一性的重要性,以及 tcpdump 在网络排查中的有效性。在高可用架构的设计和实现中,保持配置的唯一性是至关重要的,这不仅能够避免集群间的资源冲突,还能保证系统在故障时的可靠性和可用性。

对于类似的高可用集群环境,严格遵循配置管理规范、保证参数的唯一性和正确性是避免故障的关键。此次排查和解决的过程也突显了掌握网络抓包工具的重要性,这些工具在复杂网络环境下的故障分析中发挥着不可或缺的作用。通过这些手段,我们不仅解决了当前的问题,也为未来可能遇到的类似问题积累了宝贵的经验,从而进一步提升了系统的稳定性和可靠性。

相关推荐
海是倒过来的天~5 小时前
【Clickhouse】客户端连接工具配置
clickhouse
Biturd5 小时前
clickhouse运维篇(三):生产环境一键生成配置并快速部署ck集群
运维·clickhouse
Biturd6 小时前
clickhouse运维篇(二):多机器手动部署ck集群
运维·clickhouse·debian
freesharer6 小时前
ClickHouse 5节点集群安装
clickhouse·database
昨天今天明天好多天21 小时前
【Linux】ClickHouse 部署
linux·服务器·clickhouse
freesharer4 天前
ClickHouse 3节点集群安装
clickhouse·database
天地风雷水火山泽5 天前
二百七十、Kettle——ClickHouse中增量导入清洗数据错误表
clickhouse·kettle
杰克逊的日记5 天前
ClickHouse与各种组件的关系
数据仓库·clickhouse·1024程序员节
alfiy5 天前
zabbix 6.0 监控clickhouse(单机)
clickhouse·zabbix