[toc]
Kubernetes 调度简介
除了让 kubernetes 集群调度器自动为 pod 资源选择某个节点(默认调度考虑的是资源足够,并且 load 尽量平均),有些情况我们希望能更多地控制 pod 应该如何调度。比如,集群中有些机器的配置更好( SSD,更好的内存等),我们希望比较核心的服务(比如说数据库)运行在上面;或者某两个服务的网络传输很频繁,我们希望它们最好在同一台机器上,或者同一个机房。
这种调度在kubernetes中分为两类:node affinity和pod affinity
Node选择器
这种方式其实就是我们最常用的使用label的方式,给某一个node打上特定的标签,然后在启动pod的时候,通过nodeSelector指定要调度到的node节点的标签。
给node打标签:
kubectl label nodes <node-name> <label-key>=<label-value>
示例:
kubectl label nodes k8s-node1 envir=live
apiVersion: v1kind: Podmetadata: ?name: nginx ?labels: ???env: testspec: ?containers: ?- name: nginx ???image: nginx ???imagePullPolicy: IfNotPresent ?nodeSelector: ???envir: live
需要说明的是,nodeSelector的方式比较简单直观,但是不够灵活,后面,它会被Node Affinity替代。
Node Affinity
Affinity 翻译成中文是“亲和性”,它对应的是 Anti-Affinity,我们翻译成“互斥”。这两个词比较形象,可以把 pod 选择 node 的过程类比成磁铁的吸引和互斥,不同的是除了简单的正负极之外,pod 和 node 的吸引和互斥是可以灵活配置的。
Affinity的优点:
- 匹配有更多的逻辑组合,不只是字符串的完全相等
- 调度分成软策略(soft)和硬策略(hard),在软策略下,如果没有满足调度条件的节点,pod会忽略这条规则,继续完成调度。
目前主要的node affinity:
requiredDuringSchedulingIgnoredDuringExecution
表示pod必须部署到满足条件的节点上,如果没有满足条件的节点,就不停重试。其中IgnoreDuringExecution表示pod部署之后运行的时候,如果节点标签发生了变化,不再满足pod指定的条件,pod也会继续运行。requiredDuringSchedulingRequiredDuringExecution
表示pod必须部署到满足条件的节点上,如果没有满足条件的节点,就不停重试。其中RequiredDuringExecution表示pod部署之后运行的时候,如果节点标签发生了变化,不再满足pod指定的条件,则重新选择符合要求的节点。preferredDuringSchedulingIgnoredDuringExecution
表示优先部署到满足条件的节点上,如果没有满足条件的节点,就忽略这些条件,按照正常逻辑部署。preferredDuringSchedulingRequiredDuringExecution
表示优先部署到满足条件的节点上,如果没有满足条件的节点,就忽略这些条件,按照正常逻辑部署。其中RequiredDuringExecution表示如果后面节点标签发生了变化,满足了条件,则重新调度到满足条件的节点。
软策略和硬策略的区分是有用处的,硬策略适用于 pod 必须运行在某种节点,否则会出现问题的情况,比如集群中节点的架构不同,而运行的服务必须依赖某种架构提供的功能;软策略不同,它适用于满不满足条件都能工作,但是满足条件更好的情况,比如服务最好运行在某个区域,减少网络传输等。这种区分是用户的具体需求决定的,并没有绝对的技术依赖。
下面是一个官方的示例:
apiVersion: v1kind: Podmetadata: ?name: with-node-affinityspec: ?affinity: ???nodeAffinity: ?????requiredDuringSchedulingIgnoredDuringExecution: ???????nodeSelectorTerms: ???????- matchExpressions: ?????????- key: kubernetes.io/e2e-az-name ???????????operator: In ???????????values: ???????????- e2e-az1 ???????????- e2e-az2 ?????preferredDuringSchedulingIgnoredDuringExecution: ?????- weight: 1 ???????preference: ?????????matchExpressions: ?????????- key: another-node-label-key ???????????operator: In ???????????values: ???????????- another-node-label-value ?containers: ?- name: with-node-affinity ???image: gcr.io/google_containers/pause:2.0
这个 pod 同时定义了 requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution 两种 nodeAffinity。第一个要求 pod 运行在特定 AZ 的节点上,第二个希望节点最好有对应的 another-node-label-key:another-node-label-value 标签。
这里的匹配逻辑是label在某个列表中,可选的操作符有:
- In: label的值在某个列表中
- NotIn:label的值不在某个列表中
- Exists:某个label存在
- DoesNotExist:某个label不存在
- Gt:label的值大于某个值(字符串比较)
- Lt:label的值小于某个值(字符串比较)
如果nodeAffinity中nodeSelector有多个选项,节点满足任何一个条件即可;如果matchExpressions有多个选项,则节点必须同时满足这些选项才能运行pod 。
需要说明的是,node并没有anti-affinity这种东西,因为NotIn和DoesNotExist能提供类似的功能。
Pod Affinity
通过node affinity,我们知道怎么在调度的时候让pod灵活的选择node,但有些时候我们希望调度能够考虑pod之间的关系,而不只是pod与node的关系。于是在kubernetes 1.4的时候引入了pod affinity。
为什么有这样的需求呢?举个例子,我们系统服务 A 和服务 B 尽量部署在同个主机、机房、城市,因为它们网络沟通比较多;再比如,我们系统数据服务 C 和数据服务 D 尽量分开,因为如果它们分配到一起,然后主机或者机房出了问题,会导致应用完全不可用,如果它们是分开的,应用虽然有影响,但还是可用的。
pod affinity 可以这样理解:调度的时候选择(或者不选择)这样的节点 N ,这些节点上已经运行了满足条件 X。条件 X 是一组 label 选择器,它必须指明作用的 namespace(也可以作用于所有的 namespace),因为 pod 是运行在某个 namespace 中的。
这里的X指的是集群中的节点、机架、区域等概念,通过kubernetes内置节点标签中的key来进行声明。这个key的名字为topologyKey,意为表达节点所属的topology范围:
- kubernetes.io/hostname
- failure-domain.beta.kubernetes.io/zone
- failure-domain.beta.kubernetes.io/region
和 node affinity 相似,pod affinity 也有 requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution,意义也和之前一样。如果有使用亲和性,在 affinity 下面添加 podAffinity 字段,如果要使用互斥性,在 affinity 下面添加 podAntiAffinity 字段。
先定义一个参照目标pod:
apiVersion: v1kind: Podmetadata: ?name: pod-flag ?labels: ???security: "S1" ???app: "nginx"spec: ?containers: ?- name: nginx ???image: nginx
Pod亲和性调度
下面是一个亲和性调度的示例
apiVersion: v1kind: Podmetadata: ?name: pod-affinityspec: ?affinity: ???podAffinity: ?????requiredDuringSchedulingIgnoredDuringExecution: ?????- labelSelector: ?????????matchExpressions: ?????????- key: security ???????????operator: In ???????????values: ???????????- S1 ???????topologyKey: kubernetes.io/hostname ?containers: ?- name: with-pod-affinity ???image: gcr.io/google_containers/pause:2.0
创建后可以看到这个pod与上面那个参照的pod位于同一个node上,另外,如果将这个node上的kubernetes.io/hostname标签干掉,将会发现pod会一直处于pending状态,这是因为找不到满足条件的node了。
pod互斥性调度
下面是一个互斥性调度的示例:
apiVersion: v1kind: Podmetadata: ?name: with-pod-affinityspec: ?affinity: ???podAffinity: ?????requiredDuringSchedulingIgnoredDuringExecution: ?????- labelSelector: ?????????matchExpressions: ?????????- key: security ???????????operator: In ???????????values: ???????????- S1 ???????topologyKey: "failure-domain.beta.kubernetes.io/zone" ???podAntiAffinity: ?????preferredDuringSchedulingIgnoredDuringExecution: ?????- weight: 100 ???????podAffinityTerm: ?????????labelSelector: ???????????matchExpressions: ???????????- key: security ?????????????operator: In ?????????????values: ?????????????- S2 ?????????topologyKey: kubernetes.io/hostname ?containers: ?- name: with-pod-affinity ???image: gcr.io/google_containers/pause:2.0
这个例子要求这个新pod与security=S1的pod为同一个zone,但是不与security=S2的pod为同一个node。
原则上,topologyKey可以使用任何合法的标签Key赋值,但是出于性能和安全方面的考虑,对topologyKey有如下限制:
- 在pod亲和性和RequiredDuringScheduling的pod互斥性的定义中,不允许使用空的topologyKey
- 如果admission controller包含了LimitPodHardAntiAffinityTopology,那么针对RequiredDuringScheduling的pod互斥性定义就被限制为kubernetes.io/hostname,要使用自定义的topologyKey,就要改写或禁用该控制器
- 在PerferredDuringScheduling类型的Pod互斥性定义中,空的topologyKey会被解释为kubernetes.io/hostname、failure-domain.beta.kubernetes.io/zone及failure-domain.beta.kubernetes.io/region的组合
podAffinity规则设置的注意事项:
- 在labelSelector和topologyKey同级,还可以定义namespaces列表,表示匹配哪些namespace里面的pod,默认情况下,会匹配定义的pod所在的namespace,如果定义了这个字段,但是它的值为空,则匹配所有的namespaces。
- 所有关联requiredDuringSchedulingIgnoredDuringExecution的matchExpressions全都满足之后,系统才能将pod调度到某个node上。
Taints和Tolerations(污点和容忍)
在前面介绍的NodeAffinity节点亲和性,是在pod上定义的一种属性,使得Pod能够被调度到某些node上运行。Taint刚好相反,它让Node拒绝Pod的运行。
Taint需要与Toleration配合使用,让pod避开那些不合适的node。在node上设置一个或多个Taint后,除非pod明确声明能够容忍这些“污点”,否则无法在这些node上运行。Toleration是pod的属性,让pod能够(注意,只是能够,而非必须)运行在标注了Taint的node上。
下面是一个简单的示例:
在node1上加一个Taint,该Taint的键为key,值为value,Taint的效果是NoSchedule。这意味着除非pod明确声明可以容忍这个Taint,否则就不会被调度到node1上:
kubectl taint nodes node1 ?key=value:NoSchedule
然后需要在pod上声明Toleration。下面的Toleration设置为可以容忍具有该Taint的Node,使得pod能够被调度到node1上:
tolerations:- key: "key" ?operator: "Equal" ?value: "value" ?effect: "NoSchedule"
也可以写成如下:
tolerations:- key: "key" ?operator: "Exists" ?effect: "NoSchedule"
pod的Toleration声明中的key和effect需要与Taint的设置保持一致,并且满足以下条件之一:
- operator的值为Exists,这时无需指定value
- operator的值为Equal并且value相等
如果不指定operator,则默认值为Equal。
另外还有如下两个特例:
- 空的key配合Exists操作符能够匹配所有的键和值
- 空的effect匹配所有的effect
effect说明
上面的例子中effect的取值为NoSchedule,下面对effect的值作下简单说明:
- NoSchedule: 如果一个pod没有声明容忍这个Taint,则系统不会把该Pod调度到有这个Taint的node上
- PreferNoSchedule:NoSchedule的软限制版本,如果一个Pod没有声明容忍这个Taint,则系统会尽量避免把这个pod调度到这一节点上去,但不是强制的。
- NoExecute:定义pod的驱逐行为,以应对节点故障。NoExecute这个Taint效果对节点上正在运行的pod有以下影响:
- 没有设置Toleration的Pod会被立刻驱逐
- 配置了对应Toleration的pod,如果没有为tolerationSeconds赋值,则会一直留在这一节点中
- 配置了对应Toleration的pod且指定了tolerationSeconds值,则会在指定时间后驱逐
- 从kubernetes 1.6版本开始引入了一个alpha版本的功能,即把节点故障标记为Taint(目前只针对node unreachable及node not ready,相应的NodeCondition "Ready"的值为Unknown和False)。激活TaintBasedEvictions功能后(在--feature-gates参数中加入TaintBasedEvictions=true),NodeController会自动为Node设置Taint,而状态为"Ready"的Node上之前设置过的普通驱逐逻辑将会被禁用。注意,在节点故障情况下,为了保持现存的pod驱逐的限速设置,系统将会以限速的模式逐步给node设置Taint,这就能防止在一些特定情况下(比如master暂时失联)造成的大量pod被驱逐的后果。这一功能兼容于tolerationSeconds,允许pod定义节点故障时持续多久才被逐出。
系统允许在同一个node上设置多个taint,也可以在pod上设置多个Toleration。Kubernetes调度器处理多个Taint和Toleration能够匹配的部分,剩下的没有忽略掉的Taint就是对Pod的效果了。下面是几种特殊情况:
- 如果剩余的Taint中存在effect=NoSchedule,则调度器不会把该pod调度到这一节点上。
- 如果剩余的Taint中没有NoSchedule的效果,但是有PreferNoSchedule效果,则调度器会尝试不会pod指派给这个节点
- 如果剩余Taint的效果有NoExecute的,并且这个pod已经在该节点运行,则会被驱逐;如果没有在该节点运行,也不会再被调度到该节点上。
下面是一个示例:
kubectl taint nodes node1 key1=value1:NoSchedulekubectl taint nodes node1 key1=value1:NoExecutekubectl taint nodes node1 key2=value2:NoSchedule
在pod上设置两个toleration:
tolerations:- key: "key1" ?operator: "Equal" ?value: "value1" ?effect: "NoSchedule"- key: "key1" ?operator: "Equal" ?value: "value1" ?effect: "NoExecute" ?
这样的结果是该pod无法被调度到node1上,因为第三个taint没有匹配的toleration。但是如果这个Pod已经在node1上运行了,那么在运行时设置上第三个Taint,它还能继续运行,因为pod可以容忍前两个taint。
一般来 说,如果给node加上effect=NoExecute的Taint,那么该 node上正在运行的所有无对应toleration的pod都会被立刻驱逐,而具有相应toleration的pod则永远不会被逐出。不过系统允许给具有NoExecute效果的Toleration加入一个可选 的tolerationSeconds字段,这个设置表明pod可以在Taint添加到node之后还能在这个node上运行多久(单们为s):
tolerations:- key: "key1" ?operator: "Equal" ?value: "value1" ?effect: "NoSchedule" ?tolerationSeconds: 3600
上面的例子的意思是,如果pod正在运行,所在节点被加入一个匹配的Taint,则这个Pod会持续在这个节点上存活3600s后被驱逐。如果在这个宽限期内taint被移除,则不会触发驱逐事件。
常见应用场景
节点独占
如果想要拿出一部分节点,专门给特定的应用使用,则可以为节点添加这样的Taint:
kubectl taint nodes nodename dedicated=groupName:NoSchedule
然后给这些应用的pod加入相应的toleration,则带有合适toleration的pod就会被允许同使用其他节点一样使用有taint的节点。然后再将这些node打上指定的标签,再通过nodeSelector或者亲和性调度的方式,要求这些pod必须运行在指定标签的节点上。
具有特殊硬件设备的节点
在集群里,可能有一小部分节点安装了特殊的硬件设备,比如GPU芯片。用户自然会希望把不需要占用这类硬件的pod排除在外。以确保对这类硬件有需求的pod能够顺利调度到这些节点上。可以使用下面的命令为节点设置taint:
kubectl taint nodes nodename special=true:NoSchedulekubectl taint nodes nodename special=true:PreferNoSchedule
然后在pod中利用对应的toleration来保障特定的pod能够使用特定的硬件。然后同样的,我们也可以使用标签或者其他的一些特征来判断这些pod,将其调度到这些特定硬件的服务器上。
应对节点故障
之前说到,在节点故障时,可以通过TaintBasedEvictions功能自动将节点设置Taint,然后将pod驱逐。但是在一些场景下,比如说网络故障造成的master与node失联,而这个node上运行了很多本地状态的应用即使网络故障,也仍然希望能够持续在该节点上运行,期望网络能够快速恢复,从而避免从这个node上被驱逐。Pod的Toleration可以这样定义:
tolerations:- key: "node.alpha.kubernetes.io/unreachable" ?operator: "Exists" ?effect: "NoExecute" ?tolerationSeconds: 6000
对于Node未就绪状态,可以把key设置为node.alpha.kubernetes.io/notReady
。
如果没有为pod指定node.alpha.kubernetes.io/noReady
的Toleration
,那么Kubernetes会自动为pod加入tolerationSeconds=300的node.alpha.kubernetes.io/notReady
类型的toleration
。
同样,如果没有为pod指定node.alpha.kubernetes.io/unreachable
的Toleration
,那么Kubernetes会自动为pod加入tolerationSeconds=300的node.alpha.kubernetes.io/unreachable
类型的toleration
。
这些系统自动设置的toleration用于在node发现问题时,能够为pod确保驱逐前再运行5min。这两个默认的toleration由Admission Controller "DefaultTolerationSeconds"自动加入。
kubernetes调度策略
原文地址:https://www.cnblogs.com/breezey/p/8810063.html