系统相关
首页 > 系统相关> > LinuxCon | Kubernetes中的服务亲和性路由,引导更智能的服务发现

LinuxCon | Kubernetes中的服务亲和性路由,引导更智能的服务发现

作者:互联网

Kubernetes中的拓扑

1.    拓扑概念

首先,我们讲一下kubernetes中的拓扑。根据kubernetes现在的设计,我觉得拓扑可以是任意的。用户可以指定任何拓扑关系,比如az(available zone可用区)、region、机架、主机、交换机,甚至发电机。Kubernetes中拓扑的概念已经在调度器中被广泛使用。


2.    Kubernetes调度器中的拓扑

在kubernetes中,pod是工作的基本单元,所以调度器的工作可以简化为“在哪里运行这些pod”,当然我们知道pod是运行在节点里,但是怎么选择节点,这是调度器要解决的问题。

在kubernetes中,我们通过label选择节点,从而确定pod应该放在哪个节点中。下面是kubernetes原生提供的一些label:

Ø  k8s.io/hostname

Ø  failure-domain.beta.k8s.io/zone

Ø  failure-domain.beta.k8s.io/region

Ø  beta.k8s.io/instance-type

Ø  beta.k8s.io/os

Ø  beta.k8s.io/arch

此外,kubernetes允许集群管理员和云提供商自定义label,比如机架、磁盘类型等。


3.    Kubernetes中基于拓扑的调度

比如,如果我们要将PostgreSQL服务运行在不同的zone中,假设zone的名字分别是1a和1b,那么我们可以在podSpec中定义节点亲和,如下图所示。主服务器需要运行在zone a的节点中,备用服务器需要运行在属于zone b的节点中。

图片

在kubernetes中,pod与node的亲和或反亲和是刚性的要求。那么我们之前的问题“在哪里运行这些pod”就可以简化成“可以在这个节点运行pod吗”,答案取决于节点的一些情况,比如节点的名字,节点所属的region,节点是否有SSD盘等。


另外,调度器还会考虑另外一个问题,“可以把pod和其他pod放在同一区域吗”,比如,PostgreSQL服务肯定不能和MySQL服务运行在同一个节点中。Kubernetes通过下面三个步骤解决这个问题:

1)   定义“其他pod”。这个过程与选择node的过程类似,我们通过label选择pod。比如,我们可以选择带有“app=web-frontend”label的pod

2)   定义“同一区域”。如图中的两个pod,是在同一区域吗?不是,因为它们podSpec中的topologyKey字段,它是node label中的key,用于指定拓扑区域,比如zone、rack、主机等。比如如果我们使用“k8s.io/hostname”作为topologyKey,那么同一区域就表示在同一个主机上。

图片

我们可以使用之前提到的任何nodelabel作为“同一区域”的标识,比如在同一个zone。或者使用自定义的label,下图给出了通过自定义拓扑label创建node组。

图片

3)最后是决定是否可以。这取决于亲和和反亲和(affinity/anti-affinity)。


4.    Kubernetes中其他依赖拓扑关系的特性

上面讲到的部署pod时的拓扑感知。除了调度之外,还有许多特性会依赖拓扑关系:

Ø  工作负载:在缩容或滚动升级的时候,控制器决定先杀掉哪些pod

Ø  卷存储:卷存储会有拓扑限制,来决定可以挂在卷的node集。比如GCE的持久卷只能挂在在同一个zone的节点上,本地卷被所在节点访问。


依赖拓扑的服务亲和性路由

1.    Service和endpoint

首先我们来了解一下kubernetes中的service和endpoint的 概念。

Kubernetes中的service是一个抽象的概念,它通过label选择一个pod的集合,并且定义了这些pod的访问策略。简言之,service指定一个虚拟IP,作为这些pod的访问入口,在4层协议上工作。

Kubernetes中的endpoint是service后端的pod的地址列表。作为使用者,我们不需要感知它们,service创建的时候endpoint会自动创建,并且会根据后端的pod自动配置好。


2.    Service的工作原理

Endpoints控制器会watch到创建好的service和pod,然后创建相应的endpoint。Kube-proxy会watchservice和endpoint,并创建相应的proxy规则。在kubernetes1.8之前proxy是通过底层的iptables实现,但是iptables只支持随机的负载均衡策略,并且可扩展性很差。

在1.8之后,我们实现并在社区持续推动了基于ipvs的proxy,这种proxy模式相对原来的iptables模式有很多优势,比如支持很多负载均衡算法,并且在大规模场景下接近无限扩展性等。好消息是,现在kubernetes社区基于ipvs的proxy已经GA,大家可以在生产环境使用。那么问题来了,既然我们已经有IPVS加持了,为什么还需要服务亲和性路由呢?


3.    服务亲和性路由

先看一下用户的使用场景,当我们将pod正确的放到了用户指定的区域之后,就会有下面的问题。

1)  单节点通信(访问serviceIP的时候只能访问到本节点的应用)

Ø  我们使用daemonset部署fluent的时候,应用只需要与当前节点的fluent通信。

Ø  一些用户出于安全考虑,希望确保他们只能与本地节点的服务通信,因为跨节点的流量可能会携带来自其他节点的敏感信息。

2)  限制跨zone的流量

Ø  因为跨zone的流量会收费,而同一个zone的流量则不会。而有些云提供商,比如阿里云甚至不允许跨zone的流量。

Ø  性能优势:显然,到达本区域(节点/zone等)的流量肯定比跨区访问的流量有更低的延时和更高的带宽。


4.    亲和性路由的实现需要解决的问题

正如我们前边讲到的,本地意味着一定的拓扑等级,我们需要有一个可以根据拓扑选择endpoint子集的机制。

这样我们就面临着如下问题:

Ø  是软亲和还是硬亲和。硬亲和意味着只需要本地的后端,而软亲和意味着首先尝试本地的,如果本地没有则尝试更广范围的。

Ø  如果是软亲和,那么判定标准是什么呢?可能给每个拓扑区域增加权重是一个解决方案。

Ø  如果多个后端满足条件,那么选择的依据又是什么呢?随机选择还是引入概率?


5.    我们的方案

我们提供了一个解决方案,引用一种新的资源“ServicePolicy”。集群管理员可以通过ServicePolicy配置“local”的选择标准,以及各种拓扑的权重。ServicePolicy是一种可选的namespace范围内的资源,有三种模式:Required/Perferred/Ignored,分别代表硬亲和/软亲和/忽略。

图片

上图是我们引入和ServicePolicy资源和endpoint引入的字段示例。在我们的示例中,ServicePolicy会选择namespace foo中带有label app=bar的service。由于我们将hostname设置为ServicePolicy的拓扑依据,那么对这些service的访问会只路由到与kube-proxy有在同一个host的后端。

需要说明的是,service和ServicePolicy是多对多的关系,一个ServicePolicy可以通过label选择多个service,一个service也可以被多个ServicePolicy选中,有多个亲和性要求。

另外我们还希望endpoint携带节点的拓扑信息,因此我们为endpoint添加一个新的字段Topology,用于识别pod属于的拓扑区域,比如在哪个host/rack/zone/region等。

这会改变现有的逻辑。如下图所示,Endpoint控制器需要watch两种新的资源,node和ServicePolicy,它需要维护node与endpoint的对应关系,并根据node的拓扑信息更新endpoint的Topology字段。另外,kube-proxy也会相应地作一些改动。它需要过滤掉与自身不在同一个拓扑区域的endpoint,这意味着kube-proxy会在不同的节点上创建不同的规则。

图片

下图表示从ServicePolicy到proxy规则的数据流。首先ServicePolicy通过label选择一组service,我们可以根据这些service找到它们的pod。Endpoint控制器会将pod所在节点的拓扑label放到对应的endpoint中。Kube-proxy负责仅为处在同一拓扑区域的endpoint创建proxy规则,并且当多个endpoint满足要求时提供路由策略。

图片


[总 结]

目前我们已经在华为云的CCE服务上实现了服务亲和性路由,效果很好,欢迎大家体验。我们很乐意把这个特性开源出来,并且正在做这件事,相信它会像IPVS一样,成为kubernetes下一个版本的一个重要特性。


标签:endpoint,zone,Kubernetes,亲和性,节点,service,LinuxCon,pod,拓扑
来源: https://blog.51cto.com/u_15127680/2822735