k8s-调度亲和性
简介
Scheduler 是 kubernetes 的调度器,主要的任务是把定义的 pod 分配到集群的节点上。听起来非常简单,但有
很多要考虑的问题:
- 公平:如何保证每个节点都能被分配资源
- 资源高效利用:集群所有资源最大化被使用
- 效率:调度的性能要好,能够尽快地对大批量的 pod 完成调度工作
- 灵活:允许用户根据自己的需求控制调度的逻辑
Sheduler 是作为单独的程序运行的,启动之后会一直坚挺 API Server,获PodSpec.NodeName 为空的 pod,对每个 pod 都会创建一个 binding,表明该 pod 应该放到哪个节点上
调度过程
调度分为几个部分:首先是过滤掉不满足条件的节点,这个过程称为 predicate
;然后对通过节点按照优先级排序,这个是priority
;最后从中选择优先级最高的节点。如果中间任何一步骤有错误,就直接返回错误
Predicate 有一系列的算法可以使用:
PodFitsResources :节点上剩余的资源是否大于 pod 请求的资源
PodFitsHost :如果 pod 指定了 NodeName,检查节点名称是否和 NodeName 匹配
PodFitsHostPorts :节点上已经使用的 port 是否和 pod 申请的 port 冲突
PodSelectorMatches :过滤掉和 pod 指定的 label 不匹配的节点
NoDiskConflict :已经 mount 的 volume 和 pod 指定的 volume 不冲突,除非它们都是只读
如果在 predicate 过程中没有合适的节点,pod 会一直在 pending 状态,不断重试调度,直到节点满足条件。经过这个步骤,如果有多个节点满足条件,就继续 priorities 过程: 按照优先级小对节点排序
优先级由一系列键值对组成,键是该优先级项的名称,值是它的权重(该项的重要性)。这些优先级选项包括:
LeastRequestedPriority
:通过计算 CPU 和 Memory 的使用率来决定权重,使用率越低权重越高。换句话说,这个优先级指标倾向于资源使用比例更低的节点BalancedResourceAllocation
:节点上 CPU 和 Memory 使用率越接近,权重越高。这个应该和上面的一起使用,不应该单独使用ImageLocalityPriority
:倾向于已经有要使用镜像的节点,镜像总大小值越大,权重越高通过算法对所有的优先级项目和权重进行计算,得出最终的结果
自定义调度器
除了 kubernetes 自带的调度器,你也可以编写自己的调度器。通过 spec:schedulername
参指定调度器的名字,可以为 pod 选择某个调度器进行调度。比如下面的 pod 选择 my-scheduler
进行调度,而不是默认的default-scheduler
:
apiVersion: v1
kind: Pod
metadata:
name: annotation-second-scheduler
labels:
name: multischeduler-example
spec:
schedulername: my-scheduler
containers:
- name: pod-with-second-annotation-container
image: gcr.io/google_containers/pause:2.0
节点亲和性
pod.spec.nodeAffinity
- preferredDuringSchedulingIgnoredDuringExecution:软策略
- requiredDuringSchedulingIgnoredDuringExecution:硬策略
requiredDuringSchedulingIgnoredDuringExecution(硬亲和)
例子:节点强制不在node2上调度
[root@master scheduler]# cat node-affinity.yml
apiVersion: v1
kind: Pod
metadata:
name: affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: nginx
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:#硬亲和策略
nodeSelectorTerms:#
- matchExpressions:
- key: kubernetes.io/hostname#节点键值hostname选择策略
operator: NotIn #策略选择不在,与下面的值共同存在;是不在node2的意思
values:
- node2
结果调度到node1上(多次删除pod,重新调度仍会调度到node1):
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
affinity 1/1 Running 0 14s 10.244.166.133 node1 <none> <none>
如果修改策略operator: In
values: - node3
硬亲和情况下,让pod调度到node3(节点不存在),会一直处于pendding状态。
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
affinity 0/1 Pending 0 7s
preferredDuringSchedulingIgnoredDuringExecution(软亲和)
例子:让pod尽量调度到node2节点(多次删除多次重建,仍然调度到node2)
[root@master scheduler]# cat node-prefer-affinity.yml
apiVersion: v1
kind: Pod
metadata:
name: affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: nginx
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node2
合体
[root@master scheduler]# cat affinin.yml
apiVersion: v1
kind: Pod
metadata:
name: affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: nginx
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: NotIn
values:
- node02
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: source
operator: In
values:
- qikqiak
键值运算关系:
- In:label 的值在某个列表中
- NotIn:label 的值不在某个列表中
- Gt:label 的值大于某个值
- Lt:label的值小于某个值
- Exists:某个 label 存在
- DoesNotExist:某个 label 不存在
Pod亲和性
pod.spec.affinity.podAffinity/podAntiAffinity
preferredDuringSchedulingIgnoredDuringExecution:软策略
requiredDuringSchedulingIgnoredDuringExecution:硬策略
演示:先启动两个简单的pod1,pod2
vim pod1.yml
apiVersion: v1
kind: Pod
metadata:
name: pod-1
labels:
app: pod-1
spec:
containers:
- name: pod-1
image: nginx
vim pod2.yml
apiVersion: v1
kind: Pod
metadata:
name: pod-2
labels:
app: pod-2
spec:
containers:
- name: pod-2
image: nginx
kubectl apply -f pod1.yml
kubectl apply -f pod2.yml
#查看
[root@master pod-affinity]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-1 1/1 Running 0 42s 10.244.1.47 node1 <none> <none>
pod-2 1/1 Running 0 38s 10.244.2.46 node2 <none> <none>
启动具有亲和性的的pod-3(亲pod-1,远离pod-2)
[root@master pod-affinity]# cat pod-affinity.yml
apiVersion: v1
kind: Pod
metadata:
name: pod-3
labels:
app: pod-3
spec:
containers:
- name: pod-3
image: nginx
affinity:
podAffinity:#必须先满足硬策略
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In#在pod-1
values:
- pod-1
topologyKey: kubernetes.io/hostname
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- pod-2
topologyKey: kubernetes.io/hostname
kubectl apply -f pod-affinity.yml
结果:pod-3调用到和pod-1相同的节点node1上,不在pod-2调度的节点node2
[root@master pod-affinity]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-1 1/1 Running 0 47s 10.244.1.47 node1 <none> <none>
pod-2 1/1 Running 0 43s 10.244.2.46 node2 <none> <none>
pod-3 1/1 Running 0 16s 10.244.1.48 node1 <none> <none>