记一次kubernetes集群异常:kubelet连接apiserver超时

  • 时间:
  • 浏览:3
  • 来源:uu快3棋牌_uu快3讨论群_规律

都时需通过设置环境变量DISABLE_HTTP2来禁用h2,简单验证了一下,显式设置该环境变量禁用h2后,让连接使用http1.1人太好这么你你是什么 问题报告 了。

无独有偶,某些我的事情偏偏被亲戚亲戚我们歌词 歌词 我们歌词 歌词 碰到了,接到线上絮状node not ready的报警后,立刻上线查看,发现所有的node kubelet都报如下错误:

艰 难 修 复

kubernetes是master-slave形态,master node是集群的大脑,当master node存在故障时整个集群都"out of control"。master node中最重要的当属apiserver组件,它负责解决所有请求,并持久化状态到etcd。一般亲戚亲戚我们歌词 歌词 我们歌词 歌词 会部署多份apiserver实现高可用。官方建议在多个apiserver前面部署俩个LB进行负载均衡,当其中一台apiserver存在故障随后 ,LB自动将流量切换到某些实例上方。某些我人太好简单,倘若也引入了额外的依赖,是因为LB存在故障是因为是因为详细apiserver不可用。亲戚亲戚我们歌词 歌词 我们歌词 歌词 知道在kubernetes中node节点上kubelet与apiserver心跳超时后,controller-manager会将该node状态置为notReady,随四驱逐其上的pod,使哪此pod在某些地方重建。某些当LB存在故障时,集群中所有的node都会变为notReady状态,进而是因为大规模的pod驱逐。

峰 回 路 转

E0415 17:03:11.351872 16624 kubelet_node_status.go:374] Error updating node status, will retry: error getting node "k8s-slave88": Get https://10.13.10.12:6443/api/v1/nodes/k8s-slave88?resourceVersion=0&timeout=5s: net/http: request canceled (Client.Timeout exceeded while awaiting headers)

E0415 17:03:16.352108 16624 kubelet_node_status.go:374] Error updating node status, will retry: error getting node "k8s-slave88": Get https://10.13.10.12:6443/api/v1/nodes/k8s-slave88?timeout=5s: net/http: request canceled (Client.Timeout exceeded while awaiting headers)

E0415 17:03:21.352335 16624 kubelet_node_status.go:374] Error updating node status, will retry: error getting node "k8s-slave88": Get https://10.13.10.12:6443/api/v1/nodes/k8s-slave88?timeout=5s: net/http: request canceled (Client.Timeout exceeded while awaiting headers)

E0415 17:03:26.352548 16624 kubelet_node_status.go:374] Error updating node status, will retry: error getting node "k8s-slave88": Get https://10.13.10.12:6443/api/v1/nodes/k8s-slave88?timeout=5s: net/http: request canceled (Client.Timeout exceeded while awaiting headers)

E0415 17:03:31.352790 16624 kubelet_node_status.go:374] Error updating node status, will retry: error getting node "k8s-slave88": Get https://10.13.10.12:6443/api/v1/nodes/k8s-slave88?timeout=5s: net/http: request canceled (Client.Timeout exceeded while awaiting headers)

E0415 17:03:31.352810 16624 kubelet_node_status.go:366] Unable to update node status: update node status exceeds retry count

日志中显示的10.13.10.12是LB的地址。通过你你是什么 日志判断是kubelet连接apiserver失败,初步怀疑是网络故障,手动telnet 10.13.10.12 6443后发现一切正常,这就比较奇怪了,明明网络通信正常,kubelet为哪此连不上apiserver?

https://github.com/kubernetes/kubernetes/pull/63492

}

return t

}

某些我调用了http2.ConfigureTransport来设置transport支持h2。你你是什么 句代码似乎太过简单,并这么任何Ping frame相关的解决逻辑。查了下golang标准库中Transport与Pingframe相关的土办法。

查阅文档发现这是http1.1与http2.0的差异:在http1.1中,默认采用keep-alive复用网络连接,发起新的请求时,是因为当前有闲置的连接就会复用该连接,是因为这么则新建俩个连接。当kubelet连接异常时,老的连接被占用,老是hang在等候对端响应,kubelet在下一次心跳周期,是因为这么可用连接就会新建俩个,倘若新连接正常通信,心跳包就都时需正常发送。

iptables -I OUTPUT -p tcp --sport 63876 -j DROP

[root@c4-jm-i1-k8stest03 ~]# netstat -antpl |grep kubelet

tcp 0 0 127.0.0.1:10248 0.0.0.0:* LISTEN 23665/./kubelet

tcp 0 0 10.162.1.26:63876 10.132.106.115:6443 ESTABLISHED 23665/./kubelet

tcp6 0 0 :::4194 :::* LISTEN 23665/./kubelet

tcp6 0 0 :::1023000 :::* LISTEN 23665/./kubelet

tcp6 0 0 :::10255 :::* LISTEN 23665/./kubelet

tcp6 0 0 10.162.1.26:1023000 10.132.1.300:61218 ESTABLISHED 23665/./kubelet

此时执行

集群恢复随后 ,发现有故障通报LB存在了故障,联系了相关同学发现时间点刚好相符,怀疑是因为LB异常是因为kubelet无法连接apiserver。

首先确保kubelet与apiserver连接正常,执行netstat -antpl | grep 6443都时需看过kubelet与apiserver 10.132.106.115:6443连接正常:

倘若backport到1.10.3的代码中,当连接异常都会会调用closeAllConns强制关闭掉所有的连接使其重建。

参考链接

1、https://github.com/kubernetes/kubernetes/issues/41916

2、https://github.com/kubernetes/kubernetes/issues/48638

3、https://github.com/kubernetes-incubator/kube-aws/issues/598

4、https://github.com/kubernetes/client-go/issues/374

5、https://github.com/kubernetes/apimachinery/blob/b874eabb9a4eb99cef27db5c8d06f1654253000cec/pkg/util/net/http.go#L109-L120

6、https://www.cncf.io/blog/2018/08/31/grpc-on-http-2-engineering-a-robust-high-performance-protocol/

7、https://github.com/kubernetes/kubernetes/pull/63492

8、https://github.com/kubernetes/kubernetes/pull/71174

9、https://github.com/golang/go/issues/31643

10、https://github.com/kubernetes/kubernetes/pull/7300016

明白了你你是什么 逻辑随后 修改就简单了,将closeAllConns再置为正确的值即可,给官方提交了俩个pr,官方很乐意就接受了,并backport到了1.14版本中。至此你你是什么 就算详细修复了,当然都时需通过上文提到的给h2增加Ping frame的土办法解决该问题报告 ,这是你你是什么 方案是因为比较错综复杂,修复时间比较长。

经过沟通后发现:LB会为其转发的每俩个connection维护某些数据形态,当新的一台LB server上线随后 会均摊一累积某些我的流量,倘若在其维护的数据形态中什么都这么该connection的记录就会认如保会儿 请求非法,直接DROP掉。同类于的事人太好还存在不少,在kubernetes的isuse里有不少某些我的案例,甚至时需公有云的的LB也会有某些我的问题报告 。同类于:kubernetes#41916,kubernetes#48638,kubernetes-incubator/kube-aws#598

为宜明白是因为随后 ,push LB的同学改进的一并,kubelet也应该做某些改进:当kubelet连接apiserver超时随后 ,应该reset掉连接,进行重试。简单做了俩个测试,使用iptables规则drop掉kubelet发出的流量来模拟网络异常。

令人遗憾的是,当前golang对于俩个进程的抽象ClientConn是因为支持发送Ping frame,倘若连接是交由连接池clientConnPool管理的,该形态是个外部的私有形态体,亲戚亲戚我们歌词 歌词 我们歌词 歌词 这么直接操作,封装连接池的Transport也这么暴露任何的接口来实现设置连接池中的所有连接定期发送Ping frame。是因为亲戚亲戚我们歌词 歌词 我们歌词 歌词 想实现你你是什么 功能就时需自定义俩个Transport并实现俩个连接池,要实现俩个稳定可靠的Transport似乎从不容易。不到求助golang社区看有这么解决方案,提交了俩个issue后,更快就大家回复并提交了PR,查看过一下,实现还是比较简单的,于是基于你你是什么 PR实现了clinet-go的Ping frame的探测。

来源:小米云技术

ID:mi-cloud-tech

作者:高荣

背 景

开发完毕准备上线的随后 ,想趁这次修复升级一下kubernetes版本到v1.10.11,一般patch release是保证兼容的。在测试v1.10.11的随后 惊奇的发现,即使不改任何代码,你你是什么 问题报告 也没土办法复现了。说明在v1.10.2中是有问题报告 的,在v1.10.11中恢复了,接着在master中又引入了你你是什么 问题报告 ,看来还得时需仔细阅读一下这累积代码了,到底是存在了哪此。

定 位 问 题

接下来某些我缘何修复你你是什么 问题报告 了。网上找了一下相关的issue,首先找到的是kubernetes/client-go#374你你是什么 issue,上方描述的状态和亲戚亲戚我们歌词 歌词 我们歌词 歌词 碰到的很同类于,大家说是因为使用了HTTP/2.0协议(以下简称h2),查找了一下kubelet的源码,发现kubelet默认是使用h2协议,具体的代码实现在SetTransportDefaults你你是什么 函数中。

故 障 发 生

在h2中,为了提高网络性能,俩个主机只建立俩个连接,所有的请求都通过该连接进行,默认状态下,即使网络异常,他还是重用你你是什么 连接,直到操作系统将连接关闭,而操作系统关闭僵尸连接的时间默认是十几分钟,具体的时间都时需调整系统参数:

随后 又引入了regression,将closeAllConns置为nil,是因为连接无法正常关闭。

社区那个issue是因为开了很长时间好像并这么解决的痕迹,还得另一方想土办法。亲戚亲戚我们歌词 歌词 我们歌词 歌词 知道俩个http.Client本身人太好只做了某些http协议的解决,底层的通信是交给Transport来实现,Transport决定如保根据俩个request返回对应的response。在kubernetes client-go中关于Transporth2的设置不到你是什么 个函数。

net.ipv4.tcp_retries2, net.ipv4.tcp_keepalive_time, net.ipv4.tcp_keepalive_probes, net.ipv4.tcp_keepalive_intvl

通过调整操作系统断开异常连接的时间实现快速恢复。

经过阅读代码,发现你你是什么 逻辑某些我被修复过,参考下方链接:

The less clean version is where the endpoint dies or hangs without informing the client. In this case,TCP might undergo retry for as long as 10 minutes before the connection is considered failed.Of course, failing to recognize that the connection is dead for 10 minutes is unacceptable.

gRPC solves this problem using HTTP/2 semantics:when configured using KeepAlive,gRPC will periodically send HTTP/2 PING frames.These frames bypass flow control and are used to establish whether the connection is alive.

If a PING response does not return within a timely fashion,gRPC will consider the connection failed,close the connection,and begin reconnecting (as described above)

都时需看过gRPC同样存在某些我的问题报告 ,为了快速识别故障连接并恢复采用了Ping frame。倘若目前kubernetes所建立的连接中并这么实现Ping frame,是因为了无法及时发现连接异常并自愈。

你你是什么 问题报告 和当时存在故障的状态一模一样:连接异常是因为kubelet心跳超时,重启kubelet都会新建连接,恢复正常心跳。是因为亲戚亲戚我们歌词 歌词 我们歌词 歌词 当前采用的kubernetes版本是v1.10.2,下载master分支的代码编译试了下,也是有你你是什么 问题报告 的,感觉你你是什么 问题报告 老是存在。

将kubelet发出的包丢掉,模拟网络故障,此时都时需看过netstat的输出中该连接的Send-Q正在逐步增加,倘若kubelet也打印出日志显示无法连接:

赶紧用tcpdump抓包分析了一下,发现kubelet不断地给apiservre发送包却这么收到对端的ACK,登录master查看apiserver服务也一切正常。随后 同事发现重启kubelet就好了,为了尽快解决问题报告 不到把kubelet详细重启了,上方再慢慢定位问题报告 。

[root@c4-jm-i1-k8stest03 ~]# netstat -antpl |grep kubelet

tcp 0 0 127.0.0.1:10248 0.0.0.0:* LISTEN 23665/./kubelet

tcp 0 928 10.162.1.26:63876 10.132.106.115:6443 ESTABLISHED 23665/./kubelet

连接被hang住了,重启kubelet随后 ,一切又恢复了。

h2主动探测连接故障是通过发送Ping frame来实现,这是俩个优先级比较高倘若payload很少的包,网络正常时是都时需快速返回,该frame默认不要发送,时需显式设置才会发送。在某些gRPC等要求可靠性比较高的通信框架中都实现了Ping frame,在gRPC On HTTP/2: Engineering A Robust, High Performance Protocol中谈到:

// SetTransportDefaults applies the defaults from http.DefaultTransport

// for the Proxy, Dial, and TLSHandshakeTimeout fields if unset

func SetTransportDefaults(t http.Transport) http.Transport {

t = SetOldTransportDefaults(t)

// Allow clients to disable http2 if needed.

if s := os.Getenv("DISABLE_HTTP2"); len(s) > 0 {

} else {