「K8S 生态周报」内容主要包含我所接触到的 K8S 生态相关的每周值得推荐的一些信息。欢迎订阅知乎专栏「k8s生态」。
大家好,我是张晋涛。
这是春节后的第一篇「K8S 生态周报」,主要集中在上游进展。
上游进展
这个 PR 对有大量 workload 并且 开启了 Liveness/Readiness Probes 的集群 可以说是非常有用了。
在这个场景下, 有时可能会出现应用程序是正常运行的,但是配置的 Liveness/Readiness Probes 结果是失败的,导致 Unhealthy 状态的出现,进而影响应用的稳定性。
这是因为自从 Kubernetes v1.2 版本开始,kubelet probe 就使用短链接的方式了,主要是考虑到 Kubernetes 中 Pod 和 port 都很多,每次检查都连接不同的地址,并且用短链接的方式就不再需要处理连接关闭的事情了。
对于每次的探针,都会建立新的连接,而且每个连接都会消耗对应的资源,直到 TIME-WAIT 状态超时后释放。
这对于小规模或者压力不太大的集群而言是可接受的,但是对于有大量 workload 和 probes 的场景就无法接受了,因为对于每次 探针的检查,实际都会有 conntrack 记录,并且是一直在进行消耗。
ipv4 2 tcp 6 51 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=39458 dport=6666 src=127.0.0.1 dst=127.0.0.1 sport=6666 dport=39458 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 zone=0 u
se=2
每个机器上 conntrack 记录的容量是有限的,比如在我机器上是 262144:
(MoeLove) ➜ sudo sysctl -a | grep conntrack_max
net.netfilter.nf_conntrack_max = 262144
net.nf_conntrack_max = 262144
如果连接消耗的资源持续未释放,就会导致新连接失败,进而导致 Unhealthy 的状态。
看到这里,想必你应该已经能得出解决的办法了。
- 调整机器配置: 如果遇到类似情况,你可以查看下系统日志,看看是否有
nf_conntrack: table full
类似这样的日志,如果有可以调大 conntrack ,但是这需要对每台机器操作,而且这需要集群管理员按照实际情况进行操作。当然还有一些其他的配置也需要对应调整,比如可用端口之类的,这种方式对于解决自己环境中遇到问题而言可行,但是对于 Kubernetes 而言,其实就会增加它的运维成本; - 连接复用:这会是一种有效的解决办法,但是这需要涉及到所有 probe 相关代码的重构,成本较高;
- 减少 TIME_WAIT 超时时间:这可以通过在建立 socket 连接的时候,设置 SO_LINGER 来完成。但这里有个点需要注意,之所以在此处能通过设置 SO_LINGER 来完成,是由于这种场景下,通常都是短连接探针发送数据较少,缓冲区内没有其他额外数据,所以即使设置 SO_LINGER 丢弃也没关系。这也是这个 PR 中所采用的办法。
核心逻辑是:
func ProbeDialer() *net.Dialer {
dialer := &net.Dialer{
Control: func(network, address string, c syscall.RawConn) error {
return c.Control(func(fd uintptr) {
syscall.SetsockoptLinger(int(fd), syscall.SOL_SOCKET, syscall.SO_LINGER, &syscall.Linger{Onoff: 1, Linger: 1})
})
},
}
return dialer
}
可以看到这里提供了两个选项,它们的含义如下;
- Onoff: 它表示是否忽略 Linger 的配置,0 表示关闭,非 0 表示打开;
- Linger: 这就是超时时间了,配置为 0 就表示立即关闭,发送 RST 直接丢弃缓冲区数据,但是考虑这种方式不太温和,所以设置成了 1 ,给一个尽可能短的时间,并且 1s 内也可以将缓冲区内的数据继续发送。 这里也有另一个影响需要注意 如果你部署在 Kubernetes 集群中的服务,配置健康检查探针时,用的不是专门的健康检查接口(只是返回健康状态),而是复用了其他的业务接口,并且其中传输数据量大的话,将可能导致异常。
后续还有另一个 PR 是补充了对 gPRC probes 的处理:Kubelet GRPC probes: improve network resources utilization by rphillips · Pull Request #115321 · kubernetes/kubernetes
在 Kubernetes 环境下排查问题是个挺有趣的事情,但正如我之前和这个 PR 的作者聊的那样,每个角色都有各自关注的点,conntrack 多数情况下在排查问题的时候才会有人注意到。
在这个 PR 中移除了对 IPVS scheduler 的校验,原先在代码中指定了如下 10 种 IPVS 可用的 scheduler 。
const (
RoundRobin IPVSSchedulerMethod = "rr"
WeightedRoundRobin IPVSSchedulerMethod = "wrr"
LeastConnection IPVSSchedulerMethod = "lc"
WeightedLeastConnection IPVSSchedulerMethod = "wlc"
LocalityBasedLeastConnection IPVSSchedulerMethod = "lblc"
LocalityBasedLeastConnectionWithReplication IPVSSchedulerMethod = "lblcr"
SourceHashing IPVSSchedulerMethod = "sh"
DestinationHashing IPVSSchedulerMethod = "dh"
ShortestExpectedDelay IPVSSchedulerMethod = "sed"
NeverQueue IPVSSchedulerMethod = "nq"
)
但是 IPVS 也在发展,比如 Linux 内核种还增加了 fo
, ovf
, mh
等算法,
而且也有一些团队会选择自己来扩展 IPVS scheduler,此处的限制就导致无法进行灵活的扩展了。
移除掉这里校验之后,集群管理员可以配置任意可用的 IPVS scheduler 了,但如果配置错了,那么也会有错误信息的。
在这个 PR 之前,如果创建了 ExternalName 类型的 service,会自动的创建出来 endpoints 和 endpointslice,虽然会加上 headless
的 label ,但它们没有什么真正的作用。
(MoeLove) ➜ kubectl create service externalname my-ns --external-name moelove.info
service/my-ns created
(MoeLove) ➜ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 10d
my-ns ExternalName <none> moelove.info <none> 5s
(MoeLove) ➜ kubectl get ep
NAME ENDPOINTS AGE
kubernetes 166.66.116.66:6443 10d
my-ns <none> 9s
(MoeLove) ➜ kubectl get endpointslice
NAME ADDRESSTYPE PORTS ENDPOINTS AGE
kubernetes IPv4 6443 166.66.116.66 10d
my-ns-8r2vj IPv6 <unset> <unset> 15s
my-ns-vcw59 IPv4 <unset> <unset> 15s
而且有可能会产生一些异常情况,比如如下配置
apiVersion: v1
kind: Service
metadata:
name: new-blog
namespace: default
spec:
externalName: blog.moelove.info
type: ExternalName
selector:
app: myapp
---
apiVersion: v1
kind: Pod
metadata:
name: testpod
labels:
app: myapp
spec:
containers:
- name: web
image: nginx
ports:
- name: web
containerPort: 80
protocol: TCP
将上述配置应用到集群后,看下效果:
(MoeLove) ➜ kubectl get svc,ep,endpointslices --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
service/kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 10d component=apiserver,provider=kubernetes
service/new-blog ExternalName <none> blog.moelove.info <none> 63s <none>
NAME ENDPOINTS AGE LABELS
endpoints/kubernetes 166.66.116.66:6443 10d endpointslice.kubernetes.io/skip-mirror=true
endpoints/new-blog <none> 64s service.kubernetes.io/headless=
NAME ADDRESSTYPE PORTS ENDPOINTS AGE LABELS
endpointslice.discovery.k8s.io/kubernetes IPv4 6443 166.66.116.66 10d kubernetes.io/service-name=kubernetes
endpointslice.discovery.k8s.io/new-blog-n22t7 IPv4 <unset> 10.0.0.129 64s endpointslice.kubernetes.io/managed-by=endpointslice-controller.k8s.io,kubernetes.io/service-name=new-blog,service.kubernetes.io/headless=
endpointslice.discovery.k8s.io/new-blog-r479n IPv6 <unset> <unset> 64s endpointslice.kubernetes.io/managed-by=endpointslice-controller.k8s.io,kubernetes.io/service-name=new-blog,service.kubernetes.io/headless=
(MoeLove) ➜ kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
testpod 1/1 Running 0 72s 10.0.0.129 moelove-kubernetes-cluster-node-pool-6666-ud11o <none> <none>
会发现 endpointslice 中有一个是指向了正在运行的 Pod (这是由于 label selector 导致的) ,但如果将该 service 转换成 ClusterIP 类型时候,kube-proxy 也不会再为它创建新的规则,无法进行正常的流量代理。
所以在这个 PR 中移除了对 ExternalName 类型 service 的 endpoints 和 endpointslice 的创建。
HorizontalPodAutoscaler autoscaling/v2beta2
在 v1.23+ 废弃,v1.26+ 不可用,所以将 kubectl 使用的版本切换到了 autoscaling/v2
其他
- Docker Release v23.0.0-rc.3 · moby/moby,想起来我还有一个 PR 没合并;
- Cilium Release v1.13.0-rc5 · cilium/cilium 改动还是很多;
- Knative Release Knative Serving release v1.9.0 · knative/serving 如果创建一些特权 Pod 将会有 warn 提示;
- Kyma Release 2.10.0 · kyma-project/kyma 没有太特别的改动;
以上就是本次的全部内容,我们下期再见!
欢迎订阅我的文章公众号【MoeLove】