You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Javascript Kubernetes Client实现K8s Node优雅排空?

使用@kubernetes/client-node优雅移除K8s节点的完整流程

要实现K8s节点的优雅移除,核心流程是标记节点不可调度(cordon)→ 驱逐节点上的Pod(drain)→ 删除节点(可选),下面是用@kubernetes/client-node实现的完整代码示例:

1. 依赖准备

确保已经安装客户端库:

npm install @kubernetes/client-node

2. 完整代码实现

const k8s = require('@kubernetes/client-node');

// 初始化K8s客户端配置
const kc = new k8s.KubeConfig();
kc.loadFromDefault(); // 从默认位置加载配置(~/.kube/config或环境变量)

const coreV1Api = kc.makeApiClient(k8s.CoreV1Api);
const policyV1Api = kc.makeApiClient(k8s.PolicyV1Api);

/**
 * 标记节点为不可调度(cordon)
 * @param {string} nodeName 目标节点名称
 */
async function cordonNode(nodeName) {
  try {
    const node = await coreV1Api.readNode(nodeName);
    // 修改spec.unschedulable为true
    node.body.spec.unschedulable = true;
    await coreV1Api.replaceNode(nodeName, node.body);
    console.log(`节点 ${nodeName} 已标记为不可调度`);
  } catch (err) {
    console.error(`Cordon节点失败: ${err.message}`);
    throw err;
  }
}

/**
 * 驱逐节点上的Pod(drain)
 * @param {string} nodeName 目标节点名称
 * @param {object} options 驱逐选项,比如是否忽略DaemonSet、删除本地存储Pod等
 */
async function drainNode(nodeName, options = {}) {
  const {
    ignoreDaemonSets = true,
    deleteLocalStoragePods = false,
    gracePeriodSeconds = 30
  } = options;

  try {
    // 获取节点上所有非DaemonSet的Pod
    const podList = await coreV1Api.listPodForAllNamespaces(undefined, undefined, undefined, `spec.nodeName=${nodeName}`);
    
    for (const pod of podList.body.items) {
      const podName = pod.metadata.name;
      const namespace = pod.metadata.namespace;

      // 跳过DaemonSet管理的Pod(如果设置ignoreDaemonSets)
      if (ignoreDaemonSets && pod.metadata.ownerReferences?.some(ref => ref.kind === 'DaemonSet')) {
        console.log(`跳过DaemonSet Pod: ${namespace}/${podName}`);
        continue;
      }

      // 跳过本地存储Pod(如果未设置删除)
      if (!deleteLocalStoragePods && pod.spec.volumes?.some(vol => vol.hostPath)) {
        console.log(`跳过带本地存储的Pod: ${namespace}/${podName}`);
        continue;
      }

      // 创建Eviction对象
      const eviction = {
        apiVersion: 'policy/v1',
        kind: 'Eviction',
        metadata: {
          name: podName,
          namespace: namespace
        },
        spec: {
          gracePeriodSeconds: gracePeriodSeconds
        }
      };

      // 执行驱逐
      await policyV1Api.createNamespacedEviction(namespace, eviction);
      console.log(`已驱逐Pod: ${namespace}/${podName}`);
    }
    console.log(`节点 ${nodeName} 上的可驱逐Pod已处理完成`);
  } catch (err) {
    console.error(`Drain节点失败: ${err.message}`);
    throw err;
  }
}

/**
 * 删除节点(可选,根据需求执行)
 * @param {string} nodeName 目标节点名称
 */
async function deleteNode(nodeName) {
  try {
    await coreV1Api.deleteNode(nodeName);
    console.log(`节点 ${nodeName} 已删除`);
  } catch (err) {
    console.error(`删除节点失败: ${err.message}`);
    throw err;
  }
}

// 执行完整流程的示例
async function gracefulRemoveNode(nodeName) {
  try {
    await cordonNode(nodeName);
    await drainNode(nodeName, { deleteLocalStoragePods: false });
    // 如果需要删除节点,解开下面的注释
    // await deleteNode(nodeName);
    console.log(`节点 ${nodeName} 优雅移除流程完成`);
  } catch (err) {
    console.error(`优雅移除节点失败: ${err.message}`);
  }
}

// 替换为你的目标节点名称
gracefulRemoveNode('your-node-name');

关键说明

  • Cordon节点:通过修改Node的spec.unschedulable字段,阻止新Pod调度到该节点。
  • Drain节点:遍历节点上的Pod,对符合条件的Pod调用Eviction API实现优雅驱逐。注意:
    • 默认跳过DaemonSet管理的Pod(因为DaemonSet会自动重新调度到其他节点)
    • 默认保留带本地存储的Pod(避免数据丢失,如需删除可设置deleteLocalStoragePods: true)
  • 删除节点:如果需要彻底移除节点资源,最后执行删除操作,否则节点会保留在集群中(状态为NotReady)。

内容的提问来源于stack exchange,提问作者Carlo Corradini

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 14:20:24