前言

Kubernetes在集群接入层设计并提供了两种原生资源ServiceIngress,分别负责四层和七层的网络接入层配置。

传统的做法是创建Ingress或LoadBalancer类型的Service来绑定腾讯云的负载均衡将服务对外暴露。这种做法将用户流量负载到用户节点的NodePort上,通过KubeProxy组件转发到容器网络中,但这种方案在业务的性能和能力支持会有所局限。

为了解决这个问题,TKE容器团队为在腾讯云上使用独立或托管集群的用户提供了一种新的网络模式,利用弹性网卡直连Pod的方案很大的增强了性能和业务能力的支持

本文将会从传统的模式的问题入手,比较新旧模式的区别,并在最后提供新直连模式的使用指引。

传统模式面临的问题与挑战

性能与特性

KubeProxy在集群中会将用户NodePort的流量通过NAT的方式转发到集群网络中。这个NAT转发带来了以下一些问题。

  1. NAT转发导致请求在性能上有一定的损失。

    1. 进行NAT操作本身会带来性能上的损失。
    2. NAT转发的目的地址可能会使得流量在容器网络内跨节点转发。
  2. NAT转发导致请求的来源IP被修改了,客户端无法获取来源IP。

  3. 当负载均衡的流量集中到几个NodePort时。过于集中的流量会导致NodePort的SNAT转发过多,使得源端口耗尽流量异常。还可能导致 conntrack 插入冲突导致丢包,影响性能。

  4. KubeProxy的转发具有随机性,无法支持会话保持。

  5. KubeProxy的每个NodePort其实也起到独立的负载均衡作用,由于负载均衡无法收敛到一个地方,所以难以达到全局的负载均衡。

为了解决以上问题,我们以前给用户提供的技术建议主要是通过Local转发的方式,避免KubeProxyNAT转发带来的问题。但是因为转发的随机性,一个节点上部署多个副本时会话保持依旧无法支持。而且Local转发在滚动更新时,容易出现服务的闪断,对业务的滚动更新策略以及优雅停机提出了更高的要求。我们有理由去寻找更好的方案解决这个问题。

业务可用性

通过NodePort接入服务时,NodePort的设计存在极大的容错性。负载均衡会绑定集群所有节点的NodePort作为后端。集群任意一个节点的访问服务时,流量将随机分配到集群的工作负载中。这就意味着部分NodePort的不可用,或者是Pod的不可用都不会影响服务的流量接入。

和Local访问一样,直接将负载均衡后端连接到用户Pod的情况下,当业务在滚动更新时,如果负载均衡不能够及时绑定上新的Pod,业务的快速滚动可能导致业务入口的负载均衡后端数量严重不足甚至被清空。因此,业务滚动更新的时候,接入层的负载均衡的状态良好,方能保证滚动更新的安全平稳。

负载均衡的控制面性能

负载均衡的控制面接口。包括创建删除修改四层、七层监听器,创建删除七层规则,绑定各个监听器或者规则的后端。这些接口大部分是异步接口,需要轮询请求结果,接口的调用时间相对较长。当用户集群规模较大时,大量的接入层资源同步会导致组件存在很大的时延上的压力。

新旧模式对比

性能对比

Pod直连模式已经在腾讯TKE上线,是对负载均衡的控制面优化。针对整个同步流程,重点优化了批量调用和后端实例查询两个远程调用比较频繁的地方。优化完成后,Ingress典型场景下的控制面性能较优化前版本有了95%-97%左右的性能提升。目前同步的耗时主要集中在异步接口的等待上。

后端节点突增 (应对集群扩容的场景)

七层规则突增(应对业务第一次上线部署到集群的场景)

除去控制面性能优化这样的硬核优化,负载均衡能够直接访问容器网络的Pod就是组件业务能力最重要的组成部分了,其不仅避免了NAT转发性能上的损失,同时避免了NAT转发带来的各种对集群内业务功能影响。但是在启动该项目时这一块还没有特别好的访问容器网络的支持。所以一期考虑集群CNI网络模式下Pod有弹性网卡入口,这个入口可以直接接入到负载均衡以达到直接访问的目的。负载均衡直接后端访问到容器网络,目前已经有通过云联网解决的方案,后续也会继续跟进这种更贴近集群网络的直连方案。

接下来能够直接访问了,如何保证滚动更新时的可用性保证呢?我们找到了官方提供的一个特性ReadinessGate。这个特性在1.12正式提供出来,主要是用来控制Pod的状态。默认情况下,Pod有以下Condition:PodScheduled、Initialized、ContainersReady,当这几个状态都Ready的时候,Pod Ready的Condition就通过了。但是在云原生的场景下面,Pod的状态是非常有可能需要参考其他状态的。ReadinessGate提供了这样一个机制,允许为Pod的状态判断添加一个栅栏,由第三方来进行判断与控制。这样Pod的状态就和第三方关联起来了。

负载均衡流量对比

传统NodePort模式

请求细节过程

  1. 请求流量进入负载均衡
  2. 请求被负载均衡转发到某一个节点的NodePort
  3. KubeProxy将来自NodePort的流量进行NAT转发,目的地址是随机的一个Pod。
  4. 请求进入容器网络,并根据Pod地址转发到对应节点。
  5. 请求来到Pod所属节点,转发到Pod。

新的Pod直连模式

请求细节过程

  1. 请求流量进入负载均衡
  2. 请求被负载均衡转发到某一个Pod的ENI弹性网卡

直连与Local访问的区别

看起来这两种访问方式的效果是一样的,但是在细节上还是存在一些差别。

  1. 从性能上区别不大,开启Local访问时,流量不会进行NAT操作也不会进行跨节点转发,所以仅仅多了一个到容器网络的路由。
  2. 没有进行NAT操作,来源IP就能够正确获取了。会话保持功能可能会有以下问题,当一个节点上存在多个Pod时,流量到哪一个Pod是随机的,这个机制可能会使话保持出现问题。

ReadinessGate的引入

前面有两个细节,可以在这里得到解答。

  1. 为什么要求集群版本高于 1.12
  2. 为什么kubectl get pod -o wide的结果中READINESS GATES列有内容。

这里涉及到一个滚动更新相关的问题

当用户开始为应用做滚动更新的时候,Kubernetes会根据更新策略进行滚动更新。但是其判断一批Pod启动的标识仅包括Pod自身的状态,并不会考虑这个Pod在负载均衡上是否已经进行配置健康检查是否通过。有的时候当接入层组件高负载,不能及时对这些Pod进行及时调度的话,这些滚动更新成功的Pod可能并没有正在对外提供服务,从而导致服务的中断。为了将滚动更新和负载均衡的后端状态关联起来,TKE接入层组件引入了Kubernetes 1.12中引入的新特性ReadinessGate。TKE接入层组件只有在确认后端绑定成功并且健康检查通过时,通过配置ReadinessGate的状态来使Pod达到Ready的状态,从而推动整个工作负载的滚动更新。

在集群中使用ReadinessGate的细节

Kubernetes集群提供的是一个服务注册的机制,你只需要将你的服务以MutatingWebhookConfigurations资源的形式注册到集群中就可以了。集群会在Pod创建的时候按照你的配置的回调路径通知你,这个时候就可以对Pod做一些创建前的操作,在这个Case里面就是给Pod加上ReadinessGate。唯一需要注意的就是这个回调过程必须是Https的,所以标配需要在MutatingWebhookConfigurations中配置签发请求的CA,并在服务端配置该CA签发的证书。

ReadinessGate机制的灾难恢复

用户集群中的服务注册或是证书有可能被用户删除,虽然这些系统组件资源不应该被用户修改或破坏。但在用户对集群的探索或是误操作下,这类问题会不可避免的出现。所以接入层组件在启动时会检查以上资源的完整性,在完整性受到破坏时会重建以上资源,加强系统的鲁棒性。

QPS和网络时延对比

直连与NodePort是服务应用的接入层方案,其实最终参与工作的还是用户部署的工作负载,用户工作负载的能力直接决定了业务的QPS等指标。所以我们针对这两种接入层方案,在工作负载压力较低的情况下,重点针对网络链路的时延进行了一些对比测试。直连在接入层的网络链路上能够优化10%左右的时间。同时测试中的监控也发现,直连模式减少了大量VPC网络内的流量。测试场景,从20节点到80节点,逐步增大集群规模,通过wrk工具对集群进行网络延时的测试。针对QPS和网络时延,下图给出了直连场景与NodePort的对比测试。

KubeProxy的一些设计思考

KubeProxy的缺点也在前文中提到的一样明显。但是基于云上负载均衡、VPC网络的各种特性,我们能给出各种其他更加本地化的接入层方案。但这并不意味着KubeProxy的设计不好或是作用不大。其对集群接入层的设计极具普适性、容错性,基本适用于所有业务场景下的集群,作为一个官方提供的组件这个设计是非常合适的。

新模式使用指引

前置要求

  1. Kubernetes集群版本需要高于 1.12。
  2. 集群网络模式必须开启VPC-CNI弹性网卡模式。
  3. 直连模式Service使用的工作负载需使用VPC-CNI弹性网卡模式。

控制台操作指引

  1. 登录 容器服务控制台

  2. 参考控制台创建 Service步骤,进入 “新建Service” 页面,根据实际需求设置 Service 参数。

    其中,部分关键参数信息需进行如下设置,如下图所示:

    • 服务访问方式:选择为【提供公网访问】或【VPC内网访问】。
    • 网络模式:勾选【采用负载均衡直连Pod模式】。
    • Workload绑定:选择【引用Worklocad】,并在弹出窗口中选择 VPC-CNI 模式的后端工作负载。
  3. 单击【创建服务】,完成创建。

Kubectl操作指引

  • Workload示例:nginx-deployment-eni.yaml

    注意spec.template.metadata.annotations中声明了tke.cloud.tencent.com/networks: tke-route-eni,在工作负载使用VPC-CNI弹性网卡模式。

apiVersion: apps/v1

kind: Deployment

metadata:

labels:

app: nginx

name: nginx-deployment-eni

spec:

replicas: 3

selector:

matchLabels:

app: nginx

template:

metadata:

annotations:

tke.cloud.tencent.com/networks: tke-route-eni

labels:

app: nginx

spec:

containers:

- image: nginx:1.7.9

name: nginx

ports:

- containerPort: 80

protocol: TCP


- Service示例:nginx-service-eni.yaml > 注意:`metadata.annotations`中声明了`service.cloud.tencent.com/direct-access: "true"`,Service在同步负载均衡时将采用直连的方式配置访问后端。 ```yaml
apiVersion: v1
kind: Service
metadata:
annotations:
service.cloud.tencent.com/direct-access: "true"
labels:
app: nginx
name: nginx-service-eni
spec:
externalTrafficPolicy: Cluster
ports:
- name: 80-80-no
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
sessionAffinity: None
type: LoadBalancer
``` - 部署以上内容到集群 > 注意:在你的环境你首先需要连接到集群(没有集群的需要先创建集群),可以参考文章尾部的帮助文档配置kubectl连接集群。 ```shell
➜ ~ kubectl apply -f nginx-deployment-eni.yaml
deployment.apps/nginx-deployment-eni created ➜ ~ kubectl apply -f nginx-service-eni.yaml
service/nginx-service-eni configured ➜ ~ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-eni-bb7544db8-6ljkm 1/1 Running 0 24s 172.17.160.191 172.17.0.3 <none> 1/1
nginx-deployment-eni-bb7544db8-xqqtv 1/1 Running 0 24s 172.17.160.190 172.17.0.46 <none> 1/1
nginx-deployment-eni-bb7544db8-zk2cx 1/1 Running 0 24s 172.17.160.189 172.17.0.9 <none> 1/1
➜ ~ kubectl get service -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.187.252.1 <none> 443/TCP 6d4h <none>
nginx-service-eni LoadBalancer 10.187.254.62 150.158.221.31 80:32693/TCP 6d1h app=nginx
``` ## 总结 与业界对比, - AWS有类似方案,通过弹性网卡的方式实现了Pod直连。
- GKE(Google Kubernetes Engine)也有类似方案。结合CLB(Google Cloud Load Balancing)的NEG(Network Endpoint Groups)特性实现接入层直连Pod。 现在腾讯云TKE也利用弹性网卡实现了Pod直连的网络模式,现已在腾讯云TKE上线。接下来,我们还计划对这个特性进行更多优化,包括 1. 不依赖VPC-ENI的网络模式,实现普通容器网络下的Pod直连。
2. 支持在Pod删除之前,摘除负载均衡后端。 欢迎大家一起来使用! ## 相关参考 1. [Kubernetes Service介绍](https://kubernetes.io/docs/concepts/services-networking/service)
2. [Kubernetes Ingress介绍](https://kubernetes.io/docs/concepts/services-networking/ingress)
3. [Kubernetes Deployments 滚动更新策略](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy)
4. [Kubernetes Pods ReadinessGate特性](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-readiness-gate)
5. [Kubernetes 通过Local转发获取来源IP](https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip)
6. [TKE容器服务 网络模式选型](https://cloud.tencent.com/document/product/457/41636)
7. [TKE容器服务 VPC-CNI网络模式](https://cloud.tencent.com/document/product/457/34993)
8. [TKE容器服务 配置kubectl并连接集群](https://cloud.tencent.com/document/product/457/32191)
9. [AWS ALB Ingress Controller](https://aws.amazon.com/cn/blogs/opensource/kubernetes-ingress-aws-alb-ingress-controller/)
10. [GKE 通过独立 NEG 配置容器原生负载平衡](https://cloud.google.com/kubernetes-engine/docs/how-to/standalone-neg)
>【腾讯云原生】云说新品、云研新术、云游新活、云赏资讯,扫码关注同名公众号,及时获取更多干货!!
![](https://img2020.cnblogs.com/other/2041406/202009/2041406-20200914091252172-200457670.png)

TKE基于弹性网卡直连Pod的网络负载均衡的更多相关文章

  1. 为什么基于Windows Server 2008 R2的网络负载均衡(NLB)配置的时候总会报错“主机不可访问”?

    配置基于Windows的网络负载均衡是很容易的,操作也很简单,点点鼠标基本上就能完成,但是在进行节点(真实服务器)操作的过程中有时候会遇到一些主机不可访问的报错信息.这个又是为什么呢? Figure ...

  2. 基于Spring cloud Ribbon和Eureka实现客户端负载均衡

    前言 本案例将基于Spring cloud Ribbon和Eureka实现客户端负载均衡,其中Ribbon用于实现客户端负载均衡,Eureka主要是用于服务注册及发现: 传统的服务端负载均衡 常见的服 ...

  3. 基于amoeba实现mysql数据库的读写分离/负载均衡

    一.Amoeba的简述:[来自百度百科]      Amoeba是一个以MySQL为底层数据存储,并对应用提供MySQL协议接口的proxy.它集中地响应应用的请求,依据用户事先设置的规则,将SQL请 ...

  4. 基于spring-boot、spring-cloud的websocket服务器多点负载均衡改造

    背景 为应对更多用户使用socket的场景,准备对存放websocket的服务器进行多点搭建并配置负载均衡. 问题 服务器上了多点负载均衡以后,基于socket的部分功能发生了有规律的失效,查看后台日 ...

  5. linux系统多网卡热备实现高并发负载均衡

    #nmcli实现bonding #先停止NetworkManagerservice NetworkManager stop chkconfig NetworkManager off   //开机自启动 ...

  6. 大规模集群FTP代理(基于lvs的vsftpd网络负载均衡方案部署(PASV))

    [目的] 在日常工作中,我们经常需要在某服务器上开FTP(Server)服务.但就是这么简单的事情通常也会变得很复杂,原因如下:1.需要开通FTP的服务器没有公网IP地址:(即不能直接访问到)2.这样 ...

  7. 双网卡单IP实现网卡冗余与负载均衡

    WINDOWS下: 所谓双网卡,就是通过软件将双网卡绑定为一个IP地址,这个技术对于许多朋友来说并不陌生,许多高档服务器网卡(例如intel8255x系列.3COM服务器网卡等)都具有多网卡绑定功能, ...

  8. Linux 服务器下多网卡的负载均衡

    Linux 服务器下多网卡负载均衡的实现   一.引言    现今几乎各行各业内部都建立了自己的服务器,由于服务器的特殊地位,它的可靠性.可用性及其 I/O 速度就显得非常的重要, 保持服务器的高可用 ...

  9. Ubuntu 10.04下实现双网卡负载均衡

    摘要:本文主要介绍和配置 在Ubuntu下 实现 bonding,双网卡负载,bonding模式为0,好处是负载平衡,另一网卡断了,也能工作. 什么是bonding Linux bonding 驱动提 ...

随机推荐

  1. Kubernetes基于haproxy实现ingress服务暴露

    HAproxy Ingress控制器 HAproxy Ingress简介 HAProxy Ingress watches in the k8s cluster and how it builds HA ...

  2. 树莓派搭建网站wordpress的url写错 问题解决方法 有效GUI方法

    这个时候wordpress的后台已经登陆不了了,所以要对数据库做一些改变. 先说一下我是跟b站韩博士学的,LNMP.如果我们用的不一个方法的话下面就不用看了.下面是具体方法: 1  浏览器登录phpm ...

  3. Android studio 在项目里配置签名 + cmd命令安装apk在测试机

    一.在项目里配置签名 搜索百度里有很多可视化操作在项目里配置签名,但是对于已经有签名的旧项目来说,用语句是最方便的. 方法: 第一步:把签名文件放到项目中,和build.gradle的同一级目录下.当 ...

  4. 为什么 java.util.Stack不被官方所推荐使用!

    Java 为什么不推荐使用 Stack 呢? 因为 Stack 是 JDK 1.0 的产物.它继承自 Vector,Vector 都不被推荐使用了,你说 Stack 还会被推荐吗? 当初 JDK1.0 ...

  5. DataGrid添加进度条列

    DataGridColumn类型的继承树 DataGridColumn的派生类: 一般情况下DataGridBoundColumn和DataGridComboBoxColumn足以满足多数列的样式,如 ...

  6. Jmeter 常用函数(1)- 详解 __Random

    如果你想查看更多 Jmeter 常用函数可以在这篇文章找找哦 https://www.cnblogs.com/poloyy/p/13291704.html 作用 产生一个随机数 语法格式 ${__Ra ...

  7. postman with xdebug

    Set the url with ?XDEBUG_SESSION_START=PHPSTORM and set a header Cookie: XDEBUG_SESSION=PHPSTORM

  8. 使用Spring Cloud Config统一管理配置,别再到处放配置文件了

    1 前言 欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章! 可配置是一个成熟软件系统应该提供的特性,而配置管理对于大型系统就显得十分重要,特别是对于拥有多个应用的微服务系统.可喜的是, ...

  9. MySQL引擎【转】

    数据库引擎介绍MySQL数据库引擎取决于MySQL在安装的时候是如何被编译的.要添加一个新的引擎,就必须重新编译MYSQL.在缺省情况下,MYSQL支持三个引擎:ISAM.MYISAM和HEAP.另外 ...

  10. js byte字节流和数字,字符串之间的转换,包含无符和有符之间的转换

    var NumberUtil={ //byte数组转换为int整数 bytesToInt2:function(bytes, off) { var b3 = bytes[off] & 0xFF; ...