现象
表现是:通过kubectl
请求远端接口时,出现部分报错
$ kubectl get deploy -n xxx
E1104 18:00:08.121482 32134 request.go:1058] Unexpected error when reading response body: http2: client connection lost
unexpected error when reading response body. Please retry. Original error: http2: client connection lost
分析过程
观测apiserver日志
其中没有错误
查询 connection lost
问题
首先 http status code
没有此类状态;
紧接着,检查kubectl
中是否有特殊逻辑
// go / net / master / . / http2 / transport.go
// 在golang的net包中,有此类异常给出
// closes the client connection immediately. In-flight requests are interrupted.
func (cc *ClientConn) closeForLostPing() {
err := errors.New("http2: client connection lost")
if f := cc.t.CountError; f != nil {
f("conn_close_lost_ping")
}
cc.closeForError(err)
}
// pingTimeout = 15s
func (cc *ClientConn) healthCheck() {
pingTimeout := cc.t.pingTimeout()
// We don't need to periodically ping in the health check, because the readLoop of ClientConn will
// trigger the healthCheck again if there is no frame received.
ctx, cancel := context.WithTimeout(context.Background(), pingTimeout)
defer cancel()
cc.vlogf("http2: Transport sending health check")
err := cc.Ping(ctx)
if err != nil {
cc.vlogf("http2: Transport health check failure: %v", err)
cc.closeForLostPing()
} else {
cc.vlogf("http2: Transport health check success")
}
}
func (rl *clientConnReadLoop) run() error {
cc := rl.cc
gotSettings := false
readIdleTimeout := cc.t.ReadIdleTimeout
var t *time.Timer
if readIdleTimeout != 0 {
t = time.AfterFunc(readIdleTimeout, cc.healthCheck)
defer t.Stop()
}
// ...
}
这个是golang net/http2
包具体首先,在 client
建立 connect
后,通过在 TCP
连接中定期发送 healthcheck
的网络包,网络是有问题
因此,合理推测:网络丢包导致tcp中断重连
查看网络情况
- Check 网络 Ping
$ ping 192.168.0.158
PING 192.168.0.158 (192.168.0.158) 56(84) bytes of data.
64 bytes from 192.168.0.158: icmp_seq=1 ttl=255 time=0.586 ms
64 bytes from 192.168.0.158: icmp_seq=2 ttl=255 time=0.355 ms
64 bytes from 192.168.0.158: icmp_seq=3 ttl=255 time=0.469 ms
ping
使用的ICMP
格式,和 http2.0 方式不同
- 使用
traceroute
查看各个网络设备(每一跳)的延时情况
使用 traceroute
支持 TCP
、 UDP
、ICMP
协议包检查, 为了模拟和 http2.0
一致
因此使用 tcp 模式(2006版本之后支持)
$ traceroute --tcp 192.168.0.158
1 192.168.0.158 (192.168.0.158) 0.957 ms 0.999 ms 1.000 ms
目前只有 1
跳,表示 两台主机中 只有一层 网络设备
, 可能是 AP路由器 或者 交换机;
单次请求没有问题,是不是这一层中的部分节点
异常
$ time for i in {1..100};do traceroute --tcp 192.168.0.158;done
...
traceroute to 192.168.0.158 (192.168.0.158), 30 hops max, 60 byte packets
1 192.168.0.158 (192.168.0.158) 0.745 ms 0.675 ms 0.693 ms
traceroute to 192.168.0.158 (192.168.0.158), 30 hops max, 60 byte packets
1 192.168.0.158 (192.168.0.158) 0.614 ms * * // * 表示丢包 或者 5s内无响应
traceroute to 192.168.0.158 (192.168.0.158), 30 hops max, 60 byte packets
1 192.168.0.158 (192.168.0.158) 0.788 ms 0.382 ms 0.757 ms
...
real 1m30.669s
user 0m0.078s
sys 0m0.151s
确实在这一层网络设备中,有部分请求(部分节点问题),导致tcp丢包
,直接timeout
进一步验证
为了近一步确定是那一层的问题,验证 node->node
、 pod->node
、node-> LB IP
和 pod-> LB IP
首先部署 网络network-multitool
工具
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: network-dockor
labels:
tier: node
app: network-dockor
spec:
selector:
matchLabels:
tier: node
app: network-dockor
template:
metadata:
labels:
tier: node
app: network-dockor
spec:
hostNetwork: true
tolerations:
- operator: Exists
effect: NoSchedule
containers:
- name: network-multitool
image: dongjiang1989/network-dockor:latest
env:
- name: HTTP_PORT
value: "1180"
- name: HTTPS_PORT
value: "11443"
ports:
- containerPort: 1180
name: http-port
- containerPort: 11443
name: https-port
resources:
requests:
cpu: "1m"
memory: "20Mi"
limits:
cpu: "10m"
memory: "20Mi"
securityContext:
runAsUser: 0
capabilities:
add: ["NET_ADMIN"]
结论:
node->lb ip 网络访问有回环问题(kubelet/kubectl 访问apiserver一类问题)
pod->node网络访问有回环问题
node->node网络没回环问题
pod->pod网络没回环问题
猜想和佐证
本身就是网络设备有问题,应该控制面内部的请求也会有此类问题
系统中除了http2
以外, etcd
的grpc
也是tcp长连接。是否etcd也有问题
检查etcd日志:
Error from server: etcdserver: leader changed
确实有因为网络设备问题,导致etcd
之间数据同步grpc
请求lost
,导致脑裂重新选主
具体那个TOR上的网络设备配置问题?
待网络专家确定吧…
「如果这篇文章对你有用,请随意打赏」
如果这篇文章对你有用,请随意打赏
使用微信扫描二维码完成支付