Kubernetes之调度器和调度过程
概述
当用户请求向API server创建新的Pod时,API server检查授权、权限等没有任何问题的话,他会把这个请求交由Scheduler,由Scheduler检查所有符合该Pod要求的列表,开始执行Pod调度逻辑,选定Node完成后会把选择结果返回给API server,并将选定信息写入etcd中。接下来API server将指挥被选定Node的kubelet去创建Pod(或者说kubelet始终去watch API server当中与当前节点相关联的事件变动),因此接下来这个node就要去尝试着获取到API server当中定义的这个pod的配置清单,根据配置清单当中的定义去创建pod。Scheduler在整个系统中承担了承上启下的作用,承上是负责接收创建的新Pod,为安排一个落脚的地(Node),启下是安置工作完成后,目标Node上的kubelet服务进程接管后继工作,负责Pod生命周期的后半生。具体来说Scheduler的作用是将待调度的Pod按照特定的调度算法和调度策略绑定到集群中的某个合适的Node上,整个调度过程中涉及三个对象,分别是:待调度的Pod列表,可用的Node列表,以及调度算法和策略。
调度流程
Kubernetes Scheduler 提供的调度流程分三步:
- 预选策略(predicate) 遍历nodelist,选择出符合要求的候选节点,Kubernetes内置了多种预选规则供用户选择。
- 优选策略(priority) 在选择出符合要求的候选节点中,采用优选规则计算出每个节点的积分,最后选择得分最高的。
- 选定(select) 如果最高得分有好几个节点,select就会从中随机选择一个节点。
如图:
常用的预选策略(代码里的策略不一定都会被使用,Filtering-预选阶段)
常用的预选策略 | 用途 |
---|---|
CheckNodeConditionPred | 检查节点是否正常 |
GeneralPred HostName | 如果pod定义hostname属性,会检查节点是否匹配。(pod.spec.hostname) |
PodFitsHostPorts | 检查pod要暴露的hostpors是否被占用。(pod.spec.containers.ports.hostPort) |
MatchNodeSelector pod.spec.nodeSelector | 看节点标签能否适配pod定义的nodeSelector |
PodFitsResources | 判断节点的资源能够满足Pod的定义(如果一个pod定义最少需要1C2G node上的低于此资源的将不被调度。用kubectl describe node NODE名称 可以查看资源使用情况) |
NoDiskConflict | 判断pod定义的存储是否在node节点上使用。(默认没有启用) |
PodToleratesNodeTaints | 检查pod上Tolerates的能否容忍污点(pod.spec.tolerations) |
CheckNodeLabelPresence | 检查节点上的标志是否存在 (默认没有启动) |
CheckServiceAffinity | 根据pod所属的service。将相同service上的pod尽量放到同一个节点(默认没有启动) |
CheckVolumeBinding | 检查是否可以绑定(默认没有启动) |
NoVolumeZoneConflict | 检volume区域是否冲突(默认没有启动) |
CheckNodeMemoryPressure | 检查内存压力是否过大 |
CheckNodeDiskPressure | 检查磁盘IO压力是否过大 |
CheckNodePIDPressure | 检查pid资源是否过大 |
优选策略(scoring-打分阶段)
优选过滤器名 | 说明 |
---|---|
LeastRequestedPriority | 调度至请求较少、资源更空闲的节点上 |
BalancedResourceAllocation | 调度至资源平衡的节点 |
NodePreferAvoidPodsPriority | 通过scheduler.alpha.kubernetes.io/preferAvoidPods注释,避免pod间调度至同一个node节点 |
SelectorSpreadPriority | 标签优先级计算 |
InterPodAffinityPriority | pod亲和度计算 |
MostRequestedPriority | 调度至请求较多、资源更紧凑的节点上(尽量将一个节点上的资源用完) |
RequestedToCapacityRatioPriority | 使用默认资源打分函数进行优选计算 |
NodeAffinityPriority | node亲和度计算 |
TaintTolerationPriority | 根据节点上无法忍受的污点数量,为所有节点准备优先级列表 |
ImageLocalityPriority | 倾向于本地已有pod缓存的节点。(根据已有镜像体积大小之和) |
ServiceSpreadingPriority | 对于给定服务,此策略旨在确保该服务的Pod在不同的节点上运行 |
CalculateAntiAffinityPriorityMap | 此策略有助于实现pod的反亲和力 |
EqualPriorityMap | 所有节点给定一个相同的权重 |
高级调度方式
当我们想把Pod调度到希望的节点上时,我们可以可以使用如下高级调度方式进行Pod调度:
- 节点选择器: nodeSelector(Pod仅运行在能匹配到节点标签的主机上)、nodeName(Pod仅运行特定node)
- 节点亲和性调度: nodeAffinity
- Pod亲和性调度:PodAffinity
- Pod反亲和性调度:podAntiAffinity
注:可通过kubectl explain deployment.spec.template.spec.affinity
查看
调度器说明:
- nodeSelector:直接选定某个节点,选择性强,如果不存在匹配的node节点,pod将不会被调度
- nodeAffinity:节点亲和性,主要控制pod要部署在哪些主机,以及pod不能部署在哪些主机上的问题,处理的是pod和主机之间的关系。
- podAffinity/podAntiAffinity:主要控制pod可以和哪些pod部署在同一个拓扑域中的问题(拓扑域用主机标签实现,可以是单个主机,也可以是多个主机组成的cluster、zone等)。podAntiAffinity和podAffinity它们都是处理Kubernetes集群内部pod和pod之间的关系。比如一个 pod 在一个节点上了,那么我这个pod也得部署在这个节点,或者你这个 pod 在节点上了,那么我这个pod就不想和你部署在同一个节点上。
三种亲和性策略的比较如下
策略名称 | 匹配目标 | 支持的操作符 | 支持拓扑域 | 设计目标 |
---|---|---|---|---|
nodeAffinity | 主机标签 | In,NotIn,Exists,DoesNotExist,Gt,Lt | 不支持 | 决定Pod可以部署在哪些主机上 |
podAffinity | Pod标签 | In,NotIn,Exists,DoesNotExist | 支持 | 决定Pod可以和哪些Pod部署在同一拓扑域 |
PodAntiAffinity | Pod标签 | In,NotIn,Exists,DoesNotExist | 支持 | 决定Pod不可以和哪些Pod部署在同一拓扑域 |
- In:label 的值在某个列表中
- NotIn:label 的值不在某个列表中
- Gt:label 的值大于某个值
- Lt:label 的值小于某个值
- Exists:某个 label 存在(Values必须为空)
- DoesNotExist:某个 label 不存在(Values必须为空)
示例
语法说明
使用命令 kubectl explain deployment.spec.template.spec.affinity
可以查看语法格式,大致说明:
1)nodeAffinity
node亲和性,pod和node之间的亲和性,表示是否愿意调度到某个node上,其细分为以下两种:
- 硬亲和性(required):使用requiredDuringSchedulingIgnoredDuringExecution字段定义,必须满足条件才会调度,否则Pod对象的状态会一直是Pending状态。
- 软亲和性(preferred):使用preferredDuringSchedulingIgnoredDuringExecution字段定义,如果条件不满足,也会按一定的策略计算打分从中“尽量”的选择一个进行调度。
2)podAffinity
pod亲和性,pod和pod之间的亲和性,表示该pod愿意与其他pod调度到一个区域(可以是node、机架、也可以是机房)
3)podAntiAffinity
pod反亲和性,和podAffinity相反表示该pod不愿意与其他pod调度到一个区域。
podAffinity和podAntiAffinity也也可以细分为以下两种:
- 硬亲和性(required):使用requiredDuringSchedulingIgnoredDuringExecution字段定义,必须满足条件才会调度,否则Pod对象的状态会一直是Pending状态。
- 软亲和性(preferred):使用preferredDuringSchedulingIgnoredDuringExecution字段定义,如果条件不满足,也会按一定的策略计算打分从中“尽量”的选择一个进行调度。
使用场景
nodeAffinity使用场景:
- 将某个服务的所有Pod部署到指定的符合标签规则的主机上。
- 将某个服务的所有Pod部署到除部分主机外的其他主机上。
podAffinity使用场景:
- 将某特定服务的pod部署在同一拓扑域中,不用指定具体的拓扑域。
- 如果某服务和另一服务依赖性比较强,为了减少它们之间的网络延迟(或其它原因),就可以把=该服务的pod和另一服务的pod部署在同一拓扑域中。
podAntiAffinity使用场景:
- 将某服务的pod分散在不同的主机或者拓扑域中,提高服务本身的稳定性。
- 将某服务的pod单独部署到一个节点中来保证资源隔离,保证不会有其它pod来分享该节点资源。
- 把可能会相互影响的服务的POD分散在不同的主机上。
NodeSelector
k8s中的节点默认自带一些标签,通过如下查看:
[root@k8s-m1 k8s-scheduler]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-m1 Ready master 133d v1.19.16 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-m1,kubernetes.io/os=linux,node-role.kubernetes.io/master=
k8s-m2 Ready master 133d v1.19.16 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-m2,kubernetes.io/os=linux,node-role.kubernetes.io/master=
k8s-m3 Ready master 133d v1.19.16 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-m3,kubernetes.io/os=linux,node-role.kubernetes.io/master=
[root@k8s-m1 k8s-scheduler]#
我们定义一个pod,让其选择带有kubernetes.io/hostname=k8s-m2这个标签的节点
[root@k8s-m1 k8s-scheduler]# cat node-selector-pod.yml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: myapp
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
kubernetes.io/hostname: k8s-m2
[root@k8s-m1 k8s-scheduler]# kubectl apply -f node-selector-pod.yml
pod/nginx create
[root@k8s-m1 k8s-scheduler]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 16s 10.244.81.170 k8s-m2 <none> <none>
#定向调度到节点k8s-m2上
从上面的效果可以看出,设置了nodeSelector以后只会将Pod调度到符合标签的node1上,但是需要注意的是如果没有一个node满足nodeSelector的标签那么Pod会一直处于Pending状态直到有Node满足条件。
nodeAffinity
- requiredDuringSchedulingIgnoredDuringExecution 硬亲和性 必须满足亲和性。
- matchExpressions 匹配表达式,这个标签可以指定一段,例如pod中定义的key为kubernetes.io/hostname,operator为In(包含那些),values为 k8s-m2和k8s-m3。表示就是在节点中kubernetes.io/hostname的值为k8s-m2和k8s-m3的标签中调度
- matchFields 匹配字段 和上面定义方式一样,不过他定义的不是标签值而是定义字段。
- preferredDuringSchedulingIgnoredDuringExecution 软亲和性 能满足最好,不满足也没关系。
- preference 配置节点选择器,与相应的权重相关联。
- weight 权重1-100范围内,因为它是软性条件,所以并非一定要全匹配。在preference中匹配到的条目越多越符合条件。最后通过计算权重决定那个节点更符合条件。
硬亲和性:
硬亲和性,必须满足条件才能正常调度。
[root@k8s-m1 k8s-scheduler]# cat node-hard-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: hard-affinity-nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
name: my-nginx
labels:
app: nginx
spec:
containers:
- name: my-nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: #硬亲和
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values: ["k8s-m2","k8s-m3"]
[root@k8s-m1 k8s-scheduler]# kubectl apply -f node-hard-affinity.yaml
deployment.apps/hard-affinity-nginx created
[root@k8s-m1 k8s-scheduler]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hard-affinity-nginx-d5cbfc49-g6gzj 1/1 Running 0 78s 10.244.81.163 k8s-m2 <none> <none>
hard-affinity-nginx-d5cbfc49-p74ch 1/1 Running 0 56s 10.244.11.20 k8s-m3 <none> <none>
# 通过上面结果发现两个pod分别分布在k8s-m2和k8s-m3上
软亲和性:
与requiredDuringSchedulingIgnoredDuringExecution比较,这里需要注意的是preferredDuringSchedulingIgnoredDuringExecution是个列表对象,而preference就是一个对象。
[root@k8s-m1 k8s-scheduler]# cat node-soft-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: soft-affinity-nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
name: my-nginx
labels:
app: nginx
spec:
containers:
- name: my-nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 50
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values: ["k8s-m4"]
##设置了一个不存在的节点
[root@k8s-m1 k8s-scheduler]# kubectl apply -f node-soft-affinity.yaml
deployment.apps/soft-affinity-nginx created
[root@k8s-m1 k8s-scheduler]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
soft-affinity-nginx-6cdb7b4899-z5bhw 1/1 Running 0 20s 10.244.11.63 k8s-m3 <none> <none>
soft-affinity-nginx-6cdb7b4899-z72gd 1/1 Running 0 15s 10.244.81.167 k8s-m2 <none> <none>
#通过结果发现,软亲和性的pod在节点不存在的情况下页能正常调度
podAffinity
Pod亲和性场景,我们的k8s集群的节点分布在不同的区域或者不同的机房,当服务A和服务B要求部署在同一个区域或者同一机房的时候,我们就需要使用亲和性调度。
podAffinity和NodeAffinity是一样的,都是有硬亲和性和软亲和性。
参数:
- labelSelector 选择跟那组Pod亲和
- namespaces 选择哪个命名空间
- topologyKey 用来缩小节点选择范围,其值可以是任何合法的节点标签(key),在大规模集群中,为此字段不指定或者指定错误值,可能引发巨大的性能、安全问题。因此,对其使用有如下限制:
- 对于Pod亲和与Pod硬性反亲和,topologyKey字段值不能为空。
- 对于硬性反亲和,topoloygKey只能是kubernetes.io/hostname,除非禁止LimitPodHardAntiAffinityTopology允入控制器或者修改其实现。
- 对于Pod软反亲和,允许topoloygKey为空,表示对节点拓扑没有限制。
- 以上情况外,topologyKey可以是任何合法标签(key)。
硬亲和性:
由于我的集群都是虚拟机创建的节点,并没有划分区域或者机房,所以我这里直接使用主机名来作为拓扑域(topologyKey),把 pod 创建在同一个主机上面。
[root@k8s-m1 k8s-scheduler]# kubectl get node -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k8s-m1 Ready master 133d v1.19.16 192.168.2.140 <none> CentOS Linux 7 (Core) 3.10.0-957.el7.x86_64 docker://19.3.15
k8s-m2 Ready master 133d v1.19.16 192.168.2.141 <none> CentOS Linux 7 (Core) 3.10.0-957.el7.x86_64 docker://19.3.9
k8s-m3 Ready master 133d v1.19.16 192.168.2.142 <none> CentOS Linux 7 (Core) 6.1.9-1.el7.elrepo.x86_64 docker://19.3.9
[root@k8s-m1 k8s-scheduler]# cat pod-affinity-pod.yml
apiVersion: v1
kind: Pod
metadata:
name: pod-affinity-pod1
labels:
name: podaffinity-nginx1
spec:
containers:
- name: myapp
image: nginx:latest
imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Pod
metadata:
name: pod-affinity-pod2
labels:
spec:
containers:
- name: myapp
image: nginx:latest
imagePullPolicy: IfNotPresent
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: name
operator: In
values:
- podaffinity-nginx1
topologyKey: kubernetes.io/hostname
[root@k8s-m1 k8s-scheduler]# kubectl apply -f pod-affinity-pod.yml
pod/pod-affinity-pod1 created
pod/pod-affinity-pod2 created
[root@k8s-m1 k8s-scheduler]# kubectl get pod
NAME READY STATUS RESTARTS AGE
pod-affinity-pod1 1/1 Running 0 81s
pod-affinity-pod2 1/1 Running 0 81s
上面这个例子中的Pod pod-affinity-pod2需要调度到某个指定的主机上,至少有一个节点上运行了这样的 pod:这个 pod 有一个name=pod-affinity-pod1的 label
如果我们不部署上面的 node-affinity-pod1,然后重新创建 pod-affinity-pod2这个pod,看看能不能正常调度呢,先注释 node-affinity-pod1的相关yaml文件。
[root@k8s-m1 k8s-scheduler]# kubectl apply -f pod-affinity-pod.yml
pod/pod-affinity-pod2 created
[root@k8s-m1 k8s-scheduler]# kubectl get pod
NAME READY STATUS RESTARTS AGE
pod-affinity-pod2 0/1 Pending 0 3s
[root@k8s-m1 k8s-scheduler]# kubectl get pod
NAME READY STATUS RESTARTS AGE
pod-affinity-pod2 0/1 Pending 0 5s
[root@k8s-m1 k8s-scheduler]# kubectl describe po pod-affinity-pod2
Name: pod-affinity-pod2
Namespace: default
Priority: 0
Node: <none>
Labels: <none>
Annotations: <none>
Status: Pending
IP:
IPs: <none>
Containers:
myapp:
Image: nginx:latest
Port: <none>
Host Port: <none>
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-glxls (ro)
Conditions:
Type Status
PodScheduled False
Volumes:
default-token-glxls:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-glxls
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 5s (x2 over 5s) default-scheduler 0/3 nodes are available: 3 node(s) didn't match pod affinity rules, 3 node(s) didn't match pod affinity/anti-affinity.
可以看到pod-affinity-pod2处于Pending状态了,这是因为现在没有一个节点上面拥有name=pod-affinity-pod1这个 label 的 pod,而上面我们的调度使用的是硬策略,所以就没办法进行调度了,也可以去尝试下重新将pod-affinity-pod1 这个 pod 调度到 某个具体节点上,看看上面的 pod-affinity-pod2会不会也被调度到 相同节点上去?
[root@k8s-m1 k8s-scheduler]# cat pod-affinity-pod.yml
apiVersion: v1
kind: Pod
metadata:
name: pod-affinity-pod1
labels:
name: podaffinity-nginx1
spec:
containers:
- name: myapp
image: nginx:latest
imagePullPolicy: IfNotPresent
nodeSelector:
kubernetes.io/hostname: k8s-m3
---
apiVersion: v1
kind: Pod
metadata:
name: pod-affinity-pod2
labels:
spec:
containers:
- name: myapp
image: nginx:latest
imagePullPolicy: IfNotPresent
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: name
operator: In
values:
- podaffinity-nginx1
topologyKey: kubernetes.io/hostname
[root@k8s-m1 k8s-scheduler]# kubectl apply -f pod-affinity-pod.yml
pod/pod-affinity-pod1 created
pod/pod-affinity-pod2 configured
[root@k8s-m1 k8s-scheduler]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-affinity-pod1 1/1 Running 0 51s 10.244.11.7 k8s-m3 <none> <none>
pod-affinity-pod2 1/1 Running 0 5m28s 10.244.11.22 k8s-m3 <none> <none>
我们这个地方使用的是kubernetes.io/hostname这个拓扑域,意思就是我们当前调度的 pod 要和目标的 pod 处于同一个主机上面,因为要处于同一个拓扑域下面kubernetes.io/hostname=k8s-m3中,为了说明这个问题,我们把拓扑域改成beta.kubernetes.io/os,同样的我们当前调度的 pod 要和目标的 pod 处于同一个拓扑域中,目标的 pod 是不是拥有beta.kubernetes.io/os=linux的标签,而我们这里3个节点都有这样的标签,这也就意味着我们3个节点都在同一个拓扑域中,所以我们这里的 pod 可能会被调度到任何一个节点(因为master的污点被清除了,所以和普通节点一样都可以调度),判断他们是否在同一拓扑域中是根据topologyKey中指定的node标签的values是否相同,如果相同则表示在同一拓扑域中。软亲和性类似,关键字为preferredDuringSchedulingIgnoredDuringExecution
请大家自行测试使用。
podAntiAffinity
和Pod亲和性的用法类似,只是podAntiAffinity是反着来的。比如一个节点上运行了某个应用服务pod,那么我们的redis服务pod则尽量不要在同一台节点上,这就是反亲和性。podAntiAffinity和上面两个策略也是有硬亲和性和软亲和性两种不同用法;
硬亲和性:
[root@k8s-m1 k8s-scheduler]# cat pod-antiaffinity-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-antiaffinity-pod1
labels:
name: podantiaffinity-nginx
tier: service
spec:
containers:
- name: myapp
image: nginx:latest
imagePullPolicy: IfNotPresent
nodeSelector:
kubernetes.io/hostname: k8s-m2
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: antiaffinity-nginx
labels:
app: myapp
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: myapp
image: nginx:latest
imagePullPolicy: IfNotPresent
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: name
operator: In
values: ["podantiaffinity-nginx"]
topologyKey: kubernetes.io/hostname
[root@k8s-m1 k8s-scheduler]# kubectl apply -f pod-antiaffinity-pod.yaml
pod/pod-antiaffinity-pod1 created
deployment.apps/antiaffinity-nginx created
这里的意思就是如果一个节点上面有一个podantiaffinity-nginx这样的 pod 的话,那么我们的pod 就不能调度到这个节点上面来,上面我们把podantiaffinity-nginx这个 pod 固定到了 k8s-m2这个节点上面来,所以正常来说我们这里的 pod 不会出现在 k8s-m2 节点上:
[root@k8s-m1 k8s-scheduler]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
antiaffinity-nginx-769b467cf4-4qkh6 1/1 Running 0 75s 10.244.42.189 k8s-m1 <none> <none>
antiaffinity-nginx-769b467cf4-6qthb 1/1 Running 0 75s 10.244.11.25 k8s-m3 <none> <none>
antiaffinity-nginx-769b467cf4-7ppbj 1/1 Running 0 75s 10.244.42.130 k8s-m1 <none> <none>
pod-antiaffinity-pod1 1/1 Running 0 75s 10.244.81.179 k8s-m2 <none> <none>
更实际的用例
在三个节点集群中,web应用程序需要使用redis做缓存,比如redis。我们希望web服务器尽可能与redis共存。
下面的yaml文件是部署一个简单的redis,其中包含三个副本和label标签app=store。还配置了PodAntiAffinity,以确保调度器不会在单个节点上同时调度多个副本。
[root@k8s-m1 k8s-scheduler]# cat redis-podaffinity.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-cache
spec:
selector:
matchLabels:
app: redis
replicas: 3
template:
metadata:
labels:
app: redis
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- redis
topologyKey: "kubernetes.io/hostname"
containers:
- name: redis-server
image: redis:5.0.14-alpine
下面是web服务器部署的yaml文件配置了podAntiAffinity和podAffinity。这将通知调度器,它的所有副本都将与具有选择器标签app=redis的pod共存。这还将确保每个web服务器副本不会同时位于单个节点上。
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
selector:
matchLabels:
app: nginx-server
replicas: 3
template:
metadata:
labels:
app: nginx-server
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx-server
topologyKey: "kubernetes.io/hostname"
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- redis
topologyKey: "kubernetes.io/hostname"
containers:
- name: web-app
image: nginx:1.22.1-alpine
如果我们创建上面的两个部署,我们集群中的3个节点上每个节点都有一个nginx-server和redis服务。如下:
[root@k8s-m1 k8s-scheduler]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis-cache-64d4c5fbd6-mhxzl 1/1 Running 0 6m22s 10.244.81.144 k8s-m2 <none> <none>
redis-cache-64d4c5fbd6-qh82v 1/1 Running 0 6m23s 10.244.42.167 k8s-m1 <none> <none>
redis-cache-64d4c5fbd6-sfw6s 1/1 Running 0 6m22s 10.244.11.29 k8s-m3 <none> <none>
web-server-7f965dcbcc-crsd9 1/1 Running 0 2m9s 10.244.81.151 k8s-m2 <none> <none>
web-server-7f965dcbcc-rsmhr 1/1 Running 0 2m9s 10.244.42.190 k8s-m1 <none> <none>
web-server-7f965dcbcc-tblsz 1/1 Running 0 2m9s 10.244.11.14 k8s-m3 <none> <none>
上面的例子使用PodAntiAffinity规则和topologyKey: "kubernetes.io/hostname"配合使用来部署redis集群,使其同一主机上不存在两个实例;而在实际工作中,我们的集群节点一般都是都以hostname来划分拓扑域。
最后注意,亲和性设置中多个key之间的关系是与关系(and),而matchExpressions之间的关系是是或关系(or),但实际使用中我们也不会设置太复杂。
更多关于kubernetes的知识分享,请前往博客主页。编写过程中,难免出现差错,敬请指出