Kubernetes 调度策略

庆云2年前技术文章590

1、资源调度


创建一个新的 Pod 资源对象时,该 Pod 应该运行在哪个工作节点,由 Scheduler 调度器进行调度。


Scheduler 是一个守护进程,其内部有很多调度算法。


Pod 调度流程:

当用户向 APIServer 请求创建 Pod 时,APIServer 检查相关权限没问题后,将请求交给 Scheduler,由 Scheduler 在众多节点当中,选择一个匹配的节点做为运行此 Pod 的工作节点。Scheduler 的选择结果并不直接反映到节点之上,而是会告诉 APIServer ,并将相关信息保存到 etcd 当中。APIServer 指挥着被选定节点的 kubelet,或者说 kubelet 始终 Watch 着 APIServer 当中与当前节点相关联的事件变动,进而根据配置模版运行 Pod。


Pod 调度步骤:

  1. Predicate(预选)
    先排除完全不符合 Pod 运行法则的节点(最低资源需求、最大资源限额 limit)

  1. Priority(优选)
    基于一系列的算法函数,计算节点的优先级,找出最佳匹配节点

  1. Select(选定)
    将 Pod 绑定在优选后的节点之上


Pod 高级调度:

一般而言是基于最大资源空闲率的均衡调度来实现,我们还可以基于自己的调度行为、调度标识影响预选和优选的调度结果,进而来完成高级调度行为。


  • 偶尔有特殊偏好的 Pod 要运行在特定节点(SSD 节点、GPU 节点)之上,可以对节点使用标签进行分类

$ kubectl explain pods.spec
......
   nodeName     <string>
   nodeSelector <map[string]string>   # 预选
......

  • 节点亲和性/反亲和性调度

  • Pod 亲和性/反亲和性调度(某一组 Pod 运行在同一节点或相邻节点)

  • 污点、污点容忍调度(Taints-nodes,Tolerations-pod)


2、常见的预选策略


  • CheckNodeCondition
    检查节点是否可以在磁盘、网络不可用或未准备好的前提下把 Pod 调度到此节点

  • GeneralPredicates(通用预选策略)

    • HostName:检查 Pod 对象是否定义 pod.spec.hostname

    • PodFitsHostPort:检查 Pod 对象是否定义 pod.spec.containers.ports.hostPort

    • MatchNodeSelector:检查节点标签是否适配 pods.spec.nodeSelector

    • PodFitsResources:检查 Pod 的资源需求是否能被节点所满足

  • NoDiskConflict(默认没有启用)
    检查 Pod 依赖的存储卷能否能满足需求

  • PodToleratesNodeTaints
    检查 Pod 上的 spec.tolerations  可容忍的污点是否完全包含节点上的污点

  • PodToleratesNodeNoExecuteTaints(默认没有启用)
    默认节点污点变动后,容忍之前调度在该节点之上的 Pod 存在。而该项则不容忍 Pod,会驱离 Pod

  • CheckNodeLabelPresence(默认没有启用)
    检查节点标签存在性

  • CheckserviceAffinity(默认没有启用)
    检查服务亲和性,将相同 Service 的 Pod 对象尽可能放在一块

  • MaxEBSVolumeCount
    亚马逊弹性存储卷最大数量,默认 39

  • MaxGCEPDVolumeCount
    谷歌容器引擎最大存储卷数量,默认 16

  • MaxAzureDiskVolumeCount
    Azure 最大磁盘数量,默认 16

  • CheckVolumeBinding
    检查节点上已绑定和未绑定的 PVC

  • NoVolumeZoneConflict
    没有数据卷空间冲突(逻辑限制)

  • CheckNodeMemoryPressure
    检查节点是否存在内存压力

  • CheckNodePIDPressure
    检查节点是否存在进程压力

  • CheckNodeDiskPressure
    检查节点是否存在磁盘压力

  • MatchInterPodAffinity
    检查节点是否能满足 Pod 亲和性或反亲和性条件


3、常见的优选函数


  • LeastRequested

# 节点空闲资源/节点总容量: 根据空闲比例评估
(cpu(capacity-sum(requested))*10/capacity)+(memory(capacity-sum(requested))*10/capacity)/2

capacity-sum(requested) # (总容量-已经被pod拿走的总容量)*10/总容量。*10是因为每一个优选函数的得分是10分,最后计算得分。

  • BalancedResourceAllocation
    CPU 和内存资源的被占用率相近的胜出,目的是平衡节点资源的使用率。

  • NodePreferAvoidPods
    节点亲向不要运行这个 Pod(优先级高),根据节点注解信息 "scheduler.alpha.kubernetes.io/preferAvoidPods" 判定,没有注解信息得分为 10,权重为 10000,存在此注解信息时,由控制器管控的 Pod 得分为 0。

  • TaintToleration
    将 Pod 对象的 spec.tolerations 与节点的 taints 列表项进行匹配度检查,匹配的条目越多得分越低。

  • SelectorSpreading: 调度器将 pod 分散调度。

  • InterPodAffinity: 根据 Pod 间的亲和性。

  • NodeAffinity: 根据节点亲和性。

  • MostRequested: 根据最多被请求的节点。

  • NodeLabel: 根据节点标签。

  • ImageLocality: 根据满足当前 Pod 对象需求的已有镜像的体积大小之和。


4、高级调度


高级调度设计机制:

  • 节点选择器:nodeSelector

  • 节点亲和器:nodeAnffinity

  • 污点容忍:taints / tolerations


4.1 nodeSelector

"新增节点标签"
$ kubectl label nodes worker2 disk=ssd
node/worker2 labeled

"创建测试Pod"
$ cat demo-pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: demo-pod
  namespace: qingyun
spec:
  containers:
  - name: myapp
    image: zhangyyhub/myapp:v1.0
    imagePullPolicy: IfNotPresent
  nodeSelector:  # 节点选择
    disk: ssd

$ kubectl apply -f demo-pod.yaml 
pod/demo-pod created

"验证测试Pod: 在节点标签所在节点之上"
$ kubectl get pods -n qingyun -o wide
NAME       READY   STATUS    RESTARTS   AGE   IP              NODE      NOMINATED NODE   READINESS GATES
demo-pod   1/1     Running   0          14s   10.244.189.98   worker2   <none>           <none>


4.2 nodeAnffinity

"nodeAnffinity"
$ kubectl explain pods.spec.affinity
   nodeAffinity <Object>
      preferredDuringSchedulingIgnoredDuringExecution      <[]Object>  # 软亲和性,尽量满足
         preference   <Object> -required-
            matchExpressions     <[]Object>      # 匹配表达式
            matchFields  <[]Object>              # 匹配字段
               key  <string> -required-          # key
               operator     <string> -required-  # 操作符(In, NotIn, Exists, DoesNotExist, Gt, and Lt.)
               values       <[]string>           # value
         weight       <integer> -required-
      requiredDuringSchedulingIgnoredDuringExecution       <Object>    # 硬亲和性,必须满足
         nodeSelectorTerms    <[]Object> -required-
            matchExpressions     <[]Object>      # 匹配表达式
            matchFields  <[]Object>              # 匹配字段
               key  <string> -required-          # key
               operator     <string> -required-  # 操作符(In, NotIn, Exists, DoesNotExist, Gt, and Lt.)
               values       <[]string>           # value
   podAffinity  <Object>
   podAntiAffinity      <Object>


实例:

"创建测试Pod"
cat demo-pod-nodeAffinity.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: demo-pod-affinity
  namespace: qingyun
spec:
  containers:
  - name: myapp
    image: zhangyyhub/myapp:v1.0
    imagePullPolicy: IfNotPresent
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:  # 硬亲和性,必须满足
        nodeSelectorTerms:
        - matchExpressions:    # 匹配表达式
          - key: disk          # key
            operator: In       # 操作符
            values:            # value
            - ssd
            - essd

$ kubectl apply -f demo-pod-nodeAffinity.yaml
pod/demo-pod-affinity created

"验证测试Pod"
$ kubectl get pods -n qingyun -o wide
NAME                READY   STATUS    RESTARTS   AGE   IP               NODE      NOMINATED NODE   READINESS GATES
demo-pod            1/1     Running   0          41m   10.244.189.98    worker2   <none>           <none>
demo-pod-affinity   1/1     Running   0          19s   10.244.189.123   worker2   <none>           <none>


"部署测试Pod"
$ cat demo-pod-nodeAffinity2.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: demo-pod-affinity2
  namespace: qingyun
spec:
  containers:
  - name: myapp
    image: zhangyyhub/myapp:v1.0
    imagePullPolicy: IfNotPresent
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:  # 软亲和性,尽量满足
      - preference:
          matchExpressions:    # 匹配表达式
          - key: disk          # key
            operator: In       # 操作符
            values:            # value
            - hssd
            - essd
        weight: 60
        
$ kubectl apply -f demo-pod-nodeAffinity2.yaml
pod/demo-pod-affinity2 created

"验证测试Pod"
$ kubectl get nodes --show-labels | grep hssd
$ kubectl get nodes --show-labels | grep essd

$ kubectl get pods -n qingyun -o wide
NAME                 READY   STATUS    RESTARTS   AGE   IP               NODE      NOMINATED NODE   READINESS GATES
demo-pod             1/1     Running   0          52m   10.244.189.98    worker2      <none>           <none>
demo-pod-affinity    1/1     Running   0          10m   10.244.189.123   worker2      <none>           <none>
demo-pod-affinity2   1/1     Running   0          12s   10.244.235.255   k8s-master   <none>           <none>


4.3 podAffinity


Pod 亲和性/反亲和性调度:一组 Pod 运行在一起,允许调度器将第一个 Pod 随机选择一个节点,第二个 Pod 根据第一个 Pod 实现调度。


可以根据标签分类:机架(rack)、一排(row)、机房(zone),来判断是否为同一位置。

"podAnffinity"
$ kubectl explain pods.spec.affinity
   nodeAffinity <Object>
   podAffinity  <Object>
      preferredDuringSchedulingIgnoredDuringExecution      <[]Object>  # 软亲和性,尽量满足
         preference   <Object> -required-
            matchExpressions     <[]Object>      # 匹配表达式
            matchFields  <[]Object>              # 匹配字段
               key  <string> -required-          # key
               operator     <string> -required-  # 操作符(In, NotIn, Exists, DoesNotExist, Gt, and Lt.)
               values       <[]string>           # value
         weight       <integer> -required-
      requiredDuringSchedulingIgnoredDuringExecution       <[]Object>  # 硬亲和性,必须满足
         labelSelector        <Object>           # 选定要亲和的Pod
            matchExpressions     <[]Object>
            matchLabels  <map[string]string>
         namespaceSelector    <Object>           # 选定要亲和的Namespace
         namespaces   <[]string>                 # 选定要亲和的Namespace
         topologyKey  <string> -required-        # 位置拓扑键
   podAntiAffinity      <Object>


实例: podAffinity

"创建测试Pod"
$  cat demo-pod-podAffinity.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-frontend
  namespace: qingyun
  labels:
    app: myapp
    tier: frontend
spec:
  containers: 
  - name: myapp
    image: zhangyyhub/myapp:v1.0
    imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-afterend
  namespace: qingyun
  labels:
    app: db
    tier: afterend
spec:
  containers:
  - name: myapp
    image: zhangyyhub/myapp:v1.0
    imagePullPolicy: IfNotPresent
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:  # 硬亲和性,必须满足
      - labelSelector:
          matchExpressions:                  # 匹配表达式
          - key: app                         # key
            operator: In                     # 操作符
            values:                          # value
            - myapp
        topologyKey: kubernetes.io/hostname  # 位置拓扑键

$ kubectl apply -f demo-pod-podAffinity.yaml
pod/pod-frontend unchanged
pod/pod-afterend created

"验证测试Pod"
$ kubectl get pods -n qingyun -o wide
NAME           READY   STATUS    RESTARTS   AGE     IP               NODE      NOMINATED NODE   READINESS GATES
pod-afterend   1/1     Running   0          14s     10.244.235.141   worker1   <none>           <none>
pod-frontend   1/1     Running   0          3m26s   10.244.235.171   worker1   <none>           <none>


实例: podAntiAffinity

"创建测试Pod"
$ cat demo-pod-podAffinity2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-frontend
  namespace: qingyun
  labels:
    app: myapp
    tier: frontend
spec:
  containers: 
  - name: myapp
    image: zhangyyhub/myapp:v1.0
    imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-afterend
  namespace: qingyun
  labels:
    app: db
    tier: afterend
spec:
  containers:
  - name: myapp
    image: zhangyyhub/myapp:v1.0
    imagePullPolicy: IfNotPresent
  affinity:
    podAntiAffinity:   # Pod反亲和
      requiredDuringSchedulingIgnoredDuringExecution:  # 硬亲和性,必须满足
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - myapp
        topologyKey: kubernetes.io/hostname

$ kubectl apply -f demo-pod-podAffinity2.yaml
pod/pod-frontend created
pod/pod-afterend created

"验证测试Pod"
$ kubectl get pods -n qingyun -o wide
NAME           READY   STATUS    RESTARTS   AGE   IP               NODE         NOMINATED NODE   READINESS GATES
pod-afterend   1/1     Running   0          14s   10.244.235.143   worker1      <none>           <none>
pod-frontend   1/1     Running   0          14s   10.244.235.210   k8s-master   <none>           <none>


4.4 容忍调度


容忍调度/污点调度:

kubectl explain nodes.spec.taints
   effect        <string> -required-  # 定义对Pod排斥效果
      NoSchedule         # 仅影响调度过程,对现有Pod不影响
      PreferNoSchedule   # 最好不影响调度,如果必须调度也行
      NoExecute          # 即影响调度过程,也影响现有的Pod
   key  <string> -required-
   timeAdded     <string>
   value         <string>


实例:

"打污点:生产环境专用"
$ kubectl taint node worker1 node-type=production:NoSchedule


apiVersion: v1
kind: Deployment
metadata:
  name: deploy-myapp
  namespace: qingyun
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp
      release: canary
  template:
    metadata:
      labels:
        app: myapp
        release: canary
    spec:
      containers:
      - name: myapp
        image: zhangyyhub/myapp:v1.0
        imagePullPolicy: IfNotPresent
      tolerations:
      - key: "node-type"
        operator: "Equal"        # Exists(污点存在就行不在乎值) and Equal(等值比较)
        value: "production"
        effect: "NoExecute"      # 即影响调度过程,也影响现有的Pod
        tolerationSeconds: 3600


相关文章

Trino对接ldap

Tls 证书生成生成的证书分发到每个节点 #ip和主机名为**coordinator**的ip和主机名及对应的vipkeytool -genkeypair -validity 36500 -ext S...

kaniko构建镜像的方式

docker构建镜像用docker来构建容器镜像也是常用的方法,在具备构建容器镜像所需的两个要素(Dockerfile & 上下文)的前提下,用下述命令就能构建一个容器镜像出来```Plain...

大数据集群监控配置操作指导(一)prometheus+grafana部署

大数据集群监控配置操作指导(一)prometheus+grafana部署

1.prometheus+grafana部署(单独部署到一台服务器。4c8g。系统盘300G。操作系统建议7.6到7.9)1.1下载prometheus和grafana的二进制包mkdir /opt/...

Oozie安装web页面

Oozie安装web页面

1、查看web页面,页面显示页面显示无法打开oozie的web安装页面,如果需要安装页面需要安装Ext js的lib库2、安装ExtJS 2.2库进入/var/lib/oozie路径中cd  /var...

MySQL性能优化(四)隐式类型转换

我们知道, where条件中,对一个索引字段进行运算会导致无法使用该字段的索引。有些情况下,即使没有显式地对索引字段进行运算,但是数据库会进行隐式类型转换,这也会导致无法使用索引。会发生隐式类型转换的...

DRDS 整库恢复介绍

DRDS 整库恢复介绍

1 整库恢复注意事项1、PolarDB-X 1.0自动备份策略默认关闭,需要您手动开启。PolarDB-X 1.0日志备份能力依赖下层RDS,PolarDB-X1.0控制台设置的日志备份策略会自动同步...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。