Kubernetes调度器

TOC

Kubernetes调度器了解

Pod调度一般情况下是通过群集的自动调度策略来选择节点调度,默认情况下调度器考虑的是资源足够,并且负载尽量平均。以下的方式可以帮助我们更加细粒度地去控制pod的调度。

在这里我们主要的概念:

  • 亲和性和反亲和性:又分为节点亲和性(nodeAffinity)和pod亲和性(podAffinity)。
  • 污点和容忍

k8s的调度过程的简单示意图,如下图所示:
k8s.002

nodeSelector

nodeSelector是非常常用的调度方式,我们可以使用nodeSelector根据节点的label来进行调度来控制pod的调度。
首先我们先给其中一个节点增加一个标签,并可以通过–show-labels参数查看节点标签。

#给node2节点创建标签
kubectl label nodes node2 name=apptest
#查看节点的标签
kubectl get nodes --show-labels

我们创建一个pod使用nodeSelector来控制他调度到node2节点,资源清单编写如下:

# nodeselector-test.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: test-pod
  name: test-pod
spec:
  containers:
  - command:
    - sleep
    - "3600"
    image: busybox
    imagePullPolicy: Always
    name: test-pod
  nodeSelector:
    name: apptest

nodeSelector的方式比较直观,但是不够灵活。

亲和性和反亲和性调度

一共分为nodeAffinity(节点亲和性)podAffinity(pod 亲和性)podAntiAffinity(pod 反亲和性)
亲和性调度又分为两种方式:

  • 软策略:没有满足调度要求的节点话,pod就会忽略这条规则,继续完成调度过程。设置项的值为preferredDuringSchedulingIgnoredDuringExecution
  • 硬策略:如果没有满足条件的节点的话,就不断重试直到满足条件为止。设置项的值为requiredDuringSchedulingIgnoredDuringExecution

节点亲和性

主要是用来控制pod要部署在哪些节点上,以及不能部署在哪些节点上的,它可以用一些简单逻辑进行组合使用。
下面是节点亲和性列举的配置项,需要在spec下配置,需和containers配置项对齐,如下所示:

affinity:
  nodeAffinity:
    # 硬策略,不调度到标签为kubernetes.io/hostname=master1的节点
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: NotIn
          values:
          - master1
    # 软策略,尽量调度到标签为name=apptest的节点,比重为1进行平均调度
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 1
      preference:
        matchExpressions:
        - key: name
          operator: In
          values:
          - apptest

现在K8S提供的操作符有下面的几种:

  • In:label 的值在某个列表中
  • NotIn:label 的值不在某个列表中
  • Gt:label 的值大于某个值
  • Lt:label 的值小于某个值
  • Exists:某个 label 存在
  • DoesNotExist:某个 label 不存在
    以下是完整的deployment的例子,比如我们让pod不能部署在master节点上,部署在标签为name=apptest的节点上,资源清单编写如下:
# node-affinity-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deploy
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.23.1
        ports:
        - containerPort: 80
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/hostname
                operator: NotIn
                values:
                - master1
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 1
            preference:
              matchExpressions:
              - key: name
                operator: In
                values:
                - apptest

Pod亲和性

主要用来解决pod可以和哪些pod部署在同一个拓扑域中的问题,这个拓扑域用主机标签实现,可以是单个主机,也可以是多个主机组成,处理的都是pod与pod之间的关系。
下面是pod亲和性列举的配置项,位置和节点亲和性的配置一样,如下所示:

affinity:
  podAffinity:
    # 硬策略,调度到和标签为app=nginx的pod在同一域里面
    # 这里拓扑域为kubernetes.io/hostname值,意思就是同一主机上
    requiredDuringSchedulingIgnoredDuringExecution:
    - labelSelector:
        matchExpressions:
        - key: app
          operator: In
          values:
          - nginx
      topologyKey: kubernetes.io/hostname
    # 软策略,调度到和标签为app=tomcat的pod在同一域里面
	preferredDuringSchedulingIgnoredDuringExecution:
	- podAffinityTerm:
	    labelSelector:
	      matchExpressions:
	      - key: app
	        operator: In
	        values:
	        - tomcat
	    topologyKey: kubernetes.io/hostname
	  weight: 1

下面是一个完整的deployment例子,我们通过标签为app=nginx的pod所在的节点,获取该节点标签kubernetes.io/hostname,只要节点标签kubernetes.io/hostname值和标签app=nginx的pod所在节点的标签kubernetes.io/hostname值一样,就可以调度到该节点,再尽量调度到app=tomcat的pod所在节点,资源清单编写如下:

# pod-affinity-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-affinity
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: pod-affinity
  template:
    metadata:
      labels:
        app: pod-affinity
    spec:
      containers:
      - name: pod-affinity
        image: busybox
        ports:
        - containerPort: 80
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - nginx
            topologyKey: kubernetes.io/hostname
		  preferredDuringSchedulingIgnoredDuringExecution:
		  - podAffinityTerm:
			  labelSelector:
			    matchExpressions:
			    - key: app
				  operator: In
				  values:
				  - tomcat
			  topologyKey: kubernetes.io/hostname
		    weight: 1

如果我们删除了标签为app=nginx的所有pod,那么该deployment的所有pod将会一直处于Pending状态,直到标签为app=nginx的pod出现。

Pod反亲和性

反亲和度则是跟上面的亲和度相反,来决定不和哪些pod调度到同一拓扑域里面。
下面是pod反亲和性列举的配置项,位置和节点亲和性的配置一样,如下所示:

affinity:
  podAntiAffinity:
    # 硬策略,调度到和标签为app=nginx的pod不在同一域里面
    # 这里拓扑域为kubernetes.io/hostname值,意思就是不在同一主机上
    requiredDuringSchedulingIgnoredDuringExecution:
    - labelSelector:
        matchExpressions:
        - key: app
          operator: In
          values:
          - nginx
      topologyKey: kubernetes.io/hostname

下面是一个完整的deployment例子,我们通过标签为app=nginx的pod所在的节点,获取该节点标签kubernetes.io/hostname,只要节点标签kubernetes.io/hostname值和标签app=nginx的pod所在节点的标签kubernetes.io/hostname值一样,就不调度到该节点,资源清单编写如下:

# pod-antiaffinity-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-antiaffinity
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: pod-antiaffinity
  template:
    metadata:
      labels:
        app: pod-antiaffinity
    spec:
      containers:
      - name: pod-antiaffinity
        image: busybox
        ports:
        - containerPort: 80
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - nginx
            topologyKey: kubernetes.io/hostname

污点和容忍

如果一个节点标记为Taints,除非pod也被标识为可以容忍污点节点,否则该有污点节点就不会被调度 Pod。
比如当我们希望我们的应用服务pod不调度到master节点上去抢占K8S组件的资源,或者把一些特殊资源预留给某些pod,这个时候污点的作用就体现出来了。
污点的策略分类:

  • NoSchedule:pod不会被调度到标记有污点的节点上。
  • PreferNoSchedule:NoSchedule的软策略,表示尽量不调度到有污点的节点上去。
  • NoExecute:一旦Taint 生效,如该节点内正在运行的pod没有对应容忍(Tolerate)设置,则会直接被逐出。
    1.污点标记使用
    节点标记污点
kubectl taint nodes node2 test=node2:NoSchedule

2.污点的容忍使用
下面是污点的容忍配置项以及参数,位置需要在spec下配置,需和containers配置项对齐,如下所示:

tolerations:
- key: "node-role.kubernetes.io/master"
  operator: "Exists"
  effect: "NoSchedule"

operator的值:

  • Exists:value属性可省略。
  • Equal:则表示key与value之间的关系相等。
  • 不指定值:则默认值为Equal。
    其他两种情况:
  • key为空,operator为Exists:能容忍所有节点的所有Taints。
  • effect为空:匹配所有的effect。
    如果需要将pod可以调度到标记为污点的节点上,需要yaml进行容忍声明,资源清单编写如下:
# taint-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: taint-pod
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: taint-pod
  template:
    metadata:
      labels:
        app: taint-pod
    spec:
      containers:
      - name: taint-pod
        image: busybox
        ports:
        - containerPort: 80
      tolerations:
      - key: "node-role.kubernetes.io/master"
        operator: "Exists"
        effect: "NoSchedule"

☆Deployment实现DaemonSet功能

资源清单编写如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-cp-ds
spec:
  replicas: 3
  selector:
    matchLabels:
      app: deploy-cp-ds
  template:
    metadata:
      labels:
        app: deploy-cp-ds
    spec:
      tolerations:
      - key: "node-role.kubernetes.io/master"
        operator: "Exists"
        effect: "NoSchedule"
      containers:
      - image: nginx:1.23.1
        name: deploy-cp-ds
        ports:
        - containerPort: 80
          name: deploy-cp-ds
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values: ["deploy-cp-ds"]
            topologyKey: kubernetes.io/hostname

Pod拓扑分布约束

在pod调度的亲和性和非亲和性中主要的两个概念就是堆叠打散

  • :可以将无数个pod都调度到某一个特定的拓扑域中。
  • 打散:可以控制一个拓扑域中只存在pod的一个副本。

但是这样的方式不太灵活,比较极端,无法达到很多场景理想的状态。
PodTopologySpread(Pod 拓扑分布约束)就是为了更加精细的控制,提高服务可用性和资源利用率。

注意:PodTopologySpread由EvenPodsSpread特性门所控制,在v1.16版本第一次发布,并在v1.18版本进入beta阶段默认启用。

使用规范
以下是拓扑分布约束的配置项,在Spec下新增了一个topologySpreadConstraints字段即可配置拓扑分布约束,如下所示:

spec:
  topologySpreadConstraints:
    - maxSkew: 1
      topologyKey: zone
      whenUnsatisfiable: DoNotSchedule
      labelSelector:
        matchLabels:
          app: nginx

Deployment、DaemonSet、StatefulSet控制器都能使用这个配置。

如下图所示:
k8s.003