一个“看不见摸不着”的 Leader

简介: 记录了一次线上 volume provisioner 选举(LeaderElection)失败的问题,对问题进行了模拟场景复现

前言

团队的产品中有一个组件叫做 volume-provisioner,主要实现了 provsioner 的能力:挂载目录,根据 pvc 申请去创建 pv 以及对应的目录。


组件是 Daemonset 资源,通过 LeaderElection 进行选主,实现逻辑也较为简单,主实例根据请求中的目标节点,去 exec 到该节点的实例中,在挂载目录下执行操作。


这样一个稳定跑了三四年的组件,突然一天在某个客户的环境中崩掉了,所有实例卡在选举过程中,导致流水线任务、Addon创建失败,为此研究了一下。


排查过程

集群环境是阿里云托管版,一开始的确怀疑过这个方向,因为 kubernetes 中的选举大概率与 ETCD 脱不了干系,托管版又无法直接看到 Master 组件相关的信息,于是直接从代码下手。

首先来说,所有的实例都卡在选主的过程中,怀疑的一个方向就是已经有人持锁当 leader 了,所以先从选举的方式来看起,抓出这个 leader 来。

选举方式

组件通过这个包实现的:https://github.com/kubernetes-sigs/sig-storage-lib-external-provisioner

从代码中可以看到,选举是通过 endpoints 作为资源锁来进行的,leader 持有锁以后会不断的进行续租。

LeaderElection 同样可以通过 configmap 作为资源锁

image.png

这时候查看 ep 资源,的确有两个与 sc 相关的 endpoint

image.png

查看具体的信息,在 annotaion 中可以看到当前持锁的实例是谁,如下两个字段是主要信息

  • holderIdentity:当前的持锁的实例信息
  • renewTime:最近一次的续租时间
control-plane.alpha.kubernetes.io/leader: '{"holderIdentity":"xxxxxxxxxxx_5928a5b2-968a-42c5-b5b0-85123e027030","leaseDurationSeconds":15,"acquireTime":"2021-12-14T06:05:15Z","renewTime":"2022-02-12T14:29:03Z","leaderTransitions":15}'

这个名称是怎么组成的,继续查看代码,发现由 hostname + _ + uuid  的格式组成

image.png


消失的 Pod

了解了命名规则就好说了,hostname 对于 pod 来说就是 pod 的名称,也就是 uuid 前面这部分就是当前主实例的 pod 名称。可找到你了,当兴奋的去查这个 pod 的时候,发现集群中并没有这个 pod

稍微懵了一下,想到了下一个排查的方向,会不会某个节点上有进程没有退出,一直在参与选举,之后的一个操作让我更加的确信了这个想法。

首先给组件的 ds 增加亲和性,停掉所有的实例,这时候删除两个 ep 资源,马上会创建出新的 ep,并且查看锁持有者仍是那个 Pod,确认了这一点,快速遍历了一下所有节点的进程,奇怪的是并没有发现有残留的进程在 :(


临时解决方法

这时候客户急着用,加上手上还有很多其他工作。就关掉了选举,每个实例通过事件请求中的节点信息与自己的所在的节点是否匹配,来决定是否消费这个事件执行操作。

大概操作如下,如果你也有类似的问题可以临时这样解决掉, 传入 controller.LeaderElection(false) 即可

pc = controller.NewProvisionController(client, config.LocalProvisionerName, lvp, version.GitVersion,
      controller.LeaderElection(false))

可以通过环境变量注入来知道当前 ds 所在的节点信息

- name: NODE_NAME
  valueFrom:    fieldRef:      fieldPath: spec.nodeName


二次排查

问题原因

手里工作宽松后,又继续看起了这个问题,这次找到了一些眉目。进入集群查看 ep 信息,发现锁已经被释放掉了,正是这个关键的锁,解开了疑惑。

可以看到锁是 2月12日申领的,到2月18日是最后的续约时间,与同事确认过,在 2月18日左右客户下线了一部分机器并且对部分机器进行了重启。

image.png

所以之前的怀疑方向没什么太大的问题,就是有残留的进程在不断的持锁。但是为什么当时没有搜到这个进程呢。

其中有个比较值得注意的地方,客户本次下线机器并没有按照我们提供的标准流程去进行,具体的操作已经不得而知了,根据这点,我进行了一种场景的模拟,得到了一样的结果。


场景复原

之前对所有节点的进程进行了排查,并没有发现异常,所以大概率这个进程并不在集群的节点中了。

但是不在集群中的节点 kubelet 会自动的去清理驱逐掉节点上的容器,如果这时候 kubelet 是异常的呢,容器并没有被清理掉,仍然在与 apiserver 进行通信,参与选举,带着这个疑问进行了一个场景复原,具体流程如下。


  1. 首先确认实验环境中持锁的 pod 以及其所在的节点。可以确认的是 zkb5w 这个 POD,在 node132  这个节点

image.pngimage.png

  1. 这时候我们到 132 节点,停止到它的 kubelet。此时查看 ep 发现还是在正常的进行续约。

image.png

  1. 此时从集群中移除掉 node132  这个节点

image.png

  1. 这时候我们再次查看 ep 中锁的持有者,仍然是 zkb5w 这个实例,多次查看,发现仍然在进行续约。此时从集群中已经找不到这个 Pod 了。

image.png


场景到这里基本就完毕了,这只是一种可能性。但是可以确定的是,集群中的实例都停止的情况下,资源锁仍然被持有被续约,是有其他的进程在作祟。这个 “看不见摸不着” 的 Leader 在背后抢占着这一切。

虽然无法查找到当时导致问题的详细步骤了,但是对于 LeaderElection 有了一个新的了解,还是很值得的。

目录
相关文章
|
1月前
|
存储 NoSQL 关系型数据库
十年大厂员工终明白:MySQL性能优化的尽头,是对B+树的极致理解
存储引擎是数据库的核心组件,负责数据的存储与管理。常见存储引擎如MySQL的InnoDB采用B+树结构,以优化读取性能,支持高效查询、范围检索和有序遍历。相比哈希表和B树,B+树通过减少I/O次数,提升大规模数据下的查询效率。本文深入解析B+树的原理、优势及其在MySQL中的应用。
90 0
|
关系型数据库 Linux Apache
|
负载均衡 网络协议 安全
负载均衡4层和7层区别
所谓四层就是基于IP+端口的负载均衡;七层就是基于URL等应用层信息的负载均衡
|
缓存 关系型数据库 MySQL
【缓存大对决】Memcached VS MySQL查询缓存,谁才是真正的性能之王?
【8月更文挑战第24天】在现代Web应用中,缓存技术对于提升性能与响应速度至关重要。本文对比分析了Memcached与MySQL查询缓存这两种常用方案。Memcached是一款高性能分布式内存对象缓存系统,支持跨服务器共享缓存,具备灵活性与容错性,但受限于内存大小且不支持数据持久化。MySQL查询缓存内置在MySQL服务器中,简化了缓存管理,特别适用于重复查询,但功能较为单一且扩展性有限。两者各有所长,实际应用中可根据需求单独或结合使用,实现最佳性能优化。
409 0
|
存储 域名解析 Kubernetes
从零开始,在 Kubernetes 上玩转 Erda(二)
本章节介绍 Erda 的部署以及配置细节
1948 0
从零开始,在 Kubernetes 上玩转 Erda(二)
|
缓存 Java 数据库
kswapd0 进程CPU占用过高
kswapd0 进程CPU占用过高
990 2
kswapd0 进程CPU占用过高
|
存储 网络协议 调度
淘宝移动端统一网络库的架构演进和弱网优化技术实践
本文将介绍淘宝 APP 统一网络库演进的过程,讲述如何围绕体验持续构建南北向从监测到加速一体化的终端网络架构,通过构建 NPM 弱网诊断感知能力,落地原生多通道技术/多协议择优调度手段,贴合厂商附能网络请求加速,实现去 SPDY 及规模化 IPv6/H3 协议簇的平滑过渡,为用户提供弱网更好、好网更优的 APP 加载浏览体验,支撑业务创造更多的可能性。
606 0
|
存储 JSON Kubernetes
kubernetes为何需要默认的serviceaccount?
在 Kubernetes 中,ServiceAccount 是一种用于身份验证和授权的对象。它为 Pod 提供了一种身份,以便它们可以与 Kubernetes API 交互,并且可以通过 Role 和 RoleBinding 为它们分配特定的权限。
673 0
|
设计模式 运维 安全
边车模式的介绍
边车模式的介绍
343 0
|
前端开发 rax Linux
汇编语言与x64函数参数传递
汇编语言与x64函数参数传递
442 0