讲师:王泽锋 / 华为云 Kubernetes 开源负责人
编辑:夏天
Kubernetes 原生的 Service 负载平衡基于 Iptables 实现,其规定链会随 Service 的数量呈线性增长,在大规模场景下对 Service 性能影响重大。本次分享介绍了华为云在 Kubernetes service 性能优化方面的摸索与实际。
大家好,明天给大家带来咱们在 Kubernetes Service 上的一些优化实际,这是一个网络相干的话题。首先,我将给大家介绍 Kubernetes 的 Service 机制。当初 Kubernetes 中 Service 的三种模式,包含原来的 Userspace 和 Iptables,以及起初咱们奉献的 IPVS;第二局部会介绍原来社区是如何应用 Iptables 来实现 Service 负载平衡的;第三局部次要是 Iptables 实现中存在的一些问题;接下来是如何应用 IPVS 来做 Service 的负载实现的;最初是一个比照。
Kubernetes 的 Service 机制
先看一下 Kubernetes 外面的 Service。在用 Kubernetes 之前,当咱们有了容器网络之后,拜访一个利用最间接的做法,就是客户端间接去拜访一个 Backend Container。这种做法最直观和容易,同时问题也是不言而喻的。 当利用有多个后端容器的时候,怎么做负载平衡,会话放弃怎么做,某个容器迁了之后 IP 跟着变怎么办,还有对应的健康检查怎么配,如果想用域名来做拜访入口要怎么解决……这些其实就是 Kubernetes 的 Service 引入所要解决的问题。
Kubernetes Service 与 Endpoints
这张图体现了 Service 与其它几个对象的对应关系。首先是 Service,它保留的是服务的拜访入口信息(如 IP、端口),能够简略了解为 Kubernetes 内置的一个 LoadBalancer,它的作用就是给多个 Pod 提供负载平衡。
图中是一个 Replication Controller 部署进去 2 个 pod 所对应的 Service。咱们晓得 RC 和 pod 的关系是通过 label-selector 来关联的,service 也是一样,通过 Selector 来匹配它所要做负载平衡的 Pod。实际上这两头还有一个对象,叫做 Endpoint,为什么要有这个对象呢?因为在理论利用中,一个 pod 被创立,并不代表它马上就能对外提供服务,而这个 pod 如果将被删除,或处于其余不良状态,咱们都心愿客户端的申请不被散发到这个无奈提供服务的 pod 上。Endpoint 的引入,就是用来映射那些能对外提供服务的 pod。每个 Endpoints 对象的 IP 对应一个 Kubernetes 的外部域名,能够通过这个域名间接拜访到具体的 pod。
再看 Service 和 Endpoint 的定义。这里留神 Service 有一个 ClusterIP 的属性字段,能够简略了解为是虚 IP。Service 的域名解析通常失去的就是这个 ClusterIP。另外值得注意的是 Service 反对端口映射,即 Service 裸露的端口不用和容器端口统一。
Service 外部逻辑
方才介绍了 Service、Pods 跟 Endpoint 三者的关系,再来看 Service 的外部逻辑。这里次要看下 Endpoint Controller,它会 watch Service 对象、还有 pod 的变动状况,保护对应的 Endpoint 信息。而后在每一个节点上,KubeProxy 依据 Service 和 Endpoint 来保护本地的路由规定。
实际上,每当一个 Endpoint 发生变化(即 Service 以及它关联的 Pod 状态发生变化),Kubeproxy 都会在每个节点上做对应的规定刷新,所以这个其实更像是一个凑近客户端的负载平衡——一个 Pod 拜访其余服务的 Pod 时,申请在出节点之前,就曾经通过本地的路由规定选好了它的目标 Pod。
Iptables 实现负载平衡
好,咱们来看一下 Iptables 模式是怎么实现的。
Iptables 次要分两局部,一个是它的命令行工具,在用户态;而后它也有内核模块,但实质上还是通过 Netfilter 这个内核模块来封装实现的,Iptables 的特点是反对的操作比拟多。
这是 IPtables 解决网络包的一个流程图,能够看到,每个包进来都会按程序通过几个点。首先是 PREROUTING,它会判断接管到的这个申请包,是拜访本地过程还是其余机器的,如果是拜访其余机器的,就要走 FORWARD 这个 chain,而后再会做一次 Routing desicion,确定它要 FORWARD 到哪里,最初经 POSTROUTING 进来。如果是拜访本地,就会进来到 INPUT 这条线,找到对应要拜访哪个本地申请,而后就在本地解决了。解决完之后,其实会生成一个新的数据包,这个时候又会走 OUTPUT,而后经 POSTROUTING 进来。
Iptables 实现流量转发与负载平衡
咱们晓得,Iptables 做防火墙是业余的,那么它是如何做流量转发、负载平衡甚至会话放弃的呢?如下图所示:
Iptables 在 Kubernetes 的利用举例
那么,在 Kubernetes 外面是怎么用 Iptables 来实现负载平衡呢?来看一个理论的例子。在 Kubernetes 中,从 VIP 到 RIP,两头通过的 Iptables 链路包含:PREROUTING/OUTPUT(取决于流量是从本机还是外机过去的)-> KUBE-SERVICES(所有 Kubernetes 自定义链的入口)->KUBE-SVC-XXX(前面那串 hash 值由 Service 的虚 IP 生成)->KUBE-SEP->XXX(前面那串 hash 值由后端 Pod 理论 IP 生成)。
以后 Iptables 实现存在的问题
Iptables 做负载平衡的问题
那么 Iptables 做负载平衡次要有什么缺点呢? 起初咱们只是剖析了原理,起初在大规模场景下实测,发现问题其实非常明显。
- 首先是时延,匹配时延和规定更新时延 。咱们从刚刚的例子就能看出,每个 Kubernetes Service 的虚 IP 都会在 kube-services 下对应一条链。Iptables 的规定匹配是线性的,匹配的工夫复杂度是 O(N)。规定更新是非增量式的,哪怕减少 / 删除一条规定,也是整体批改 Netfilter 规定表。
- 其次是可扩展性。 咱们晓得当零碎中的 Iptables 数量很大时,更新会十分慢。同时因为全量提交的过程中做了爱护,所以会呈现 kernel lock,这时只能期待。
- 最初是可用性。 服务扩容 / 缩容时,Iptables 规定的刷新会导致连贯断开,服务不可用。
Iptables 规定匹配时延
上图阐明了 Service 拜访时延随着规定数的减少而增长。但其实也还能承受,因为时延最高也就 8000us(8ms),这阐明真正的性能瓶颈并不在这里。
Iptables 规定更新时延
那么 Iptables 的规定更新,到底慢在哪里呢
首先,Iptables 的规定更新是全量更新,即便 –no–flush 也不行(–no–flush 只保障 iptables-restore 时不删除旧的规定链)。
再者,kube-proxy 会周期性的刷新 Iptables 状态:先 iptables-save 拷贝零碎 Iptables 状态,而后再更新局部规定,最初再通过 iptables-restore 写入到内核。当规定数达到肯定水平时,这个过程就会变得十分迟缓。
呈现如此高时延的起因有很多,在不同的内核版本下也有肯定的差别。另外,时延还和零碎以后内存使用量密切相关。因为 Iptables 会整体更新 Netfilter 的规定表,而一下子调配较大的内核内存(>128MB)就会呈现较大的时延。
Iptables 周期性刷新导致 TPS 抖动
上图就阐明了在高并发的 loadrunner 压力测试下,kube-proxy 周期性刷新 Iptables 导致后端服务连贯断开,TPS 的周期性稳定。
K8S Scalability
所以这个就给 Kubernetes 的数据面的性能带来一个十分大的限度,咱们晓得社区治理面的规模,其实在去年就曾经反对到了 5000 节点,而数据面因为不足一个权威的定义,没有给出规格。
咱们在多个场景下评估发现 Service 个数其实很容易达到成千上万,所以优化还是很有必要的。过后先到的优化计划次要有两个 :
- 用树形构造来组织 Iptables 的规定,让匹配和规定更新过程变成树的操作,从而优化两个时延。
- 应用 IPVS,前面会讲它的益处。
应用树形构造组织 Iptables 规定的一个例子如下所示:
在这个例子中,树根是 16 位地址,根的两个子节点是 24 位地址,虚 IP 作为叶子节点,依据不同的网段,别离挂在不同的树节点下。这样,规定匹配的时延就从 O(N) 升高到 O(N 的 M 次方根),M 即树的高度。但这么做带来的代价是 Iptables 规定变得更加简单。
IPVS 实现 Service 负载平衡
什么是 IPVS
- 传输层 Load Balancer,LVS 负载均衡器的实现;
- 同样基于 Netfilter,但应用的是 hash 表;
- 反对 TCP, UDP,SCTP 协定,IPV4,IPV6;
- 反对多种负载平衡策略,如 rr, wrr, lc, wlc, sh,dh, lblc…
- 反对会话放弃,persistent connection 调度算法。
IPVS 的三种转发模式
IPVS 有三种转发模式,别离是:DR,隧道和 NAT。
● DR 模式工作在 L2,应用的 MAC 地址,速度最快。申请报文通过 IPVS director,转发给后端服务器,响应报文间接回给客户端。毛病是不反对端口映射,于是这种模式就很惋惜地 PASS 掉了。
● 隧道模式,应用 IP 包封装 IP 包。后端服务器接管到隧道包后,首先会拆掉封装的 IP 地址头,而后响应报文也会间接回给客户端。IP 模式同样不反对端口映射,于是这种模式也被 PASS 掉了。
● NAT 模式反对端口映射,与后面两种模式不同的是,NAT 模式要求回程报文通过 IPVS 的 director。内核原生版本 IPVS 只做 DNAT,不做 SNAT。
应用 IPVS 实现流量转发
应用 IPVS 做流量转发只需通过以下几个简略的步骤。
- 绑定 VIP
因为 IPVS 的 DNAT 钩子挂在 INPUT 链上,因而必须要让内核辨认 VIP 是本机的 IP。绑定 VIP 至多有三种形式:
1. 创立一块 dummy 网卡,而后绑定,如下所示。
# ip link add dev dummy0 type dummy # ip addr add 192.168.2.2/32 dev dummy0
2. 间接在本地路由表中加上 VIP 这个 IP 地址。
# ip route add to local 192.168.2.2/32 dev eth0proto kernel
3. 在本地网卡上减少一个网卡别名。
# ifconfig eth0:1 192.168.2.2netmask255.255.255.255 up
- 为这个虚 IP 创立一个 IPVS 的 virtual server
# ipvsadm -A -t 192.168.60.200:80 -s rr -p 600
这下面的例子中,IPVS virtual server 的虚 IP 是 192.168.60.200:80,会话放弃工夫 600s。
- 为这个 IPVS service 创立相应的 real server
# ipvsadm -a -t 192.168.60.200:80 -r 172.17.1.2:80–m
# ipvsadm -a -t 192.168.60.200:80 -r 172.17.2.3:80–m
这下面的例子中,为 192.168.60.200:80 这个 IPVS 的 virtual server 创立了两个 real server:172.17.1.2:80 和 172.17.2.3:80。
Iptables vs. IPVS
Iptables vs. IPVS 规定减少时延
通过观察上图很容易发现:
- 减少 Iptables 规定的时延,随着规定数的减少呈“指数”级回升;
- 当集群中的 Service 达到 2 万个时,新增规定的时延从 50us 变成了 5 小时;
- 而减少 IPVS 规定的时延始终保持在 100us 以内,简直不受规定基数影响。这两头的渺小差别甚至能够认为是系统误差。
- Iptables vs. IPVS 网络带宽
这是咱们用 iperf 实测失去两种模式下的网络带宽。能够看到 Iptables 模式下第一个 Service 和最初一个 Service 的带宽有差别。最初一个 Service 带宽显著小于第一个,而且随着 Service 基数的回升,差别越来越显著。
而 IPVS 模式下,整体带宽体现高于 Iptables。当集群中的 Service 数量达到 2.5 万时,Iptables 模式下的带宽已根本为零,而 IPVS 模式的服务仍然可能放弃在先前一半左右的程度,提供失常拜访。
Iptables vs. IPVS CPU/ 内存耗费
很显著,IPVS 在 CPU/ 内存两个维度的指标都要远远低于 Iptables。
个性社区状态
这个个性从 1.8 版本引入 Alpha,到 1.9 版本公布 Beta,修复了大部分的问题,目前曾经比较稳定,强烈推荐大家应用。 另外这个个性目前次要是咱们华为云 K8S 开源团队在保护,大家在应用中如果发现问题,欢送反映到社区,或者咱们这边。谢谢大家!
王泽锋 / 华为云 Kubernetes 开源负责人
多年电信畛域系统软件开发和性能调优教训,对深度报文解析、协定辨认颇有钻研。华为云 PaaS 服务团队核心成员,专一于 PaaS 产品和容器开源社区,目前负责华为云 K8S 开源团队在社区奉献的整体工作。