【kubernetes系列】Kubernetes之调度器和调度过程

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就会从中随机选择一个节点。

如图:
【kubernetes系列】Kubernetes之调度器和调度过程_第1张图片

常用的预选策略(代码里的策略不一定都会被使用,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的知识分享,请前往博客主页。编写过程中,难免出现差错,敬请指出

你可能感兴趣的:(Kubernetes,kubernetes,算法)