Kubernetes Pod调度原理与实战
前言
Kubernetes作为云原生时代的容器编排标准,其调度器(Scheduler)是整个系统的”大脑”。调度器负责决定Pod应该运行在哪个节点上,这个看似简单的决策背后,却蕴含着复杂的算法和精妙的设计。
本文将深入剖析Kubernetes Pod调度的底层原理,并提供丰富的实战配置示例。
一、Pod调度流程
1.1 调度流程概览
1
| 创建Pod → API Server → Scheduler → 预选(Filtering) → 优选(Scoring) → 绑定节点
|
详细步骤:
- 用户创建Pod:通过kubectl或API创建Pod
- 写入etcd:API Server将Pod信息写入etcd
- Scheduler监听:Scheduler watch到新的未调度Pod
- 预选阶段:过滤掉不符合条件的节点
- 优选阶段:对剩余节点打分排序
- 绑定节点:选择得分最高的节点,更新Pod的nodeName字段
1.2 Scheduler工作原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| func Schedule(pod *Pod) string { nodes := getAllNodes() feasibleNodes := runPredicates(pod, nodes) scores := runPriorities(pod, feasibleNodes) bestNode := selectBestNode(scores) bindPodToNode(pod, bestNode) return bestNode }
|
二、预选策略(Predicates)
预选阶段会执行一系列检查,任何一个检查失败,该节点就会被淘汰。
2.1 常见预选策略
| 策略 |
说明 |
| PodFitsResources |
节点资源是否满足Pod需求 |
| PodFitsHostPorts |
节点端口是否被占用 |
| MatchNodeSelector |
是否匹配节点选择器 |
| NoVolumeZoneConflict |
存储卷zone是否冲突 |
| CheckNodeMemoryPressure |
节点内存是否有压力 |
| CheckNodeDiskPressure |
节点磁盘是否有压力 |
2.2 资源请求示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| apiVersion: v1 kind: Pod metadata: name: resource-demo spec: containers: - name: app image: nginx:1.21 resources: requests: memory: "256Mi" cpu: "500m" limits: memory: "512Mi" cpu: "1000m"
|
如果节点剩余资源不足,预选阶段会被淘汰:
1 2 3 4 5 6 7
| kubectl describe node node-1
Allocated resources: CPU Requests: 950m (95%) Memory Requests: 3Gi (75%)
|
三、优选策略(Priorities)
通过预选的节点会进入优选阶段,每个节点会得到一个分数(0-10分)。
3.1 常见优选策略
| 策略 |
说明 |
权重 |
| LeastRequestedPriority |
资源剩余越多,分数越高 |
1 |
| BalancedResourceAllocation |
CPU和内存使用率越均衡,分数越高 |
1 |
| NodeAffinityPriority |
满足节点亲和性,分数越高 |
1 |
| TaintTolerationPriority |
容忍的污点越多,分数越高 |
1 |
3.2 优选算法示例
1 2 3 4 5 6 7 8 9 10 11 12
| func LeastRequestedPriority(pod *Pod, node *Node) int { cpuScore := (node.AllocatableCPU - node.RequestedCPU) / node.AllocatableCPU * 10 memScore := (node.AllocatableMem - node.RequestedMem) / node.AllocatableMem * 10 return (cpuScore + memScore) / 2 }
|
四、节点选择器(NodeSelector)
最简单的节点选择方式,通过标签匹配节点。
4.1 给节点打标签
1 2 3 4 5 6 7
| kubectl get nodes --show-labels
kubectl label nodes node-1 disk=ssd kubectl label nodes node-2 disk=hdd kubectl label nodes node-3 env=production
|
4.2 使用NodeSelector
1 2 3 4 5 6 7 8 9 10 11
| apiVersion: v1 kind: Pod metadata: name: nginx-ssd spec: nodeSelector: disk: ssd env: production containers: - name: nginx image: nginx:1.21
|
特点:
- 简单易用
- 硬性要求(必须匹配,否则调度失败)
- 无法表达复杂逻辑(如”或”关系)
五、节点亲和性(Node Affinity)
比NodeSelector更强大,支持更复杂的选择逻辑。
5.1 硬亲和性(Required)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| apiVersion: v1 kind: Pod metadata: name: nginx-affinity-required spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - node-1 - node-2 - key: disk operator: NotIn values: - hdd containers: - name: nginx image: nginx:1.21
|
说明:
requiredDuringSchedulingIgnoredDuringExecution:调度时必须满足,运行时不检查
In:节点标签值必须在列表中
NotIn:节点标签值必须不在列表中
支持的操作符:In、NotIn、Exists、DoesNotExist、Gt、Lt
5.2 软亲和性(Preferred)
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
| apiVersion: v1 kind: Pod metadata: name: nginx-affinity-preferred spec: affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 80 preference: matchExpressions: - key: disk operator: In values: - ssd - weight: 20 preference: matchExpressions: - key: zone operator: In values: - zone-a containers: - name: nginx image: nginx:1.21
|
说明:
- 尽量满足条件,但不是强制要求
- 权重越高,优先级越高
- 如果没有满足的节点,依然可以调度到其他节点
六、Pod亲和性与反亲和性
控制Pod之间的关系,决定Pod是否应该调度到同一节点。
6.1 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 25 26 27
| apiVersion: apps/v1 kind: Deployment metadata: name: redis-cache spec: replicas: 3 selector: matchLabels: app: redis template: metadata: labels: app: redis spec: affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - web topologyKey: kubernetes.io/hostname containers: - name: redis image: redis:6.2
|
说明:
- Redis Pod会调度到与web Pod相同的节点上
topologyKey:拓扑域,kubernetes.io/hostname表示节点级别
6.2 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 25 26 27
| apiVersion: apps/v1 kind: Deployment metadata: name: web-app spec: replicas: 3 selector: matchLabels: app: web template: metadata: labels: app: web spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - web topologyKey: kubernetes.io/hostname containers: - name: web image: nginx:1.21
|
说明:
- 同一个节点上不会运行多个web Pod
- 实现高可用:Pod分散到不同节点
6.3 软反亲和性示例
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
| apiVersion: apps/v1 kind: Deployment metadata: name: api-server spec: replicas: 5 selector: matchLabels: app: api template: metadata: labels: app: api spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app operator: In values: - api topologyKey: kubernetes.io/hostname containers: - name: api image: api-server:v1.0
|
说明:
- 尽量不在同一节点运行多个api Pod
- 如果节点数量不足,允许多个Pod在同一节点
七、污点和容忍(Taints and Tolerations)
污点用于标记节点,容忍用于Pod,决定Pod是否可以调度到有污点的节点。
7.1 给节点添加污点
1 2 3 4 5 6 7 8 9 10 11
| kubectl taint nodes node-1 key=value:NoSchedule
kubectl taint nodes node-2 gpu=true:PreferNoSchedule
kubectl taint nodes node-3 maintenance=true:NoExecute
kubectl taint nodes node-1 key:NoSchedule-
|
污点效果:
NoSchedule:不允许调度新Pod
PreferNoSchedule:尽量不调度
NoExecute:不允许调度,且驱逐已有Pod
7.2 Pod容忍污点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| apiVersion: v1 kind: Pod metadata: name: gpu-pod spec: tolerations: - key: "gpu" operator: "Equal" value: "true" effect: "NoSchedule" - key: "maintenance" operator: "Exists" effect: "NoExecute" tolerationSeconds: 3600 containers: - name: gpu-app image: gpu-app:v1.0
|
操作符:
Equal:key和value都必须匹配
Exists:只要key存在就匹配
7.3 实战场景:专用GPU节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| kubectl taint nodes gpu-node-1 nvidia.com/gpu=true:NoSchedule
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: cuda-app spec: tolerations: - key: "nvidia.com/gpu" operator: "Equal" value: "true" effect: "NoSchedule" nodeSelector: gpu: "true" containers: - name: cuda image: nvidia/cuda:11.0-base resources: limits: nvidia.com/gpu: 1 EOF
|
八、自定义调度器
除了默认调度器,Kubernetes支持自定义调度器。
8.1 指定调度器
1 2 3 4 5 6 7 8 9
| apiVersion: v1 kind: Pod metadata: name: custom-scheduled-pod spec: schedulerName: my-custom-scheduler containers: - name: nginx image: nginx:1.21
|
8.2 自定义调度器部署
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 37 38 39 40 41
| apiVersion: v1 kind: ServiceAccount metadata: name: my-scheduler namespace: kube-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: my-scheduler-as-kube-scheduler subjects: - kind: ServiceAccount name: my-scheduler namespace: kube-system roleRef: kind: ClusterRole name: system:kube-scheduler apiGroup: rbac.authorization.k8s.io --- apiVersion: apps/v1 kind: Deployment metadata: name: my-scheduler namespace: kube-system spec: replicas: 1 selector: matchLabels: component: my-scheduler template: metadata: labels: component: my-scheduler spec: serviceAccountName: my-scheduler containers: - name: scheduler image: my-custom-scheduler:v1.0 command: - /usr/local/bin/my-scheduler - --leader-elect=false
|
九、调度性能优化
9.1 调度器配置
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: v1 kind: ConfigMap metadata: name: scheduler-config namespace: kube-system data: scheduler-config.yaml: | apiVersion: kubescheduler.config.k8s.io/v1 kind: KubeSchedulerConfiguration profiles: - schedulerName: default-scheduler plugins: score: disabled: - name: NodeResourcesLeastAllocated enabled: - name: NodeResourcesBalancedAllocation weight: 2 pluginConfig: - name: NodeResourcesBalancedAllocation args: resources: - name: cpu weight: 1 - name: memory weight: 1
|
9.2 监控调度性能
1 2 3 4 5 6 7 8
| kubectl get --raw /metrics | grep scheduler_scheduling_duration_seconds
kubectl get pods --all-namespaces --field-selector=status.phase=Pending
kubectl describe pod <pod-name> | grep -A 5 Events
|
十、最佳实践
10.1 资源配置建议
1 2 3 4 5 6 7 8
| resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m"
|
10.2 高可用部署
1 2 3 4 5 6 7 8 9 10
| affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchLabels: app: myapp topologyKey: kubernetes.io/hostname
|
10.3 节点维护
1 2 3 4 5 6 7 8
| kubectl cordon node-1
kubectl drain node-1 --ignore-daemonsets --delete-emptydir-data
kubectl uncordon node-1
|
总结
Kubernetes Pod调度是一个复杂而精妙的系统:
- 预选阶段:快速过滤不合格节点
- 优选阶段:综合评分选择最佳节点
- 亲和性:控制Pod与节点、Pod与Pod的关系
- 污点容忍:实现节点隔离和专用场景
掌握这些机制,可以:
- 优化资源利用率
- 提高应用可用性
- 实现复杂的部署策略
- 满足特殊硬件需求
希望本文能帮助你更好地理解和应用Kubernetes调度机制!