伦伦影院久久影视,天天操天天干天天射,ririsao久久精品一区 ,一本大道香蕉大久在红桃,999久久久免费精品国产色夜,色悠悠久久综合88,亚洲国产精品久久无套麻豆,亚洲香蕉毛片久久网站,一本一道久久综合狠狠老

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Kubernetes Pod調度策略原理與落地指南

馬哥Linux運維 ? 來源:馬哥Linux運維 ? 2026-02-27 11:08 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

一、概述

1.1 背景介紹

Pod調度是Kubernetes的核心機制之一,決定了Pod最終運行在哪個節點上。默認調度器kube-scheduler通過一系列預選(Filtering)和優選(Scoring)算法完成調度決策,但默認行為在生產環境中往往不夠用。

實際場景中經常遇到的問題:數據庫Pod被調度到了沒有SSD的節點上,導致IO性能差;兩個高負載服務的Pod被調度到同一個節點,互相搶資源;GPU節點上跑了一堆普通業務Pod,真正需要GPU的任務反而調度不上去。

這些問題都需要通過調度策略來解決。Kubernetes提供了nodeSelector、nodeAffinity、podAffinity/podAntiAffinity、taints/tolerations、topologySpreadConstraints等多種調度機制,本文逐一講解并給出生產環境的配置方案。

1.2 技術特點

多層調度控制:從簡單的nodeSelector到復雜的自定義調度器,提供不同粒度的調度控制能力

軟硬約束結合:requiredDuringScheduling是硬約束(不滿足就不調度),preferredDuringScheduling是軟約束(盡量滿足,不滿足也能調度)

拓撲感知:topologySpreadConstraints支持按zone、node、rack等拓撲域分散Pod,實現跨故障域部署

搶占機制:PriorityClass支持高優先級Pod搶占低優先級Pod的資源

1.3 適用場景

場景一:將數據庫、緩存等IO密集型Pod調度到SSD節點,計算密集型Pod調度到高CPU節點

場景二:同一服務的多個副本分散到不同節點/可用區,避免單點故障導致服務全部不可用

場景三:GPU、FPGA等特殊硬件資源的獨占調度,防止普通Pod占用專用資源

場景四:多租戶集群中,不同團隊的Pod隔離到各自的節點池

1.4 環境要求

組件 版本要求 說明
Kubernetes 1.24+ topologySpreadConstraints在1.19 GA,PodSecurity在1.25 GA
kube-scheduler 與集群版本一致 自定義調度器需要單獨部署
節點標簽 提前規劃 調度策略依賴節點標簽,需要統一標簽規范
metrics-server 0.6+ 資源感知調度需要metrics數據

二、詳細步驟

2.1 準備工作

2.1.1 節點標簽規劃

調度策略的基礎是節點標簽,先把標簽體系規劃好:

# 查看現有節點標簽
kubectl get nodes --show-labels

# 按硬件類型打標簽
kubectl label node k8s-worker-01 disktype=ssd
kubectl label node k8s-worker-02 disktype=ssd
kubectl label node k8s-worker-03 disktype=hdd

# 按業務用途打標簽
kubectl label node k8s-worker-01 workload-type=database
kubectl label node k8s-worker-02 workload-type=application
kubectl label node k8s-worker-03 workload-type=application

# 按可用區打標簽(如果是多機房部署)
kubectl label node k8s-worker-01 topology.kubernetes.io/zone=zone-a
kubectl label node k8s-worker-02 topology.kubernetes.io/zone=zone-b
kubectl label node k8s-worker-03 topology.kubernetes.io/zone=zone-c

# GPU節點標簽
kubectl label node k8s-gpu-01 accelerator=nvidia-tesla-v100
kubectl label node k8s-gpu-02 accelerator=nvidia-tesla-a100

# 驗證標簽
kubectl get nodes -L disktype,workload-type,topology.kubernetes.io/zone

注意:標簽key的命名要有規范,建議用/格式,如company.com/team=backend。Kubernetes內置標簽用kubernetes.io和k8s.io前綴,自定義標簽不要用這兩個前綴。

2.1.2 理解調度流程

kube-scheduler的調度流程分為兩個階段:

預選(Filtering):過濾掉不滿足條件的節點,比如資源不足、nodeSelector不匹配、taint不容忍等

優選(Scoring):對通過預選的節點打分,選擇得分最高的節點

# 查看scheduler的調度日志(需要提高日志級別)
# 修改/etc/kubernetes/manifests/kube-scheduler.yaml
# 在command中添加 --v=4
# 然后查看日志
kubectl logs -n kube-system kube-scheduler-k8s-master-01 --tail=50

2.1.3 調度策略優先級

多種調度策略同時存在時的生效順序:

nodeName(最高優先級):直接指定節點名,跳過調度器

taints/tolerations:節點污點過濾,不容忍的Pod直接排除

nodeSelector:簡單的標簽匹配過濾

nodeAffinity:更靈活的節點親和性規則

podAffinity/podAntiAffinity:Pod間的親和/反親和

topologySpreadConstraints:拓撲分散約束

資源請求:節點剩余資源是否滿足Pod的requests

2.2 核心配置

2.2.1 nodeSelector(最簡單的調度約束)

nodeSelector是最基礎的調度方式,通過標簽鍵值對匹配節點:

# 文件:nginx-nodeselector.yaml
apiVersion:apps/v1
kind:Deployment
metadata:
name:nginx-ssd
namespace:default
spec:
replicas:3
selector:
 matchLabels:
  app:nginx-ssd
template:
 metadata:
  labels:
   app:nginx-ssd
 spec:
  nodeSelector:
   disktype:ssd
  containers:
   -name:nginx
    image:nginx:1.24
    resources:
     requests:
      cpu:100m
      memory:128Mi
     limits:
      cpu:200m
      memory:256Mi
kubectl apply -f nginx-nodeselector.yaml

# 驗證Pod只調度到了ssd節點
kubectl get pods -l app=nginx-ssd -o wide

注意:nodeSelector是硬約束,如果沒有節點匹配標簽,Pod會一直Pending。生產環境建議配合nodeAffinity的軟約束使用。

2.2.2 nodeAffinity(節點親和性)

nodeAffinity比nodeSelector更靈活,支持多種操作符和軟硬約束:

# 文件:app-node-affinity.yaml
apiVersion:apps/v1
kind:Deployment
metadata:
name:app-with-affinity
namespace:default
spec:
replicas:6
selector:
 matchLabels:
  app:app-affinity
template:
 metadata:
  labels:
   app:app-affinity
 spec:
  affinity:
   nodeAffinity:
    # 硬約束:必須調度到zone-a或zone-b
    requiredDuringSchedulingIgnoredDuringExecution:
     nodeSelectorTerms:
      -matchExpressions:
        -key:topology.kubernetes.io/zone
         operator:In
         values:
          -zone-a
          -zone-b
    # 軟約束:優先調度到ssd節點,權重1-100
    preferredDuringSchedulingIgnoredDuringExecution:
     -weight:80
      preference:
       matchExpressions:
        -key:disktype
         operator:In
         values:
          -ssd
     -weight:20
      preference:
       matchExpressions:
        -key:workload-type
         operator:In
         values:
          -application
  containers:
   -name:app
    image:nginx:1.24
    resources:
     requests:
      cpu:200m
      memory:256Mi

操作符說明

In:標簽值在列表中

NotIn:標簽值不在列表中

Exists:標簽存在(不關心值)

DoesNotExist:標簽不存在

Gt:標簽值大于指定值(僅限數字)

Lt:標簽值小于指定值(僅限數字)

注意:requiredDuringSchedulingIgnoredDuringExecution中的IgnoredDuringExecution表示Pod已經運行后,即使節點標簽變了也不會驅逐Pod。Kubernetes計劃實現RequiredDuringExecution但目前還沒有。

2.2.3 podAffinity和podAntiAffinity(Pod間親和/反親和)

控制Pod之間的調度關系,典型場景:Web應用和緩存部署在同一節點減少網絡延遲,同一服務的多個副本分散到不同節點。

# 文件:web-cache-affinity.yaml
apiVersion:apps/v1
kind:Deployment
metadata:
name:web-frontend
namespace:default
spec:
replicas:3
selector:
 matchLabels:
  app:web-frontend
template:
 metadata:
  labels:
   app:web-frontend
 spec:
  affinity:
   # Pod親和:和redis-cache部署在同一節點
   podAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
     -weight:100
      podAffinityTerm:
       labelSelector:
        matchExpressions:
         -key:app
          operator:In
          values:
           -redis-cache
       topologyKey:kubernetes.io/hostname
   # Pod反親和:同一服務的副本分散到不同節點
   podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
     -labelSelector:
       matchExpressions:
        -key:app
         operator:In
         values:
          -web-frontend
      topologyKey:kubernetes.io/hostname
  containers:
   -name:web
    image:nginx:1.24
    resources:
     requests:
      cpu:200m
      memory:256Mi

說明

topologyKey: kubernetes.io/hostname表示以節點為拓撲域,同一節點上的Pod視為同一拓撲域

topologyKey: topology.kubernetes.io/zone表示以可用區為拓撲域

podAntiAffinity的硬約束會限制副本數不能超過節點數,3個副本至少需要3個節點

警告:podAffinity/podAntiAffinity的計算復雜度是O(N^2),N是集群中的Pod數量。在Pod數量超過5000的大集群中,大量使用podAffinity會導致調度延遲從毫秒級上升到秒級。

2.2.4 Taints和Tolerations(污點和容忍)

Taints從節點角度排斥Pod,Tolerations從Pod角度容忍污點。兩者配合實現節點專用化。

# 給GPU節點添加污點,只允許GPU任務調度
kubectl taint nodes k8s-gpu-01 gpu=true:NoSchedule
kubectl taint nodes k8s-gpu-02 gpu=true:NoSchedule

# 給維護中的節點添加NoExecute污點,驅逐現有Pod
kubectl taint nodes k8s-worker-03 maintenance=true:NoExecute

# 查看節點污點
kubectl describe node k8s-gpu-01 | grep -A 5 Taints

# 刪除污點
kubectl taint nodes k8s-worker-03 maintenance=true:NoExecute-

Pod中配置Tolerations:

# 文件:gpu-job.yaml
apiVersion:batch/v1
kind:Job
metadata:
name:gpu-training-job
namespace:ml-training
spec:
template:
 spec:
  tolerations:
   # 容忍gpu污點
   -key:"gpu"
    operator:"Equal"
    value:"true"
    effect:"NoSchedule"
  nodeSelector:
   accelerator:nvidia-tesla-v100
  containers:
   -name:training
    image:pytorch/pytorch:2.0.1-cuda11.7-cudnn8-runtime
    resources:
     limits:
      nvidia.com/gpu:1
     requests:
      cpu:"4"
      memory:"16Gi"
  restartPolicy:Never

Taint Effect說明

NoSchedule:新Pod不會調度到該節點,已有Pod不受影響

PreferNoSchedule:盡量不調度,但資源不足時仍可調度

NoExecute:新Pod不調度,已有Pod如果不容忍會被驅逐??梢栽O置tolerationSeconds指定驅逐前的等待時間

# 容忍NoExecute污點,但最多等待300秒后被驅逐
tolerations:
-key:"maintenance"
 operator:"Equal"
 value:"true"
 effect:"NoExecute"
 tolerationSeconds:300

2.2.5 topologySpreadConstraints(拓撲分散約束)

1.19版本GA的功能,比podAntiAffinity更精細地控制Pod在拓撲域間的分布:

# 文件:app-topology-spread.yaml
apiVersion:apps/v1
kind:Deployment
metadata:
name:app-spread
namespace:default
spec:
replicas:9
selector:
 matchLabels:
  app:app-spread
template:
 metadata:
  labels:
   app:app-spread
 spec:
  topologySpreadConstraints:
   # 跨可用區均勻分布,最大偏差1
   -maxSkew:1
    topologyKey:topology.kubernetes.io/zone
    whenUnsatisfiable:DoNotSchedule
    labelSelector:
     matchLabels:
      app:app-spread
   # 跨節點均勻分布,最大偏差1,軟約束
   -maxSkew:1
    topologyKey:kubernetes.io/hostname
    whenUnsatisfiable:ScheduleAnyway
    labelSelector:
     matchLabels:
      app:app-spread
  containers:
   -name:app
    image:nginx:1.24
    resources:
     requests:
      cpu:100m
      memory:128Mi

參數說明

maxSkew:拓撲域間Pod數量的最大差值。設為1表示任意兩個域的Pod數量差不超過1

topologyKey:拓撲域的標簽key

whenUnsatisfiable:不滿足約束時的行為,DoNotSchedule(硬約束)或ScheduleAnyway(軟約束)

9個副本在3個zone中的分布結果:zone-a=3, zone-b=3, zone-c=3。如果zone-c只有1個節點且資源不足,DoNotSchedule會導致部分Pod Pending,ScheduleAnyway則會盡量均勻但允許偏差。

2.2.6 PriorityClass(優先級和搶占)

高優先級Pod可以搶占低優先級Pod的資源:

# 定義優先級類
apiVersion:scheduling.k8s.io/v1
kind:PriorityClass
metadata:
name:critical-production
value:1000000
globalDefault:false
preemptionPolicy:PreemptLowerPriority
description:"生產核心服務,可搶占低優先級Pod"
---
apiVersion:scheduling.k8s.io/v1
kind:PriorityClass
metadata:
name:normal-production
value:500000
globalDefault:true
preemptionPolicy:PreemptLowerPriority
description:"普通生產服務"
---
apiVersion:scheduling.k8s.io/v1
kind:PriorityClass
metadata:
name:batch-job
value:100000
globalDefault:false
preemptionPolicy:Never
description:"批處理任務,不搶占其他Pod"

在Pod中引用:

apiVersion:apps/v1
kind:Deployment
metadata:
name:core-api
spec:
replicas:3
selector:
 matchLabels:
  app:core-api
template:
 metadata:
  labels:
   app:core-api
 spec:
  priorityClassName:critical-production
  containers:
   -name:api
    image:myapp:v1.0
    resources:
     requests:
      cpu:"1"
      memory:"2Gi"

警告:preemptionPolicy: PreemptLowerPriority會驅逐低優先級Pod來騰出資源,被驅逐的Pod會收到SIGTERM信號。確保應用能正確處理優雅關閉,否則會丟數據。生產環境建議批處理任務設置preemptionPolicy: Never。

2.3 啟動和驗證

2.3.1 驗證調度結果

# 查看Pod調度到了哪個節點
kubectl get pods -o wide -l app=app-spread

# 查看Pod的調度事件
kubectl describe pod  | grep -A 10 Events

# 查看調度失敗的原因
kubectl get events --field-selector reason=FailedScheduling -A

# 查看節點資源分配情況
kubectl describe node k8s-worker-01 | grep -A 20"Allocated resources"

2.3.2 調度模擬測試

# 使用kubectl創建一個dry-run的Pod,查看是否能調度成功
kubectl runtest-schedule --image=nginx:1.24 --dry-run=server -o yaml 
 --overrides='{
  "spec": {
   "nodeSelector": {"disktype": "ssd"},
   "containers": [{"name": "test", "image": "nginx:1.24", "resources": {"requests": {"cpu": "100m", "memory": "128Mi"}}}]
  }
 }'

# 查看各節點的可分配資源
kubectl get nodes -o custom-columns=
NAME:.metadata.name,
CPU_ALLOC:.status.allocatable.cpu,
MEM_ALLOC:.status.allocatable.memory,
PODS_ALLOC:.status.allocatable.pods

2.3.3 驗證拓撲分散

# 查看Pod在各zone的分布
kubectl get pods -l app=app-spread -o custom-columns=
NAME:.metadata.name,
NODE:.spec.nodeName,
ZONE:.spec.nodeName

# 更精確的方式:通過節點標簽查看
forpodin$(kubectl get pods -l app=app-spread -o jsonpath='{.items[*].spec.nodeName}');do
 zone=$(kubectl get node$pod-o jsonpath='{.metadata.labels.topology.kubernetes.io/zone}')
echo"Node:$pod, Zone:$zone"
done

三、示例代碼和配置

3.1 完整配置示例

3.1.1 多租戶節點池隔離方案

生產環境中不同團隊共用一個集群,通過taint+nodeAffinity實現節點池隔離:

# 文件:namespace-resource-setup.yaml
# 第一步:創建團隊namespace
apiVersion:v1
kind:Namespace
metadata:
name:team-backend
labels:
 team:backend
---
apiVersion:v1
kind:Namespace
metadata:
name:team-data
labels:
 team:data
---
# 第二步:為每個團隊設置ResourceQuota
apiVersion:v1
kind:ResourceQuota
metadata:
name:backend-quota
namespace:team-backend
spec:
hard:
 requests.cpu:"20"
 requests.memory:40Gi
 limits.cpu:"40"
 limits.memory:80Gi
 pods:"100"
---
apiVersion:v1
kind:ResourceQuota
metadata:
name:data-quota
namespace:team-data
spec:
hard:
 requests.cpu:"40"
 requests.memory:80Gi
 limits.cpu:"80"
 limits.memory:160Gi
 pods:"200"

節點打標簽和污點:

# backend團隊節點池
kubectl label node k8s-worker-{01..05} node-pool=backend
kubectl taint nodes k8s-worker-{01..05} node-pool=backend:NoSchedule

# data團隊節點池
kubectl label node k8s-worker-{06..10} node-pool=data
kubectl taint nodes k8s-worker-{06..10} node-pool=data:NoSchedule

# 公共節點池(不加污點,所有Pod都能調度)
kubectl label node k8s-worker-{11..15} node-pool=shared

團隊Deployment模板:

# 文件:backend-app-template.yaml
apiVersion:apps/v1
kind:Deployment
metadata:
name:backend-api
namespace:team-backend
spec:
replicas:5
selector:
 matchLabels:
  app:backend-api
template:
 metadata:
  labels:
   app:backend-api
 spec:
  tolerations:
   -key:"node-pool"
    operator:"Equal"
    value:"backend"
    effect:"NoSchedule"
  affinity:
   nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
     nodeSelectorTerms:
      -matchExpressions:
        -key:node-pool
         operator:In
         values:
          -backend
          -shared
   podAntiAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
     -weight:100
      podAffinityTerm:
       labelSelector:
        matchExpressions:
         -key:app
          operator:In
          values:
           -backend-api
       topologyKey:kubernetes.io/hostname
  containers:
   -name:api
    image:backend-api:v2.1.0
    ports:
     -containerPort:8080
    resources:
     requests:
      cpu:500m
      memory:512Mi
     limits:
      cpu:"1"
      memory:1Gi
    readinessProbe:
     httpGet:
      path:/health
      port:8080
     initialDelaySeconds:10
     periodSeconds:5
    livenessProbe:
     httpGet:
      path:/health
      port:8080
     initialDelaySeconds:30
     periodSeconds:10

3.1.2 調度策略自動注入腳本

通過Kyverno策略自動為特定namespace的Pod注入調度規則,避免每個Deployment都手動配置:

# 文件:kyverno-scheduling-policy.yaml
apiVersion:kyverno.io/v1
kind:ClusterPolicy
metadata:
name:inject-node-affinity-backend
spec:
rules:
 -name:add-backend-scheduling
  match:
   any:
    -resources:
      kinds:
       -Pod
      namespaces:
       -team-backend
  mutate:
   patchStrategicMerge:
    spec:
     tolerations:
      -key:"node-pool"
       operator:"Equal"
       value:"backend"
       effect:"NoSchedule"
     affinity:
      nodeAffinity:
       requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
         -matchExpressions:
           -key:node-pool
            operator:In
            values:
             -backend
             -shared

注意:Kyverno需要單獨安裝(helm install kyverno kyverno/kyverno -n kyverno --create-namespace)。這種方式比在每個Deployment里寫調度規則更易維護,團隊只需要關注業務配置,調度策略由平臺團隊統一管理。

3.2 實際應用案例

案例一:數據庫Pod的調度策略

場景描述:MySQL主從集群部署在K8s中,主庫需要SSD+高內存節點,從庫可以用普通節點。主從Pod不能在同一節點上,避免節點故障導致主從同時不可用。

實現代碼

# 文件:mysql-master-statefulset.yaml
apiVersion:apps/v1
kind:StatefulSet
metadata:
name:mysql-master
namespace:database
spec:
serviceName:mysql-master
replicas:1
selector:
 matchLabels:
  app:mysql
  role:master
template:
 metadata:
  labels:
   app:mysql
   role:master
 spec:
  priorityClassName:critical-production
  affinity:
   nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
     nodeSelectorTerms:
      -matchExpressions:
        -key:disktype
         operator:In
         values:
          -ssd
        -key:workload-type
         operator:In
         values:
          -database
   podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
     -labelSelector:
       matchExpressions:
        -key:app
         operator:In
         values:
          -mysql
      topologyKey:kubernetes.io/hostname
  containers:
   -name:mysql
    image:mysql:8.0.35
    ports:
     -containerPort:3306
    env:
     -name:MYSQL_ROOT_PASSWORD
      valueFrom:
       secretKeyRef:
        name:mysql-secret
        key:root-password
    resources:
     requests:
      cpu:"2"
      memory:4Gi
     limits:
      cpu:"4"
      memory:8Gi
    volumeMounts:
     -name:mysql-data
      mountPath:/var/lib/mysql
volumeClaimTemplates:
 -metadata:
   name:mysql-data
  spec:
   accessModes:["ReadWriteOnce"]
   storageClassName:local-ssd
   resources:
    requests:
     storage:100Gi
---
# MySQL從庫
apiVersion:apps/v1
kind:StatefulSet
metadata:
name:mysql-slave
namespace:database
spec:
serviceName:mysql-slave
replicas:2
selector:
 matchLabels:
  app:mysql
  role:slave
template:
 metadata:
  labels:
   app:mysql
   role:slave
 spec:
  priorityClassName:normal-production
  affinity:
   podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
     -labelSelector:
       matchExpressions:
        -key:app
         operator:In
         values:
          -mysql
      topologyKey:kubernetes.io/hostname
  topologySpreadConstraints:
   -maxSkew:1
    topologyKey:topology.kubernetes.io/zone
    whenUnsatisfiable:DoNotSchedule
    labelSelector:
     matchLabels:
      app:mysql
      role:slave
  containers:
   -name:mysql
    image:mysql:8.0.35
    ports:
     -containerPort:3306
    resources:
     requests:
      cpu:"1"
      memory:2Gi
     limits:
      cpu:"2"
      memory:4Gi
    volumeMounts:
     -name:mysql-data
      mountPath:/var/lib/mysql
volumeClaimTemplates:
 -metadata:
   name:mysql-data
  spec:
   accessModes:["ReadWriteOnce"]
   storageClassName:standard
   resources:
    requests:
     storage:100Gi

運行結果

NAME       READY  STATUS  NODE      ZONE
mysql-master-0  1/1   Running  k8s-worker-01  zone-a  (SSD+database節點)
mysql-slave-0  1/1   Running  k8s-worker-02  zone-b  (不同節點不同zone)
mysql-slave-1  1/1   Running  k8s-worker-03  zone-c  (不同節點不同zone)

案例二:混合調度策略實現灰度發布

場景描述:灰度發布時,新版本Pod先調度到特定的灰度節點,驗證通過后再擴展到所有節點。通過標簽和調度策略控制灰度范圍。

實現步驟

給灰度節點打標簽:

kubectl label node k8s-worker-01 canary=true

灰度版本Deployment:

# 文件:app-canary.yaml
apiVersion:apps/v1
kind:Deployment
metadata:
name:myapp-canary
namespace:production
spec:
replicas:2
selector:
 matchLabels:
  app:myapp
  version:canary
template:
 metadata:
  labels:
   app:myapp
   version:canary
 spec:
  affinity:
   nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
     nodeSelectorTerms:
      -matchExpressions:
        -key:canary
         operator:In
         values:
          -"true"
  containers:
   -name:myapp
    image:myapp:v2.0.0-rc1
    ports:
     -containerPort:8080
    resources:
     requests:
      cpu:200m
      memory:256Mi

灰度驗證通過后,全量發布:

# 移除灰度節點約束,更新穩定版Deployment的鏡像
kubectlsetimage deployment/myapp-stable myapp=myapp:v2.0.0 -n production

# 縮容灰度Deployment
kubectl scale deployment/myapp-canary --replicas=0 -n production

# 清理灰度標簽
kubectl label node k8s-worker-01 canary-

四、最佳實踐和注意事項

4.1 最佳實踐

4.1.1 性能優化

減少podAffinity的使用范圍:podAffinity/podAntiAffinity的調度計算復雜度高,在500+節點集群中,一個帶podAffinity的Pod調度耗時從5ms增加到200ms。能用topologySpreadConstraints替代的場景優先用topologySpreadConstraints。

# 不推薦:用podAntiAffinity實現分散
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
 -labelSelector:
   matchLabels:
    app:myapp
  topologyKey:kubernetes.io/hostname

# 推薦:用topologySpreadConstraints替代
topologySpreadConstraints:
-maxSkew:1
 topologyKey:kubernetes.io/hostname
 whenUnsatisfiable:DoNotSchedule
 labelSelector:
  matchLabels:
   app:myapp

合理設置resource requests:調度器根據requests而非limits做調度決策。requests設太高導致節點利用率低(實測平均CPU利用率只有15%),設太低導致節點超賣嚴重,Pod被OOMKill。建議requests設為實際使用量的P95值。

# 查看Pod實際資源使用,作為requests設置參考
kubectl top pods -n production --sort-by=cpu
kubectl top pods -n production --sort-by=memory

使用Descheduler重平衡:節點擴容或Pod漂移后,集群負載可能不均衡。Descheduler可以驅逐不符合當前調度策略的Pod,讓調度器重新調度。

# 安裝Descheduler
helm repo add descheduler https://kubernetes-sigs.github.io/descheduler/
helm install descheduler descheduler/descheduler -n kube-system 
 --setschedule="*/5 * * * *"

4.1.2 安全加固

限制nodeName直接指定:nodeName會跳過調度器的所有檢查(包括資源檢查),生產環境通過RBAC限制普通用戶使用nodeName字段。

# OPA/Gatekeeper策略:禁止使用nodeName
apiVersion:constraints.gatekeeper.sh/v1beta1
kind:K8sDenyNodeName
metadata:
name:deny-nodename
spec:
match:
 kinds:
  -apiGroups:[""]
   kinds:["Pod"]
 excludedNamespaces:["kube-system"]

PriorityClass權限控制:高優先級PriorityClass的創建和使用需要限制,防止普通用戶創建高優先級Pod搶占核心服務資源。

# RBAC:只允許admin使用critical-production優先級
apiVersion:rbac.authorization.k8s.io/v1
kind:ClusterRole
metadata:
name:use-critical-priority
rules:
-apiGroups:["scheduling.k8s.io"]
 resources:["priorityclasses"]
 resourceNames:["critical-production"]
 verbs:["get","list"]

節點污點防篡改:關鍵節點(如GPU節點)的污點被誤刪會導致普通Pod涌入。通過準入控制webhook攔截對特定節點taint的修改操作。

4.1.3 高可用配置

HA方案一:核心服務至少3副本,配合podAntiAffinity硬約束分散到不同節點,再用topologySpreadConstraints分散到不同可用區

HA方案二:使用PodDisruptionBudget(PDB)限制同時不可用的Pod數量,防止節點維護時服務中斷

apiVersion:policy/v1
kind:PodDisruptionBudget
metadata:
name:myapp-pdb
spec:
minAvailable:2
selector:
 matchLabels:
  app:myapp

備份策略:調度策略配置(PriorityClass、節點標簽、污點)納入GitOps管理,用ArgoCD或FluxCD同步

4.2 注意事項

4.2.1 配置注意事項

警告:調度策略配置錯誤可能導致Pod無法調度或調度到錯誤節點,修改前在測試環境驗證。

注意nodeAffinity的requiredDuringSchedulingIgnoredDuringExecution中,多個nodeSelectorTerms之間是OR關系,同一個nodeSelectorTerm中的多個matchExpressions之間是AND關系。搞混了會導致調度結果不符合預期。

注意podAntiAffinity硬約束會限制副本數上限。如果topologyKey是kubernetes.io/hostname,副本數不能超過可用節點數,否則多出來的Pod永遠Pending。

注意topologySpreadConstraints的labelSelector必須和Pod自身的標簽匹配,否則約束不生效。這個錯誤很隱蔽,Pod能正常調度但分布不均勻。

4.2.2 常見錯誤

錯誤現象 原因分析 解決方案
Pod一直Pending,事件顯示0/6 nodes are available nodeSelector或nodeAffinity沒有匹配的節點 檢查節點標簽是否正確:kubectl get nodes --show-labels
Pod調度成功但分布不均勻 topologySpreadConstraints的labelSelector寫錯 確認labelSelector和Pod的labels完全一致
高優先級Pod搶占后,被搶占的Pod無法重新調度 集群資源不足,被搶占的Pod也找不到合適節點 擴容節點或降低resource requests
taint添加后已有Pod沒被驅逐 使用了NoSchedule而非NoExecute NoSchedule只影響新Pod,要驅逐已有Pod用NoExecute
節點維護drain失敗 Pod有PDB限制,minAvailable不滿足 先擴容副本數,或臨時調整PDB的minAvailable

4.2.3 兼容性問題

版本兼容:topologySpreadConstraints在1.18 Beta、1.19 GA;minDomains字段在1.25 Beta,使用前確認集群版本

平臺兼容:云廠商托管K8s通常自動設置topology.kubernetes.io/zone標簽,自建集群需要手動設置

組件依賴:Descheduler版本需要和K8s版本匹配,0.27.x支持K8s 1.25-1.28

五、故障排查和監控

5.1 故障排查

5.1.1 日志查看

# 查看kube-scheduler日志
kubectl logs -n kube-system -l component=kube-scheduler --tail=100

# 查看調度事件
kubectl get events -A --field-selector reason=FailedScheduling --sort-by='.lastTimestamp'

# 查看特定Pod的調度事件
kubectl describe pod  -n  | grep -A 20 Events

# 查看節點資源分配詳情
kubectl describe node  | grep -A 30"Allocated resources"

5.1.2 常見問題排查

問題一:Pod Pending,報Insufficient cpu

# 診斷命令
kubectl describe pod  | grep -A 5"Events"
kubectl get nodes -o custom-columns=NAME:.metadata.name,CPU_REQ:.status.allocatable.cpu,MEM_REQ:.status.allocatable.memory

# 查看各節點已分配資源
fornodein$(kubectl get nodes -o jsonpath='{.items[*].metadata.name}');do
echo"===$node==="
 kubectl describe node$node| grep -A 5"Allocated resources"
done

解決方案

檢查Pod的resource requests是否設置過高

檢查節點是否有足夠的可分配資源(allocatable - 已分配)

考慮擴容節點或優化現有Pod的資源配置

問題二:調度策略不生效,Pod沒有按預期分布

# 診斷命令
kubectl get pod  -o yaml | grep -A 50"affinity"
kubectl get pod  -o yaml | grep -A 20"topologySpreadConstraints"

# 檢查節點標簽是否正確
kubectl get nodes --show-labels | grep 

解決方案

確認YAML縮進正確,affinity字段層級關系容易寫錯

確認labelSelector和Pod標簽一致

用kubectl apply --dry-run=server驗證YAML語法

問題三:節點drain卡住不動

癥狀:kubectl drain命令長時間無響應

排查

# 查看哪些Pod阻止了drain
kubectl drain  --ignore-daemonsets --delete-emptydir-data --dry-run=client

# 檢查PDB
kubectl get pdb -A

解決

DaemonSet的Pod加--ignore-daemonsets跳過

使用emptyDir的Pod加--delete-emptydir-data

PDB限制導致的,先擴容副本再drain

有Pod設置了terminationGracePeriodSeconds很長,加--timeout=300s限制等待時間

5.1.3 調試模式

# 提高scheduler日志級別
# 編輯/etc/kubernetes/manifests/kube-scheduler.yaml
# 在command中添加 --v=10(最詳細,僅調試用)

# 查看scheduler的調度決策過程
kubectl logs -n kube-system kube-scheduler-k8s-master-01 | grep"pod-name"

# 使用kubectl-scheduler-simulator模擬調度(需要單獨安裝)
# https://github.com/kubernetes-sigs/kube-scheduler-simulator

# 查看scheduler的metrics
kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes

5.2 性能監控

5.2.1 關鍵指標監控

# 調度延遲
kubectl get --raw /metrics | grep scheduler_scheduling_algorithm_duration_seconds

# 調度隊列長度
kubectl get --raw /metrics | grep scheduler_pending_pods

# 搶占次數
kubectl get --raw /metrics | grep scheduler_preemption_victims

# 節點資源使用率
kubectl top nodes

5.2.2 監控指標說明

指標名稱 正常范圍 告警閾值 說明
調度延遲(P99) <100ms >500ms 超過500ms說明調度器過載或策略過于復雜
Pending Pod數量 0 >10持續5分鐘 持續有Pod無法調度需要排查
調度失敗率 <1% >5% 高失敗率說明資源不足或策略配置有問題
節點CPU分配率 40%-70% >85% 分配率過高會導致新Pod無法調度
節點內存分配率 50%-80% >90% 接近100%時需要擴容
搶占事件數 0 >5次/小時 頻繁搶占說明資源規劃不合理

5.2.3 Prometheus監控規則

# 文件:scheduler-alerts.yaml
apiVersion:monitoring.coreos.com/v1
kind:PrometheusRule
metadata:
name:scheduler-alerts
namespace:monitoring
spec:
groups:
 -name:kube-scheduler
  rules:
   -alert:SchedulerHighLatency
    expr:|
      histogram_quantile(0.99,
       sum(rate(scheduler_scheduling_algorithm_duration_seconds_bucket[5m])) by (le)
      ) > 0.5
    for:10m
    labels:
     severity:warning
    annotations:
     summary:"Scheduler P99 latency exceeds 500ms"

   -alert:PodsPendingTooLong
    expr:|
      sum(scheduler_pending_pods{queue="active"}) > 10
    for:5m
    labels:
     severity:warning
    annotations:
     summary:"More than 10 pods pending for over 5 minutes"

   -alert:SchedulerUnhealthy
    expr:absent(up{job="kube-scheduler"}==1)
    for:3m
    labels:
     severity:critical
    annotations:
     summary:"kube-scheduler is not running"

   -alert:NodeHighAllocation
    expr:|
      (1 - sum(kube_node_status_allocatable{resource="cpu"} - kube_pod_container_resource_requests{resource="cpu"}) by (node)
      / sum(kube_node_status_allocatable{resource="cpu"}) by (node)) > 0.85
    for:10m
    labels:
     severity:warning
    annotations:
     summary:"Node{{ $labels.node }}CPU allocation exceeds 85%"

   -alert:FrequentPreemption
    expr:|
      increase(scheduler_preemption_victims[1h]) > 5
    for:5m
    labels:
     severity:warning
    annotations:
     summary:"More than 5 preemption events in the last hour"

5.3 備份與恢復

5.3.1 備份策略

#!/bin/bash
# 調度策略配置備份腳本
# 文件:/opt/scripts/scheduling-config-backup.sh

set-euo pipefail

BACKUP_DIR="/data/scheduling-backup/$(date +%Y%m%d)"
mkdir -p"${BACKUP_DIR}"

# 備份PriorityClass
kubectl get priorityclass -o yaml >"${BACKUP_DIR}/priorityclasses.yaml"

# 備份PDB
kubectl get pdb -A -o yaml >"${BACKUP_DIR}/pdbs.yaml"

# 備份節點標簽和污點
fornodein$(kubectl get nodes -o jsonpath='{.items[*].metadata.name}');do
 kubectl get node"$node"-o jsonpath='{.metadata.labels}'>"${BACKUP_DIR}/${node}-labels.json"
 kubectl get node"$node"-o jsonpath='{.spec.taints}'>"${BACKUP_DIR}/${node}-taints.json"
done

# 備份Kyverno策略(如果使用)
kubectl get clusterpolicy -o yaml >"${BACKUP_DIR}/kyverno-policies.yaml"2>/dev/null ||true

echo"[$(date)] Scheduling config backup completed:${BACKUP_DIR}"

5.3.2 恢復流程

停止服務:暫停業務部署,避免恢復過程中的調度沖突

恢復數據:kubectl apply -f ${BACKUP_DIR}/priorityclasses.yaml

驗證完整性:kubectl get priorityclass確認PriorityClass恢復

恢復節點配置:逐節點恢復標簽和污點

驗證調度:創建測試Pod驗證調度策略是否生效

六、總結

6.1 技術要點回顧

要點一:nodeSelector適合簡單場景,nodeAffinity適合需要軟硬約束結合的場景,兩者都基于節點標簽,標簽規劃是基礎

要點二:podAntiAffinity硬約束會限制副本數不超過節點數,大規模集群中計算開銷大,優先用topologySpreadConstraints替代

要點三:taints/tolerations從節點角度控制調度,適合節點專用化場景(GPU節點、數據庫節點),NoExecute會驅逐已有Pod

要點四:PriorityClass的搶占機制要謹慎使用,批處理任務設置preemptionPolicy: Never,核心服務設置高優先級

要點五:topologySpreadConstraints是生產環境跨故障域部署的首選方案,maxSkew: 1配合DoNotSchedule保證嚴格均勻分布

6.2 進階學習方向

自定義調度器:當內置調度策略無法滿足需求時,可以開發自定義調度器,通過Scheduling Framework擴展點實現

學習資源:Scheduling Framework

實踐建議:先用調度器擴展(Extender)驗證邏輯,再考慮寫Framework插件

Descheduler深度使用:配置LowNodeUtilization、RemoveDuplicates等策略,自動重平衡集群負載

學習資源:Descheduler GitHub

實踐建議:先在非生產環境測試Descheduler策略,避免誤驅逐核心服務

Volcano批調度器:針對AI/大數據場景的批調度器,支持Gang Scheduling(一組Pod要么全部調度成功,要么全部不調度)

6.3 參考資料

Kubernetes調度官方文檔 - 調度機制全面說明

kube-scheduler源碼 - 理解調度算法實現

Descheduler項目 - Pod重調度工具

Volcano項目 - 批調度器

附錄

A. 命令速查表

# 節點標簽管理
kubectl label node  key=value       # 添加標簽
kubectl label node  key=value --overwrite # 修改標簽
kubectl label node  key-          # 刪除標簽
kubectl get nodes --show-labels         # 查看所有標簽
kubectl get nodes -L key1,key2          # 查看指定標簽列

# 污點管理
kubectl taint nodes  key=value:NoSchedule # 添加污點
kubectl taint nodes  key=value:NoSchedule- # 刪除污點
kubectl taint nodes  key-         # 刪除指定key的所有污點
kubectl describe node  | grep Taints    # 查看污點

# 調度排查
kubectl get events --field-selector reason=FailedScheduling -A # 調度失敗事件
kubectl describe pod  | grep -A 10 Events  # Pod事件
kubectl get pods -o wide             # 查看Pod所在節點
kubectl top nodes                # 節點資源使用
kubectl describe node  | grep -A 20"Allocated resources"# 已分配資源

# 節點維護
kubectl drain  --ignore-daemonsets --delete-emptydir-data # 騰空節點
kubectl uncordon              # 恢復調度
kubectl cordon               # 標記不可調度

B. 配置參數詳解

nodeAffinity操作符

操作符 含義 示例
In 值在列表中 values: ["ssd", "nvme"]
NotIn 值不在列表中 values: ["hdd"]
Exists 標簽存在 不需要values字段
DoesNotExist 標簽不存在 不需要values字段
Gt 值大于 values: ["100"] (數字字符串)
Lt 值小于 values: ["50"] (數字字符串)

Taint Effect對比

Effect 對新Pod 對已有Pod 適用場景
NoSchedule 不調度 不影響 節點專用化
PreferNoSchedule 盡量不調度 不影響 軟限制
NoExecute 不調度 驅逐 節點維護、故障隔離

topologySpreadConstraints參數

參數 類型 說明
maxSkew int 拓撲域間Pod數量最大差值,1表示嚴格均勻
topologyKey string 拓撲域標簽key
whenUnsatisfiable string DoNotSchedule 硬約束 /ScheduleAnyway軟約束
labelSelector object 匹配Pod的標簽選擇器
minDomains int 最少拓撲域數量(1.25 Beta)
matchLabelKeys []string 用于區分不同版本的Pod(1.27 Beta)

C. 術語表

術語 英文 解釋
預選 Filtering 調度第一階段,過濾不滿足條件的節點
優選 Scoring 調度第二階段,對候選節點打分排序
親和性 Affinity Pod傾向于調度到滿足條件的節點或Pod附近
反親和性 Anti-Affinity Pod傾向于遠離滿足條件的Pod
污點 Taint 節點上的排斥標記,阻止不容忍的Pod調度
容忍 Toleration Pod上的聲明,表示可以容忍節點的污點
拓撲域 Topology Domain 按標簽劃分的節點組,如同一可用區的節點
搶占 Preemption 高優先級Pod驅逐低優先級Pod以獲取資源
PDB PodDisruptionBudget Pod中斷預算,限制同時不可用的Pod數量

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • gpu
    gpu
    +關注

    關注

    28

    文章

    5229

    瀏覽量

    135861
  • 數據庫
    +關注

    關注

    7

    文章

    4065

    瀏覽量

    68453
  • kubernetes
    +關注

    關注

    0

    文章

    270

    瀏覽量

    9516

原文標題:別再讓 Pod “亂跑”:Kubernetes 調度策略原理與落地指南

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    Kubernetes的Device Plugin設計解讀

    設計解讀最近在調研Kubernetes的GPU調度和運行機制,發現傳統的alpha.kubernetes.io/nvidia-gpu即將在1.11版本中下線,和GPU相關的調度和部署的
    發表于 03-12 16:23

    阿里云容器Kubernetes監控(二) - 使用Grafana展現Pod監控數據

    kubernetes中承擔的責任遠不止監控數據的采集,還包括控制臺的監控接口、HPA的POD彈性伸縮等都依賴于Heapster的功能。簡介在kubernetes的監控方案中
    發表于 05-10 15:28

    從零開始入門 K8s| 詳解 Pod 及容器設計模式

    一、為什么需要 Pod容器的基本概念我們知道 PodKubernetes 項目里面一個非常重要的概念,也是非常重要的一個原子調度單位,但是為什么我們會需要這樣一個概念呢?在使用容
    發表于 09-20 15:12

    不吹不黑,今天我們來聊一聊 Kubernetes 落地的三種方式

    Kubernetes 作為自己的基礎設施重心,"一萬個人眼中就有一萬個哈姆雷特",雖說 Kubernetes 是容器管理領域的事實標準,但實際上在不同背景的企業中,Kubernetes
    發表于 10-12 16:07

    Pod資源配置

    Kubernetes進階實戰》第四章《管理Pod資源對象》
    發表于 10-22 14:39

    基于5G邊緣計算的資源調度策略Kubernetes

    的權重值計算Node優先級作為調度依據,該機制無法適應邊緣計算場景下精細化的資源調度需求。面向5G邊緣計算的資源調度場景,通過擴展 Kubernetes資源
    發表于 03-10 17:21 ?23次下載
    基于5G邊緣計算的資源<b class='flag-5'>調度</b><b class='flag-5'>策略</b><b class='flag-5'>Kubernetes</b>

    深入研究Kubernetes調度

    “本文從 Pod 和節點的配置開始,介紹了 Kubernetes Scheduler 框架、擴展點、API 以及可能發生的與資源相關的瓶頸,并展示了性能調整設置,涵蓋了 Kubernetes
    的頭像 發表于 08-23 10:39 ?1997次閱讀

    Kubernetes組件pod核心原理

    1. 核心組件原理 —— pod 核心原理 1.1 pod 是什么 pod 也可以理解是一個容器,裝的是 docker 創建的容器,也就是用來封裝容器的一個容器; pod 是一個虛擬化
    的頭像 發表于 09-02 09:27 ?2528次閱讀

    Kubernetes中的Pod簡易理解

    PodKubernetes中非常重要的概念,也是Kubernetes管理的基本單位。正如其名,Pod像一個豌豆莢,可以容納多個container,擁有相同的IP地址。
    的頭像 發表于 02-15 10:44 ?2140次閱讀

    Kubernetes Pod如何獨立工作

    在學習 Kubernetes 網絡模型的過程中,了解各種網絡組件的作用以及如何交互非常重要。本文就介紹了各種網絡組件在 Kubernetes 集群中是如何交互的,以及如何幫助每個 Pod 都能獲取 IP 地址。
    的頭像 發表于 05-16 14:29 ?1251次閱讀
    <b class='flag-5'>Kubernetes</b> <b class='flag-5'>Pod</b>如何獨立工作

    Kubernetes Pod如何獲取IP地址呢?

    Kubernetes 網絡模型的核心要求之一是每個 Pod 都擁有自己的 IP 地址并可以使用該 IP 地址進行通信。很多人剛開始使用 Kubernetes 時,還不清楚如何為每個 Pod
    的頭像 發表于 07-21 10:00 ?1556次閱讀
    <b class='flag-5'>Kubernetes</b> <b class='flag-5'>Pod</b>如何獲取IP地址呢?

    配置KubernetesPod使用代理的兩種常見方式

    在企業網絡環境中進行Kubernetes集群的管理時,經常會遇到需要配置Pods通過HTTP代理服務器訪問Internet的情況。這可能是由于各種原因,如安全策略限制、網絡架構要求或者訪問特定資源
    的頭像 發表于 01-05 11:22 ?2331次閱讀
    配置<b class='flag-5'>Kubernetes</b>中<b class='flag-5'>Pod</b>使用代理的兩種常見方式

    Kubernetes Pod常用管理命令詳解

    Kubernetes Pod常用管理命令詳解
    的頭像 發表于 02-17 14:06 ?1532次閱讀
    <b class='flag-5'>Kubernetes</b> <b class='flag-5'>Pod</b>常用管理命令詳解

    詳解Kubernetes中的Pod調度親和性

    Kubernetes(K8s)中,Pod 調度親和性(Affinity) 是一種高級調度策略,用于控制
    的頭像 發表于 06-07 13:56 ?1021次閱讀

    Kubernetes Pod異常問題排查實戰

    集群跑著跑著,Pod 掛了。Slack 告警一刷屏,腦子一片空白。打開終端敲 kubectl get pods,看到一堆 CrashLoopBackOff、ImagePullBackOff、Pending,不知道從哪下手。這是每個剛接觸 Kubernetes 運維的人都會
    的頭像 發表于 03-18 15:43 ?153次閱讀