Kubernetes 节点摘除指南

举报
William 发表于 2025/05/08 09:18:26 2025/05/08
【摘要】 Kubernetes 节点摘除指南介绍 (Introduction)在 Kubernetes 集群的生命周期管理中,移除节点是一个常见的操作。原因可能包括:执行节点维护(如操作系统升级、硬件更换)、集群扩缩容、节点故障等。简单粗暴地直接关闭或删除节点可能导致在该节点上运行的应用 Pod 意外终止,影响服务的可用性。Kubernetes 提供了一套标准的、优雅的节点摘除流程,旨在最大限度地减少...

Kubernetes 节点摘除指南

介绍 (Introduction)

在 Kubernetes 集群的生命周期管理中,移除节点是一个常见的操作。原因可能包括:执行节点维护(如操作系统升级、硬件更换)、集群扩缩容、节点故障等。简单粗暴地直接关闭或删除节点可能导致在该节点上运行的应用 Pod 意外终止,影响服务的可用性。

Kubernetes 提供了一套标准的、优雅的节点摘除流程,旨在最大限度地减少对应用 Pod 的影响。这个流程包括三个主要阶段:锁定 (Cordon)驱逐 (Drain)删除 (Delete)。本指南将详细介绍这三个步骤以及相关的实践操作。

引言 (Foreword/Motivation)

Kubernetes 集群是动态变化的,节点可能会因为各种原因加入或离开。当一个节点需要离线时,我们希望其上运行的应用 Pod 能够被安全地迁移到集群中的其他可用节点上,而不是突然死亡。这种优雅的节点移除是保障应用高可用性和无中断维护的关键。

通过遵循标准的摘除流程,我们可以确保:

  • 新的 Pod 不会被调度到即将移除的节点上。
  • 正在运行的 Pod 会被逐步、安全地终止,并由 Kubernetes 的控制器(如 Deployment, StatefulSet)在其他健康节点上重新创建。
  • 考虑到应用的可用性预算 (PodDisruptionBudget, PDB),避免一次性驱逐太多 Pod 导致服务中断。
  • 集群状态干净,不再引用已不存在的节点。

技术背景 (Technical Background)

  • 节点 (Node): Kubernetes 集群中的工作机器,可以是物理机或虚拟机。Pod 运行在节点上。
  • 调度器 (Scheduler): Kubernetes 控制平面组件,负责监测新创建的 Pod,并为其选择合适的节点进行运行。
  • 控制器管理器 (Controller Manager): Kubernetes 控制平面组件,包含多个控制器,如 Deployment Controller, StatefulSet Controller, Node Controller, EndpointSlice Controller 等。它们负责维护集群的状态。在节点摘除过程中,特别是驱逐 Pod 时,Node Controller 和其他工作负载控制器协同工作。
  • PodDisruptionBudget (PDB): 一个 API 对象,定义了在自愿中断(如节点维护、升级)期间,一个应用副本集中可以同时有多少个 Pod 不可用。kubectl drain 命令会尊重 PDB。
  • DaemonSet: 一种特殊的工作负载,确保在集群中的 所有部分 节点上运行一个 Pod 的副本。DaemonSet Pod 通常不应被 drain 驱逐,因为它们需要运行在节点本地。

应用使用场景 (Application Scenarios)

优雅的 Kubernetes 节点摘除流程适用于以下场景:

  1. 计划内节点维护: 对节点进行操作系统补丁升级、内核更新、硬件升级或更换。
  2. 集群扩缩容 (缩容): 当集群的计算资源过剩时,移除部分节点以节约成本。
  3. 更换不健康的节点: 当某个节点出现硬件故障或软件问题,但在完全失效前仍能响应部分请求时,可以先优雅地将其上的 Pod 迁移。
  4. Kubernetes 版本升级: 在进行集群版本升级时,可能需要逐个节点进行排空和升级。

原理解释 (Principle Explanation)

节点优雅摘除的核心思想是逐步转移节点上的工作负载,而不是立即停止它们。

  1. 锁定 (Cordon):
    • 原理: 将节点的调度状态设置为 SchedulingDisabled
    • 目的: 告知 Kubernetes 调度器,不要再将新的 Pod 调度到这个节点上。这确保了在开始迁移现有 Pod 之前,不会有新的工作负载被添加到该节点。节点上已有的 Pod 会继续运行。
  2. 驱逐 (Drain):
    • 原理: 这是一个复杂的过程,由 kubectl drain 命令触发,并通过 Kubernetes 控制平面协调完成。它会尝试从节点上删除 所有 非豁免的 Pod。
    • 过程: kubectl drain 命令会向 Kubernetes API Server 发送删除请求,请求删除目标节点上的 Pod。控制器管理器接收到这些删除请求,会尝试在集群中的其他可用节点上重新创建这些 Pod(如果它们是由 Deployment, StatefulSet, ReplicaSet 等控制器管理的)。
    • 尊重 PDB: 在驱逐过程中,控制器管理器会检查受影响的 Pod 是否属于某个设置了 PDB 的应用。如果驱逐会导致该应用同时不可用的副本数超过 PDB 允许的范围,控制器管理器会暂停驱逐,直到其他节点上的 Pod 被成功创建并变为可用状态。
    • 处理特殊 Pod: 默认情况下,drain 命令不会驱逐由 DaemonSet 管理的 Pod,因为这些 Pod 通常需要运行在节点本地,它们会在其他节点上自动启动。drain 命令通常也需要特别处理或忽略使用 emptyDir 卷的 Pod,因为 emptyDir 卷的数据与 Pod 的生命周期绑定,驱逐会导致数据丢失。
    • 目的: 将节点上的所有工作负载安全、有序地迁移到集群中的其他健康节点上。
  3. 删除 (Delete):
    • 原理: 从 Kubernetes API Server 中删除代表该节点的 Node 对象。
    • 目的: 告知 Kubernetes 控制平面该节点已不再是集群的一部分。调度器不再考虑该节点,控制器管理器也不再尝试管理该节点上的任何资源。
  4. 底层基础设施清理:
    • 原理: 这是 Kubernetes 外部的操作。根据你的集群类型,可能是关闭虚拟机、物理服务器,或在云服务商控制台/API 中终止实例。
    • 目的: 停止为该节点支付资源费用,释放底层资源。

核心特性 (Core Features - of the process)

  • 优雅性: 逐步迁移 Pod,避免服务中断。
  • 自动化: kubectl drain 命令自动化执行 Pod 驱逐过程。
  • 策略遵循: 尊重 PodDisruptionBudget,保障应用可用性。
  • 逐步操作: 先锁定,后驱逐,最后删除,每一步都有明确的目的。
  • 需要清理底层资源: Kubernetes 本身不负责关闭或删除虚拟机/物理机。

原理流程图以及原理解释 (Principle Flowchart and Explanation)

(此处无法直接生成图形,用文字描述流程图)

图示:Kubernetes 节点优雅摘除流程

+---------------------+       +---------------------+       +---------------------+
|  识别待移除节点     | ----> |   kubectl cordon    | ----> | 节点状态: SchedulingDisabled |
|                     |       |     <node-name>     |       | (阻止新 Pod 调度)    |
+---------------------+       +---------------------+       +---------------------+
                                        ^                               |
                                        | 等待 Pod 迁移                  |
                                        |                               v
                               +---------------------+       +---------------------+
                               |  应用高可用性 (PDB) | <---- |   kubectl drain     |
                               | (防止同时中断过多 Pod)|       |     <node-name>     |
                               +---------------------+       |  (驱逐现有 Pod)     |
                                                              +---------------------+
                                        ^                               |
                                        | Pods 在其他节点重建              |
                                        v                               | 等待所有 Pod 驱逐
                               +---------------------+       +---------------------+
                               |   Pod 在新节点运行  | <---- |    所有 Pod 已驱逐  |
                               +---------------------+       |   (除了 DaemonSets) |
                                        ^                               |
                                        |                               v
+--------------------------+       +---------------------+       +---------------------+
|  物理机/虚拟机下电/删除  | <---- |  kubectl delete     | ----> | 节点对象从 K8s API |
| (云平台/虚拟化平台操作)   |       |    node <node-name> |       |     中移除          |
+--------------------------+       +---------------------+       +---------------------+

原理解释:

  1. 识别节点: 确定需要移除的节点名称。
  2. kubectl cordon: 对节点执行 cordon 操作,将其标记为不可调度 (SchedulingDisabled)。调度器将不再考虑该节点作为新的 Pod 运行目标。
  3. kubectl drain: 执行 drain 操作。此命令的核心是调用 Kubernetes API 驱逐节点上的 Pod。
    • 控制器管理器接收到驱逐请求,并协调 Pod 的终止和在其他节点上的重建。
    • 等待 Pod 迁移: 这个过程不是瞬时的,特别是对于设置了 PDB 的应用,drain 命令会等待,直到满足 PDB 的要求,或者直到 Pod 在其他地方成功运行。
    • 忽略 DaemonSets: drain 通常会忽略 DaemonSet Pod,因为它们会在其他节点上自动补偿性创建。
    • 处理 emptyDir: 默认情况下 drain 可能无法处理使用 emptyDir 的 Pod,因为它不知道数据的重要性。通常需要额外的标志 --delete-emptydir-data(如果确认数据可以丢弃)或 --force
    • 所有 Pod 已驱逐: drain 命令成功完成后,除了被忽略的 Pod,目标节点上将不再运行任何工作负载 Pod。
  4. kubectl delete node: 从 Kubernetes API 中删除该节点的表示。此时 Kubernetes 集群不再知道这个节点的存在。
  5. 物理机/虚拟机下电/删除: 在 Kubernetes API 中删除节点对象后,才能安全地关闭或删除底层物理机或虚拟机实例。这个步骤是在 Kubernetes 外部进行的。

核心特性 (Core Features)

(同上,此处略)

环境准备 (Environment Setup)

  • 一个运行中的 Kubernetes 集群,至少包含一个控制平面节点和至少两个工作节点(以便 Pod 可以迁移)。
  • 已安装并配置好的 kubectl 命令行工具,拥有足够的权限(通常是集群管理员权限)来修改节点和删除 Pod。
  • 集群中有一些 Deployment 或 StatefulSet 部署的应用 Pod 正在运行,其中一部分 Pod 运行在待移除的节点上。

不同场景下详细代码实现 & 代码示例实现 (Detailed Code Examples & Code Sample Implementation)

假设我们要移除的节点名称是 worker-node-1

  1. 列出集群中的节点:

    kubectl get nodes
    

    输出示例:

    NAME            STATUS   ROLES    AGE     VERSION
    master-node-1   Ready    master   2d1h    v1.28.5
    worker-node-1   Ready    <none>   2d      v1.28.5
    worker-node-2   Ready    <none>   2d      v1.28.5
    
  2. 锁定节点 (Cordon): 将节点标记为不可调度。

    kubectl cordon worker-node-1
    

    输出示例:

    node/worker-node-1 cordoned
    

    再次检查节点状态,会看到 SchedulingDisabled

    kubectl get nodes
    

    输出示例:

    NAME            STATUS                     ROLES    AGE     VERSION
    master-node-1   Ready                      master   2d1h    v1.28.5
    worker-node-1   Ready,SchedulingDisabled   <none>   2d      v1.28.5
    worker-node-2   Ready                      <none>   2d      v1.28.5
    
  3. 驱逐节点 (Drain): 驱逐节点上的所有 Pod。

    # 这是最常用的 drain 命令,忽略 DaemonSet 和 emptyDir 卷,并强制删除未受控制器管理的 Pod
    kubectl drain worker-node-1 --ignore-daemonsets --delete-emptydir-data --force
    

    命令参数说明:

    • worker-node-1: 待驱逐的节点名称。
    • --ignore-daemonsets: 忽略 DaemonSet Pod,不尝试驱逐它们。这通常是期望的行为。
    • --delete-emptydir-data: 删除使用 emptyDir 卷的 Pod。注意:这将导致 emptyDir 卷中的数据丢失。 仅在确认 emptyDir 中的数据不重要时使用。
    • --force: 强制删除未由 ReplicationController, ReplicaSet, Deployment, DaemonSet, StatefulSet 或 Job 等控制器管理的 Pod。对于不受控制器管理的单体 Pod,没有 --force 将无法驱逐。
    • --grace-period=<seconds>: 给 Pod 优雅终止的时间,默认为 Pod.Spec.TerminationGracePeriodSeconds,通常为 30 秒。
    • --timeout=<duration>: 等待 Pod 驱逐完成的总超时时间,默认为 0 (无限等待)。建议设置一个合理的超时时间,例如 --timeout=5m (等待 5 分钟),防止无限期等待。

    执行 drain 命令后,终端会输出正在驱逐的 Pod 列表。

    输出示例:

    node/worker-node-1 cordoned # 如果之前没cordon,drain会自动执行cordon
    evicting pod "my-app-pod-xxx"
    evicting pod "another-app-pod-yyy"
    ...
    node/worker-node-1 evicted
    

    如果在驱逐过程中遇到 PDB 限制或其他问题,drain 命令可能会暂停或报错。你可以根据提示处理,或使用 --force (不推荐在生产环境随意使用,除非确定后果)。

  4. 删除节点对象 (Delete):drain 命令成功完成后,节点上已无应用 Pod。现在可以从 Kubernetes API 中删除该节点对象。

    kubectl delete node worker-node-1
    

    输出示例:

    node "worker-node-1" deleted
    

    再次检查节点列表,worker-node-1 将不再显示。

    kubectl get nodes
    
  5. 清理底层基础设施 (Kubernetes 外部操作):

    • 云环境: 登录云服务商的控制台或使用其 CLI/API,找到对应于 worker-node-1 的虚拟机实例,执行终止或删除操作。
    • 自建集群: 登录到物理服务器或虚拟机管理平台,安全地关闭操作系统,然后执行下电操作或删除虚拟机。

运行结果 (Execution Results)

运行上述命令后,你将观察到:

  • kubectl cordon 后,kubectl get nodes 输出中,目标节点状态显示 SchedulingDisabled
  • kubectl drain 运行时,终端输出显示正在驱逐 Pod 的列表。
  • 在另一个终端中运行 kubectl get pods -n <namespace> -o wide 可以看到目标节点上的 Pod 逐渐进入 Terminating 状态,并在其他节点上出现新的 Pod 副本进入 ContainerCreatingRunning 状态。
  • kubectl drain 成功完成后,终端输出节点已驱逐的消息。
  • kubectl get pods -n <namespace> -o wide 不再显示目标节点上的应用 Pod (DaemonSet Pod 除外)。
  • kubectl delete node 后,kubectl get nodes 输出中不再包含被删除的节点。
  • (根据实际操作)底层虚拟机/物理机被成功关闭或删除。

测试步骤以及详细代码 (Testing Steps and Detailed Code)

测试节点摘除过程是否成功且优雅,可以从以下几个方面进行:

  1. 验证节点状态变化: 按照上述部署步骤,在执行 cordon, drain, delete 命令后,分别使用 kubectl get nodes 验证节点状态是否正确流转(Ready -> Ready,SchedulingDisabled -> 不存在)。

  2. 验证 Pod 迁移:

    • 步骤: 在执行 drain 命令前,使用 kubectl get pods -o widekubectl get pods --all-namespaces -o wide 查看所有 Pod 及其所在的节点,确认待移除节点上有运行中的 Pod。执行 drain 命令过程中和完成后,再次查看 Pod 列表,验证这些 Pod 是否已从目标节点消失,并在其他节点上成功启动新的副本。
    • 命令:
      # Drain 前查看 Pod 分布
      kubectl get pods --all-namespaces -o wide | grep worker-node-1
      
      # 执行 drain 命令 (如上所示)
      
      # Drain 后检查目标节点上是否还有应用 Pod (DaemonSet 除外)
      kubectl get pods --all-namespaces -o wide | grep worker-node-1
      # 期望除了 DaemonSet Pod,没有其他应用 Pod
      
      # 检查被迁移的 Pod 是否在其他节点成功运行
      # 例如,如果有一个名为 my-app-deployment 的 Deployment
      kubectl get deployment my-app-deployment -o yaml | grep replicas # 验证期望的副本数
      kubectl get pods -l app=my-app -o wide # 验证运行中的 Pod 数量和所在节点
      
  3. 验证应用可用性 (如果在生产环境且有 PDB):

    • 步骤: 如果你的应用设置了 PDB,且有多个副本,可以在执行 drain 命令过程中,持续地访问应用的服务端点。观察服务是否中断或错误率是否显著上升。
    • 自动化: 编写一个脚本或使用监控工具,每隔几秒钟向应用的服务端点发起请求,记录请求的成功率和响应时间。在 drain 过程前后对比数据。
    • 示例脚本 (概念性 - Bash 简单连续访问):
      #!/bin/bash
      SERVICE_URL="http://<你的服务IP或域名>:<端口>" # 替换为你的服务地址
      COUNT=0
      SUCCESS_COUNT=0
      while true; do
        COUNT=$((COUNT+1))
        HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" $SERVICE_URL)
        if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 400 ]; then
          echo "$(date) - Request $COUNT: SUCCESS (HTTP $HTTP_CODE)"
          SUCCESS_COUNT=$((SUCCESS_COUNT+1))
        else
          echo "$(date) - Request $COUNT: FAILURE (HTTP $HTTP_CODE)"
        fi
        # 可以根据需要添加延迟
        # sleep 0.1
      done
      # 在另一个终端执行 drain 命令,观察这个脚本的输出
      
      运行脚本,然后执行 kubectl drain 命令。理想情况下,即使有 Pod 被驱逐和迁移,脚本的输出应该持续显示成功请求(取决于 PDB 设置和迁移速度)。
  4. 验证节点对象删除: 确认执行 kubectl delete node 后,节点不再出现在 kubectl get nodes 的输出中。

  5. 验证底层资源清理: 登录云平台控制台或虚拟化平台,确认对应的虚拟机或物理机已经被关闭或删除。

部署场景 (Deployment Scenarios)

节点摘除流程是 Kubernetes 运维的标准实践,广泛应用于:

  • 云上托管 Kubernetes 服务: 在 AWS EKS, GKE, AKS, 阿里云 ACK 等服务中,通常通过控制台或云服务商的 CLI/API 来触发节点池的缩容操作,底层会自动执行优雅的节点摘除流程。但理解这些步骤有助于故障排查。
  • 自建 Kubernetes 集群 (kubeadm): 对于使用 kubeadm 搭建的集群,需要手动执行 kubectl cordon, kubectl drain, kubectl delete node 命令。
  • 自动化运维脚本: 将节点摘除流程集成到自动化运维脚本或 CI/CD 流水线中,用于自动化的集群扩缩容或维护操作。
  • 使用集群 Autoscaler: 集群 Autoscaler 在缩容时会自动执行节点的优雅排空和删除操作。

疑难解答 (Troubleshooting)

  1. kubectl drain 命令卡住或失败:

    • 原因:
      • PodDisruptionBudget (PDB) 阻止了驱逐。
      • 存在不受控制器管理的 Pod 且未使用 --force
      • 存在使用 emptyDir 卷的 Pod 且未使用 --delete-emptydir-data--force
      • Pod 无法正常终止或迁移(例如,应用本身的问题、其他节点资源不足)。
      • DaemonSet Pod 未被忽略。
    • 排查:
      • 查看 drain 命令的输出,它通常会指示哪个 Pod 导致了问题。
      • 检查相关 Pod 的状态 (kubectl get pod <pod-name> -o yaml) 和 Events (kubectl describe pod <pod-name>),了解为什么它无法终止或迁移。
      • 检查相关 Deployment/StatefulSet 等控制器的状态,看是否有副本启动失败。
      • 检查 PDB 状态 (kubectl get pdb --all-namespaces),确认是哪个 PDB 阻止了驱逐。如果必要,可能需要临时修改或删除 PDB (风险操作)。
      • 如果确认数据不重要且 Pod 不受控制器管理,可以尝试添加 --force--delete-emptydir-data 标志(再次强调需谨慎)。
      • 确保其他节点有足够的资源来接收被迁移的 Pod。
  2. 节点状态显示 NotReady 或无法访问:

    • 原因: 在尝试优雅摘除前,节点已经出现故障或网络中断。
    • 排查: 如果节点已经物理失效或网络不通,优雅 drain 可能无法执行。在这种情况下,你可能只能跳过 drain 步骤(因为已经无法驱逐其上的 Pod),直接使用 kubectl delete node --force 强制删除节点对象,然后在其他节点上重新创建 Pod。对于 StatefuleSet,可能需要手动干预以处理数据卷。
  3. 删除节点后 Pod 未在其他节点启动:

    • 原因: 原本运行在已删除节点上的 Pod 不受控制器管理;或者其他节点资源不足;或者存在调度限制。
    • 排查: 检查 Pod 的创建方式。如果它是一个裸 Pod,你需要手动在其他节点上创建它(通常推荐使用控制器管理 Pod)。检查其他节点的资源使用和调度限制 (kubectl describe node <other-node-name>)。检查 Pod 的 Events,看调度器为什么没有将其调度到其他节点。

未来展望 (Future Outlook)

  • 更智能的自动缩容: 集群 Autoscaler 将进一步优化其节点摘除策略,更好地处理 StatefuleSet 和复杂的 PDB。
  • 增强的 StatefuleSet 支持: 改进 StatefulSet 在节点故障或摘除场景下的数据卷处理和迁移能力。
  • 与底层基础设施的深度集成: 云服务商提供的 Kubernetes 服务可能会提供更无缝、更自动化的节点生命周期管理,包括底层的资源清理。

技术趋势与挑战 (Technology Trends and Challenges)

技术趋势:

  • 声明式运维: 将节点生命周期管理也尽可能声明式化。
  • 自动化与智能化: 利用自动化工具和 AI/ML 提升集群运维效率。
  • 不可变基础设施: 减少对运行中节点的直接操作,更多地通过替换节点来更新。

挑战:

  • 处理有状态工作负载: 安全、可靠地迁移带有持久化存储(特别是 ReadWriteOnce 模式)的 Pod 依然是挑战。
  • PDB 的复杂性: 正确配置和管理 PDB 以平衡可用性和资源利用率。
  • 处理非预期故障: 如何在节点突然失效的情况下快速恢复服务,而优雅摘除流程主要针对计划内操作。
  • 多云和混合云环境下的节点管理: 在不同的基础设施提供商之间标准化节点管理流程。
  • 教育和最佳实践推广: 确保开发者和运维人员理解并遵循优雅的节点摘除流程。

总结 (Conclusion)

优雅地摘除 Kubernetes 节点是保障应用高可用性和执行集群维护的关键步骤。通过遵循 锁定 (Cordon)驱逐 (Drain)删除 (Delete) 的标准流程,您可以安全地将节点上的工作负载迁移到集群中的其他节点上,最大限度地减少对服务的影响。理解每个步骤的目的、kubectl drain 命令的参数以及如何处理潜在问题(如 PDB, emptyDir, 非受控 Pod)至关重要。将这些步骤集成到你的日常运维和自动化流程中,可以显著提升集群的可靠性和管理效率。最后,不要忘记在 Kubernetes 外部清理底层的物理机或虚拟机资源。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。