|
集群以及环境信息的信息:
- k8s v1.18.4
- 3 节点 Master 均为 8核 16G,50Gi-SSD
- 差异化配置的 19 节点 Minion
- control-plane 组件 (kube-apiserver,etcd,kube-controller-manager,kube-scheduler) 以 static-pod 的模式进行部署
- 3 个 kube-apiserver 前端有一个 VIP 进行流量的 LoadBalance
- 腾讯云的 SSD 性能大概是 130MB/s
故障描述
在2021-9-10下午“诡异”的事情开始出现:kubectl 偶尔卡住无法正常 CRUDW 标准资源 (Pod, Node 等),此时已经意识到是部分的 kube-apiserver 无法正常工作,然后尝试分别接入 3 台 kube-apiserver 所在宿主机,发现以下不正常的情况: 现场的信息
k8s control-plane kube-apiserver Pod 信息:
$ kubectl get pods -n kube-system kube-apiserver-x.x.x.x -o yaml
9月10日,kube-apiserver 因为 OOM 被 Kill 过。
周边监控
IaaS 层提供监控信息 (control-plane 所在宿主的黑盒监控信息):
有效信息:
- 内存、CPU、读取磁盘呈现正相关,并且在9月10日开始明显下降各方面指标回归正常
Kube-apiserver Prometheus 的监控信息:
有效信息:
- kube-apiserver 的 IO 出现了问题,Prometheus 在中途的某段事件无法正常的刮取 kube-apiserver 的 metrics 指标
- kube-apiserver 占用的内存单调递增,内部 workqueue 的 ADD iops 很高
实时的 Debug 信息
有效信息:
- 两个 Master 节点的内存都几乎被占用了 80%~90% 的样子
- 大量的内存都被 kube-apiserver 进程吃掉了
- 有一台 Master 组件格外特殊,除了内存之外,CPU 也已经被打满,并且大量的 CPU 陷入内核态,wa 很高
- 机器上几乎每个进程都 “ 疯 “ 了,都在进行大量的读盘操作,接入的 shell 已经基本不可用
- 唯一内存消耗相对较低(当时占用 8Gi)的 Master 节点上,kube-apiserver 曾经被 OOM-Kill 过
一些疑问以及相关的猜想
为什么 kube-apiserver 消耗大量内存?
- 存在 Client 在进行全量 List 核心资源
- etcd 无法提供正常服务,引发 kube-apiserver 无法正常为其他的 control-plane 组件无法提供选主服务,kube-controller-manager, kube-scheduler 不断重启,不断 ListAndWatch 进而打垮整个机器
- kube-apiserver 代码有 bug,存在内存泄漏
etcd 集群为何无法正常工作?
- etcd 集群内网络抖动
- 磁盘性能下降,无法满足正常的 etcd 工作
- etcd 所在宿主机计算资源匮乏 (CPU,RAM),etcd 只能分配到很少的时间轮片。而代码中设置的网络描述符的 Deadline 到期
kube-controller-manager,kube-scheduler 这种无状态服务为什么会大量的读盘?
- kube-controller-manager,kube-scheduler 读取本地的配置文件
- 操作系统内存极度紧缩的情况下,会将部分代码段很大的进程的内存页驱逐。保证被调度的进程能够运行。当被驱逐的进程再度被调度的时候会重新读入内存。这样会导致 I/O 增加
一些日志
kube-apiserver相关:
I0907 07:04:17.611412 1 trace.go:116] Trace[1140445702]: "Get" url:/apis/http://storage.k8s.io/v1/volumeattachments/csi-b8d912ae5f2cd6d9cfaecc515568c455afdc729fd4c721e4a6ed24ae09d9bcb6,user-agent:kube-controller-manager/v1.18.4 (linux/amd64) kubernetes/f8797eb/system:serviceaccount:kube-system:attachdetach-controller,client:10.0.0.42 (started: 2021-09-07 07:04:16.635225967 +0800 CST m=+23472113.230522917) (total time: 976.1773ms):
可以看到对 etcd 的操作耗时越来越慢。并且最后甚至丢失了与 etcd 的连接
etcd日志【已经丢失9月7日的日志】:
{"level":"warn","ts":"2021-09-10T17:14:50.559+0800","caller":"embed/config_logging.go:279","msg":"rejected connection","remote-addr":"10.0.0.42:49824","server-name":"","error":"read tcp 10.0.0.8:2380->10.0.0.42:49824: i/o timeout"}
可以看出,该节点的 etcd 与集群中的其他节点的通信也异常了。无法正常对外提供服务
深入调查
查看 kube-apiserver Heap-Profile
获取到 kube-apiserver 最近的 Profile 可以发现大量的内存都被 registry(*Store).DeleteCollection 吃掉了。DeleteCollection 会先进行 List 操作,然后并发删除每个 Item。瞬间吃掉大量内存也在情理之中。也许我这么倒霉,这次抓的时候就正好处于 Delete 的疯狂调用期间,休息 10 分钟,等下再抓一次看看。
怎么回事?怎么 DeleteCollection 还是持有这么多内存。此处其实已经开始怀疑 kube-apiserver 有 goroutine 泄漏的可能了。
看来 heap 的 Profile 无法提供足够的信息进行进一步的问题确定,继续抓取 kube-apiserver goroutine 的 profile 查看 kube-apiserver goroutine-profile
1.8W goroutine
goroutine 18970952966 [chan send, 429 minutes]:
果然大量密集的 goroutine 全部都 Block 在 chan send,并且时间上很密集。DeleteCollection 该接口的调用一般都是 kube-controller-manager 的 namespace_deleter。如果 kube-apiserver 的接口调用异常会进行 Backoff,然后继续请求,此处的异常是和 kube-apiserver 的通讯没问题,但是 kube-apiserver 无法正常和 etcd 交互。
查看 kube-controller-manager 的日志
E1027 15:15:01.016712 1 leaderelection.go:320] error retrieving resource lock kube-system/kube-controller-manager: etcdserver: request timed out
虽然没有输出调用 DeleteCollection 相关的日志,但是 kube-controller-manager 进行 LeaderElection 的 ConfigMap 也无法正常的刷新了。并且报错也非常明确的指出是 kube-apiserver 请求 etcd 出现 timeout。
kube-apiserver DeleteCollection 实现
func (e *Store) DeleteCollection(ctx context.Context, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions, listOptions *metainternalversion.ListOptions) (runtime.Object, error) {
如果e.Delete发生异常(也就是我们场景下的 etcd 异常)。worker goroutine 将会正常退出,但是任务分配 goroutine 无法正常退出,Block 在发送 toProcess chan 的代码块中。如果该 Goroutine 无法结束,那么通过 etcd 的 List 接口获取到的 items 也无法被 GC,在内存中大量堆积,导致 OOM。 总结
1、排障之前需要对正常有一个比较明确的定位,何谓“正常”?
根据之前的实践经验:100Node,1400Pod, 50 Configmap, 300 event。kube-apiserver消耗的资源大概在 2Gi 的内存以及单核心 10% 的计算资源。 2、本次排障过程中大概如下:
- 通过一些不正常的表现,感觉到问题的存在
- 大概确定引发故障的组件,通过管理工具获取该组件相关的信息
- 在相关的监控系统中寻找异常发生的时间,提取相关的有效信息比如 CPU,RAM,Disk 的消耗
- 对触发故障的原因做假设
- 查找相关组件的日志,Profile 来印证假设
3、如何防止 control-plane 链式崩溃
- 明确设置 kube-apiserver 的 CPU 资源内存资源的消耗。防止在混部模式下因为 kube-apiserver 消耗大量内存资源的前提下影响到 etcd 的正常工作
- etcd 集群独立于 control-plane 其他的组件部署
原文链接:https://github.com/k8s-club/k8s-club/blob/main/articles/%E6%8A%93%E8%99%AB%E6%97%A5%E8%AE%B0%20-%20kube-apiserver.md转自:高效运维 |
|