背景介绍:
在k8s环境中,通常情况下,Pod分配到哪些Node是不需要管理员操心的,这个过程会由scheduler调度实现,合理的分配到最优的节点上。但在实际项目中,我们可能需要指定一些调度的限制,例如某些应用需要跑在具有SSD存储或带gpu的节点上,或某些需进行大量计算解析且耗费很多cpu等资源的应用,需要负载均衡的打到集群中的各个节点中,避免调度到同一个node上,影响应用性能;这时候我们就需要用到一些调度机制来干预pod的调度;
nodeSelector
基于上述需求,可能最先想到的是通过nodeSelector来进行调度;基本流程就是给node节点打标签,然后在创建部署的时候,通过使用nodeSelector标签来指定Pod运行在哪些节点上;操作示例如下:
1)给node定义label:
格式:kubectl lable node [node-name] [key=value]
2)通过 --show-labels
可以查看当前nodes的labels:
3)当然,也可通过 kubectl label node
删除指定的 labels (标签 key 接 - 号即可)
4)创建pod,并指定nodeSelector
选项绑定节点;示例如下:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: docker.io/nginx
imagePullPolicy: IfNotPresent
nodeSelector:
gpu: server2
nodeSelector
可以很方便的解决一些简单需求的场景,但是它还不够灵活;比如生产环境中,某个数据治理服务,按需开了12个replicas,需要平均的分散到各个服务器节点上;此时 nodeSelector
可能就没办法满足了。因此,kubernetes引入了亲和性和反亲和性的概念。
Affinity and anti-affinity (亲和 and 反亲和)
nodeSelector的调度方式略显简单,通过亲和和反亲和配置,能够为调度提供更灵活的策略,主要有以下几点增强:
- 1,不仅仅是"与"的逻辑操作,支持更多的逻辑表达式。
- 2,nodeSelect是硬性要求,亲和与反亲和支持软硬两种要求;
- 3,除了节点标签,亲和与反亲和支持根据节点上已经部署的pod进行节点选择(比如不想将两个计算密集类型的pod部署在同一节点上,后部署pod可选择过滤)。
细分成两种类型的选择器: "节点亲和" 与 "内部pod亲和,反亲和"。节点亲和与nodeSelector相似,具备上述1,2两条优点。内部pod亲和依赖的是节点上已有pod的标签而不是节点的标签
;兼具上述三个优点。因为节点亲和能完成nodeSelector所工作并且具备额外的优点。
1,Node affinity(节点亲和) node affinity支持两种形式:
requiredDuringSchedulingIgnoredDuringExecution (硬限制, 同nodeSelector)
preferredDuringSchedulingIgnoredDuringExecution(软限制)
前一种是必须满足,如果不满足则不进行调度(未调度的pod状态为pending);后一种是倾向满足,不满足的情况下pod会调度到不符合条件的node上。
IgnoreDuringExecution
表示如果在pod运行期间node的标签发生变化,导致亲和性策略不能满足,则继续运行在当前的pod;
示例如下:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: //硬限制
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- cdss-server1
- cdss-server2
preferredDuringSchedulingIgnoredDuringExecution: //软限制
- weight: 1 //取值范围1-100
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- cdss-server3
containers:
- name: nginx
image: docker.io/nginx
以上规则表达的意思是,该pod只能调度到拥有
kubernetes.io/hostname=cdss-server1
或kubernetes.io/hostname=cdss-server2
标签的节点上,其中在满足之前标签条件的同时更倾向于调度在拥有kubernetes.io/hostname=cdss-server3
标签的节点上。
标签判断的操作符除了使用In之外,还支持以下运算符:NotIn,Exists,DoesNotExist,Gt,Lt;可以使用NotIn和DoesNotExist实现节点的反亲和行为。
In:
label的值在某个列表中NotIn:
label的值不在某个列表中Gt:
label的值大于某个值Lt:
label的值小于某个值Exists:
某个label存在DoesNotExist:
某个label不存在
调度匹配规则Notice: 1)如果同时指定nodeSelector和nodeAffinity,则必须满足两个条件,才能将pod调度到候选节点上。
2)如果nodeAffinity类型下指定了多个nodeSelectorTerms对象【对象不能有多个,如果存在多个只有最后一个生效】,那么只有最后一个nodeSelectorTerms对象生效。
3)如果在nodeSelectorTerms下指定了多个matchExpressions列表,那么只要能满足其中一个matchExpressions,就可以将pod调度到某个节点上【针对节点硬亲和】
4)如果在matchExpressions下有多个key列表,那么只有当所有key满足时,才能将pod调度到某个节点【针对硬亲和】
5)在key下的values只要有一个满足条件,那么当前的key就满足条件
6)如果pod已经调度在该节点,当我们删除或修改该节点的标签时,pod不会被移除;也就是亲和性选择只有在pod调度期间有效,添加完affinity后需要使其重新调度生效
。
7)软亲和中的weight(权重)字段在1-100范围内。对于每个满足所有调度需求的节点,调度器将通过迭代该字段的元素来计算一个总和,如果节点与相应的匹配表达式匹配,则向该总和添加 "权重",然后将该分数与节点的其他优先级函数的分数结合起来,总得分最高的节点是最受欢迎的。
pod affinity/anti-affinity (pod亲和/反亲和)
这个特性是kubeernetes1.4后增加的。Pod 间亲和性与反亲和性使你可以基于已经在节点上运行的 Pod 的标签来约束 Pod 可以调度到的节点,而不是基于节点上的标签。
规则的形式是:如果X已经运行了一个或多个符合规则Y的pod,则此pod应该在X中运行(如果是反亲和的情况下,则不应该在X中运行)。当然pod必须处在同一名称空间,不然亲和性/反亲和性无作用。从概念上讲,X是一个拓扑域。我们可以使用topologyKey来表示它,topologyKey 的值是node节点标签的键以便系统用来表示这样的拓扑域。当然这里也有个隐藏条件,就是node节点标签的键值相同时,才是在同一拓扑域中;如果只是节点标签名相同,但是值不同,那么也不在同一拓扑域。也就是说 pod的亲和性/反亲和性调度是根据拓扑域来界定调度的,而不是根据node节点
。
同node affinity,pod亲和性和反亲和也有两种类型:
requiredDuringSchedulingIgnoredDuringExecution,硬性要求,必须精确匹配
preferredDuringSchedulingIgnoredDuringExecution,软性要求
notice:
1)pod亲和性和反亲和性需要大量的计算,会显著降低集群的调度速度,不建议在数百个节点的集群中使用。
2)pod反亲和性要求集群中的所有节点必须具有topologykey
匹配的标签,否则可能会导致意外情况发生。
//示例如下:
apiVersion: v1
kind: Pod
metadata:
name: cdss-deploy
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- cdss-server1
topologyKey: "kubernetes.io/hostname"
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution: //生产环境中反亲和建议使用"软亲和",否则会导致不符合条件的pod pending; gnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- cdss-standard-baidu-cdss-cdss-server //service name
topologyKey: "kubernetes.io/hostname"
namespaces: [baidu-cdss] //需指定生效于pod所运行的namespace
containers:
- name: cdss-deploy
image: k8s.gcr.io/pause:2.0
以上示例分别表示pod的亲和和反亲和;首先pod必须调度在至少运行一个kubernetes.io/hostname=cdss-server1
标签的pod的节点上(更准确的说,这个pod可以运行在节点N上,如果该节点有topologyKey
为kubernetes.io/hostname
,而且运行着标签为kubernetes.io/hostname=cdss-server1
的实例)
反亲和的规则表示:最好不要调度到运行·有app=cdss-standard-baidu-cdss-cdss-server
标签的pod的节点上。(更准确的说,如果这个节点拥有topologyKey为kubernetes.io/hostname
,但运行有app=cdss-standard-baidu-cdss-cdss-server
标签的pod,那么这个节点就不会被优化选择调度);有效限制创建的实例的时候,如果节点上已经存在具有相同标签的实例,则不进行调度,避免了一个节点上部署多个相同的实例;
总结:
podAffinity(pod亲和) 和 podAntiAffinity(pod反亲和)支持 In
, NotIn
, Exists
,DoesNotExist
四种表达式。
原则上,topologykey 可以为任何合法的键值对。但是因为性能和安全的原因,有以下限制:
-
1,针对 podAffinity 和 podAntiAffinity 中的
requiredDuringSchedulingIgnoredDuringExecution(硬限制)
和preferredDuringSchedulingIgnoredDuringExecution(软限制)
,topologyKey
为空是不允许的。 -
2,针对 podAntiAffinity 中的
requiredDuringSchedulingIgnoredDuringExecution
,准入控制器选项LimitPodHardAntiAffinityTopology
可以把topologyKey
限制为kubernetes.io/hostname
,如果想自定义值,可以修改准入控制器或者直接禁用。 -
3,除上述情况外,
topologyKey
可以为任意合法的键值对。
除了 labelSelector
和 topologyKey
,你也可以指定 labelSelector
作为匹配。labelSelector
和 topologykey
属于同一级别,如果未定义或设置为空值,那么默认为定义 pod affinity亲和 和 anti-affinity 反亲和所在的空间。
更多案例及内容参考官方文档: https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity
标签:标签,cdss,调度,anti,io,affinity,pod,k8s,节点 From: https://blog.51cto.com/u_13972012/7470862