kubectl net/http: TLS handshake timeout

    这是个困扰笔者2天的问题,过程中也查阅大量 stackoverflow / google / baidu(大多数解决方案都是内存小了 / 升级 / 重装等,这边都 not work ),今天终于想通解决了,故在此记录,望给有相同经历的同学提供一种思路。

    先来看下具体问题,集群完成后发现 kubectl version 报错:net/http: TLS handshake timeout,追加 --v 9 查看详细日志后发现 Client 端正常,服务端服务正常响应。

[root@***-24-69-3 ~]# kubectl version --v 9
I0511 09:49:55.099313 2329027 loader.go:372] Config loaded from file:  /etc/kubernetes/admin.conf
I0511 09:49:55.099762 2329027 round_trippers.go:466] curl -v -XGET  -H "Accept: application/json, */*" -H "User-Agent: kubectl/v1.23.6 (linux/amd64) kubernetes/ad33385" 'https://***.24.69.222:6443/version?timeout=32s'
I0511 09:49:55.100226 2329027 round_trippers.go:510] HTTP Trace: Dial to tcp:***.24.69.222:6443 succeed
I0511 09:50:05.100639 2329027 round_trippers.go:570] HTTP Statistics: DNSLookup 0 ms Dial 0 ms TLSHandshake 10000 ms Duration 10000 ms
I0511 09:50:05.100654 2329027 round_trippers.go:577] Response Headers:
Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.6", GitCommit:"ad3338546da947756e8a88aa6822e9c11e7eac22", GitTreeState:"clean", BuildDate:"2022-04-14T08:49:13Z", GoVersion:"go1.17.9", Compiler:"gc", Platform:"linux/amd64"}
I0511 09:50:05.100728 2329027 helpers.go:237] Connection error: Get https://***.24.69.222:6443/version?timeout=32s: net/http: TLS handshake timeout
F0511 09:50:05.100742 2329027 helpers.go:118] Unable to connect to the server: net/http: TLS handshake timeout
goroutine 1 [running]:
k8s.io/kubernetes/vendor/k8s.io/klog/v2.stacks(0x1)
# 省略部分异常栈

# 直接执行 curl 访问,问题一致
[root@***-24-69-3 ~]# curl -v -k -H "Accept: application/json, */*" -H "User-Agent: kubectl/v1.23.6 (linux/amd64) kubernetes/ad33385" 'https://***.24.69.222:6443/version?timeout=32s'
* About to connect() to ***.24.69.222 port 6443 (#0)
*   Trying ***.24.69.222...
* Connected to ***.24.69.222 (***.24.69.222) port 6443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb

    之后笔者用了比较笨的办法恢复了故障的节点,并保留了一台作为故障排查。

    正常节点上用命令观察其实能发现故障节点服务一切是正常的,那么这里可以基本上判断 CNI 网络插件 Flannel 是正常服务的,这里就开始回看 kubectl 的 https 协议问题了。

[root@***-24-69-2 ~]# kubectl get no
NAME                              STATUS   ROLES                  AGE     VERSION
***-24-69-2.***    Ready    control-plane,master   4d19h   v1.23.6
***-24-69-3.***    Ready    control-plane,master   4d19h   v1.23.6
***-24-69-30.***   Ready    <none>                 35m     v1.23.6
***-24-69-31.***   Ready    <none>                 15m     v1.23.6
***-24-69-32.***   Ready    <none>                 30s     v1.23.6
***-24-69-4.***    Ready    control-plane,master   4d19h   v1.23.6
***-24-69-5.***    Ready    <none>                 23h     v1.23.6
***-24-69-6.***    Ready    <none>                 22h     v1.23.6

# 24-69-3 观察 flannel 正常
[root@***-24-69-3 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
4: bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 38:68:dd:4f:e7:58 brd ff:ff:ff:ff:ff:ff
6: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:9b:fa:a8:00 brd ff:ff:ff:ff:ff:ff
    inet ***.17.0.1/16 brd ***.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
13: bond0.169@bond0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 38:68:dd:4f:e7:58 brd ff:ff:ff:ff:ff:ff
    inet ***.24.69.3/24 brd ***.24.69.255 scope global bond0.169
       valid_lft forever preferred_lft forever
14: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default 
    link/ether 6a:89:1a:8a:92:1f brd ff:ff:ff:ff:ff:ff
    inet 10.244.1.0/32 scope global flannel.1
       valid_lft forever preferred_lft forever

这里需要回顾下 HTTPS 的安全通信机制(这里借用了《图解HTTP》的相关内容)

    1. 客户端发送 Client Hello 报文与服务器进行SSL通信,报文中指明了客户端支持的SSL的版本,加密组件(加密算法及密钥长度)
    2. 服务器可以进行 SSL 通信时,以 Server Hello 报文作为回应,报文中也包含了SSL版本及加密组件
    3. 紧接着服务器发送 Certificate 报文,将自己的公开密钥证书发给客户端
    4. 服务器发送 Server Hello Done 报文通知客户端,握手部分结束

    5. SSL第一次握手结束后,客户端发送Client Key Exchange报文回应,报文中包含使用服务器公开密钥加密的一种被称为Pre-master secret的随机密码串,这个密码串十分重要,它作为后面通信的共享密钥
    6. 接着客户端继续发送Change Cipher Spec报文,该报文提示服务器,在此报文之后的通信会采用Pre-master secret密钥加密
    7. 客户端继续发送Finished报文,此报文包含了之前所有报文的整体校验值,服务器能否正确的解密该报文决定了此次握手协商是否成功
    8. 服务器同样发送Change Cipher Spec报文
    9. 服务器同样发送Finished报文
  10. 服务器和客户端的Finished报文交换完毕后,SSL连接建立完成,通信加密完成受到SSL的保护,之后进行应用层的通信,即发送HTTP请求

  11. 应用层通信,发送HTTP响应
  12. 最后由客户端断开连接,发送close_notify报文,之后进行TCP的四次挥手断开连接

    到这里,可以明确在客户端发送 Client Hello 报文与服务器进行 SSL 通信时,并未得到服务端 Server Hello 报文的应答,这里比较奇怪的是 ***.24.69.222 服务测网卡 IP 可以正常 ping 通,这里在说明下,***.24.69.222 是由 Master 节点中的一台虚拟出来的 IP,那么其他正常节点也可以通过该虚拟 IP 完成 HTTPS通信,其实这里问题就比较明显了(特别是高可用集群部署时,参照了 keepalived + lvs 做 LB 的同学们)。

[root@***-24-69-3 bin]# ping ***.24.69.222
PING ***.24.69.222 (***.24.69.222) 56(84) bytes of data.
64 bytes from ***.24.69.222: icmp_seq=1 ttl=64 time=0.140 ms
64 bytes from ***.24.69.222: icmp_seq=2 ttl=64 time=0.151 ms
64 bytes from ***.24.69.222: icmp_seq=3 ttl=64 time=0.084 ms
^C
--- ***.24.69.222 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.084/0.125/0.151/0.029 ms

这里需要引入 IP 地址和硬件地址以及 ARP 的相关知识:

  • 物理地址是数据链路层和物理层使用的地址,而 IP 地址是网络层和以上各层使用的地址,是一种逻辑地址(称 IP 地址是逻辑地址是因为 IP 地址是用软件实现的)。

  • 在发送数据时,数据从高层下到低层,然后才到通信链路上传输。使用 IP 地址的 IP 数据包一旦交给了数据链路层,就被封装成 MAC 帧了。MAC 帧在传送时使用的源地址和目的地址都是硬件地址,这两个硬件地址都写在 MAC 帧的首部中。

接着引入 ARP 地址解析协议了。

  • 首先当我们知道了一个机器(主机或路由器)的 IP 地址,需要找出其相应的硬件地址。地址解析协议 ARP 就是用来解决这样的问题的。

  • 地址解析协议 ARP 会在主机 ARP 高速缓存中存放一个 从 IP 地址到硬件地址的映射表,并且这个映射表还经常动态更新(新增或超时删除)。

好了,我们再来观察下 ARP 的高速缓存对应的 MAC 物理地址,看看是否有问题。

# 故障节点查看 ARP 高速缓存
[root@***-24-69-3 bin]# arp -e            
Address                  HWtype  HWaddress           Flags Mask            Iface
***.24.69.222            ether   b4:05:5d:7d:89:3a   C                     bond0.169
......

# 222 节点查看对应网卡
# 这里发现了,两者的 MAC 地址竟然不一致
[root@***-24-69-2 ~]# ip a
......
5: bond0.169@bond0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 38:68:dd:4f:e7:e8 brd ff:ff:ff:ff:ff:ff
    inet ***.24.69.2/24 brd ***.24.69.255 scope global bond0.169
       valid_lft forever preferred_lft forever
    inet ***.24.69.222/24 brd ***.24.69.255 scope global secondary bond0.169:1
       valid_lft forever preferred_lft forever

问题定位到,我们接着操作(以下就是实际的解决方案了

[root@***-24-69-3 ~]# arp -d ***.24.69.222
[root@***-24-69-3 ~]# ping ***.24.69.222
[root@***-24-69-3 ~]# arp -e            
Address                  HWtype  HWaddress           Flags Mask            Iface
***.24.69.222            ether   38:68:dd:4f:e7:e8   C                     bond0.169
[root@***-24-69-3 ~]# kubectl version
Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.6", GitCommit:"ad3338546da947756e8a88aa6822e9c11e7eac22", GitTreeState:"clean", BuildDate:"2022-04-14T08:49:13Z", GoVersion:"go1.17.9", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.6", GitCommit:"ad3338546da947756e8a88aa6822e9c11e7eac22", GitTreeState:"clean", BuildDate:"2022-04-14T08:43:11Z", GoVersion:"go1.17.9", Compiler:"gc", Platform:"linux/amd64"}

OK,顺利修复,又看到了 Server Version 正常响应了。

总结

  • kubectl version 命令返回 TLS handshake timeout 异常;
  • 对应目标节点无法正常返回 Server Hello 报文;
  • 对应节点 arp 高速缓存对应 IP 地址被其他网卡污染;
  • 清理 arp 高速缓存,问题修复。

参考文献:

《计算机网络(第6版)》谢希仁;

《图解HTTP》上野宣。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值