CSDN多平台一键发布功能开通链接https://mp.csdn.net/vip?utm_sourceweitingfu你是否遇到过CPU天天跑不满、一扩容就出问题、Pod被调度到同一个节点导致局部过载的尴尬Kubernetes的调度器其实很聪明只是你没告诉它想要的姿势。本文将手把手教你玩转K8s调度策略。目录调度器K8s的智能管家调度流程揭秘从predicates到bindingDefaultPreemption高优先级Pod的插队艺术资源配额三剑客LimitRange、ResourceQuota、PDB自动扩缩容HPA、VPA、KEDA怎么选实战案例从10%到80%的资源利用率总结与思考1. 调度器K8s的智能管家想象一下你是一个大型公寓的物业经理。每天有几十个租客Pod要入住而你的公寓楼集群有上百个房间节点。每个租客有不同的需求有的要朝南的房间节点亲和性有的不要和吵闹的邻居住一起反亲和性有的需要特定的家具资源需求。Kubernetes调度器就是那个物业经理。但问题是——如果你不给它明确的规则它就会像刚入职的新人一样随机分配房间。结果有的楼层挤爆了有的楼层空荡荡。CPU天天跑不满一扩容就出问题Pod被调度到同一个节点导致局部过载…效率技巧调度器不是不聪明是你没告诉它想要的姿势。2. 调度流程揭秘从predicates到bindingK8s调度器的工作流程可以用三步概括┌─────────────────────────────────────────────────────────────────┐ │ K8s 调度流程架构图 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │ │ │ 待调度 │───▶│ Predicates │───▶│ Priorities │ │ │ │ Pod │ │ (前置过滤) │ │ (优先级排序) │ │ │ └─────────────┘ └──────┬──────┘ └────────┬────────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌─────────────────┐ ┌──────────────┐ │ │ │ 过滤掉不合 │ │ 为每个节点 │ │ │ │ 适的节点 │ │ 打分排序 │ │ │ │ (硬约束检查) │ │ (软约束优化)│ │ │ └─────────────────┘ └──────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────┐ │ │ │ Select Bind │ │ │ │ (选择并绑定节点) │ │ │ └─────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘2.1 Predicates前置过滤硬约束Predicates就像相亲时的硬性条件——不符合的直接pass没得商量。常见的predicates包括Predicate作用示例PodFitsResources检查节点资源是否充足CPU/内存是否够PodFitsHost检查是否指定了特定节点nodeName约束PodFitsHostPorts检查端口是否冲突80端口是否被占用PodMatchNodeSelector检查节点标签匹配disktypessdNoDiskConflict检查存储卷冲突同一个PVC不能被多个Pod挂载# 示例使用nodeSelector进行硬约束 apiVersion: v1 kind: Pod metadata: name: nginx-ssd spec: nodeSelector: disktype: ssd # 硬性要求必须在SSD节点上运行 containers: - name: nginx image: nginx⚠️避坑警告如果所有节点都不满足predicatesPod会一直处于Pending状态。这时候别怪调度器去检查你的约束条件是不是写得太苛刻了。2.2 Priorities优先级排序软约束如果说predicates是硬性条件priorities就是加分项。调度器会给每个通过过滤的节点打分选择得分最高的那个。常见的priorities包括Priority作用权重LeastRequestedPriority优先选择资源空闲多的节点默认启用BalancedResourceAllocation平衡CPU和内存使用默认启用ServiceSpreadingPriority分散同一Service的Pod避免单点故障ImageLocalityPriority优先选择已有镜像的节点减少拉取时间NodeAffinityPriority根据节点亲和性打分软约束# 示例使用preferredDuringScheduling进行软约束 apiVersion: v1 kind: Pod metadata: name: nginx-preferred spec: affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 preference: matchExpressions: - key: disktype operator: In values: - ssd # 软约束优先选择SSD节点但不是必须的 containers: - name: nginx image: nginx效率技巧软约束的weight范围是1-100数值越大优先级越高。如果你有多个软约束合理分配权重可以让调度器做出更智能的选择。2.3 Select Bind选择并绑定最后一步调度器会选择得分最高的节点然后调用API将Pod绑定到该节点上。这个过程是原子性的确保不会出现多个Pod被调度到同一个节点导致资源超售的情况。幽默时间调度器选节点的过程就像你在淘宝上买东西——先筛选出符合条件的predicates然后按销量/评分排序priorities最后点击立即购买bind。只不过调度器不会手抖点错也不会半夜冲动消费。3. DefaultPreemption高优先级Pod的插队艺术想象这样一个场景你是一个VIP会员去餐厅吃饭发现满座了。这时候服务员走过来礼貌地请一位普通会员让出座位——这就是抢占Preemption。在K8s中高优先级的Pod可以抢占低优先级Pod的资源。3.1 抢占机制工作原理┌─────────────────────────────────────────────────────────────────┐ │ 抢占机制流程图 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ │ │ │ 高优先级Pod │ │ │ │ 无法调度 │ │ │ └──────┬──────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ ┌─────────────────┐ ┌─────────────┐ │ │ │ 查找可被 │───▶│ 选择牺牲者 │───▶│ 驱逐低优 │ │ │ │ 抢占的节点 │ │ (最低优先级) │ │ 先级Pod │ │ │ └─────────────┘ └─────────────────┘ └──────┬──────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ 调度高优先 │ │ │ │ 级Pod到节点 │ │ │ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘3.2 配置PriorityClass# 定义高优先级类 apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: high-priority value: 1000000 # 优先级数值越大优先级越高 globalDefault: false # 不是默认优先级 description: 用于关键业务应用 --- # 定义低优先级类 apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: low-priority value: 1000 globalDefault: false description: 用于开发测试环境# 使用PriorityClass apiVersion: v1 kind: Pod metadata: name: critical-app spec: priorityClassName: high-priority # 指定高优先级 containers: - name: app image: critical-app:latest resources: requests: memory: 4Gi cpu: 2⚠️避坑警告抢占会导致低优先级Pod被驱逐可能引发服务中断。生产环境中请谨慎使用并确保低优先级应用有完善的优雅退出机制。3.3 抢占策略配置# kube-scheduler配置 apiVersion: kubescheduler.config.k8s.io/v1 kind: KubeSchedulerConfiguration profiles: - schedulerName: default-scheduler plugins: preemption: enabled: - name: DefaultPreemption pluginConfig: - name: DefaultPreemption args: minCandidateNodesPercentage: 10 # 至少检查10%的节点 minCandidateNodesAbsolute: 100 # 至少检查100个节点效率技巧合理设置minCandidateNodesPercentage和minCandidateNodesAbsolute可以在调度性能和抢占效果之间取得平衡。如果集群很大可以适当降低百分比以减少计算开销。4. 资源配额三剑客LimitRange、ResourceQuota、PDB如果说调度器是物业经理那资源配额就是小区管理规定。没有规矩不成方圆。4.1 LimitRange默认资源限制LimitRange用于设置命名空间内Pod/容器的默认资源请求和限制。apiVersion: v1 kind: LimitRange metadata: name: cpu-memory-limits namespace: production spec: limits: # 容器级别限制 - default: cpu: 1000m memory: 512Mi defaultRequest: cpu: 100m memory: 128Mi type: Container # Pod级别限制 - max: cpu: 2000m memory: 1Gi min: cpu: 50m memory: 64Mi type: Pod字段作用default容器未指定limit时的默认值defaultRequest容器未指定request时的默认值max资源上限硬限制min资源下限⚠️避坑警告如果不设置LimitRange用户可能会创建没有资源限制的Pod导致节点资源被耗尽影响其他应用。4.2 ResourceQuota命名空间资源配额ResourceQuota用于限制命名空间可以使用的资源总量。apiVersion: v1 kind: ResourceQuota metadata: name: team-quota namespace: team-a spec: hard: # 计算资源 requests.cpu: 20 requests.memory: 50Gi limits.cpu: 40 limits.memory: 100Gi # 对象数量限制 pods: 50 services: 10 secrets: 20 configmaps: 20 persistentvolumeclaims: 10 # 存储资源 requests.storage: 500Gi standard.storageclass.storage.k8s.io/requests.storage: 300Gi查看配额使用情况$ kubectl describe quota team-quota -n team-a Name: team-quota Namespace: team-a Resource Used Hard -------- ---- ---- configmaps 3 20 limits.cpu 8500m 40 limits.memory 20Gi 100Gi pods 12 50 requests.cpu 3200m 20 requests.memory 8Gi 50Gi secrets 5 20 services 3 10幽默时间ResourceQuota就像你妈妈给你的零花钱限额——你可以随便花但超过限额就别想再要。不同的是K8s不会听你撒娇。4.3 PodDisruptionBudget优雅中断预算PDB用于控制在自愿中断如节点维护、升级期间同时不可用的Pod数量。apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: app-pdb namespace: production spec: minAvailable: 2 # 至少保留2个Pod可用 # 或者使用 maxUnavailable: 1 表示最多允许1个不可用 selector: matchLabels: app: critical-service# 使用maxUnavailable的示例 apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: web-pdb spec: maxUnavailable: 25% # 最多25%的Pod不可用 selector: matchLabels: app: web-server效率技巧对于关键业务建议设置minAvailable对于可以容忍部分中断的服务使用maxUnavailable更灵活。5. 自动扩缩容HPA、VPA、KEDA怎么选手动扩缩容那是上个世纪的玩法。现代云原生应用需要自动扩缩容。5.1 三种扩容方式对比┌─────────────────────────────────────────────────────────────────┐ │ 自动扩缩容方案对比 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ HPA (Horizontal) VPA (Vertical) KEDA │ │ ┌─────────────┐ ┌─────────────┐ ┌───────────┐ │ │ │ 增加Pod │ │ 增加单Pod │ │ 基于事件 │ │ │ │ 数量 │ │ 资源配额 │ │ 驱动 │ │ │ └──────┬──────┘ └──────┬──────┘ └─────┬─────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ 适合无状态应用 适合有状态应用 适合事件驱动│ │ 响应 30秒 响应~3分钟 响应 30秒 │ │ 场景Web服务 场景数据库 场景消息队列│ │ │ └─────────────────────────────────────────────────────────────────┘特性HPAVPAKEDA扩容维度水平Pod数量垂直Pod资源水平基于事件响应时间 30秒~3分钟 30秒适用场景无状态Web服务有状态应用、数据库事件驱动架构指标来源CPU/内存/自定义资源实际使用Kafka/RabbitMQ/SQS等是否开源内置开源组件开源项目5.2 HPA水平Pod自动扩缩容apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: web-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: web-app minReplicas: 3 # 最少保留3个Pod maxReplicas: 50 # 最多扩展到50个Pod metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 # CPU平均使用率70%时扩容 - type: Resource resource: name: memory target: type: Utilization averageUtilization: 80 # 内存平均使用率80%时扩容 behavior: # 扩缩容行为配置 scaleUp: stabilizationWindowSeconds: 60 # 扩容前观察60秒 policies: - type: Percent value: 100 periodSeconds: 60 # 每分钟最多扩容100% scaleDown: stabilizationWindowSeconds: 300 # 缩容前观察5分钟 policies: - type: Percent value: 10 periodSeconds: 60 # 每分钟最多缩容10%效率技巧stabilizationWindowSeconds可以防止因指标抖动导致的频繁扩缩容。扩容可以激进一点60秒缩容要保守一点300秒。5.3 VPA垂直Pod自动扩缩容VPA会分析Pod的实际资源使用自动调整CPU和内存的request/limit。apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: name: db-vpa spec: targetRef: apiVersion: apps/v1 kind: StatefulSet name: postgres updatePolicy: updateMode: Auto # 自动更新模式 # 其他模式Off(仅建议)、Initial(仅初始)、Recreate(重建更新) resourcePolicy: containerPolicies: - containerName: postgres minAllowed: cpu: 50m memory: 256Mi maxAllowed: cpu: 4 memory: 8Gi controlledResources: [cpu, memory]⚠️避坑警告VPA和HPA不能同时使用CPU/内存指标否则会冲突。如果必须同时使用建议HPA使用自定义指标VPA管理资源分配。5.4 KEDA事件驱动的自动扩缩容KEDAKubernetes Event-driven Autoscaling支持基于各种事件源的弹性扩容。# KEDA Kafka 示例 apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: kafka-scaled-app namespace: default spec: scaleTargetRef: name: consumer-app pollingInterval: 10 # 检查事件频率秒 cooldownPeriod: 300 # 缩容冷却时间秒 minReplicaCount: 0 # 可以缩容到0 maxReplicaCount: 100 # 最大副本数 triggers: - type: kafka metadata: bootstrapServers: kafka-cluster:9092 consumerGroup: my-group topic: orders lagThreshold: 100 # 积压超过100条时扩容 activationLagThreshold: 10 # 激活阈值支持的事件源包括消息队列Kafka、RabbitMQ、AWS SQS、Azure Service Bus、Google Pub/Sub数据库PostgreSQL、MySQL、MongoDB云服务AWS CloudWatch、Azure Monitor、GCP Stackdriver其他Prometheus、Cron、GitHub Webhook、Liiklus等幽默时间KEDA就像一个消息灵通的黄牛党——哪里有需求消息积压它就立刻安排人手扩容Pod。没活干的时候它也不会白养着人缩容到0。6. 实战案例从10%到80%的资源利用率某电商平台的Kubernetes集群长期面临资源利用率低的问题指标优化前优化后CPU平均利用率10%78%内存平均利用率15%72%节点数量50台12台月度云成本¥50万¥12万6.1 问题诊断# 查看节点资源使用情况 $ kubectl top nodes NAME CPU(cores) CPU% MEMORY(bytes) MEMORY% node-01 250m 6% 4Gi 12% node-02 300m 7% 5Gi 15% ... # 查看Pod资源请求vs实际使用 $ kubectl describe node node-01 | grep -A 5 Allocated resources Allocated resources: (Total limits may be over 100 percent, i.e., overcommitted.) Resource Requests Limits -------- -------- ------ cpu 3500m (87%) 8000m (200%) memory 8Gi (25%) 16Gi (50%)问题发现Pod的resource request设置过大请求了3500m实际只用了250m没有使用HPA所有服务都是固定副本数调度策略不合理资源分布不均匀6.2 优化方案步骤1使用VPA优化资源请求apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: name: app-vpa spec: targetRef: apiVersion: apps/v1 kind: Deployment name: web-app updatePolicy: updateMode: Auto步骤2配置HPA自动扩缩容apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: web-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: web-app minReplicas: 2 maxReplicas: 20 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70步骤3优化调度策略# 使用Pod反亲和性分散Pod apiVersion: apps/v1 kind: Deployment metadata: name: web-app spec: replicas: 3 selector: matchLabels: app: web template: metadata: labels: app: web spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app operator: In values: - web topologyKey: kubernetes.io/hostname containers: - name: web image: web-app:latest resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 512Mi步骤4设置资源配额防止滥用apiVersion: v1 kind: ResourceQuota metadata: name: team-quota spec: hard: requests.cpu: 20 requests.memory: 40Gi limits.cpu: 40 limits.memory: 80Gi6.3 效果验证# 优化后查看节点资源使用 $ kubectl top nodes NAME CPU(cores) CPU% MEMORY(bytes) MEMORY% node-01 3200m 80% 28Gi 70% node-02 3000m 75% 29Gi 72% ... # HPA状态 $ kubectl get hpa NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web-hpa Deployment/web-app 68%/70% 2 20 8 7d效率技巧优化资源利用率不是一蹴而就的建议先用VPA分析一段时间获取真实的资源使用数据再设置合理的HPA阈值。7. 总结与思考7.1 核心要点回顾调度流程predicates硬约束过滤→ priorities软约束排序→ bind绑定抢占机制高优先级Pod可以驱逐低优先级Pod但要谨慎使用资源配额LimitRange默认限制 ResourceQuota总量限制 PDB中断预算自动扩缩容HPA水平 VPA垂直 KEDA事件驱动按需选择7.2 选择建议场景推荐方案无状态Web服务HPA 合理的resource request有状态应用数据库等VPA PDB消息队列消费者KEDA混合场景HPA自定义指标 VPA仅初始模式文末三件套1. 【源码获取】关注此系列获取后续更新后台回复’scheduler’获取本文完整代码示例和配置文件。2. 【思考题】你的应用需要水平扩容还是垂直扩容如果是无状态应用建议优先考虑HPA水平扩容如果是有状态应用如数据库VPA垂直扩容更合适如果是事件驱动架构KEDA是最佳选择欢迎在评论区分享你的选择和理由3. 【系列预告】云原生系列文章持续更新中GitOps声明式持续交付实践IaC基础设施即代码安全扫描容器镜像安全最佳实践标签Scheduler, HPA, VPA, KEDA, AutoScaling, 资源调度, 扩缩容如果觉得本文对你有帮助欢迎点赞、收藏、转发有任何问题可以在评论区留言我会一一回复。CSDN多平台一键发布功能开通链接https://mp.csdn.net/vip?utm_sourceweitingfu