故障现象
在一次运维k8s集群的时候,需要进入pod内部执行命令,kubectl exex <pod_name> -it bash,
出现报错,无法进入容器内部。报错信息如下:
类似于这样:
kubectl exec -it -n my-ns my-pod sh
error: unable to upgrade connection: error dialing backend: dial tcp 127.0.0.1:37751: connect: connection refused
其他的pod可以正常进入。十分费解。但是可以确认的是,能够进入的pod配置了对应的service包暴露服务,而不能进入的pod配置是nodeport方式暴露.
kubectl exec原理与问题分析
为了解决这个问题,就需要对kubectl exex原理有所了解。
以下是一个大致的kubectl exec请求流程。
- 客户端 kubectl exec -i -t ...
- kube-apiserver 向 Kubelet 发送流式请求 /exec/
- Kubelet 通过 CRI 接口向 CRI Shim 请求 Exec 的 URL
- CRI Shim 向 Kubelet 返回 Exec URL
- Kubelet 向 kube-apiserver 返回重定向的响应
- kube-apiserver 重定向流式请求到 Exec URL,接着就是 CRI Shim 内部的 Streaming Server 跟 kube-apiserver 进行数据交互,完成 Exec 的请求和响应
查询了网上的资料,说kubelte为了处理attach容器,监听的port,这个port是随机linseten的,因为port随机,极有可能是pod对应的service使用了nodeport随机暴露的端口与容器的streaming server随机端口冲突,因此streaming server端口占用,与kubelete通信异常,无法响应,自然就无法进入 pod了。
在github同样找到了有类似的问题。详细见
https://github.com/kubernetes/kubernetes/issues/85418
解决办法
1.修改node节点的linux内核,修改net.ipv4.ip_local_port_range参数,因为nodeport端口范围是3000-32767,指定可以使用net.ipv4.ip_local_port_range = 1024 20000(范围需要评估下)来限制kubelet随机端口的使用范围。需要重启kubelet让其重新在这个范围监听端口。需要重启node
- 修改nodeport的范围。 Master 节点上会有一个文件 /etc/kubernetes/manifests/kube-apiserver.yaml,修改此文件,向其中添加 --service-node-port-range=20000-22767 ,避免冲突,需要重启master
- 直接修改 pod对应的service的yaml文件指定nodeport端口,避免冲突,如下所示:
需要重启service
4 直接修改service的类型,由nodeport改为port,
需要重启service