Pod 调度
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Pod 调度相关的知识,希望对你有一定的参考价值。
在大部分情况下,Pod只是容器的载体,通常我们会使用Deployment,RC,Job,ReplicaSet等对象来完成一组Pod的调度和控制。
当我们创建一个deployment或者RC后,kuernetes会自动根据我们的要求将一个或多个Pod副本自动调度到合适的节点上,这个过程kube-scheduler经过一系列算法自动完成,用户无法干预。在某些场景,我们也可以使用Kubernetes提供的其他调度策略来满足我们的特殊需求。这些调度策略包括:
- NodeSelector
- NodeAffinity
- PodAffinity
- Pod驱逐
- Taints和Tolerations
NodeSelector 定向调度
NodeSelector非常简单,就是将pod调度到我们指定的Node节点上,这里分为两个步骤:
(1) 对Node节点打上特定的label
(2) 创建pod时指定此label.
下面是一个简单示例:
1、对node添加标签,并验证:
kubectl label nodes <node-name> <label-key>=<label-value>
eg:
[[email protected] ~]# kubectl get node
NAME STATUS ROLES AGE VERSION
10.0.0.2 Ready <none> 9d v1.10.4
10.0.0.3 Ready <none> 9d v1.10.4
[[email protected] ~]# kubectl label nodes 10.0.0.2 disk=ssd
node "10.0.0.2" labeled
[[email protected] ~]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
10.0.0.2 Ready <none> 9d v1.10.4 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disk=ssd,kubernetes.io/hostname=10.0.0.2
10.0.0.3 Ready <none> 9d v1.10.4 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=10.0.0.3
- 创建一个pod,指定Label:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent # 节点上没有nginx镜像时才执行pull操作
nodeSelector:
disk: ssd
提示:如果指定的label在所有node上都无法匹配,则创建Pod失败,会提示无法调度:
Warning FailedScheduling 7s (x6 over 22s) default-scheduler 0/2 nodes are available: 2 node(s) didn‘t match node selector.
调度成功后会显示:
# kubectl describe pod nginx
...
Node-Selectors: disk=ssd
Tolerations: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 4m default-scheduler Successfully assigned nginx to 10.0.0.2
Normal SuccessfulMountVolume 4m kubelet, 10.0.0.2 MountVolume.SetUp succeeded for volume "default-token-hmvnc"
Normal Pulling 4m kubelet, 10.0.0.2 pulling image "nginx"
Normal Pulled 3m kubelet, 10.0.0.2 Successfully pulled image "nginx"
Normal Created 3m kubelet, 10.0.0.2 Created container
Normal Started 3m kubelet, 10.0.0.2 Started container
默认情况下,kubernetes也自带了一些标签:
# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
10.0.0.2 Ready <none> 9d v1.10.4 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disk=ssd,kubernetes.io/hostname=10.0.0.2
10.0.0.3 Ready <none> 9d v1.10.4 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=10.0.0.3
NodeAffinity Node亲和性调度
Node亲和性调度是用于替换NodeSelector的全新调度策略,有两种节点亲和性表示:
- requiredDuringSchedulingIgnoredDuringExecution:必须满足指定的规则才可以调度Pod到Node上(功能与nodeSelector很像,语法不同),相当于硬限制。
- preferredDuringSchedulingIgnoredDuringExecution:强调优先级,可以设置权重,但不是强制的,相当于软限制。
这里使用如下示例:
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
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: k8s.gcr.io/pause:2.0
此节亲和性规则指出,只能将pod放置在具有标签的节点上,标签的关键字为kubernetes.io/e2e-az-name,其值必须为e2e-az1或e2e-az2。 另外,在满足该标准的节点中,应该优选具有其标签为another-node-label-key其值是another-node-label-value的节点。
上面的示例使用了in 操作符,NodeAffinity语法支持的操作符包括in, NotIn, Exists, DoesNotExit, Gt, Lt。使用NotIn和DoesNotExist就可以实现排斥功能了。
- 如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都得到满足,Pod才能最终运行在指定的Node上。
- 如果nodeAffinity指定了多个nodeSelectorTerms,那么只需要其中一个能够匹配成功即可。
- 如果nodeSelectorTerms中有多个matchExpressions,则一个节点必须满足所有matchExpressions才能运行该Pod。
- 如果一个Pod所在的节点在Pod运行期间标签发生了变化,不再符合该Pod的亲和性需求,Pod不会进行重新调度,继续在该节点上运行。
PodAffinity: Pod亲和性与互斥调度策略
Pod亲和性的调度策略将节点上的Pod也纳入了考虑范围,这种规则可以描述为:
如果在具有标签X的Node节点上运行了一个或者多个符合条件Y的Pod,那么Pod应该(拒绝/允许)运行在这个Node上。
这里的X指的时集群中的节点,机架,区域等概念,通过内置的节点标签topologyKey来实现。
条件Y表达的是pod对应的一个或者全部命名空间中的一个Label Selector
Pod的亲和与互斥的条件设置也是requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution
如下示例,podAffinity 和 podAntiAffinity都被定义在affinity区域中:
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
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: k8s.gcr.io/pause:2.0
这个示例表示了pod在调度时,需要满足以下几个要求:
- 被调度的node节点与Pod标签的key为‘security’,值为‘S1’的节点属于同一个zone
-
最好不要调度到运行label key 为 ‘security’,值为‘S1’ pod的节点上。
pod affinity和 anti-affinity的逻辑操作运算有:
In
,NotIn
,Exists
,DoseNotExit
.
原则上,topologyKey可以使用任何合法的标签Key赋值,但是出于性能和安全方面的考虑,对topologyKey有如下限制:- 在Pod亲和性和requiredDuringSchedulingIgnoredDuringExecution的互斥性定义中,不允许使用空的topologyKey。
- 如果Admisson controller 包含了LimitPodHardAntiAffinityTopology,那么针对requiredDuringSchedulingIgnoredDuringExecution的Pod互斥性定义就被限制为kubernetes.io/hostname,要使用自定义的topologyKey,就要改写或禁用该控制器。
- 在preferredDuringSchedulingIgnoredDuringExecution 类型的pod互斥性定义中,空的topologyKey会被解释为kubernetes.io/hostname, failure-domain.beta.kubernetes.io/zone 和 failure-domain.beta.kubernetes.io/region的组合。
- 如果不是上述情况,就可以采用任意合法的topologyKey 了。
附加说明:
- 除了设置LabelSelector和topologyKey,用户还可以指定namespace列表来进行限制,同样,使用Label Selector对namespace进行选择。namespace的定义和Label Selector及topologyKey同级。 省略namespace的设置,表示使用定义了affinity/anti-affinity的Pod所在的namespace。 如果namespace设置为空值(“”),则表示所有namespace。
- 在所有关联requiredDuringSchedulingIgnoredDuringExecution的matchExpressions 全部满足之后,系统才能将Pod调度到某个Node上。
Taints和Tolerations 污点和容忍
Taint前面的节点亲和性作用相反,使用Taints规则,将拒绝Pod在Node上运行。
Taint需要和Tolerations配合使用,让Pod避开那些不合适的Node。在Node上设置一个或多个Taint之后,除非Pod明确声明能够容忍这些“污点”,否则无法在这些Node上运行。Toleration是Pod的属性,让Pod能够运行在标注了Taint的Node节点上。
1、使用kubectl taint
命令为Node设置Taint信息:
kubectl taint nodes 10.0.0.3 key=value:NoSchedule
为node节点的10.0.0.3加上一个Taint,Taint的键为Key,值为value, Taint的效果是NoSchedule。这里表示的含义是任何Pod都不能调度到这个节点,除非设置了toleration.
删除节点上的taint:
kubectl taint nodes 10.0.0.3 key:NoSchedule-
2、在Pod中定义容忍(Tolerate)具有该Taint的Node,使得Pod可以被调度到这个节点:
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取值可以是NoSchedule,还可以取值为PreferNoSchedule,这个值的意思是优先,也可以算作NoSchedule的软限制版本。
系统允许在同一个Node上设置多个Taint,也可以在Pod上设置多个Toleration,匹配上的就会按照规则执行,而剩下的会对Pod产生效果:
- 如果剩余的Taint中存在effect=NoSchedule,则调度器不会把该Pod调度到这一节点上。
- 如果剩余的Taint中没有NoSchedule效果,但是有PreferNoSchedule效果,则调度器会尝试不把这个Pod指派给这个节点。
- 如果剩余的Taint的效果有NoExecute,并且这个Pod已经在该节点上运行,则会被驱逐;如果没有在该节点上运行,也不会再被调度到该节点上。
- 如果pod已经在节点上正常运行,此时添加一个新的Taint,使得effect=NoSchedule,Pod还会继续在此节点上运行。
- 如果给Node加上effect=NoSchedule的Taint,那么该节点Node上正在运行的所有无对应Toleration的Pod都会被立刻驱逐,而具有相应Toleration的Pod则永远不会被逐出,可以加入一个可选的
tolerationSeconds
,当taint添加后,还可以继续运行多长时间,单位为秒:
tolerations:
- key: "node.alpha.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 6000
如果在宽限时间(这里示例是6000s)内Taint被移除,则不会触发驱逐事件。
使用污点和容忍策略一般可以运用于以下场景:
- 独占节点:如要拿出一部分节点给特定的应用使用,结合Admisson Controller实现。
- 具有特殊硬件设备的节点
- 定义Pod驱逐行为,以应对节点故障(Alpha)
DaemonSet 在每个Node上调度一个Pod
DaemonSet用于管理集群中每个节点仅运行一份Pod的副本实例。这种用法一般适合如下应用:
- 在每个Node上运行一个GlusterFS存储或者Ceph存储的Daemon进程。
- 在每个Node上运行一个日志采集程序,例如Fluentd或者Logstash。
- 在每个Node上运行一个性能监控程序,采集Node的性能数据,如Prometheus Node Exporter, collectd, Datadog agent, New Relic agent, 或 Ganglia gmond。
如下示例,会在每个节点创建一个DaemonSet:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd-elasticsearch
image: k8s.gcr.io/fluentd-elasticsearch:1.20
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
由于上面的示例中,使用的kube-system的namespace示例,在查询daemonset的时候需要指定namespace
DaemonSet的Pod调度策略与RC类似,除了使用系统内置的算法在每台Node进行调度,也可以使用NodeSelector或NodeAffinity来指定满足条件的Node范围进行调度。
Job 批处理调度
批处理任务通常并行或串行启动多个计算进程去处理一批工作项,处理完成之后,整个批处理任务结束。批处理任务分为如下几种类型:
1、Non-parallel Jobs
- 通常一个Job只能启动一个Pod,除非Pod异常,才会重启该Pod.
- 一旦Pod正常结束,Job将结束。
2、Parallel Jobs with a fixed completion count
- 并行Job会启动多个Pod,此时需要设定Job的.spec.completions参数为一个正数
- 当正常结束的Pod数量达至此参数的设定的值后,Job结束。
3、并行Job需要一个独立的Queue,Work item都在一个Queue中存放,不能设置Job的.spec.completions参数,此时Job有以下特性:
- 每个Pod能独立判断和决定是否还有任务项需要处理。
- 如果某个Pod正常结束,则Job不会再启动新的Pod.
- 如果一个Pod成功结束,此时应该不存在其他Pod还在干活的情况,他们应该都处于即将结束,退出的状态。
- 如果所有Pod都结束了,其至少有一个Pod成功结束,则整个Job算成功结束。
Cronjob 定时任务
Kubernetes的定时任务和Linux Cron定时任务语法类似,这里有如下示例:
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
可以发现每隔一分钟执行了一次:
# kubectl get cronjob hello -o wide
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE CONTAINERS IMAGES SELECTOR
hello */1 * * * * False 0 33s 7m hello busybox <none>
# kubectl get jobs --watch
NAME DESIRED SUCCESSFUL AGE
hello-1529412060 1 1 3m
hello-1529412120 1 1 2m
hello-1529412180 1 1 1m
hello-1529412240 1 0 6s
查看日志信息:
# kubectl get pods --selector=job-name=hello-1529412600
NAME READY STATUS RESTARTS AGE
hello-1529412600-slcps 0/1 Completed 0 20s
# kubectl get pod hello-1529412600-slcps
NAME READY STATUS RESTARTS AGE
hello-1529412600-slcps 0/1 Completed 0 51s
# kubectl logs hello-1529412600-slcps
Tue Jun 19 12:50:17 UTC 2018
Hello from the Kubernetes cluster
删除cronjob:
# kubectl delete cronjob hello
自定义调度器
除了上面所述的相关调度策略外,kubernetes还支持自定义调度器,可以使用任何语言开发我们需要的调度规则,将自定义的调度器使用kubectl proxy来运行,指定的文件格式如下:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
schedulerName: my-scheduler
containers:
- name: nginx
image: nginx
以上是关于Pod 调度的主要内容,如果未能解决你的问题,请参考以下文章