Scheduler负责调度Pod到集群内的节点,监听APIServer,查询还未分配Node的Pod,然后根据调度策略为这些Pod分配节点(更新Pod的NodeName字段)。

调度器需要充分考虑诸多因素:

  • 公平调度
  • 资源高效利用
  • Qos
  • affinity和anti-affinity
  • 数据本地化

调度

调度分为两个阶段,两个阶段都是可配置的插件式的,挨个过插件

  • Predicate,过滤不符合条件的节点
  • Priority,节点打分,选择分值最高的节点

除了内置的插件,也支持编写自己的策略,在Pod的spec中指定自己的调度器。

资源需求

cpu/memory

在Pod的spec中指定container某个容器的资源request和limit。满足request量,就能调度上去。

  • CPU
    • requests 判断当前节点上在运行的Pod的CPU requests的总和,再加上当前调度的Pod的request,是否超过CPU的可分配资源
    • limits 配置cgroup以限制资源上限
  • 内存
    • requests 判断节点剩余内存是否满足Pod的内存请求量
    • limits 配置cgroup以限制资源上限

使用LimitRange对象约定某个namespace中的Pod的cpu、内存的默认限制值。在Pod没有限制资源使用量时,使用该默认设置。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
          resources:
            limits:
              memory: 1Gi
              cpu: 1
            requests:
              memory: 256Mi
              cpu: 100m

临时存储

容器临时存储包含日志和可写层的数据,可以通过定义Pod Spec中的下面两个字段来申请

  • limits.ephemeral-storage
  • requests.ephemeral-storage

Pod调度完成后,对临时存储的限制不是基于cgroup的,而是kubelet定时检查容器日志和可写层的磁盘使用,如果超过限制就进行驱逐。

init container

调度包含多个init container的Pod时,只计算cpu.request最多的init容器,而不是计算所有init容器的和。

  • 多个init容器是按顺序执行的,完成后退出,并不是一起执行
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      initContainers:
        - name: init-myservice
          image: busybox:1.28
          command: ['sh', '-c', 'echo The app is running! && sleep 10']
      containers:
        - name: nginx
          image: nginx

控制调度

通过nodeSelector/nodeAffinity/podAffinity/Taints/tolerations 来控制Pod在Node上的调度,也可以通过设置NodeName将Pod调度到指定Node。

nodeSelector

1
kubectl label nodes node-01 disktype=ssd
1
2
3
spec:
  nodeSelector:
    disktype: ssd

nodeAffinity

pod和node之间的亲和性。目前支持两种,分别代表必须满足的条件和优选条件

  • requiredDuringScheduling, predicted阶段
  • preferredDuringScheduling, priority阶段

修改亲和性时,应该使用kubectl replace执行。假设之前是required,需要改为prefered,如果用apply执行,相当于-XPATCH方法,会追加prefered,而不删除required,导致无法生效。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: disktype
                    operator: In
                    values:
                      - ssd
      containers:
        - name: nginx
          image: nginx

podAffinity

pod之间的亲和性。

示例:必须调度到有带security=S1标签的运行Pod的node上,尽量不和带有security=S2标签的运行Pod在一个node。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
                - key: security
                  operator: In
                  values:
                    - S1
            topologyKey: kubernetes.io/zone
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
                - key: security
                  operator: In
                  values:
                    - S2
            topologyKey: kubernetes.io/hostname
      containers:
        - name: nginx
          image: nginx

Taints/Tolerations

Taints和Tolerations保证Pod不会被调度到不合适的Node上

  • Taints应用于Node
  • Tolerations应用于Pod

目前支持的Taints类型:

  • NoSchedule 新的Pod不调度到该Node,不影响正在运行的Pod
  • PreferNoSchedule 尽量不调度到该Node
  • NoExecute 新的Pod不调度上来,并evict删除正在运行的Pod,可以增加一个延时tolerationSeconds
1
kubectl taint nodes master user-name=cadmin:NoSchedule # 格式 k=v:type

当Pod的toleration匹配Node上所有Taints时,就可以调度到该Node。

1
2
3
4
5
6
7
spec:
  containers:
  tolerations:
    - key: "user-name"
      operator: "Equal"
      value: "cadmin"
      effect: "NoSchedule"

优先级调度

支持定义Pod的优先级,保证高优先级的Pod优先调度。

  • 需要在apiserver和scheduler的启动参数中配置
  • 需要定义ProrityClass定义优先级,然后在Pod的spec中指定优先级名称

生产经验

  • 放大效应:当一个node上出现问题导致负载较小时,用户的pod会优先调度到该node,导致用户pod创建失败
  • 应用炸弹:恶意Pod运行时打开过多句柄,导致node 宕机,pod会被驱逐到其它node,再对其他node进行伤害