k8s v1.11.1高可用集群的问题排查

k8s集群环境基于阿里云VPC网络,采用kubeadm工具快速部署k8s高可用集群。

服务器:

  • ubuntu 16.04.4 LTS (Xenial Xerus),4.4.0-117-generic
  • kubelet –version: Kubernetes v1.11.1

  • docker -v: Docker version 17.03.3-ce, build e19b718

问题现象是通过kubectl get nodes -w监控node节点的状态时,发现偶尔会出现NotReady状态。这时候集群访问正常,外部访问应用服务正常,只是自动化接口测试时,偶尔报出 http接口请求的错误:java.net.ConnectException: 拒绝连接 (Connection refused)。初步怀疑是Node如果是NotReady状态,则对应节点上的服务会受到影响,导致请求被拒绝。

通过kubectl describe node查看node的信息,发现Conditions status Unknown, Kubelet stopped posting node status信息。

通过google得知,node挂了后到底多久会notready 多久开始调度? 40s判死刑,缓期5min善后!

注意:这个由kube-controller-manager的两个参数决定的

  • –pod-eviction-timeout:缺省为 5m,五分钟,在 Pod 驱逐行为的超时时间。
  • –node-monitor-grace-period:缺省为 40s,也就是 40 秒,无响应 Node 在标记为 NotReady 之前的等候时间。
    初步怀疑原因是,node的notready状态,触发了Kubernetes的Pod重调度流程。

首先分析一下该重调度流程:

  1. kubelet周期性的调用rest apiserver接口将Node状态更新至etcd,周期为node-status-update-frequency,默认值10s
  2. controller manager每node-monitor-period通过apiserver查询etcd,检查kubelet上报上来的状态,默认值为5s

  3. node的状态将会在node-monitor-grace-period周期内更新,controller manager中设置默认值为40s

  4. kubelet在更新自身状态的时候有nodeStatusUpdateRetry次重试,nodeStatusUpdateRetry的默认值为5次

说明: controller manager会在node-monitor-grace-period 40s之内获取etcd中的node状态,而期间kubelet更新了40s/10s=4次node状态。

通过查看/etc/kubernetes/manifests/kube-controoller-manager.yaml,发现配置是

- --node-monitor-grace-period=10s
- --pod-eviction-timeout=10s

因为kubelet默认更新Node状态的时间是10s,而controller manager判断Node是否health的时间也是10s,但是考虑到kubelet,controller manager通过apiserver,操作etcd之间是异步访问,实际更新Node的时间应该可能超过10s,他们之间的delay将会受到network latency, apiserver latency ,etcd latency,以及master node上的负载的影响。根据Kubernetes的list-watch机制,每次watch之前需要按照本地最大version list所有etcd中更新的logs,所以在delay的情况下,controller manager可能获取不到Node的状态更新,从而认为其Unhealthy,且10s之后根据eviction 10s,如果Node仍然为Unhealthy,则触发Pod的重调度,从而导致服务震荡,出现以上的connection refuse的问题。

通过上面解释,我们知道导致服务偶尔中断的原因是controller manager设置Node状态的时间和kubelet更新时间设置的太短,解决办法也很简单,去除这两个配置,采用默认值即可,经测试,没有发现NotReady现象,且接口自动化测试也通过。

社区默认的配置

快速更新和快速反应

该模式下会产生频繁的node update事件,加重etcd的负担

这种场景下20s之后,会认为node down了,接着–pod-eviction-timeout=30s之后,pod将会被驱逐,也就是50s会发生evict。但是会增加etcd的负载,每个node将会每2s更新一次etcd的状态。

如果环境有1000个节点,1分钟内将有15000个节点更新,这可能需要大型的etcd容器甚至是etcd的专用节点。

中等更新和平均反应

将会20s更新一次node状态,也就是说controller manager认为node状态不正常之前,会有2m60/205=30次更新node的状态。Node状态为down之后1m,就会触发evict。
如果1000node的场景,1分钟内将会有60s/20s*1000=3000次 etcd node状态的更新。

低更新和慢反应

Kubelet将会1m更新一次node的状态,在认为不健康之后会有5m/1m*5=25次重试更新的机会。Node为不健康的时候,1m之后pod开始evict。

目前我们采用的是默认的配置。

这里需要先了解kubelet和apiserver之间的交互过程。

k8s集群采用kubeadm init –config kubeadm-init.yaml进行高可用部署。

apiserver的高可用和etcd的高可用,采用的是static pod方式运行,基于容器方式运行k8s相关组件。

附录!

Kubernetes作为容器化应用集群管理系统,关键系统服务有 api-server, scheduler, controller-manager 三个。api-server 一方面作为 Kubernetes 系统的访问入口点,一方面作为背后 etcd 存储的代理服务器,Kubernetes 里的所有资源对象,包括 Service、Deployment、ReplicaSet、DaemonSet,Pod、Endpoint、ConfigMap、Secret 等等,都是通过 api-server 检查格式后,以 protobuf 格式序列化并存入 etcd。

Kubernetes为容器化应用提供了便利的资源调度, 部署运行,服务发现, 扩容缩容,自动运维等功能。

  1. 系统各个组件分工明确(APIServer是所有请求入口,CM是控制中枢,Scheduler主管调度,而Kubelet负责运行),配合流畅,整个运行机制一气呵成。
  2. 可以看出除了配置管理和持久化组件ETCD,其他组件并不保存数据。意味除ETCD外其他组件都是无状态的。因此从架构设计上对kubernetes系统高可用部署提供了支撑。

  3. 同时因为组件无状态,组件的升级,重启,故障等并不影响集群最终状态,只要组件恢复后就可以从中断处继续运行。

  4. 各个组件和kube-apiserver之间的数据推送都是通过list-watch机制来实现。所以理解list-watch机制是深入理解kubernetes的关键。

  5. 整体运行机制基于声明式数据(如nginx-depolyment.yaml)而非用户输入各种命令来工作,这是kubernetes最核心的设计理念。

list-watch机制分析

kubernetes中结合watch请求增加了list请求,主要做如下两件事情:

  1. watch请求开始之前,先发起一次list请求,获取集群中当前所有该类数据(同时得到最新的ResourceVersion),之后基于最新的ResourceVersion发起watch请求。
  2. 当watch出错时(比如说网络闪断造成客户端和服务端数据不同步),重新发起一次list请求获取所有数据,再重新基于最新ResourceVersion来watch。

kubernetes中基于ResourceVersion信息采用list-watch(http streaming)机制来保证组件间的数据实时可靠传送。

标签:集群 发布于:2019-10-19 10:11:46