TIPS之 Kubernetes kubectl http2:client connect lost 问题排查

Kubernetes kubectl http2:client connect lost 问题排查

Posted by 董江 on Sunday, November 6, 2022

现象

表现是:通过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中断重连

查看网络情况

  1. 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 方式不同

  1. 使用 traceroute 查看各个网络设备(每一跳)的延时情况

使用 traceroute 支持 TCPUDPICMP 协议包检查, 为了模拟和 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->nodepod->nodenode-> LB IPpod-> 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 以外, etcdgrpc 也是tcp长连接。是否etcd也有问题

检查etcd日志:

Error from server: etcdserver: leader changed

确实有因为网络设备问题,导致etcd之间数据同步grpc请求lost,导致脑裂重新选主

具体那个TOR上的网络设备配置问题?

待网络专家确定吧…

「如果这篇文章对你有用,请随意打赏」

Kubeservice博客

如果这篇文章对你有用,请随意打赏

使用微信扫描二维码完成支付