k8s-Pod Scheduler

Kubernetes Pod Scheduler

学习参考:调度、抢占和驱逐

环境准备

bash 复制代码
root@master30:~# kubectl create ns scheduler
root@master30:~# kubectl config set-context --current --namespace scheduler

调度介绍

Kubernetes 调度 是指将 Pod 放置到合适的节点上的过程。

kube-scheduler 是 Kubernetes 集群的默认调度器,通过监测(Watch)机制发现集群中未被调度到节点上的 Pod,并将这些未调度的 Pod 调度到一个合适的节点上来运行。

调度过程

Kube-scheduler 选择一个最佳节点来运行新创建的或尚未调度(unscheduled)的 Pod。 由于 Pod 中的容器和 Pod 本身可能有不同的要求,调度程序会过滤掉任何不满足 Pod 特定调度需求的节点。

调度术语

  • 满足 Pod 调度请求的所有节点称之为 可调度节点

  • 调度器将 pod 调度到特定节点的这个过程叫做 绑定

Kubernetes 调度过程:

  1. 过滤,将满足 Pod 调度需求的所有节点选出来。 例如,PodFitsResources 过滤函数会检查候选节点的可用资源能否满足 Pod 的资源请求。 在过滤之后,得出一个节点列表,里面包含了所有可调度节点;通常情况下, 这个节点列表包含不止一个节点。如果这个列表是空的,代表这个 Pod 不可调度。
  2. 打分,根据当前启用的打分规则,调度器会给每一个可调度节点进行打分,调度器将 Pod 调度到得分最高的节点上。 如果存在多个得分最高的节点,kube-scheduler 会从中随机选取一个。

在做调度决定时需要考虑的因素包括:

  • 单独和整体的资源请求
  • 硬件/软件/策略限制
  • 亲和以及反亲和要求
  • 数据局部性
  • 负载间的干扰
  • 等等。

kubernetes 使用以下两种方式配置调度器的过滤和打分行为:

  1. 调度策略,配置过滤所用的 断言(Predicates) 和打分所用的 优先级(Priorities)
  2. 调度配置,允许配置实现不同调度阶段的插件, 包括:QueueSortFilterScoreBindReservePermit 等等。

过滤

以下**断言(Predicates)**用于主机过滤:

  • PodFitsHostPorts:检查节点是否有空闲端口(网络协议类型)用于 Pod 请求的 Pod 端口。
  • PodFitsHost:检查 Pod 是否通过其主机名指定特定节点。
  • PodFitsResources: 检查 Node 是否有空闲资源(例如 CPU 和内存)来满足 Pod 的要求。
  • MatchNodeSelector: 检查 Pod 的 Node Selector匹配节点的 标签.
  • NoVolumeZoneConflict: 评估是否 考虑到该存储的故障区域限制,Pod 请求在节点上可用。
  • NoDiskConflict:评估 Pod 是否可以根据它请求的卷以及已经挂载的卷安装在节点上。
  • MaxCSIVolumeCount: 决定多少 CSI 应附加卷,以及是否超过配置的限制。
  • PodToleratesNodeTaints: 检查Pod 的 toleration 可以容忍节点的 taints
  • CheckVolumeBinding:评估 Pod 是否因它请求的卷而适合。这适用于绑定和未绑定 PVCs.

计分

以下 **优先级 **Priorities 用于主机评分:

  • SelectorSpreadPriority: 跨主机传播 Pod,考虑属于相同的 Pod 服务, 状态集 或者 副本集
  • InterPodAffinityPriority:实现首选的 pod 间亲和性和反亲和性
  • LeastRequestedPriority:支持请求资源较少的节点。换句话说,节点上放置的 Pod 越多,这些 Pod 使用的资源越多,该策略给出的排名就越低。
  • MostRequestedPriority:支持请求资源最多的节点。此策略将使计划的 Pod 适合运行整个工作负载集所需的最少数量的节点。
  • RequestedToCapacityRatioPriority:使用默认资源评分函数形状创建基于 requestsToCapacity 的 ResourceAllocationPriority。
  • BalancedResourceAllocation:支持资源使用均衡的节点。
  • NodePreferAvoidPodsPriority:根据节点注释对节点进行优先级排序 scheduler.alpha.kubernetes.io/preferAvoidPods。您可以使用它来暗示两个不同的 Pod 不应在同一个节点上运行。
  • NodeAffinityPriority:根据PreferredDuringSchedulingIgnoredDuringExecution 中指示的节点关联性调度首选项对节点进行优先级排序。您可以在将 Pod 分配给节点中阅读更多相关信息。
  • TaintTolerationPriority:根据节点上不可容忍污点的数量,为所有节点准备优先级列表。此策略会在考虑该列表的情况下调整节点的等级。
  • ImageLocalityPriority: 优先选择已经拥有 容器镜像 对于本地缓存的 Pod。
  • ServiceSpreadingPriority:对于给定的 Service,此策略旨在确保 Service 的 Pod 运行在不同的节点上。它倾向于调度到没有已分配服务的 Pod 的节点上。总体结果是服务对单个节点故障变得更有弹性。
  • EqualPriority:给所有节点一个相等的权重。
  • EvenPodsSpreadPriority:实现首选 pod 拓扑扩展约束

控制 pod 运行位置

参考学习:将 Pod 指派给节点

你可以约束一个 Pod 只能在特定的 节点 上运行。 通常这样的约束不是必须的,因为调度器将自动进行合理的放置(比如,将 Pod 分散到节点上, 而不是将 Pod 放置在可用资源不足的节点上等等)。不过有些情况我们希望将Pod部署到指定的Node, 比如将有大量磁盘I/O的Pod部署到配置了SSD的Node; 或者Pod需要GPU, 需要运行在配置了GPU的节点上。

你可以使用下列方法中的任何一种来选择 Kubernetes 对特定 Pod 的调度:

nodeName

nodeName 是 PodSpec 的一个字段,其值是节点名称,调度器将Pod调度到给定节点上运行 。

nodeName 是节点选择约束的最简单方法,也是优先级最高的方法,通常不使用。

使用 nodeName 选择节点的一些限制:

  • 如果指定的节点不存在,Pod 将不会运行,甚至会被自动删除。

  • 如果指定的节点没有资源来容纳 Pod,Pod 将会调度失败,并且显示实际原因。

    例如:OutOfmemory 或 OutOfcpu。

  • 云环境中的节点名称并非总是可预测或稳定的。

示例:

示例:

bash 复制代码
root@master30 ~ 11:40:09# kubectl create deployment webapp --image nginx --replicas 2 -o yaml --dry-run=client>deploy-with-nodeName.yaml
root@master30 ~ 11:41:22# vim deploy-with-nodeName.yaml
yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: webapp
  name: webapp
spec:
  replicas: 2
  selector:
    matchLabels:
      app: webapp
  strategy: {}
  template:
    metadata:
      labels:
        app: webapp
    spec:
      # 添加nodeName配置
      nodeName: worker32.ggg.cloud
      containers:
      - image: nginx
        name: nginx
bash 复制代码
root@master30 ~ 11:41:45# kubectl apply  -f deploy-with-nodeName.yaml
root@master30 ~ 11:42:08# kubectl get pods -o wide | awk '{print $1,$3,$7}'
NAME STATUS NODE
webapp-5d7668459f-hjd5s Running worker31.ggg.cloud
webapp-5d7668459f-v582v Running worker31.ggg.cloud
bash 复制代码
# 清理资源
root@master30 ~ 11:42:18# kubectl delete deployments.apps webapp

nodeSelector

nodeSelector 是 PodSpec 的一个字段。 它包含键值对的映射。为了使 pod 可以在某个节点上运行,该节点的标签中 必须包含这里的每个键值对(它也可以具有其他标签)。

**最常见的用法的是使用label。**label是key-value对, 各种资源都可以设置label, 灵活添加各种自定义属性。

提示:nodeSelector 是节点选择约束的推荐形式。

bash 复制代码
# 查看node标签
root@master30 ~ 13:45:30# kubectl get nodes --show-labels 
NAME                 STATUS   ROLES           AGE     VERSION   LABELS
master30.ggg.cloud   Ready    control-plane   7d      v1.30.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master30.ggg.cloud,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=
worker31.ggg.cloud   Ready    <none>          6d22h   v1.30.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker31.ggg.cloud,kubernetes.io/os=linux
worker32.ggg.cloud   Ready    <none>          6d22h   v1.30.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker32.ggg.cloud,kubernetes.io/os=linux

# 标注worker31 disktype是ssd
root@master30 ~ 13:43:02# kubectl label node worker31.ggg.cloud disktype=ssd
root@master30 ~ 13:43:49# kubectl get node -L disktype
NAME                 STATUS   ROLES           AGE     VERSION   DISKTYPE
master30.ggg.cloud   Ready    control-plane   6d23h   v1.30.2   
worker31.ggg.cloud   Ready    <none>          6d22h   v1.30.2   ssd
worker32.ggg.cloud   Ready    <none>          6d22h   v1.30.2 

在Pod模板的spec里通过nodeSelector指定将此Pod部署到具有label为 disktype=ssd 的Node上。

bash 复制代码
root@master30:~# vim deploy-with-nodeSelector.yaml
yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: webapp
  name: webapp
spec:
  replicas: 2
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      # 添加 nodeSelector
      nodeSelector:
        disktype: ssd
      containers:
      - image: hub.laoma.cloud/library/nginx
        name: nginx
bash 复制代码
root@master30:~# kubectl apply -f deploy-with-nodeSelector.yaml
root@master30 ~ 13:44:03# kubectl get pods -o wide 
NAME                       STATUS           NODE        
webapp-7d675f9d9c-h2v2p    Running    worker31.ggg.cloud   
webapp-7d675f9d9c-qr9wb    Running    worker31.ggg.cloud  


# 删除worker31标签
root@master30 ~ 13:44:08# kubectl label nodes worker31.ggg.cloud disktype-
root@master30 ~ 13:44:41# kubectl get node -L disktype
NAME                 STATUS   ROLES           AGE     VERSION   DISKTYPE
master30.ggg.cloud   Ready    control-plane   6d23h   v1.30.2   
worker31.ggg.cloud   Ready    <none>          6d22h   v1.30.2   
worker32.ggg.cloud   Ready    <none>          6d22h   v1.30.2    

# 此时不会触发重新部署deployment

# 删除deployment的nodeSelector
root@master30 ~ 13:44:47# kubectl patch deployment webapp --type json \
-p '[{"op":"remove","path":"/spec/template/spec/nodeSelector"}]'

# 自动触发重新部署
root@master30 ~ 13:44:59# kubectl get pods -o wide | awk '{print $1,$3,$7}'
NAME STATUS NODE
webapp-7fb6dc88f9-2hlq4 Running worker32.ggg.cloud
webapp-7fb6dc88f9-nnvxs Running worker31.ggg.cloud
bash 复制代码
# 清理资源
root@master30 ~ 13:45:23# kubectl delete deployments.apps webapp

Affinity and AntiAffinity

亲和性功能包含两种类型的亲和性,即"节点亲和性"和"Pod 间亲和性/反亲和性"。

nodeAffinity

概念上节点亲和性类似于 nodeSelector,它使你可以根据节点上的标签来约束 Pod 可以调度到哪些节点。

节点亲和性有两种:

  • requiredDuringSchedulingIgnoredDuringExecution ,指定 必须 将 Pod 调度到满足规则的节点上,就像 nodeSelector
  • preferredDuringSchedulingIgnoredDuringExecution,指定 优先 将 Pod 调度到满足规则的节点上,但不会强制执行调度。

注意:如果节点的标签在运行时发生变更,那么 Pod 将仍然继续在该节点上运行。

使用 Pod 规约中的 .spec.affinity.nodeAffinity 字段来设置节点亲和性。

示例:

bash 复制代码
root@master30:~# vim deploy-with-nodeAffinity.yaml
yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web
  name: web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: CPU
                operator: In
                values:
                - L1
                - L2
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 1
            preference:
              matchExpressions:
              - key: MEM
                operator: In
                values:
                - L1
                - L2
      containers:
      - image: hub.laoma.cloud/library/nginx
        name: nginx

示例说明:

  • 此节点亲和性规则表示:Pod 只能放置在具有标签键 CPU 且标签值为 L1L2 的节点上。 另外,在满足这些标准的节点中,具有标签键为 MEM 且标签值为 L1L2 的节点应该优先使用。

  • preferredDuringSchedulingIgnoredDuringExecution 中的 weight 字段值的范围是 1-100。 对于每个符合所有调度要求(资源请求、RequiredDuringScheduling 亲和性表达式等) 的节点,调度器将遍历该字段的元素来计算总和,并且如果节点匹配对应的 MatchExpressions,则添加"权重"到总和。 然后将这个评分与该节点的其他优先级函数的评分进行组合。 总分最高的节点是最优选的。

  • 你可以使用 operator 字段来为 Kubernetes 设置在解释规则时要使用的逻辑操作符。operator(操作符)支持: InNotInExistsDoesNotExistGtLt

  • 你可以使用 NotInDoesNotExist 来实现节点反亲和性行为,或者使用 节点污点 将 Pod 从特定节点中驱逐。

  • 如果你同时指定了 nodeSelectornodeAffinity两者必须都要满足, 才能将 Pod 调度到候选节点上。

  • 如果你指定了多个与 nodeAffinity 类型关联的 nodeSelectorTerms,则 如果其中一个 nodeSelectorTerms 满足的话,pod将可以调度到节点上。

  • 如果你指定了多个与 nodeSelectorTerms 关联的 matchExpressions,则 只有当所有 matchExpressions 满足的话,Pod 才会可以调度到节点上。

如果你修改或删除了 pod 所调度到的节点的标签,Pod 不会被删除。 换句话说,亲和性选择只在 Pod 调度期间有效。

验证1:节点未打标签

bash 复制代码
root@master30 ~ 13:59:41# kubectl apply -f deploy-with-nodeAffinity.yaml 
root@master30 ~ 13:59:54# kubectl get pods
NAME                  READY   STATUS    RESTARTS   AGE
web-c74fd5fbd-6xs2z   0/1     Pending   0          5s
web-c74fd5fbd-wcwgm   0/1     Pending   0          5s
#节点是挂起状态
#可以用kubectl describe pod 某个节点name查看失败情况

验证2:节点打 CPU 标签

bash 复制代码
root@master30 ~ 13:59:59# kubectl label nodes worker31.ggg.cloud CPU=L1
root@master30 ~ 14:00:29# kubectl label nodes worker32.ggg.cloud CPU=L2

# 再次查看pod调度情况
root@master30 ~ 14:00:35#  kubectl get pods -o wide --no-headers |awk '{print $1,$3,$7}'
web-c74fd5fbd-6xs2z Running worker31.ggg.cloud
web-c74fd5fbd-wcwgm Running worker31.ggg.cloud

#因为31节点先打上标签,所以pod都调度到31上了
#重新部署
root@master30 ~ 14:00:44# kubectl rollout restart deployment web 
root@master30 ~ 14:01:37# kubectl get pods -o wide --no-headers |awk '{print $1,$3,$7}'
web-5d5b5c99f9-ttpvd Running worker31.ggg.cloud
web-5d5b5c99f9-xm8cr Running worker32.ggg.cloud

验证3:worker32节点打标签MEM

bash 复制代码
root@master30 ~ 14:01:47# kubectl label nodes worker32.ggg.cloud MEM=L2

# 重新部署
root@master30 ~ 14:02:15# kubectl rollout restart deployment web 
deployment.apps/web restarted

# 只在worker32上运行
root@master30 ~ 14:02:21# kubectl get pods -o wide --no-headers |awk '{print $1,$3,$7}'
web-cd7b88d47-2ht82 Running worker32.ggg.cloud
web-cd7b88d47-dlhrs Running worker32.ggg.cloud

清理环境

bash 复制代码
root@master30:~# kubectl delete deployments.apps web
root@master30:~# kubectl label nodes worker31.ggg.cloud CPU-
root@master30:~# kubectl label nodes worker32.ggg.cloud CPU-
root@master30:~# kubectl label nodes worker32.ggg.cloud MEM-

节点亲和性权重

用户可以为 preferredDuringSchedulingIgnoredDuringExecution 亲和性类型的每个实例设置 weight 字段,其取值范围是 1 到 100。 当调度器找到能够满足 Pod 的其他调度请求的节点时,调度器会遍历节点满足的所有的偏好性规则, 并将对应表达式的 weight 值加和。

最终的加和值会添加到该节点的其他优先级函数的评分之上。 在调度器为 Pod 作出调度决定时,总分最高的节点的优先级也最高。

例如,考虑下面的 Pod 规约:

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: with-affinity-anti-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/os
            operator: In
            values:
            - linux
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: label-1
            operator: In
            values:
            - key-1
      - weight: 50
        preference:
          matchExpressions:
          - key: label-2
            operator: In
            values:
            - key-2
  containers:
  - name: with-node-affinity
    image: hub.laoma.cloud/library/nginx

如果存在两个候选节点,都满足 preferredDuringSchedulingIgnoredDuringExecution 规则, 其中一个节点具有标签 label-1: key-1,另一个节点具有标签 label-2: key-2, 调度器会考察各个节点的 weight 取值,并将该权重值添加到节点的其他得分值之上。

Inter-pod affinity and anti-affinity

Pod 间 亲和性反亲和性 提供基于已经在节点上运行的 Pod 的标签来约束 Pod 可以调度到的节点,而不是基于节点上的标签。

Pod 间亲和性与反亲和性规则的格式为,如果 X 节点上已经运行了一个或多个满足规则 Y 的 Pod:

  • 在亲和性的情况下,Pod 应该运行在 X 节点。
  • 在反亲和性的情况下,Pod 不应该运行在 X 节点。

说明:

  • 这里的 X 可以是节点、机架、云提供商可用区或地理区域或类似的拓扑域。你可以使用 topologyKey 来表示它,topologyKey 是节点标签的键以便系统 用来表示这样的拓扑域。
  • Y 则是 Kubernetes 尝试满足的规则,通过标签选择算符 的形式来表达规则(Y)。

注意:

  • Pod 间亲和性与反亲和性会消耗大量计算资源,可能会显著减慢大规模集群中的调度。 我们不建议在超过数百个节点的集群中使用它们。
  • Pod 反亲和性需要对节点进行一致的标记,即集群中的每个节点必须具有适当的标签匹配 topologyKey。如果某些或所有节点缺少指定的 topologyKey 标签,可能会导致意外行为。

Pod 间亲和性与反亲和性的类型:

  • requiredDuringSchedulingIgnoredDuringExecution,例如将两个通信非常频繁的 Pod 调度到同一个可用区内。
  • preferredDuringSchedulingIgnoredDuringExecution,例如将同一服务的多个 Pod 调度到不同可用区中。

配置位置:

使用 Pod 规约中的 .affinity.podAffinity 字段设置Pod 间亲和性。

使用 Pod 规约中的 .affinity.podAntiAffinity 字段设置Pod 间反亲和性。

使用场景:

Pod 间亲和性与反亲和性可以轻松配置一组应位于相同定义拓扑(例如,节点)中的工作负载。在与更高级别的集合(例如 ReplicaSets、StatefulSets、 Deployments 等)一起使用时,更加有用。

语法说明

本示例定义了一条 Pod 亲和性规则和一条 Pod 反亲和性规则。

bash 复制代码
root@master30:~# vim pod-with-podaffinity.yaml
yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: pod-with-podaffinity
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: pod-with-podaffinity
    image: hub.laoma.cloud/library/nginx
  • 亲和性规则规定 ,只有节点属于特定的 区域 且该区域中的其他 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 内的任何节点。相反,如果 Zone R 中没有带有 security=S2 标签的 Pod, 则反亲和性规则不会影响将 Pod 调度到 Zone R。

补充说明:

  • Pod 亲和性与反亲和性的合法操作符有 InNotInExistsDoesNotExist
  • 原则上,topologyKey 可以是任何合法的标签键。 出于性能和安全考虑,topologyKey 受到一些限制:
    1. 对于 Pod 亲和性而言,在 requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution 中,topologyKey 不允许为空。
    2. 对于 Pod 反亲和性而言,requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution 中,topologyKey 不允许为空。
    3. 除上述情况外,topologyKey 可以是任何合法的标签键。
示例 1:根据节点拓扑--亲和性调度
bash 复制代码
# node节点打标签
root@master30 ~ 14:25:46# kubectl label nodes worker31.ggg.cloud topology.kubernetes.io/zone=v
root@master30 ~ 14:25:22# kubectl label nodes worker32.ggg.cloud topology.kubernetes.io/zone=v

# 创建两个pod
root@master30:~# vim deploy-with-podaffinity.yaml
yaml 复制代码
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web1
spec:
  selector:
    matchLabels:
      app: web1
  replicas: 1
  template:
    metadata:
      labels:
        app: web1
    spec:
      nodeName: worker31.ggg.cloud
      containers:
      - name: web-server
        image: hub.laoma.cloud/library/nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web2
spec:
  selector:
    matchLabels:
      app: web2
  replicas: 1
  template:
    metadata:
      labels:
        app: web2
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web1
            topologyKey: topology.kubernetes.io/zone
      containers:
      - name: web-server
        image: hub.laoma.cloud/library/nginx
bash 复制代码
root@master30 ~ 14:28:51# kubectl apply -f deploy-with-podaffinity.yaml 
root@master30 ~ 14:29:07# kubectl get pods -o wide --no-headers |awk '{print $1,$3,$7}'
web1-7d8dbb6494-fqjdk Running worker31.ggg.cloud
web2-767459494f-f65s6 Running worker32.ggg.cloud

**实验结果:**pod-with-podaffinity 调度到 worker32节点。

如果此时将work32节点的 topology.kubernetes.io/zone 标签删除,重新创建pod pod-with-podaffinity,会调度到哪个节点呢?

bash 复制代码
root@master30 ~ 14:29:13# kubectl label nodes worker32.ggg.cloud topology.kubernetes.io/zone-
root@master30 ~ 14:29:40# kubectl rollout restart deployment web2

# 调度到worker31节点
root@master30 ~ 14:29:56# kubectl get pods -o wide --no-headers |awk '{print $1,$3,$7}'
web1-7d8dbb6494-fqjdk Running worker31.ggg.cloud
web2-8dcdf9769-v85tw Running worker31.ggg.cloud
bash 复制代码
# 清理环境
root@master30:~# kubectl delete -f deploy-with-podaffinity.yaml 
root@master30:~# kubectl label nodes worker31.ggg.cloud \
topology.kubernetes.io/zone-

**结论:**pod 亲和性调度必须满足两个条件。

  1. **node 必须具有相应标签。**使用标签可以将node定义为属于同一个机架,也可以属于同一个房间等物理区域。
  2. 在具有相应标签的 node上至少运行一个具有相应标签的pod。
示例 2:根据节点主机名--反亲和性调度

下面是一个简单 redis Deployment 的 YAML 代码段,它有三个副本和选择器标签 app=store。 Deployment 配置了 PodAntiAffinity,用来确保调度器不会将多个副本调度到单个节点上。

bash 复制代码
# 准备deployment
root@master30 ~ 14:30:42# vim deploy-with-nodeAffinity.yaml
yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: store
spec:
  selector:
    matchLabels:
      app: store
  replicas: 3
  template:
    metadata:
      labels:
        app: store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: web-server
        image: hub.laoma.cloud/library/nginx
bash 复制代码
# 查看pod调度情况
root@master30 ~ 14:44:53# kubectl apply -f deploy-with-nodeAffinity.yaml 
root@master30 ~ 14:45:26# kubectl get pods -o wide --no-headers |awk '{print $1,$3,$7}'
store-bff9b9ffd-2lg4q Running worker32.ggg.cloud
store-bff9b9ffd-cqdbn Running worker31.ggg.cloud
store-bff9b9ffd-xlbhj Pending <none>

结果:在 topologyKey: "kubernetes.io/hostname作用下,**每个节点只能运行一个pod。**因为每个节点的hostname都是自己的主机名,是唯一的。最后一个pod,找不到可用节点。

示例 3:多个应用亲和性和反亲和性调度

下面 webserver Deployment 的 YAML 代码段中配置了 podAntiAffinitypodAffinity,确保每个 web 服务器副本不会调度到单个节点上,同时所有副本与具有 app=store 选择器标签的 Pod 放置在一起。

bash 复制代码
# 缩容 deployment
root@master30 ~ 15:03:04# kubectl scale deployment store --replicas 1
root@master30 ~ 15:03:16# kubectl get pods -o wide --no-headers |awk '{print $1,$3,$7}'
store-bff9b9ffd-h8txg Running worker31.ggg.cloud

# 准备新deployment
root@master30 ~ 15:03:26# vim deploy-with-multi-Affinity.yaml
yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-store
spec:
  selector:
    matchLabels:
      app: web-store
  replicas: 1
  template:
    metadata:
      labels:
        app: web-store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web-store
            topologyKey: "kubernetes.io/hostname"
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: web-app
        image: hub.laoma.cloud/library/nginx
bash 复制代码
root@master30 ~ 15:03:39# kubectl apply -f deploy-with-multi-Affinity.yaml
root@master30 ~ 15:03:52# kubectl get pods -o wide --no-headers |awk '{print $1,$3,$7}'
store-bff9b9ffd-h8txg Running worker31.ggg.cloud
web-store-58757df49b-msm9t Running worker31.ggg.cloud

# 扩容:验证新副本位置
root@master30 ~ 15:03:56# kubectl scale deployment web-store --replicas 2
root@master30 ~ 15:05:33# kubectl get pods -o wide --no-headers |awk '{print $1,$3,$7}'
store-bff9b9ffd-h8txg Running worker31.ggg.cloud
web-store-58757df49b-b4qsk Pending <none>
web-store-58757df49b-msm9t Running worker31.ggg.cloud
# 在亲和性作用下,只有worker31节点具备满足含有标签store的pod
# 在反亲和性作用下,worker31节点上已经运行了含有标签web-store的pod
# 最终结果:由于deploy-with-multi-Affinity.yaml中规定了亲和store,但是deploy-with-nodeAffinity.yaml中规定了反亲和 store,矛盾了,所以新扩容的被挂起

Taint 和 Toleration

学习参考:污点和容忍度

  • node 使用 Taint(污点),允许特定Pod在本机运行,未匹配的Pod则不能在该节点运行。

  • pod 使用 Toleration(容忍度),允许被调度到带有与之匹配的污点的节点上。

污点和容忍度相互配合,可以用来避免 Pod 被分配到不合适的节点上。 每个节点上都可以应用一个或多个污点,这表示对于那些不能容忍这些污点的 Pod,是不会被该节点接受的。

思考:nodeAffinity 和 Taint 区别?

  • nodeAffinity,Pod 选 Node 的必要条件。
  • Taint,Node 选 Pod 的必要条件。

设置 Pod tolerations

在 Pod.Spec 中定义 Pod 的 tolerations。

示例

yaml 复制代码
tolerations:
- key: "CPU"
  operator: "Equal"
  value: "L1"
  effect: "NoSchedule"

operator 可选值

  • Equal,默认值,容忍度和污点的键值对相同,则"匹配"。

  • Exists,此时容忍度不能指定 value,只要污点中存在 key ,则容忍度和污点相"匹配"。

    operator 匹配逻辑 是否需要 value
    Equal key 相等 并且 value 相等 ✅ 必须写 value
    Exists 只要 key 存在即可,value 无视 ❌ 不能写 value
    bash 复制代码
      tolerations:
      - key: "CPU"
        operator: "Exists"
        effect: "NoSchedule"

effect 可选值

effect 翻译 作用
NoSchedule 不调度 调度阶段拦截 :不具备对应容忍的 Pod,不能调度到这个节点;已经在节点上跑着的 Pod,不受影响,不会被驱逐
PreferNoSchedule 尽量不调度 🟡 软限制:调度器尽量避开这个节点;实在没别的节点可用,依然可以调度上来
NoExecute 不执行(驱逐) 🔴 强驱逐:1. 不能调度新 Pod 上来2. 已经运行在这个节点、无对应容忍的 Pod,会直接被驱逐删掉(可加 tolerationSeconds 设置等待时间)

**注意:**如果容忍度的 key 为空且 operatorExists, 表示这个容忍度与任意的 key 、value 和 effect 都匹配,即这个容忍度能容忍任意 taint。

一个容忍度和一个污点 匹配

示例:

设置 node 污点:

bash 复制代码
root@master30 ~ 15:48:54# kubectl taint nodes worker31.ggg.cloud CPU=L1:NoSchedule
root@master30 ~ 15:49:35# kubectl taint nodes worker32.ggg.cloud CPU=L2:NoSchedule

创建 pod :

bash 复制代码
root@master30 ~ 15:49:44#  vim deploy-with-tolerations.yaml
yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web
  name: web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      tolerations:
      - key: "CPU"
        operator: "Equal"
        value: "L3"
        effect: "NoSchedule"
      containers:
      - name: nginx
        image: hub.laoma.cloud/library/nginx
        imagePullPolicy: IfNotPresent
bash 复制代码
# 创建上述pod示例
root@master30 ~ 15:49:56# kubectl apply -f deploy-with-tolerations.yaml 

root@master30 ~ 15:50:07# kubectl get pods
NAME                   READY   STATUS    RESTARTS   AGE
web-7d9d5f896c-949vw   0/1     Pending   0          5s
web-7d9d5f896c-9w5tj   0/1     Pending   0          5s
# pod状态为Pending

# 事件表明3个节点上的污点与pod不匹配,所以无法创建pod
root@master30 ~ 15:50:12# kubectl describe pod web-7d9d5f896c-949vw
......
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  46s   default-scheduler  0/3 nodes are available: 1 node(s) had untolerated taint {CPU: L1}, 1 node(s) had untolerated taint {CPU: L2}, 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }. preemption: 0/3 nodes are available: 3 Preemption is not helpful for scheduling.


# 删除 deployments,将容忍度改为CPU=L1
root@master30 ~ 15:50:53# kubectl delete deployments.apps web
root@master30 ~ 15:51:12# vim deploy-with-tolerations.yaml
yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web
  name: web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      tolerations:
      - key: "CPU"
        operator: "Equal"
        value: "L1"
        effect: "NoSchedule"
      containers:
      - name: nginx
        image: hub.laoma.cloud/library/nginx
        imagePullPolicy: IfNotPresent
bash 复制代码
root@master30 ~ 15:51:24# kubectl apply -f deploy-with-tolerations.yaml 
root@master30 ~ 15:51:35# kubectl get pods -o wide 
NAME                  READY   STATUS    RESTARTS   AGE   IP              NODE                 NOMINATED NODE   READINESS GATES
web-78945b5cc-jf8xs   1/1     Running   0          21s   10.224.26.153   worker31.ggg.cloud   <none>           <none>
web-78945b5cc-vdvnv   1/1     Running   0          21s   10.224.26.152   worker31.ggg.cloud   <none>           <none>

# Pod调度成功,而且分配到了worker31

# 清理环境
root@master30 ~ 15:52:30# kubectl taint node worker31.ggg.cloud CPU:NoSchedule-
root@master30 ~ 15:52:36# kubectl taint node worker32.ggg.cloud CPU:NoSchedule-
root@master30 ~ 15:52:40# kubectl delete deployments.apps web

多个容忍度和多个污点 匹配

Kubernetes 可以给一个节点添加多个污点,也可以给一个 Pod 添加多个容忍度设置。

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

  • 如果未被过滤的污点中存在至少一个 effect 值为 NoSchedule 的污点, 则 Kubernetes 不会将 Pod 分配到该节点。
  • 如果未被过滤的污点中不存在 effect 值为 NoSchedule 的污点, 但存在 effect 值为 PreferNoSchedule 的污点, 则 Kubernetes 尝试 不将 Pod 分配到该节点。
  • 如果未被过滤的污点中存在至少一个 effect 值为 NoExecute 的污点, 则 Kubernetes 不会将 Pod 分配到该节点(如果 Pod 还未在节点上运行), 或者将 Pod 从该节点驱逐(如果 Pod 已经在节点上运行)。

例如,某个节点添加了如下污点:

shell 复制代码
root@master30:~# kubectl taint nodes worker31.ggg.cloud CPU=L1:NoSchedule
root@master30:~# kubectl taint nodes worker31.ggg.cloud CPU=L1:NoExecute
root@master30:~# kubectl taint nodes worker31.ggg.cloud MEM=L2:NoSchedule

假定某个 Pod 有两个容忍度:

yaml 复制代码
tolerations:
- key: "CPU"
  operator: "Equal"
  value: "L1"
  effect: "NoSchedule"
- key: "CPU"
  operator: "Equal"
  value: "L1"
  effect: "NoExecute"

在这种情况下,上述 Pod 不会被调度到上述节点,因为其没有容忍度和第三个污点相匹配。 但是如果在给节点添加上述污点之前,该 Pod 已经在上述节点运行, 那么它还可以继续运行在该节点上,因为第三个污点是三个污点中唯一不能被这个 Pod 容忍的。

示例:

设置 node 多个污点

bash 复制代码
root@master30 ~ 15:52:45# kubectl taint nodes worker31.ggg.cloud CPU=L1:NoSchedule
root@master30 ~ 15:53:25# kubectl taint nodes worker31.ggg.cloud CPU=L1:NoExecute
root@master30 ~ 15:53:40# kubectl taint nodes worker31.ggg.cloud MEM=L2:NoSchedule
root@master30 ~ 15:54:01# kubectl taint nodes worker32.ggg.cloud CPU=L2:NoSchedule

创建 Deployment:

bash 复制代码
root@master30 ~ 15:54:09# vim deploy-with-tolerations.yaml
yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web
  name: web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      tolerations:
      - key: "CPU"
        operator: "Equal"
        value: "L1"
        effect: "NoSchedule"
      - key: "CPU"
        operator: "Equal"
        value: "L1"
        effect: "NoExecute"
      containers:
      - name: nginx
        image: hub.laoma.cloud/library/nginx
        imagePullPolicy: IfNotPresent
bash 复制代码
# 创建上述pod示例
root@master30 ~ 15:55:29# kubectl apply -f deploy-with-tolerations.yaml
root@master30 ~ 15:55:57# kubectl get pods
NAME                   READY   STATUS    RESTARTS   AGE
web-745f6fc44d-xlwm2   0/1     Pending   0          8s
web-745f6fc44d-zh47n   0/1     Pending   0          8s
# pod状态为Pending

# 事件表明节点上的3个污点与pod不匹配,所以无法创建pod
root@master30 ~ 15:55:59# kubectl describe pod web-745f6fc44d-xlwm2 
......
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  34s   default-scheduler  0/3 nodes are available: 1 node(s) had untolerated taint {CPU: L2}, 1 node(s) had untolerated taint {MEM: L2}, 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }. preemption: 0/3 nodes are available: 3 Preemption is not helpful for scheduling.

# 删除deployments,添加容忍度MEM=L2
root@master30 ~ 15:58:09# kubectl delete deployments.apps web
root@master30:~# vim deploy-with-tolerations.yaml
yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web
  name: web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      tolerations:
      - key: "CPU"
        operator: "Equal"
        value: "L1"
        effect: "NoSchedule"
      - key: "CPU"
        operator: "Equal"
        value: "L1"
        effect: "NoExecute"
      - key: "MEM"
        operator: "Equal"
        value: "L2"
        effect: "NoSchedule"
      containers:
      - name: nginx
        image: hub.laoma.cloud/library/nginx
        imagePullPolicy: IfNotPresent
bash 复制代码
root@master30 ~ 15:58:38# kubectl apply -f deploy-with-tolerations.yaml 
root@master30 ~ 15:58:47# kubectl get pod -o wide 
NAME                  READY   STATUS    RESTARTS   AGE   IP              NODE                 NOMINATED NODE   READINESS GATES
web-65bfcb4fc-7llcw   1/1     Running   0          5s    10.224.26.155   worker31.ggg.cloud   <none>           <none>
web-65bfcb4fc-rj75k   1/1     Running   0          5s    10.224.26.154   worker31.ggg.cloud   <none>           <none>
# Pod 调度成功,而且分配到了worker31

# 清理环境
root@master30:~# kubectl taint nodes worker31.ggg.cloud CPU=L1:NoSchedule-
root@master30:~# kubectl taint nodes worker31.ggg.cloud CPU=L1:NoExecute-
root@master30:~# kubectl taint nodes worker31.ggg.cloud MEM=L2:NoSchedule-
root@master30:~# kubectl taint nodes worker32.ggg.cloud CPU=L2:NoSchedule-
root@master30:~# kubectl delete deployments.apps web

Taint 和 Toleration 用例

通过污点和容忍度,可以灵活地让 Pod 避开 某些节点或者将 Pod 从某些节点驱逐。

下面是几个使用例子:

  • 用户专用节点 :如果想将某些节点专门分配给特定的一组用户使用,可以给这些节点添加一个污点,例如 dedicated=groupName:NoSchedule 。然后给这组用户的 Pod 添加一个相对应的 toleration。 拥有上述容忍度的 Pod 就能够被分配到上述专用节点,同时也能够被分配到集群中的其它节点。

    如果希望这些 Pod 只能被分配到上述专用节点,那么还需要给这些专用节点添加一个和上述 污点类似的 label ,例如 dedicated=groupName ,同时还要给 Pod 增加节点亲和性,要求上述 Pod 只能被分配到添加了 dedicated=groupName 标签的节点上。

  • 配备了特殊硬件的节点 :在部分节点配备了特殊硬件(比如 GPU)的集群中, 我们希望不需要这类硬件的 Pod 不要被分配到这些特殊节点,以便为后继需要这类硬件的 Pod 保留资源。 要达到这个目的,可以先给配备了特殊硬件的节点添加 taint ,例如 special=true:NoSchedule, 然后给使用这类特殊硬件的 Pod 添加一个相匹配的 toleration。

基于污点的驱逐

前文提到过污点 effect 值 NoExecute 会影响已经在节点上运行的 Pod:

  • 如果 Pod 不能忍受 effect 值为 NoExecute 的污点,那么 Pod 将马上被驱逐。
  • 如果 Pod 能够忍受 effect 值为 NoExecute 的污点,但是在容忍度定义中没有指定 tolerationSeconds,则 Pod 还会一直在这个节点上运行。
  • 如果 Pod 能够忍受 effect 值为 NoExecute 的污点,而且指定了 tolerationSeconds, 则 Pod 还能在这个节点上继续运行这个指定的时间长度。

当某种条件为真时,节点控制器会自动给节点添加一个污点。当前内置的污点包括:

  • node.kubernetes.io/not-ready:节点未准备好。这相当于节点状态 Ready 的值为 "False"。
  • node.kubernetes.io/unreachable:节点控制器访问不到节点. 这相当于节点状态 Ready 的值为 "Unknown"。
  • node.kubernetes.io/memory-pressure:节点存在内存压力。
  • node.kubernetes.io/disk-pressure:节点存在磁盘压力。
  • node.kubernetes.io/pid-pressure: 节点的 PID 压力。
  • node.kubernetes.io/network-unavailable:节点网络不可用。
  • node.kubernetes.io/unschedulable: 节点不可调度。
  • node.cloudprovider.kubernetes.io/uninitialized:如果 kubelet 启动时指定了一个 "外部" 云平台驱动, 它将给当前节点添加一个污点将其标志为不可用。在 cloud-controller-manager 的一个控制器初始化这个节点后,kubelet 将删除这个污点。

节点被驱逐时,节点控制器或者 kubelet 会添加带有 NoExecute 效应的相关污点。 如果异常状态恢复正常,kubelet 或节点控制器能够移除相关的污点。

说明: 为了保证由于节点问题引起的 Pod 驱逐 速率限制 行为正常, 系统实际上会以限定速率的方式添加污点。在像主控节点与工作节点间通信中断等场景下, 这样做可以避免 Pod 被大量驱逐。

使用这个功能特性,结合 tolerationSeconds,Pod 就可以指定当节点出现一个 或全部上述问题时还将在这个节点上运行多长的时间。

比如,一个使用了很多本地状态的应用程序在网络断开时,仍然希望停留在当前节点上运行一段较长的时间, 愿意等待网络恢复以避免被驱逐。在这种情况下,Pod 的容忍度可能是下面这样的:

yaml 复制代码
tolerations:
- key: "node.kubernetes.io/unreachable"
  operator: "Exists"
  effect: "NoExecute"
  tolerationSeconds: 6000

说明:

Kubernetes 会自动给 Pod 添加一个 key 为 node.kubernetes.io/not-ready 的容忍度,并配置 tolerationSeconds=300,除非用户提供的 Pod 配置中已经已存在了 key 为 node.kubernetes.io/not-ready 的容忍度。

同样,Kubernetes 会给 Pod 添加一个 key 为 node.kubernetes.io/unreachable 的容忍度并配置 tolerationSeconds=300,除非用户提供的 Pod 配置中已经已存在了 key 为 node.kubernetes.io/unreachable 的容忍度。

这种自动添加的容忍度意味着在其中一种问题被检测到时 Pod 默认能够继续停留在当前节点运行 5 分钟。

DaemonSet 中的 Pod 被创建时, 针对以下污点自动添加的 NoExecute 的容忍度将不会指定 tolerationSeconds

  • node.kubernetes.io/unreachable
  • node.kubernetes.io/not-ready

这保证了出现上述问题时 DaemonSet 中的 Pod 永远不会被驱逐。

cordon 和 uncordon

kubectl cordon 命令,标记node为不可调度,将无法在node上创建新的pod。

bash 复制代码
root@master30:~# kubectl cordon -h
Mark node as unschedulable.

Usage:
  kubectl cordon NODE [options]

root@master30:~# kubectl get nodes
NAME                  STATUS   ROLES           AGE   VERSION
master30.ggg.cloud   Ready    control-plane   9d    v1.30.2
worker31.ggg.cloud   Ready    <none>          9d    v1.30.2
worker32.ggg.cloud   Ready    <none>          9d    v1.30.2

# 标记worker31.ggg.cloud为SchedulingDisabled
root@master30:~# kubectl cordon worker31.ggg.cloud 
root@master30:~# kubectl get nodes
NAME                  STATUS                     ROLES           AGE   VERSION
master30.ggg.cloud   Ready                      control-plane   9d    v1.30.2
worker31.ggg.cloud   Ready,SchedulingDisabled   <none>          9d    v1.30.2
worker32.ggg.cloud   Ready                      <none>          9d    v1.30.2


# 创建一个deployment
root@master30:~# kubectl create deployment web --image=hub.laoma.cloud/library/nginx --replicas=2
root@master30:~# kubectl get pod -o wide
NAME                   READY   STATUS    RESTARTS   AGE   IP              NODE               NOMINATED NODE   READINESS GATES
web-59b9bb7664-flhvx   1/1     Running   0          10s   10.224.225.69   worker32.ggg.cloud   <none>           <none>
web-59b9bb7664-ss586   1/1     Running   0          10s   10.224.225.68   worker32.ggg.cloud   <none>           <none>

# 取消worker31.ggg.cloud标记SchedulingDisabled
root@master30:~# kubectl uncordon worker31.ggg.cloud 
root@master30:~# kubectl get nodes
NAME                STATUS   ROLES    AGE   VERSION
master.ggg.cloud   Ready    control-plane   36d   v1.30.2
worker31.ggg.cloud    Ready    <none>   36d   v1.30.2
worker32.ggg.cloud    Ready    <none>   36d   v1.30.2

root@master30:~# kubectl scale deployment web --replicas=4
root@master30:~# kubectl get pod -o wide
NAME                   READY   STATUS    RESTARTS   AGE    IP              NODE               NOMINATED NODE   READINESS GATES
web-59b9bb7664-flhvx   1/1     Running   0          2m6s   10.224.225.69   worker32.ggg.cloud   <none>           <none>
web-59b9bb7664-jkb5q   1/1     Running   0          5s     10.224.51.133   worker31.ggg.cloud   <none>           <none>
web-59b9bb7664-ss586   1/1     Running   0          2m6s   10.224.225.68   worker32.ggg.cloud   <none>           <none>
web-59b9bb7664-w6mbs   1/1     Running   0          5s     10.224.51.132   worker31.ggg.cloud   <none>           <none>

drain

学习参考:安全地清空一个节点

在对节点执行维护(例如内核升级、硬件维护等)之前, 可以使用 kubectl drain 从节点安全地逐出所有 Pod。 安全的驱逐过程允许 Pod 的容器体面地终止, 并确保满足指定的 PodDisruptionBudgets

准备开始

此任务假定你已经满足了以下先决条件:

  1. 在节点清空期间,确保应用具有高可用性。
  2. 你已经了解了 PodDisruptionBudget 的概念, 并为需要它的应用配置了 PodDisruptionBudget

为了确保负载在维护期间仍然可用,可以配置一个 PodDisruptionBudget。 如果可用性对于正在清空的该节点上运行或可能在该节点上运行的任何应用程序很重要, 首先 配置一个 PodDisruptionBudgets 并继续遵循本指南。

建议为你的 PodDisruptionBudgets 设置 AlwaysAllow 不健康 Pod 驱逐策略, 以在节点清空期间支持驱逐异常的应用程序。 默认行为是等待应用程序的 Pod 变为 健康后, 才能进行清空操作。

kubectl drain 实践

kubectl drain 命令,驱逐 node 上所有 pod,同时标记 node 为不可调度。默认情况下,该命令忽略节点上不能驱逐的 Pod。有关更多细节,请参阅 kubectl drain 文档。

bash 复制代码
root@master30:~# kubectl drain -h
Drain node in preparation for maintenance.

 The given node will be marked unschedulable to prevent new pods from arriving.
'drain' evicts the pods if the APIServer supports.

Usage:
  kubectl drain NODE [options]
  
# 默认不驱逐 DaemonSet 控制的pod
root@master30:~# kubectl drain worker31.ggg.cloud 
node/worker31.ggg.cloud cordoned
error: unable to drain node "worker31.ggg.cloud", aborting command...

There are pending nodes to be drained:
 worker31.ggg.cloud
error: cannot delete DaemonSet-managed Pods (use --ignore-daemonsets to ignore): kube-system/calico-node-jwdzx, kube-system/kube-proxy-98qkl

# 使用--ignore-daemonsets驱逐DaemonSet控制的pod
root@master30:~# kubectl drain worker31.ggg.cloud --ignore-daemonsets 
node/worker31.ggg.cloud already cordoned
WARNING: ignoring DaemonSet-managed Pods: kube-system/calico-node-jwdzx, kube-system/kube-proxy-98qkl
evicting pod kube-system/kuboard-74c645f5df-ljk98
evicting pod ggg/web-59b9bb7664-jkb5q
evicting pod ggg/web-59b9bb7664-w6mbs
pod/web-59b9bb7664-w6mbs evicted
pod/web-59b9bb7664-jkb5q evicted
pod/kuboard-74c645f5df-ljk98 evicted
node/worker31.ggg.cloud evicted

# 还可以使用以下选项驱逐特定pod:
  --pod-selector='': Label selector to filter pods on the node
  -l, --selector='': Selector (label query) to filter on,supports '=', '==', and '!='.(e.g. -l CPU=L1,MEM=L2). Matching objects must satisfy all of the specified label constraints.

root@master30:~# kubectl get node
NAME                   STATUS                        ROLES        AGE   VERSION
master30.ggg.cloud   Ready                         control-plane   36d   v1.30.2
worker31.ggg.cloud   Ready,SchedulingDisabled   <none>       36d   v1.30.2
worker32.ggg.cloud   Ready                      <none>       36d   v1.30.2

# 删除worker31上SchedulingDisabled标记
root@master30:~# kubectl uncordon worker31.ggg.cloud 

并行清空多个节点

kubectl drain 命令一次只能发送给一个节点,可以在不同的终端或后台为不同的节点并行地运行多个 kubectl drain 命令。 同时运行的多个 drain 命令仍然遵循你指定的 PodDisruptionBudget

例如,一个三副本的 Deployments, 设置了一个PodDisruptionBudget,指定 minAvailable: 2。 如果所有的三个 Pod 处于健康(healthy)状态, 并且你并行地发出多个 drain 命令,那么 kubectl drain 只会从 Deployments中逐出一个 Pod, 因为 Kubernetes 会遵守 PodDisruptionBudget 并确保在任何时候只有一个 Pod 不可用 (最多不可用 Pod 个数的计算方法:replicas - minAvailable)。 任何会导致处于健康(healthy) 状态的副本数量低于指定预算的清空操作都将被阻止。

环境清理

bash 复制代码
root@master30:~# kubectl delete ns scheduler
相关推荐
GGG7662 小时前
k8s-动态卷供应
云原生·容器·kubernetes
yunwei372 小时前
eBPF 教程:使用 fsession 追踪慢速 vfs_read 调用
linux·云原生
叶 落3 小时前
Docker-Compose 安装 Milvus
docker·容器·milvus
深念Y4 小时前
Windows → WSL2 全面迁移:工具链、Docker、OpenCode
linux·运维·windows·docker·容器·环境·wsl
heimeiyingwang15 小时前
【架构实战】Kubernetes存储实战:从EmptyDir到Ceph CSI的持久化存储选型指南
ceph·架构·kubernetes
QX_hao15 小时前
【Kubernetes】 的三个Probe
容器·kubernetes
Henry-SAP19 小时前
SAP S/4HANA引领物流ERP新生态
人工智能·云原生·sap·erp
这个DBA有点耶19 小时前
从DBA到数据架构师(五):数据架构演进中的技术债务管理
数据库·程序人生·云原生·架构·dba·数据库管理员
猫吃了源码20 小时前
k9s简介和安装
linux·docker·kubernetes