侧边栏壁纸
博主头像
路小飞博主等级

行动起来,活在当下

  • 累计撰写 72 篇文章
  • 累计创建 12 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

09- Kubernetes 高级调度

路小飞
2024-10-22 / 0 评论 / 0 点赞 / 22 阅读 / 18440 字

在 Kubernetes 中,调度是一个关键过程,负责将 Pods 安排到适当的节点上,以确保它们能够有效运行并满足应用程序的需求。

9.1 调度的基本概念

9.1.1 调度器

Kubernetes 集群的调度器( kube-scheduler )是一个核心组件,负责将未调度的 Pods 分配到适合的节点上。

9.1.2 调度流程

1. 调度过程

调度流程通常包含以下几个步骤:

  1. 监听 Pod 事件

    • 调度器通过 Kubernetes API Server 监听未调度的 Pods。当一个 Pod 被创建但没有指定节点时,它将被标记为未调度(Pending),并且调度器会开始处理它。
  2. 预选(过滤节点)

    • 调度器会根据 Pod 的资源需求、节点的标签、污点等条件对可用节点进行过滤,排除不合适的节点。
  3. 优选(评估节点)

    • 在过滤后的节点中,调度器会为通过筛选的每个节点打分,考虑多个因素如资源利用率、亲和性等。
  4. 选择最佳节点

    • 最后,调度器选择得分最高的节点,将 Pod 调度到该节点,并更新 Kubernetes API。
2. 预选策略(Pre-Selection Strategies)

预选阶段负责筛选出适合 Pod 的候选节点。以下是主要的预选策略:

  • PodFitsResources:检查节点上剩余的 CPU 和内存是否足够满足 Pod 的资源请求。这是最基本的筛选条件,确保节点能够承载 Pod 的负载。
  • PodFitsHost:如果 Pod 指定了 NodeName,则检查节点名称是否与 NodeName 匹配。如果匹配,Pod 将被调度到该节点上。
  • PodFitsHostPorts:确保 Pod 请求的端口不与节点上已有的端口冲突。如果有冲突,则该节点将被排除。
  • PodSelectorMatches:根据 Pod 的标签选择节点,只有标签匹配的节点才能被选中。这使得用户可以控制 Pod 的调度到特定节点。
  • NoDiskConflict:检查节点上已挂载的卷与 Pod 请求的卷之间是否存在冲突。特别是当两个卷都是只读时,允许它们共享。
  • Taints and Tolerations:节点可以有污点,而 Pod 可以有容忍度,确保只有满足条件的 Pods 能够在有污点的节点上调度。

如果在 预选过程中没有合适的节点,pod 会一直在pending状态,不断重试调度,直到有节点满足条件。经过这个步骤,如果有多个节点满足条件,就继续 优选 过程: 按照优先级大小对节点排序。

3. 优选策略(Post-Selection Strategies)

在预选阶段筛选出合适的节点后,调度器会根据优选策略对这些节点进行进一步的排序,以选择最合适的节点。主要的优选策略包括:

  • LeastRequestedPriority:计算每个节点的 CPU 和内存使用率,倾向于选择资源使用率较低的节点。这样可以帮助均衡集群负载,避免某些节点过载。
  • BalancedResourceAllocation:评估节点的 CPU 和内存使用率,使得这两个资源的使用率尽量接近,从而实现资源的平衡分配。
  • ImageLocalityPriority:优先选择已经缓存了 Pod 所需镜像的节点。这有助于减少镜像拉取时间,从而提高 Pod 启动速度,特别是在大镜像的情况下。

9.2 nodeSelector

基本概念nodeSelector 是 Pod 规格中的一个字段,用于定义节点标签的键值对。Kubernetes 只会将 Pod 调度到拥有你所指定的每个标签的节点上。

语法示例

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  nodeSelector:
    disktype: ssd
    environment: production
  containers:
  - name: my-container
    image: my-image

在这个示例中,Pod 只会调度到那些同时具有 disktype: ssdenvironment: production 标签的节点上。

在 Kubernetes 中,给节点添加标签可以通过 kubectl 命令来实现。以下是给节点添加标签的具体步骤:

# 查看当前节点
kubectl get nodes

# 给节点添加标签
kubectl label nodes <node-name> <key>=<value>
kubectl label worker-01 disktype=ssd

# 更新标签
kubectl label nodes node1 disktype=hdd --overwrite

# 删除标签
kubectl label nodes <node-name> <key>-
kubectl label nodes node1 disktype-

9.3 Taint 和 Toleration

在使用 Kubernets 过程中,经常会有以下需求:

  • Master 节点应仅部署系统组件容器,如 Calico、Metrics 和 Filebeat,而不应运行业务应用。
  • 新添加的节点在经过完整性和稳定性测试之前,不应立即允许部署业务容器。
  • 某些节点可能需要系统升级或维护,期间需将其上的 Pod 迁移到其他节点以保持可用性。
  • 对于 GPU 服务器或其他专用节点,只希望特定的 Pod 能够运行,避免部署其他 Pod。

针对这些需求,Kubernetes 提供了污点(Taint)和容忍(Toleration)机制,使得管理节点和 Pod 的调度变得更加灵活和高效。

9.3.1 基本概念

1. Taint 和 Toleration 是什么

Taint 是 Kubernetes 中的一种机制,用于控制 Pod 的调度。它是附加在节点上的标记,表明该节点不应接受未具备相应容忍(toleration)的 Pod。与 Taint 相对,Toleration 是 Kubernetes 的另一种机制,它与 Taint 配合使用,允许 Pod 在带有特定 Taint 的节点上运行。简单来说,Toleration 表示 Pod 对 Taint 的“容忍”,使得这些 Pod 能够在带有 Taint 的节点上被调度。

要在节点上添加 Taint,可以使用以下命令:

kubectl taint nodes <node-name> <key>=<value>:<effect>

示例

kubectl taint nodes node1 disktype=ssd:NoSchedule

在这个示例中,节点 node1 被标记为 disktype=ssd,并且不允许没有相应容忍的 Pod 被调度到这个节点。

2. Taint 的组成

一个 Taint 由三个部分组成:

  1. 键(Key):标识 Taint 的名称,可以是任意字符串。
  2. 值(Value):与键关联的值,可以是任意字符串。
  3. 效果(Effect):表示 Taint 的影响,有三种可能的效果。
    • NoSchedule:不允许没有相应容忍的 Pod 被调度到该节点。当前已经在节点上运行的 Pod 不会被驱逐。
    • PreferNoSchedule:尽量避免在该节点上调度 Pod,但不是强制的。
    • NoExecute:已经在该节点上运行的 Pod 会被驱逐,新的 Pod 不能调度到该节点。

你可以给一个节点添加多个污点,也可以给一个 Pod 添加多个容忍度设置。 Kubernetes 处理多个污点和容忍度的过程就像一个过滤器:从一个节点的所有污点开始遍历, 过滤掉那些 Pod 中存在与之相匹配的容忍度的污点。余下未被过滤的污点的 effect 值决定了 Pod 是否会被分配到该节点。

需要注意以下情况:

  • 如果未被忽略的污点中存在至少一个 effect 值为 NoSchedule 的污点, 则 Kubernetes 不会将 Pod 调度到该节点。

  • 如果未被忽略的污点中不存在 effect 值为 NoSchedule 的污点, 但是存在至少一个 effect 值为 PreferNoSchedule 的污点, 则 Kubernetes 会 尝试 不将 Pod 调度到该节点。

  • 如果未被忽略的污点中存在至少一个 effect 值为 NoExecute 的污点, 则 Kubernetes 不会将 Pod 调度到该节点(如果 Pod 还未在节点上运行), 并且会将 Pod 从该节点驱逐(如果 Pod 已经在节点上运行)。

3. toleration 的运算符

Kubernetes 中的 toleration 运算符有以下两种:

  1. Equal (=):表示键(key)和值(value)必须完全匹配。

    tolerations:
    - key: "key1"
      operator: "Equal"
      value: "value1"
      effect: "NoSchedule"
    
  2. Exists:表示只需匹配键(key),而不考虑值(value)。即可以容忍任何值。

    Codetolerations:
    - key: "key1"
      operator: "Exists"
      effect: "NoSchedule"
    
4. 使用 Taint 的场景
  • 资源隔离:确保某些节点只运行特定类型的 Pod,如系统组件。
  • 故障处理:标记不健康或需要维护的节点,防止新的 Pod 调度到这些节点上。
  • 节点特性:例如,GPU 节点仅允许运行需要 GPU 的特定应用。

9.3.2 使用案例

1. 特殊硬件的节点

假设我们的集群中有一些节点配备了 NVIDIA GPU,且我们希望只有需要 GPU 的应用程序(如机器学习训练任务)能够调度到这些节点上。
首先,我们需要为包含 GPU 的节点打上污点。这可以通过 kubectl taint 命令来实现。

kubectl taint nodes <gpu-node-name> gpu=true:NoSchedule

这里的意思是,如果 Pod 没有容忍这个污点(toleration),则无法调度到这个节点上。
然后,我们在需要使用 GPU 的 Pod 的定义中添加相应的容忍度,以便它们可以调度到这些节点上。

以下是一个示例,展示了如何定义一个需要 GPU 的 Pod:

apiVersion: v1
kind: Pod
metadata:
  name: gpu-enabled-pod
spec:
  containers:
  - name: my-gpu-app
    image: my-gpu-image:latest
    resources:
      limits:
        nvidia.com/gpu: 1  # 请求 1 个 GPU
  tolerations:
  - key: "gpu"
    operator: "Equal"    # 使用 Equal 运算符
    value: "true"        # 需要匹配的值
    effect: "NoSchedule" # 允许调度到被打上此污点的节点

通过以上配置,只有标记为需要 GPU 的 Pod(即 gpu-enabled-pod)能够调度到配备 GPU 的节点上,其他 Pod 则会被阻止,确保资源的合理使用和调度策略的安全性。

2. 基于 Taint 的驱逐

之前提到过的 Taint 的 effect 的值 NoExecute,它会影响已经在节点上运行的 Pod,具体影响如下:

  • 如果 Pod 不能容忍这类污点,会马上被驱逐。
  • 如果 Pod 能够容忍这类污点,但是在容忍度定义中没有指定 tolerationSeconds, 则 Pod 还会一直在这个节点上运行。
  • 如果 Pod 能够容忍这类污点,而且指定了 tolerationSeconds, 则 Pod 还能在这个节点上继续运行这个指定的时间长度。 这段时间过去后,节点生命周期控制器从节点驱除这些 Pod。

Kubernetes 会自动给 Pod 添加一个 key 为 node.kubernetes.io/not-ready 的 Toleration 并配置 tolerationSeconds=300,同样也会给 Pod 添加一个 key 为 node.kubernetes.io/unreacherable 的 Toleration 并配置 tolerationSeconds=300,除非用户自定义了上述 key,否则会采用这个默认值,意识就是说如果节点由于某些原因造成不可用,默认情况下 Pod 会在 5分钟左右才会漂移至其他节点,对于要求高可用率的 Pod 按需修改这两处的 tolerationSeconds 为较短的值,就可以在节点故障后快速进行故障恢复,当然也不能太短,因为可能某些节点是由于网络波动造成的不可用。

默认情况下,自动添加 Toleration 的机制保证了在其中一种问题被检测到时,Pod 默认还能继续停留在当前节点运行 5 分钟。这两个默认 Toleration 是由 DefaultToleration-ationSeconds admission controler 添加的。

DaemonSet 中的 Pod 被创建时,针对以下 Taint 自动添加的 NoExcute 的 Toleration 将不会指定 tolerationSeconds,这保证了出现伤处问题时,DaemonSet 中的 Pod 永远不会被驱逐,当然 DaemonSet 的 Pod 不需要进行漂移。

3. 内置污点

当集群中的节点出现某种问题或满足特定条件时,kubelet会自动给这些节点添加内置污点。以下是一些常见的内置污点:

  1. node.kubernetes.io/not-ready:节点未准备好,相当于节点状态 Ready 的值为 False。
  2. node.kubernetes.io/unreachable:Node Controller 访问不到节点,相当于节点状态 Ready 的值为 Unknown。
  3. node.kubernetes.io/out-of-disk:节点磁盘耗尽。
  4. node.kubernetes.io/memory-pressure:节点存在内存压力。
  5. node.kubernetes.io/disk-pressure:节点存在磁盘压力。
  6. node.kubernetes.io/unschedulable:节点不可调度。
  7. node.kubernetes.io/network-unavailable:节点网络不可达。

9.3.3 master 节点的污点问题

kubectl get nodes 
# 查看节点污点 
kubectl describe  nodes master-01 | grep -i taint

kubectl taint node xxx-nodename node-role.kubernetes.io/master-  
#将 Master 也当作 Node 使用(去除污点)

kubectl taint node xxx-nodename node-role.kubernetes.io/master="":NoSchedule 
#将 Master 恢复成 Master Only 状态

9.4 Affinity 亲和性

在 Kubernetes 中,使用 Affinity(亲和性)可以满足更复杂的调度需求,如优先调度到具有特定标签的节点、避免同一类 Pod 部署在统一节点、以及确保相互依赖的 Pod 在同一节点上运行。

Affinity 根据功能不同分为节点亲和性和 Pod 亲和性。

9.4.1 Node Affinity(节点亲和性)

1.基本概念

Node Affinity是根据节点上的标签选择性调度,可以让 Pod 部署在指定标签的节点、不部署在指定标签的节点撒谎给你,调度时是根据节点上的标签进行选择的。节点亲和性配置在 spec.affinity 字段下设置。

节点亲和性可分为两种类型:

  • requiredDuringSchedulingIgnoredDuringExecution:Pod 只能调度到满足条件的节点上。如果没有符合条件的节点,Pod 将无法调度。
  • preferredDuringSchedulingIgnoredDuringExecution:Pod 更倾向于调度到满足条件的节点上,但如果没有符合条件的节点,仍然可以调度到其他节点。
2. 支持的操作符
操作符行为
In标签值存在于指定的字符串集中
NotIn标签值不在指定的字符串集中
Exists对象上存在此键的标签
DoesNotExist对象上不存在此键的标签
Gt大于指定的值
Lt小于指定的值
3. 示例

以下是一个示例 YAML 配置:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: disktype
                operator: In
                values:
                - ssd
                - hdd
      containers:
      - name: my-app-container
        image: my-app-image

上述配置确保 Pods 只能调度到具有标签 disktype 且值为 ssdhdd 的节点上。

4. 注意事项

如果同时指定了 nodeSelector 和 nodeAffinity,需要两者都满足才能被调度;

如果配置了多个 nodeSelecetorTerms ,满足其一即可调度到指定节点;

如果配置了多个 matchExpressions,需要全部满才能调度到指定的节点上;

如果删除了被调度节点的标签,Pod 不会被删除,也就是说亲和性配置只有在调度的时候才会起作用。

9.4.2 Pod Affinity 和 Pod Anti-affinity(Pod 亲和性和 Pod 反亲和性)

1. 基本概念

Pod 亲和性和反亲和性是根据其他 Pod 的标签进行匹配的,比如想要 A 服务 的 Pod 不能和具有 servicce=b 标签的 Pod 部署在同一节点上,此时可以使用 Pod 亲和性和反亲和性进行配置,该调度是基于 Pod 的标签进行选择的。Pod 亲和性和 Pod 反亲和性需要配置在.spec.affinitypodAffinitypodAntiAffinity 字段下。

  • Pod Affinity:允许你将 Pods 调度到同一节点或同一拓扑域(如同一机架)上的其他 Pods。
  • Pod Anti-affinity:允许你将 Pods 调度到与其他 Pods 不同的节点或不同的拓扑域上,以提高可用性。
2. 支持的操作符
操作符行为
In标签值存在于指定的字符串集中
NotIn标签值不在指定的字符串集中
Exists对象上存在此键的标签
DoesNotExist对象上不存在此键的标签
3. 示例
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - redis
            topologyKey: "kubernetes.io/hostname" 
        podAntiAffinity:   
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 10
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - nginx
              topologyKey: "kubernetes.io/hostname"    
      containers:
        - name: nginx
          image: nginx
          resources:
            limits:
              memory: 1Gi
              cpu: 1
            requests:
              memory: 256Mi
              cpu: 100m

该配置文件定义了一个 Nginx 的 Deployment,要求 Pods 在调度时必须与 app: redis 的 Pods 在同一主机上,并且尽量避免与其他 app: nginx 的 Pods 同处于同一主机。通过这样的亲和性和反亲和性配置,可以实现 Pods 的负载均衡和高可用性。

4. 可用区
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: registry.k8s.io/pause:2.0

亲和性规则规定,只有节点属于特定的区域 且该区域中的其他 Pod 已打上 security=S1 标签时,调度器才可以将示例 Pod 调度到此节点上。 例如,如果我们有一个具有指定区域(称之为 "Zone V")的集群,此区域由带有 topology.kubernetes.io/zone=V 标签的节点组成,那么只要 Zone V 内已经至少有一个 Pod 打了 security=S1 标签, 调度器就可以将此 Pod 调度到 Zone V 内的任何节点。相反,如果 Zone V 中没有带有 security=S1 标签的 Pod, 则调度器不会将示例 Pod 调度给该区域中的任何节点。

反亲和性规则规定,如果节点属于特定的区域 且该区域中的其他 Pod 已打上 security=S2 标签,则调度器应尝试避免将 Pod 调度到此节点上。 例如,如果我们有一个具有指定区域(我们称之为 "Zone R")的集群,此区域由带有 topology.kubernetes.io/zone=R 标签的节点组成,只要 Zone R 内已经至少有一个 Pod 打了 security=S2 标签, 调度器应避免将 Pod 分配给 Zone R 内的任何节点。相

5. 注意事项

由于使用 Pod 亲和性和 Pod 反亲和性时,需要进行大量的计算,会降低大规模集群下大的调度速率,因此在节点数超过数百时,并不建议使用过多的 Pod 亲和性。

9.4.3 应用场景

  1. 高可用性:通过 Pod Anti-affinity,将 Pods 部署到不同的节点上,降低单点故障风险。
  2. 负载均衡:通过 Pod Affinity,将某些 Pods 部署到同一节点上,可以提高访问速度和效率。
  3. 特定硬件需求:通过 Node Affinity,将 Pods 调度到具有特定硬件(如 GPU 或 SSD)的节点上,以满足应用需求。

9.5 HPA

9.5.1 什么是 HPA

自动扩缩容依赖于 Metrics Server 组件。在安装完 Metrics Server 后,该组件负责在集群中采集 Pod 和 Node 的度量指标,包括 Pod 的 CPU 和内存使用率,以及节点的 CPU 和内存使用率。Metrics Server 通过对外暴露接口默认是,将这些数据提供给 Kubernetes 的 HPA(Horizontal Pod Autoscaler)功能,可以根据观察到的数据来实现 Pod 的自动扩容和缩容。

参考链接:

Metrics Server 项目地址:https://github.com/kubernetes-sigs/metrics-server

9.5.2 HPA 实践

案例:电子商务网站的产品搜索服务

假设你在运营一个电子商务网站,用户在高峰时段(如促销活动)进行大量产品搜索。这些搜索请求需要处理较高的流量,因此你希望在流量增加时自动扩展服务,以保持响应时间和性能。

1. 部署应用

首先,你需要在 Kubernetes 中部署一个处理产品搜索请求的应用。可以使用 Deployment 来管理该应用。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-search
spec:
  replicas: 2
  selector:
    matchLabels:
      app: product-search
  template:
    metadata:
      labels:
        app: product-search
    spec:
      containers:
      - name: search-container
        image: your-docker-repo/product-search:latest
        resources:
          requests:
            cpu: "250m"
            memory: "512Mi"
          limits:
            cpu: "1000m"
            memory: "1Gi"
2. 设置 HPA

接下来,你可以创建一个 HPA,根据 CPU 使用率自动调整 Pod 的数量。例如,你希望在 CPU 使用率超过 50% 时增加 Pod 的数量,低于 30% 时减少 Pod 的数量。

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: product-search-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: product-search
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 30 # 额外添加的配置来控制缩容
        

HPA 将根据 Pod 的 CPU 使用情况自动调整 Pod 的数量。你可以使用 kubectl get hpa 命令查看 HPA 的状态和当前的 Pod 数量。
在高峰期,流量增加,HPA 将会自动增加 Pod 的数量。例如,如果 CPU 使用率超过 50%,Pod 的数量可能会增加到 4 个或更多,以处理增加的请求。当流量减小时,HPA 会自动减少 Pod 的数量,以节省资源。

通过使用 HPA,电子商务网站能够在高流量期间保持良好的性能,同时有效地管理资源和成本。这种自动扩展的能力使得应用在面对不确定的流量模式时更具弹性。

9.6 调度的生产案例

9.6.1 场景

k8s 集群,分为三类节点四类服务

  • ingress节点:ingress服务、代理服务,如日志代理,监控代理,dns缓存
  • coredns节点:coredns服务、代理服务
  • 普通业务节点:业务服务、代理服务

9.6.2 实现

  • Ingress节点
    标签:ingress.node=true,污点:ingress=true:NoSchedule
    Ingress服务(DaemonSet):nodeSelector绑定标签,添加对应污点容忍,限制仅此类服务运行。
  • CoreDNS节点
    标签:coredns.node=true,污点:coredns=true:NoSchedule
    CoreDNS服务(DaemonSet):nodeSelector绑定标签,添加对应污点容忍,确保专属运行。
  • 普通业务节点
    无专用污点,运行业务服务,简单配置pod反亲和。
  • 代理服务(日志/监控/DNS缓存等)
    全局部署:不设nodeSelector,配置tolerations: [{operator: "Exists"}],容忍所有污点,覆盖三类节点。
0

评论区