解决Kubernetes APIServer流量不均衡问题
版本: 1.8版本 <= kubernetes verison < 1.18版本 都存在
在 kubernetes 1.8以上,对kubelet 和 client-go SDK进行性能提升,将与apiserver的HTTP1.1协议改为HTTP2协议,支持多路复用长连接;
问题现象
这样对于大集群(多kubelet)、多Controller(client-go)集群, 会发现APIServer的稳态
情况下的连接数
和 请求QPS
是不均衡的。
重启apiserver后, HTTP2的流量负载到其他两个节点上了. 现象一致
原因分析
由于 APIServer
与 client server
是使用 HTTP2 协议连接,HTTP2 的多个请求都会复用底层的同一个 TCP 连接并且长时间不断开。而在 APIServer
发生 RollingUpdate
或者某个 APIServer
实例重启时,又或者 APIServer
使用 MaxSurge=Replica
方式升级后, LoadBalance
没有及时的将所有副本挂载完毕,client server
能敏感的感知到连接的断开并立刻发起新的请求,这时候很容易引起较后启动(或者较后挂载 LoadBalance
)的 APIServer
没有一点流量,并且可能永远都得不到负载均衡。
解决方案
在 Kubernetes 1.18
版本中,新增了 GOAWAY Chance
新特性.
增加了一种通用的 HTTP filter
,APIServer
概率性(建议 1/1000)的随机关闭和 Client 的链接(向 Client 发送 GOAWAY
)。关闭是优雅的关闭,不会影响 APIServer
和 client server
正在进行中的长时间请求(如 Watch 等),但是收到 GOAWAY
之后,client server
新的请求就会重新建立一个新的 TCP 链接去访问 APIServer
从而能让 LoadBalance
再做一次负载均衡。
开启方式
–goaway-chance float 为防止 HTTP/2 客户端卡在单个 API 服务器上,可启用随机关闭连接(GOAWAY)。 客户端的其他运行中请求将不会受到影响,并且客户端将重新连接, 可能会在再次通过负载平衡器后登陆到其他 API 服务器上。 此参数设置将发送 GOAWAY 的请求的比例。 具有单个 API 服务器或不使用负载平衡器的集群不应启用此功能。 最小值为0(关闭),最大值为 .02(1/50 请求);建议使用 .001(1/1000)。
spec:
containers:
- command:
- kube-apiserver
- --advertise-address=x.x.x.x
- --basic-auth-file=/xxx/user
- --bind-address=0.0.0.0
- --feature-gates=ExpandInUsePersistentVolumes=true,VolumeSnapshotDataSource=true
- --client-ca-file=/xxx/ca.crt
- --enable-admission-plugins=NodeRestriction
- --enable-bootstrap-token-auth=true
- --endpoint-reconciler-type=lease
- ...
- --goaway-chance=0.001 # 1/1000的连接 请求后,断开connection
调试测试
* 注意点⚠️(条件)
- kubenetes版本 : 1.18.2
- 硬件环境:Mac Pro 4Core, 8G
- 业务复杂: kubemark mock 1000个 Node Kubelet
- 受环境、网络影响,性能数据 无绝对值意义,有相对值值意义
测试Case | 平均值(ms) | P50(ms) | P90(ms) | P95(ms) | P99(ms) | 与基线比例(%) |
---|---|---|---|---|---|---|
no goaway chance | 44 | 29 | 65 | 96 | 426 | - |
–goaway-chance=0.01 | 48 | 30 | 80 | 125 | 419 | 9% |
–goaway-chance=0.001 | 46 | 30 | 59 | 90 | 540 | 4.5% |
APIServer平均性能(response time)ms 对比 基线下降 5%左右
效果
结论: 启用goaway chance后,虽达不到绝对均衡,整体效果不错。 建议设置--goaway-chance=0.001
源码研习
type probabilisticGoawayDecider struct {
chance float64
next func() float64 // 内部有个sync.poo来做当前请求连接数据,本身 pop 和 push 会有非常小的开销
}
// 判断迭代器是否触发上线
func (p *probabilisticGoawayDecider) Goaway(r *http.Request) bool {
if p.next() < p.chance {
return true
}
return false
}
func (p *goaway) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Proto == "HTTP/2.0" && p.decider.Goaway(r) {
// apiserver 主动将HTTP2 关闭连接。 client-go 完成本次请求后主动断开连接
w.Header().Set("Connection", "close")
}
p.handler.ServeHTTP(w, r)
}
扩展思路
Pod之间TCP请求也会请求流量不均衡情况,这个下次介绍。 原理一样,但是解法不同
社区最新更新信息
版本: kubernetes 1.18 版本以上版本已解决 给社区提交的修复 PR 已经被合入 88567 。
「如果这篇文章对你有用,请随意打赏」
如果这篇文章对你有用,请随意打赏
使用微信扫描二维码完成支付