kubernetes如何调度pod

258 阅读6分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情

一、简介

在大多数情况下,我们不关心 pod 会被调度到哪个节点,只关心 pod 是否被成功调度到集群的一个可用节点。但是,在真实生产环境中存在一种需求:希望某种 pod 全部运行在一个或一些节点上。比如需要 ssd 的 pod 都运行在具有 ssd 磁盘的目标节点上。

二、全自动调度

deploymentrc的主要功能之一就是自动部署一个容器应用的多份副本,以及持续监控副本的数量,在集群中始终维持用户指定的副本数量。

nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

使用kubectl create命令创建这个deployment

# kubectl create -f nginx-deployment.yaml
deployment "nginx-deployment" created

可以看到Deployment已经创建好 3 个副本,并且所有副本都是最新可用的。从调度策略上来说,这 3 个pod由系统全自动完成调度,用户无法干预调度过程和结果。

三、NodeSelector:定向调度

通过Node的标签(Label)和PodnodeSelector属性相匹配来讲Pod调度到指定的一些Node上。

1、通过kubectl label命令给目标Node打上一些标签:

kubectl label nodes <node-name> <label-key>=<label-value>

2、在Pod的定义中加上nodeSelector的设置

apiVersion: v1
kind: ReplicationController
metadata:
  name: redis-master
  labels:
    name: redis-master
spec:
  replicas: 3
  selector:
    name: redis-master
  template:
    metadata:
      labels:
        name: redis-master
    spec:
      containers:
      - name: master
        image: reids-master
        ports:
        - containerPort: 6379
      nodeSelector:	# 节点标签选择器
        zone: north

如果给多个Node都定义了相同的标签,则scheduler会根据调度算法从Node组中挑选一个可用的Node进行调度。

除了用户可以自行给Node添加标签,kubernetes也会给Node预定义一些标签。

  • kubernetes.io/hostname
  • kubernetes.io/os
  • kubernetes.io/arch

四、NodeAffinity:Node 亲和性调度

NodeAffinity为 Node 亲和性的调度策略,是用于替换NodeSelector的全新调度策略。目前有两种节点亲和性表达。

  • RequiredDuringSchedulingIgnoredDuringExecution:必须满足指定的规则才能调度 Pod 到 Node 上,相当于硬限制。
  • PreferredDuringSchedulingIgnoredDuringExecution:强调优先满足指定规则,调度器会尝试调度 Pod 到 Node 上,但不强求,相当于软限制。多个优先级规则还可以设置权重(weight)值,以定义执行的先后顺序。

IgnoredDuringExecution:如果一个 Pod 所在的结点在 Pod 运行期间标签发生了变更,不再符合 Pod 的结点亲和性需求,则系统将忽略 Node 上的 Label 变化,该 Pod 能继续在该节点上运行。

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity: # 要求只在amd64的节点上运行
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: beta.kubernetes.io/arch
            operator: In
            values:
            - amd64
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1 # 尽量运行在磁盘类型为ssd的节点上
        preference:
          matchExpressions:
          - key: disk-type
            operator: In
            values:
            - ssd
   containers:
   - name: with-node-affinity
     image: pause:2.0

NodeAffinity语法支持的操作符包括InNotInExistsDoesNotExistGtLt

NodeAffinity规则设置的注意事项:

  • 如果同时定义了nodeSelectornodeAffinity,那么必须两个条件都得到满足,Pod 才能最终运行在指定的 Node 上
  • 如果nodeAffinity指定了多个nodeSelectorTerms,那么其中一个能够匹配成功即可
  • 如果nodeSelectorTerms中有多个matchExpressions,则一个节点必须满足所有matchExpressions才能运行 Pod。

五、PodAffinity:Pod 亲和与互斥调度策略

如果在具有标签相同的 Node 上运行了一个或多个符合条件的 Pod,那么 Pod 应该运行在这个 Node 上。

topologyKey:节点所属的topoloty

  • kubernets.io/hostname
  • failure-domain.beta.kubernetse.io/zone
  • failure-domain.beta.kubernets.io/region

pod 的亲和与互斥的条件设置也是RequiredDuringSchedulingIgnoredDuringExecutionPreferredDuringSchedulingIgnoredDuringExecution

Pod 的亲和性被定义在podAffinity,Pod 的互斥性被定义在podAntiAffinity

1、参考目标 Pod

创建一个名为pod-flag的 pod,带有标签security=S1app=nginx

apiVerson: v1
kind: Pod
metadata:
  name: pod-flag
  labels:
    security: "S1"
    app: "nginx"
spec:
  containers:
  - name: nginx
    image: nginx

2、Pod 的亲和性调度

apiVerson: v1
kind: Pod
metadata:
  name: pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: kubernets.io/hostname
  containers:
  - name: with-pod-affinity
    image: pause:2.0

3、Pod 的互斥性调度

apiVerson: v1
kind: Pod
metadata:
  name: anti-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: failure-domain.beta.kubernets.io/zone
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - nginx
        topologyKey: kubernets.io/hostname
  containers:
  - name: anti-pod-affinity
    image: pause:2.0

新 pod 与security=S1的 pod 为同一个zone,但不与app=nginx的 Pod 为同一个zone

topologyKey 的限制(出于性能和安全方面考虑):

  • 在 Pod 亲和性和 RequiredDuringScheduing 的 Pod 互斥性的定义中,不允许使用空的 topologyKey。
  • 如果 Admission Controller 包含了LimitPodHardAntiAffinityTopology,那么针对Required DuringScheduling的 Pod 互斥性定义就被限制为kubernetes.io/hostname,要使用自定义的topologyKey就要改写或禁用该控制器。
  • PreferredDruingScheduling类型的 Pod 互斥性定义中,空的topologyKey会被解释为kubernets.io/hostnamefailure-domain.beta.kubernetse.io/zonefailure-domain.beta.kubernets.io/region的组合。
  • 如果不是上述情况,就可以采用任意合法的topologyKey

PodAffinity规则设置的注意事项:

  • 除了设置Label SelectortopologyKey,用户还可以指定Namespace列表来进行限制,同样,使用Label SelectorNamespace进行选择。
  • 在所有关联requiredDuringSchedulingIgnoredDuringExecutionmatchExpressions全部满足之后,系统才能将 Pod 调度到某个 Node 上。

六、Taints 和 Tolerations(污点和容忍)

在 Node 上设置一个或多个 Taint 之后,除非 Pod 明确声明能够容忍这些污点,否则无法在这些 Node 上运行。Toleration 是 Pod 的属性,让 Pod 能够运行在标注了 taint 的 Node 上。

# kubectl taint nodes node1 key=value:NoSchedule

然后在 pod 上声明 toleration。

tolerations:
- key: "key"
  operator: "Equal"
  value: "value"
  effect: "NoSchedule"

tolerations:
- key: "key"
  operator: "Exists"
  effect: "NoSchedule"

如果不指定operator,默认值为Equal。空的 key 配合 Exists 能够匹配所有键和值。空的 effect 能够匹配所有的 effect。

  • NoSchedule:不调度
  • PreferNoSchedule:不优先调度
  • NoExecute:不运行,已经在这个节点上的 Pod 会被驱逐。

七、Pod Priority Preemption:Pod 优先级调度

优先级抢占调度策略的核心行为分别是驱逐(Eviction)和抢占(Preemption)。Evection 是 kubelet 行为,即当一个 Node 发生资源不足情况时,该节点上的 kubelet 进程会发生驱逐动作,此时 kubelet 会综合考虑 Pod 的优先级、资源申请量与实际使用量等信息极端哪些 Pod 需要被驱逐;当同样优先级的 Pod 需要被驱逐时,实际使用的资源量超过申请量最大倍数的高耗能 Pod 会被首先驱逐。

服务质量等级 Qos:

  • Guaranteed:pod 设置了limitlimit=request
  • Burstable:pod 里的一个容器limit!=request
  • Best-Effort:limitrequest均未设置

1、创建 PriorityClasses

apiVersion: scheduling.k8s.io/v1beta1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for service pods only"

2、可以在任意 pod 中引用优先级类别

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  priorityClassName: high-priority

八、其他情况

1、DaemonSet:在每个 Node 上都调度一个 Pod

2、Job 和 CronJob 的批处理调度,可以设置任务数completions和并行度parallelism