|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
引言
Kubernetes(简称K8s)作为当前最流行的容器编排平台,其调度系统是确保容器应用高效运行的核心组件之一。在复杂的集群环境中,如何合理地将Pod分配到最合适的节点上,直接关系到整个集群的资源利用率、应用性能和系统稳定性。本文将深入探讨K8s的节点调度策略,从基本原理到实际应用,帮助读者全面理解并掌握资源分配优化技巧,从而提升容器集群的整体性能与稳定性。
K8s调度器工作原理
调度器概述
Kubernetes调度器(kube-scheduler)是集群控制平面的核心组件之一,其主要职责是监视新创建的尚未分配节点的Pod,并为它们选择一个最佳节点来运行。调度决策考虑了多种因素,包括资源需求、硬件/软件/策略约束、亲和性和反亲和性规范、数据位置、工作负载间的干扰和最后期限等。
调度过程
K8s的调度过程主要分为两个阶段:
1. 过滤(Predicates)阶段:调度器会遍历所有可用节点,根据Pod的资源请求、节点选择器、亲和性/反亲和性规则等条件筛选出符合要求的候选节点。这一阶段是硬性约束,任何不满足条件的节点都会被排除。
2. 打分(Priorities)阶段:调度器对过滤阶段得到的候选节点进行打分,根据一系列优先级函数为每个节点计算一个分数,最终选择得分最高的节点作为Pod的目标节点。这一阶段是软性约束,用于在多个符合条件的节点中选择最优的一个。
过滤(Predicates)阶段:调度器会遍历所有可用节点,根据Pod的资源请求、节点选择器、亲和性/反亲和性规则等条件筛选出符合要求的候选节点。这一阶段是硬性约束,任何不满足条件的节点都会被排除。
打分(Priorities)阶段:调度器对过滤阶段得到的候选节点进行打分,根据一系列优先级函数为每个节点计算一个分数,最终选择得分最高的节点作为Pod的目标节点。这一阶段是软性约束,用于在多个符合条件的节点中选择最优的一个。
调度框架
Kubernetes从v1.19版本开始引入了调度框架(Scheduling Framework),这是一个可插拔的架构,允许用户通过自定义插件来扩展调度器的功能。调度框架定义了一系列扩展点,调度器在这些扩展点上调用插件来影响调度决策。
主要的扩展点包括:
• Filter:类似于Predicates,用于过滤不符合条件的节点
• Score:类似于Priorities,用于为节点打分
• PreFilter:在Filter之前执行,用于预处理或检查
• PostFilter:在Filter之后执行,当没有可用节点时执行
• PreScore:在Score之前执行,用于预处理或检查
• NormalizeScore:在Score之后执行,用于标准化分数
• Reserve:在绑定之前执行,用于预留资源
• Permit:在绑定之前执行,用于允许或延迟绑定
• PreBind:在绑定之前执行,用于预处理
• Bind:执行绑定操作
• PostBind:在绑定之后执行,用于清理工作
常见的节点调度策略
默认调度策略
Kubernetes调度器默认使用一系列内置的调度算法,包括:
1. 资源请求与限制:调度器会考虑Pod的资源请求(CPU、内存等)和节点的可用资源。只有当节点的可用资源满足Pod的资源请求时,该节点才会被考虑。
2. 节点选择器(NodeSelector):通过为Pod指定nodeSelector,可以限制Pod只能调度到具有特定标签的节点上。
资源请求与限制:调度器会考虑Pod的资源请求(CPU、内存等)和节点的可用资源。只有当节点的可用资源满足Pod的资源请求时,该节点才会被考虑。
节点选择器(NodeSelector):通过为Pod指定nodeSelector,可以限制Pod只能调度到具有特定标签的节点上。
- apiVersion: v1
- kind: Pod
- metadata:
- name: nginx
- spec:
- containers:
- - name: nginx
- image: nginx
- nodeSelector:
- disktype: ssd
复制代码
1. 节点亲和性(Node Affinity):比nodeSelector更灵活的节点选择机制,支持硬性要求(required)和软性偏好(preferred)。
- apiVersion: v1
- kind: Pod
- metadata:
- name: with-node-affinity
- spec:
- affinity:
- nodeAffinity:
- requiredDuringSchedulingIgnoredDuringExecution:
- nodeSelectorTerms:
- - matchExpressions:
- - key: kubernetes.io/e2e-az-name
- operator: In
- values:
- - e2e-az1
- - e2e-az2
- preferredDuringSchedulingIgnoredDuringExecution:
- - weight: 1
- preference:
- matchExpressions:
- - key: another-node-label-key
- operator: In
- values:
- - another-node-label-value
- containers:
- - name: with-node-affinity
- image: k8s.gcr.io/pause:2.0
复制代码
1. Pod亲和性与反亲和性(Pod Affinity/Anti-Affinity):基于已在节点上运行的Pod标签来决定新Pod的调度位置。
- apiVersion: v1
- kind: Pod
- metadata:
- name: with-pod-affinity
- spec:
- affinity:
- podAffinity:
- requiredDuringSchedulingIgnoredDuringExecution:
- - labelSelector:
- matchExpressions:
- - key: security
- operator: In
- values:
- - S1
- topologyKey: topology.kubernetes.io/zone
- podAntiAffinity:
- preferredDuringSchedulingIgnoredDuringExecution:
- - weight: 100
- podAffinityTerm:
- labelSelector:
- matchExpressions:
- - key: security
- operator: In
- values:
- - S2
- topologyKey: topology.kubernetes.io/zone
- containers:
- - name: with-pod-affinity
- image: k8s.gcr.io/pause:2.0
复制代码
1. 污点(Taints)和容忍(Tolerations):节点可以设置”污点”来排斥特定的Pod,而Pod可以通过设置”容忍”来接受特定的污点。
- # 节点设置污点
- kubectl taint nodes node1 key=value:NoSchedule
- # Pod设置容忍
- apiVersion: v1
- kind: Pod
- metadata:
- name: myapp
- spec:
- containers:
- - name: myapp
- image: myapp:latest
- tolerations:
- - key: "key"
- operator: "Equal"
- value: "value"
- effect: "NoSchedule"
复制代码
自定义调度策略
除了内置的调度策略,Kubernetes还支持自定义调度策略,主要通过以下方式实现:
1. 自定义调度器:用户可以开发自己的调度器替代默认的kube-scheduler。自定义调度器需要实现Kubernetes调度器接口,并能够与API Server交互。
2. 调度器扩展:通过调度框架的插件机制,用户可以开发自定义插件来扩展调度器的功能,而不需要替换整个调度器。
3. 调度策略配置文件:可以通过配置文件来调整调度器的行为,包括启用/禁用特定的调度插件、调整插件参数等。
自定义调度器:用户可以开发自己的调度器替代默认的kube-scheduler。自定义调度器需要实现Kubernetes调度器接口,并能够与API Server交互。
调度器扩展:通过调度框架的插件机制,用户可以开发自定义插件来扩展调度器的功能,而不需要替换整个调度器。
调度策略配置文件:可以通过配置文件来调整调度器的行为,包括启用/禁用特定的调度插件、调整插件参数等。
- apiVersion: kubescheduler.config.k8s.io/v1beta2
- kind: KubeSchedulerConfiguration
- profiles:
- - schedulerName: default-scheduler
- plugins:
- score:
- disabled:
- - name: NodeResourcesBalancedAllocation
- enabled:
- - name: MyCustomPlugin
复制代码
资源分配优化技巧
资源请求与限制的合理设置
合理设置Pod的资源请求(requests)和限制(limits)是优化Kubernetes集群资源利用的关键:
1. 资源请求(Requests):Pod调度时,Kubernetes会确保节点有足够的可用资源来满足Pod的资源请求。资源请求也用于Kubernetes计算Pod的QoS(服务质量)类别。
2. 资源限制(Limits):Pod可以使用的资源上限。当Pod尝试使用超过限制的资源时,可能会被限制或终止。
资源请求(Requests):Pod调度时,Kubernetes会确保节点有足够的可用资源来满足Pod的资源请求。资源请求也用于Kubernetes计算Pod的QoS(服务质量)类别。
资源限制(Limits):Pod可以使用的资源上限。当Pod尝试使用超过限制的资源时,可能会被限制或终止。
优化技巧:
• 准确评估资源需求:通过监控工具(如Prometheus、Metrics Server)收集历史数据,准确评估应用的实际资源需求。
• 为关键组件设置适当的资源请求:确保系统关键组件(如数据库、核心服务)有足够的资源请求,以保证其稳定运行。
• 避免过度设置资源限制:过高的资源限制可能导致资源浪费,而过低的限制可能导致应用性能下降或被终止。
• 使用垂直Pod自动伸缩(VPA):VPA可以根据实际使用情况自动调整Pod的资源请求和限制。
- apiVersion: v1
- kind: Pod
- metadata:
- name: resource-optimized-pod
- spec:
- containers:
- - name: app
- image: myapp:latest
- resources:
- requests:
- cpu: "500m"
- memory: "512Mi"
- limits:
- cpu: "1000m"
- memory: "1Gi"
复制代码
资源配额与限制范围
1. 资源配额(Resource Quotas):通过命名空间级别的资源配额,可以限制该命名空间中可以创建的资源总量,防止某个团队或应用占用过多资源。
- apiVersion: v1
- kind: ResourceQuota
- metadata:
- name: compute-resources
- namespace: dev-team
- spec:
- hard:
- pods: "10"
- requests.cpu: "4"
- requests.memory: "8Gi"
- limits.cpu: "10"
- limits.memory: "16Gi"
复制代码
1. 限制范围(Limit Ranges):为命名空间中的Pod或容器设置默认的资源请求和限制,以及最小和最大资源限制。
- apiVersion: v1
- kind: LimitRange
- metadata:
- name: cpu-resource-constraint
- namespace: dev-team
- spec:
- limits:
- - default:
- cpu: "1"
- defaultRequest:
- cpu: "0.5"
- max:
- cpu: "2"
- min:
- cpu: "0.25"
- type: Container
复制代码
节点资源优化
1. 节点专用于特定工作负载:通过污点和容忍、节点亲和性等机制,可以将特定类型的节点专用于特定的工作负载,如CPU密集型、内存密集型或GPU加速型任务。
2. 使用kubelet的预留资源:在kubelet配置中预留系统资源,确保系统守护进程有足够的资源运行,避免因资源不足导致节点不稳定。
节点专用于特定工作负载:通过污点和容忍、节点亲和性等机制,可以将特定类型的节点专用于特定的工作负载,如CPU密集型、内存密集型或GPU加速型任务。
使用kubelet的预留资源:在kubelet配置中预留系统资源,确保系统守护进程有足够的资源运行,避免因资源不足导致节点不稳定。
- # kubelet配置示例
- apiVersion: kubelet.config.k8s.io/v1beta1
- kind: KubeletConfiguration
- evictionHard:
- memory.available: "200Mi"
- nodefs.available: "10%"
- systemReserved:
- cpu: "500m"
- memory: "512Mi"
- kubeReserved:
- cpu: "500m"
- memory: "512Mi"
复制代码
1. 使用节点自动伸缩:结合集群自动伸缩器(Cluster Autoscaler)和节点自动伸缩组,根据负载情况动态调整节点数量,优化资源利用率。
Pod优先级与抢占
1. Pod优先级(PriorityClass):为Pod设置优先级,确保关键Pod在资源紧张时优先调度。
- apiVersion: scheduling.k8s.io/v1
- kind: PriorityClass
- metadata:
- name: high-priority
- value: 1000000
- globalDefault: false
- description: "This priority class should be used for critical service pods only."
复制代码
1. Pod抢占(Preemption):当高优先级的Pod无法调度时,Kubernetes可以尝试从节点上驱逐低优先级的Pod,以腾出资源给高优先级的Pod。
- apiVersion: v1
- kind: Pod
- metadata:
- name: high-priority-pod
- spec:
- priorityClassName: high-priority
- containers:
- - name: high-priority-app
- image: myapp:latest
复制代码
高级调度特性
拓扑分布约束
拓扑分布约束(Topology Spread Constraints)允许用户控制Pod在集群拓扑结构中的分布,如跨区域、可用区、节点等。这有助于提高应用的高可用性和故障容忍能力。
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- name: my-app
- spec:
- replicas: 6
- selector:
- matchLabels:
- app: my-app
- template:
- metadata:
- labels:
- app: my-app
- spec:
- topologySpreadConstraints:
- - maxSkew: 1
- topologyKey: topology.kubernetes.io/zone
- whenUnsatisfiable: DoNotSchedule
- labelSelector:
- matchLabels:
- app: my-app
- containers:
- - name: my-app
- image: myapp:latest
复制代码
自定义调度器
除了默认的kube-scheduler,用户可以开发自定义调度器来满足特定的调度需求。自定义调度器需要实现Kubernetes调度器接口,并能够与API Server交互。
- // 自定义调度器示例代码
- package main
- import (
- "context"
- "fmt"
- "time"
- v1 "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/client-go/kubernetes"
- "k8s.io/client-go/rest"
- "k8s.io/client-go/tools/clientcmd"
- )
- type CustomScheduler struct {
- clientset *kubernetes.Clientset
- }
- func NewCustomScheduler() (*CustomScheduler, error) {
- config, err := rest.InClusterConfig()
- if err != nil {
- // 如果在集群外运行,使用kubeconfig
- kubeconfig := clientcmd.NewDefaultClientConfigLoadingRules().GetDefaultFilename()
- config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
- if err != nil {
- return nil, fmt.Errorf("failed to build kubeconfig: %v", err)
- }
- }
- clientset, err := kubernetes.NewForConfig(config)
- if err != nil {
- return nil, fmt.Errorf("failed to create clientset: %v", err)
- }
- return &CustomScheduler{
- clientset: clientset,
- }, nil
- }
- func (s *CustomScheduler) Run() {
- for {
- // 获取未调度的Pod
- pods, err := s.getUnscheduledPods()
- if err != nil {
- fmt.Printf("Error getting unscheduled pods: %v\n", err)
- time.Sleep(10 * time.Second)
- continue
- }
- // 为每个Pod选择节点
- for _, pod := range pods {
- node, err := s.selectNodeForPod(pod)
- if err != nil {
- fmt.Printf("Error selecting node for pod %s: %v\n", pod.Name, err)
- continue
- }
- // 绑定Pod到节点
- if err := s.bindPodToNode(pod, node); err != nil {
- fmt.Printf("Error binding pod %s to node %s: %v\n", pod.Name, node.Name, err)
- continue
- }
- fmt.Printf("Successfully scheduled pod %s to node %s\n", pod.Name, node.Name)
- }
- time.Sleep(10 * time.Second)
- }
- }
- func (s *CustomScheduler) getUnscheduledPods() ([]*v1.Pod, error) {
- podList, err := s.clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{
- FieldSelector: "spec.nodeName=",
- })
- if err != nil {
- return nil, err
- }
- var unscheduledPods []*v1.Pod
- for i := range podList.Items {
- unscheduledPods = append(unscheduledPods, &podList.Items[i])
- }
- return unscheduledPods, nil
- }
- func (s *CustomScheduler) selectNodeForPod(pod *v1.Pod) (*v1.Node, error) {
- // 获取所有节点
- nodeList, err := s.clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
- if err != nil {
- return nil, err
- }
- // 简单的节点选择逻辑:选择第一个有足够资源的节点
- for i := range nodeList.Items {
- node := &nodeList.Items[i]
- if s.nodeHasEnoughResources(node, pod) {
- return node, nil
- }
- }
- return nil, fmt.Errorf("no suitable node found for pod %s", pod.Name)
- }
- func (s *CustomScheduler) nodeHasEnoughResources(node *v1.Node, pod *v1.Pod) bool {
- // 简单的资源检查逻辑
- allocatable := node.Status.Allocatable
- for _, container := range pod.Spec.Containers {
- resources := container.Resources.Requests
- cpu := resources.Cpu()
- memory := resources.Memory()
- if cpu != nil && allocatable.Cpu().Cmp(*cpu) < 0 {
- return false
- }
- if memory != nil && allocatable.Memory().Cmp(*memory) < 0 {
- return false
- }
- }
- return true
- }
- func (s *CustomScheduler) bindPodToNode(pod *v1.Pod, node *v1.Node) error {
- binding := &v1.Binding{
- ObjectMeta: metav1.ObjectMeta{Name: pod.Name, UID: pod.UID},
- Target: v1.ObjectReference{Kind: "Node", Name: node.Name},
- }
- return s.clientset.CoreV1().Pods(pod.Namespace).Bind(context.TODO(), binding, metav1.CreateOptions{})
- }
- func main() {
- scheduler, err := NewCustomScheduler()
- if err != nil {
- fmt.Printf("Error creating custom scheduler: %v\n", err)
- return
- }
- fmt.Println("Starting custom scheduler...")
- scheduler.Run()
- }
复制代码
多调度器支持
Kubernetes支持在集群中运行多个调度器,每个Pod可以指定使用哪个调度器。这允许用户为不同的工作负载使用不同的调度策略。
- apiVersion: v1
- kind: Pod
- metadata:
- name: my-pod
- spec:
- schedulerName: my-custom-scheduler
- containers:
- - name: my-container
- image: myimage:latest
复制代码
实践案例
案例1:高可用应用的跨区域部署
假设我们需要部署一个高可用的Web应用,要求Pod分布在不同的可用区,以提高容错能力。
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- name: web-app
- spec:
- replicas: 6
- selector:
- matchLabels:
- app: web-app
- template:
- metadata:
- labels:
- app: web-app
- spec:
- # 使用拓扑分布约束确保Pod分布在不同的可用区
- topologySpreadConstraints:
- - maxSkew: 1
- topologyKey: topology.kubernetes.io/zone
- whenUnsatisfiable: DoNotSchedule
- labelSelector:
- matchLabels:
- app: web-app
- # 使用Pod反亲和性避免同一应用的Pod集中
- affinity:
- podAntiAffinity:
- preferredDuringSchedulingIgnoredDuringExecution:
- - weight: 100
- podAffinityTerm:
- labelSelector:
- matchExpressions:
- - key: app
- operator: In
- values:
- - web-app
- topologyKey: kubernetes.io/hostname
- containers:
- - name: web-app
- image: webapp:latest
- resources:
- requests:
- cpu: "500m"
- memory: "512Mi"
- limits:
- cpu: "1000m"
- memory: "1Gi"
- ports:
- - containerPort: 8080
复制代码
案例2:混合工作负载的资源隔离
假设我们有一个集群同时运行批处理任务和在线服务,需要确保批处理任务不会影响在线服务的性能。
- # 首先,为批处理节点添加污点
- kubectl taint nodes node-batch1 workload=batch:NoSchedule
- kubectl taint nodes node-batch2 workload=batch:NoSchedule
- # 在线服务部署
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- name: online-service
- spec:
- replicas: 3
- selector:
- matchLabels:
- app: online-service
- template:
- metadata:
- labels:
- app: online-service
- spec:
- # 使用节点亲和性确保在线服务调度到非批处理节点
- affinity:
- nodeAffinity:
- requiredDuringSchedulingIgnoredDuringExecution:
- nodeSelectorTerms:
- - matchExpressions:
- - key: workload
- operator: NotIn
- values:
- - batch
- containers:
- - name: online-service
- image: online-service:latest
- resources:
- requests:
- cpu: "500m"
- memory: "512Mi"
- limits:
- cpu: "1000m"
- memory: "1Gi"
- ports:
- - containerPort: 8080
- # 批处理任务部署
- apiVersion: batch/v1
- kind: Job
- metadata:
- name: batch-job
- spec:
- template:
- spec:
- # 使用容忍使批处理任务能够调度到批处理节点
- tolerations:
- - key: workload
- operator: Equal
- value: batch
- effect: NoSchedule
- containers:
- - name: batch-job
- image: batch-job:latest
- resources:
- requests:
- cpu: "1000m"
- memory: "2Gi"
- limits:
- cpu: "2000m"
- memory: "4Gi"
- restartPolicy: OnFailure
复制代码
案例3:GPU资源调度优化
假设我们有一个机器学习训练任务,需要使用GPU资源,并且希望优化GPU利用率。
- # 首先,为GPU节点添加标签
- kubectl label nodes node-gpu1 accelerator=gpu
- kubectl label nodes node-gpu2 accelerator=gpu
- # GPU资源监控和共享配置
- apiVersion: v1
- kind: ConfigMap
- metadata:
- name: gpu-sharing-config
- namespace: kube-system
- data:
- gpu-sharing-config.yaml: |
- apiVersion: v1
- kind: ConfigMap
- metadata:
- name: gpu-sharing-config
- namespace: kube-system
- data:
- gpu-sharing-config.yaml: |
- version: v1
- flags:
- migStrategy: none
- failOnInitError: true
- nvidiaDriverRoot: "/"
- plugin:
- passDeviceSpecs: true
- deviceListStrategy: envvar
- deviceIDStrategy: uuid
- # GPU设备插件DaemonSet
- apiVersion: apps/v1
- kind: DaemonSet
- metadata:
- name: nvidia-device-plugin-daemonset
- namespace: kube-system
- spec:
- selector:
- matchLabels:
- name: nvidia-device-plugin-ds
- template:
- metadata:
- labels:
- name: nvidia-device-plugin-ds
- spec:
- tolerations:
- - key: "nvidia.com/gpu"
- operator: "Exists"
- effect: "NoSchedule"
- containers:
- - image: nvcr.io/nvidia/k8s-device-plugin:v0.9.0
- name: nvidia-device-plugin-ctr
- env:
- - name: FAIL_ON_INIT_ERROR
- value: "true"
- securityContext:
- allowPrivilegeEscalation: false
- capabilities:
- drop: ["ALL"]
- volumeMounts:
- - name: device-plugin
- mountPath: /var/lib/kubelet/device-plugins
- volumes:
- - name: device-plugin
- hostPath:
- path: /var/lib/kubelet/device-plugins
- # 机器学习训练任务
- apiVersion: batch/v1
- kind: Job
- metadata:
- name: ml-training-job
- spec:
- template:
- spec:
- # 使用节点亲和性确保任务调度到GPU节点
- affinity:
- nodeAffinity:
- requiredDuringSchedulingIgnoredDuringExecution:
- nodeSelectorTerms:
- - matchExpressions:
- - key: accelerator
- operator: In
- values:
- - gpu
- containers:
- - name: ml-training
- image: ml-training:latest
- resources:
- limits:
- nvidia.com/gpu: 1 # 请求1个GPU
- requests:
- cpu: "2000m"
- memory: "8Gi"
- restartPolicy: OnFailure
复制代码
性能监控与调优
调度性能监控
监控调度器的性能对于确保集群高效运行至关重要。以下是一些关键的监控指标和工具:
1. 调度延迟:从Pod创建到Pod被调度的时间间隔。可以通过以下Prometheus查询获取:
- histogram_quantile(0.99, sum(rate(scheduler_e2e_scheduling_duration_seconds_bucket[5m])) by (le))
复制代码
1. 调度尝试次数:调度器为Pod尝试调度的次数。高尝试次数可能表明资源紧张或调度策略过于严格。
- sum(rate(scheduler_scheduling_attempt_total[5m])) by (result)
复制代码
1. 队列深度:待调度Pod队列的长度,反映调度器的负载情况。
- kube_scheduler_pending_pods
复制代码
1. 调度器事件:通过Kubernetes事件监控调度相关的活动。
- kubectl get events --field-selector involvedObject.kind=Pod -A
复制代码
调度器性能调优
1. 调整调度器并发度:通过增加调度器的并发度可以提高调度吞吐量,但可能会增加CPU使用。
- apiVersion: kubescheduler.config.k8s.io/v1beta2
- kind: KubeSchedulerConfiguration
- parallelism: 16
复制代码
1. 优化调度算法:禁用不必要的调度插件或调整插件权重,以提高调度效率。
- apiVersion: kubescheduler.config.k8s.io/v1beta2
- kind: KubeSchedulerConfiguration
- profiles:
- - schedulerName: default-scheduler
- plugins:
- score:
- disabled:
- - name: NodeResourcesBalancedAllocation
- enabled:
- - name: NodeResourcesMostAllocated
- weight: 5
- - name: NodeResourcesLeastAllocated
- weight: 2
复制代码
1. 使用调度器缓存:启用调度器缓存可以减少API Server的访问次数,提高调度性能。
- apiVersion: kubescheduler.config.k8s.io/v1beta2
- kind: KubeSchedulerConfiguration
- percentageOfNodesToScore: 50
复制代码
1. 节点健康检查调优:调整节点健康检查的频率和超时时间,以平衡资源利用率和故障检测速度。
- apiVersion: kubeadm.k8s.io/v1beta2
- kind: ClusterConfiguration
- controllerManager:
- extraArgs:
- node-monitor-period: "5s"
- node-monitor-grace-period: "40s"
- pod-eviction-timeout: "2m0s"
复制代码
集群资源利用率优化
1. 资源使用情况监控:使用Prometheus和Grafana监控集群资源利用率,识别资源浪费和瓶颈。
- # CPU利用率
- sum(rate(container_cpu_usage_seconds_total{container!="", container!="POD"}[5m])) by (node) / sum(machine_cpu_cores) by (node)
- # 内存利用率
- sum(container_memory_working_set_bytes{container!="", container!="POD"}) by (node) / sum(machine_memory_bytes) by (node)
复制代码
1. 资源回收策略:设置适当的Pod回收策略,确保在资源紧张时优先回收非关键Pod。
- apiVersion: policy/v1beta1
- kind: PodDisruptionBudget
- metadata:
- name: app-pdb
- spec:
- minAvailable: 2
- selector:
- matchLabels:
- app: my-app
复制代码
1. 集群自动伸缩:配置集群自动伸缩器(Cluster Autoscaler)和水平Pod自动伸缩器(HPA),根据负载情况动态调整资源。
- # HPA配置示例
- apiVersion: autoscaling/v2beta2
- kind: HorizontalPodAutoscaler
- metadata:
- name: my-app-hpa
- spec:
- scaleTargetRef:
- apiVersion: apps/v1
- kind: Deployment
- name: my-app
- minReplicas: 2
- maxReplicas: 10
- metrics:
- - type: Resource
- resource:
- name: cpu
- target:
- type: Utilization
- averageUtilization: 50
复制代码
最佳实践和注意事项
调度策略最佳实践
1. 明确资源需求:为所有Pod设置适当的资源请求和限制,避免资源饥饿或浪费。
2. 使用命名空间隔离:通过命名空间和资源配额实现多租户资源隔离,防止单个应用占用过多资源。
3. 合理使用亲和性和反亲和性:避免过度使用严格的亲和性和反亲和性规则,以免导致Pod无法调度。
4. 优先级管理:为关键Pod设置高优先级,确保在资源紧张时优先调度。
5. 避免单点故障:通过拓扑分布约束、Pod反亲和性等机制,确保应用组件分布在不同的节点或可用区。
明确资源需求:为所有Pod设置适当的资源请求和限制,避免资源饥饿或浪费。
使用命名空间隔离:通过命名空间和资源配额实现多租户资源隔离,防止单个应用占用过多资源。
合理使用亲和性和反亲和性:避免过度使用严格的亲和性和反亲和性规则,以免导致Pod无法调度。
优先级管理:为关键Pod设置高优先级,确保在资源紧张时优先调度。
避免单点故障:通过拓扑分布约束、Pod反亲和性等机制,确保应用组件分布在不同的节点或可用区。
常见问题及解决方案
1. Pod无法调度(Pending状态)检查资源请求是否过高,节点是否有足够资源检查节点选择器、亲和性/反亲和性规则是否过于严格检查污点和容忍设置是否匹配使用kubectl describe pod <pod-name>查看详细错误信息
2. 检查资源请求是否过高,节点是否有足够资源
3. 检查节点选择器、亲和性/反亲和性规则是否过于严格
4. 检查污点和容忍设置是否匹配
5. 使用kubectl describe pod <pod-name>查看详细错误信息
6. 节点资源利用率不均衡使用节点亲和性将Pod分散到不同节点考虑使用Descheduler定期重新平衡集群调整调度器的打分策略,如启用NodeResourcesMostAllocated插件
7. 使用节点亲和性将Pod分散到不同节点
8. 考虑使用Descheduler定期重新平衡集群
9. 调整调度器的打分策略,如启用NodeResourcesMostAllocated插件
10. 关键Pod被驱逐为关键Pod设置适当的优先级确保关键Pod的资源请求合理,避免资源争用考虑使用PodDisruptionBudget保护关键应用
11. 为关键Pod设置适当的优先级
12. 确保关键Pod的资源请求合理,避免资源争用
13. 考虑使用PodDisruptionBudget保护关键应用
14. 调度性能问题监控调度延迟和队列深度调整调度器并发度和算法考虑使用多个调度器分担负载
15. 监控调度延迟和队列深度
16. 调整调度器并发度和算法
17. 考虑使用多个调度器分担负载
Pod无法调度(Pending状态)
• 检查资源请求是否过高,节点是否有足够资源
• 检查节点选择器、亲和性/反亲和性规则是否过于严格
• 检查污点和容忍设置是否匹配
• 使用kubectl describe pod <pod-name>查看详细错误信息
节点资源利用率不均衡
• 使用节点亲和性将Pod分散到不同节点
• 考虑使用Descheduler定期重新平衡集群
• 调整调度器的打分策略,如启用NodeResourcesMostAllocated插件
关键Pod被驱逐
• 为关键Pod设置适当的优先级
• 确保关键Pod的资源请求合理,避免资源争用
• 考虑使用PodDisruptionBudget保护关键应用
调度性能问题
• 监控调度延迟和队列深度
• 调整调度器并发度和算法
• 考虑使用多个调度器分担负载
安全考虑
1. 限制特权Pod:避免将特权Pod调度到关键节点,或在调度策略中限制特权Pod的数量。
2. 敏感工作负载隔离:将敏感工作负载调度到专用节点,并使用网络策略限制其通信。
3. 多租户安全:通过命名空间、网络策略和资源配额实现多租户环境的安全隔离。
限制特权Pod:避免将特权Pod调度到关键节点,或在调度策略中限制特权Pod的数量。
敏感工作负载隔离:将敏感工作负载调度到专用节点,并使用网络策略限制其通信。
多租户安全:通过命名空间、网络策略和资源配额实现多租户环境的安全隔离。
- # 网络策略示例
- apiVersion: networking.k8s.io/v1
- kind: NetworkPolicy
- metadata:
- name: app-network-policy
- namespace: dev-team
- spec:
- podSelector:
- matchLabels:
- app: my-app
- policyTypes:
- - Ingress
- - Egress
- ingress:
- - from:
- - namespaceSelector:
- matchLabels:
- name: dev-team
- - podSelector:
- matchLabels:
- app: my-app
- egress:
- - to:
- - namespaceSelector:
- matchLabels:
- name: dev-team
- - podSelector:
- matchLabels:
- app: my-app
复制代码
结论
Kubernetes的节点调度策略是优化容器集群性能和稳定性的关键因素。通过深入理解调度器的工作原理、合理配置资源请求与限制、利用亲和性/反亲和性、污点和容忍等高级特性,以及实施有效的监控和调优措施,可以显著提高集群的资源利用率和应用性能。
随着Kubernetes生态系统的发展,调度技术也在不断演进。未来,我们可以期待更智能的调度算法、更灵活的调度框架以及更精细的资源控制机制。作为Kubernetes用户和管理者,我们需要持续关注这些发展,并将最佳实践应用到实际环境中,以确保容器集群的高效、稳定运行。
通过本文的介绍,希望读者能够全面理解Kubernetes节点调度策略,并能够根据实际需求设计、实施和优化调度方案,从而充分发挥Kubernetes的潜力,提升容器集群的整体性能与稳定性。
版权声明
1、转载或引用本网站内容(K8s节点调度策略详解从原理到实践掌握资源分配优化技巧提升容器集群整体性能与稳定性)须注明原网址及作者(威震华夏关云长),并标明本网站网址(https://www.pixtech.cc/)。
2、对于不当转载或引用本网站内容而引起的民事纷争、行政处理或其他损失,本网站不承担责任。
3、对不遵守本声明或其他违法、恶意使用本网站内容者,本网站保留追究其法律责任的权利。
本文地址: https://www.pixtech.cc/thread-36816-1-1.html
|
|