如何编写集群闲置资源回收自动化脚本?

来源:网络编程作者:郑钧天头衔:网络博主
导读:本期聚焦于郑钧天创作的《如何编写集群闲置资源回收自动化脚本?》,敬请观看详情。集群长期运行后,总有一部分节点或Pod处于低负载甚至空闲状态,既占用配额又增加成本。要安全地回收这些闲置资源,需要先定义闲置判定标准,再通过脚本定时扫描、确认候选资源、执行回收动作并输出审计日志。本文基于Kubernetes环境,介绍从CPU与内存利用率采集、阈值判断、命名空间隔离到调用kubectl执行下线操作的完整实现思路,同时讨论防止误删的保护机制,比如宽限期、白名单、人工确认以及资源标注。读完可以结合自身集群的业务周期,定制一套可控的闲置资源回收方案。

在Kubernetes或大规模虚拟机集群中,资源回收并不是简单删除几个Pod或关停几台机器。闲置资源的判定需要同时考虑时间窗口、历史负载、业务标注以及节点角色,否则很容易把临时低谷当作长期闲置,触发不必要的抖动。本文以一个可直接改造的回收脚本为主线,说明如何采集指标、设置阈值、执行安全下线,并把整条流程纳入定时任务。

如何编写集群闲置资源回收自动化脚本?

一、闲置资源回收的判定指标从哪里来

自动回收的第一步不是执行删除,而是确认哪些资源可以被定义为闲置。对于Kubernetes集群,通常可以依赖metrics-server提供的资源使用数据。执行kubectl top nodes可以查看每个节点的CPU和内存实时使用量,执行kubectl top pods -n 命名空间可以查看Pod维度数据。但实时数据不能直接作为回收依据,因为业务可能存在周期性波峰,例如每天凌晨低负载、白天高负载。如果只在凌晨执行扫描,就可能在业务低谷时把正常节点回收。

更稳妥的做法是引入监控系统如Prometheus,按时间窗口查询平均利用率或百分位利用率。在脚本中可以通过Prometheus HTTP API获取过去6小时或12小时的平均CPU使用率。这样判断对象就从实时值变成趋势值,降低误判概率。实际实现中,可以先确定两类目标:一类是长期没有调度的空闲节点,另一类是长期低负载且未被业务声明保留的Pod。前者适合缩容节点池,后者适合释放命名空间配额。

下面给出一个查询空闲节点的代码示例,通过kubectl获取节点列表,再根据CPU使用量做初步过滤。这里假设threshold为20毫核,时间窗口已经在监控侧完成聚合,脚本只读取结果。

kubectl get nodes -o json | jq '.items[] | {name: .metadata.name, cpu: .status.allocatable.cpu}'

这段命令只输出节点名称和可分配CPU,实际判断还要结合kubectl top nodes或Prometheus数据。必须避免仅凭一次采样就执行下线,否则当节点出现瞬时低负载时,回收动作会造成业务Pod被驱逐,影响可用性。因此脚本中应当为每个候选资源设置持续观察窗口,例如连续三次扫描均低于阈值才进入候选池。

二、回收脚本的关键实现与执行流程

下面设计一个Python脚本,使用subprocess调用kubectl命令获取节点信息,再从Kubernetes API读取注解中的保护标记。逻辑分成四个步骤:扫描候选节点、检查白名单和保护标记、执行cordon隔离、执行drain驱逐并记录日志。为了避免误操作,脚本默认使用dry-run模式,只有传入--confirm参数时才真正执行下线。

脚本中需要特别处理节点名称和注解中的特殊字符,避免Shell注入。通过列表形式传递命令参数,而不是拼接字符串。例如使用subprocess.run(["kubectl", "get", "nodes", "-o", "json"], capture_output=True, text=True),这样节点名称中的空格或特殊符号不会破坏命令结构。

#!/usr/bin/env python3
import subprocess
import json
import sys
import time

CPU_THRESHOLD_MILLICORES = 20.0
DRY_RUN = True

def run_kubectl(args):
    result = subprocess.run(
        ["kubectl"] + args,
        capture_output=True,
        text=True,
        check=False
    )
    if result.returncode != 0:
        print("命令执行失败:", result.stderr.strip())
        sys.exit(1)
    return result.stdout

def get_low_usage_nodes():
    # 这里使用kubectl top nodes获取实时CPU使用量,实际可替换为Prometheus查询
    output = run_kubectl(["top", "nodes", "--no-headers"])
    low_nodes = []
    for line in output.splitlines():
        parts = line.split()
        if len(parts) < 3:
            continue
        name = parts[0]
        cpu_str = parts[2]
        if cpu_str.endswith("m"):
            cpu_millicores = float(cpu_str[:-1])
        else:
            cpu_millicores = float(cpu_str) * 1000
        if cpu_millicores < CPU_THRESHOLD_MILLICORES:
            low_nodes.append(name)
    return low_nodes

def has_protect_annotation(node_name):
    output = run_kubectl([
        "get", "node", node_name,
        "-o", "jsonpath={.metadata.annotations}"
    ])
    annotations = output.strip()
    if not annotations:
        return False
    return "recycle-protect" in annotations and "true" in annotations

def cordon_node(name):
    run_kubectl(["cordon", name])
    print("已隔离节点:", name)

def drain_node(name):
    run_kubectl([
        "drain", name,
        "--ignore-daemonsets",
        "--delete-emptydir-data",
        "--force",
        "--timeout=60s"
    ])
    print("已驱逐节点:", name)

def main():
    global DRY_RUN
    if "--confirm" in sys.argv:
        DRY_RUN = False
    print("扫描模式:", "dry-run" if DRY_RUN else "执行回收")
    low_nodes = get_low_usage_nodes()
    for node in low_nodes:
        if has_protect_annotation(node):
            print("跳过受保护节点:", node)
            continue
        print("候选闲置节点:", node)
        if not DRY_RUN:
            cordon_node(node)
            drain_node(node)
            time.sleep(5)

if __name__ == "__main__":
    main()

这段脚本的回收对象是节点,但同样的思路可以迁移到Pod或命名空间。如果只需要回收空闲Pod,可以把kubectl top nodes替换为kubectl top pods --all-namespaces,再根据命名空间白名单和Pod注解进行筛选。回收Pod时,执行kubectl delete pod前务必确认该Pod不受Deployment、StatefulSet等控制器管理,否则删除后会立即被重建,起不到释放作用。更有效的方式是先缩容对应的工作负载,而不是直接删除Pod。

此外,节点drain操作会驱逐节点上所有Pod,如果某些Pod配置了PodDisruptionBudget,可能阻塞drain过程。脚本中设置了--timeout=60s,超时后需要人工介入。对于有状态服务,最好先检查StatefulSet的副本数和持久卷绑定情况,避免回收导致数据不可用。建议只对标记为node-role.kubernetes.io/worker的纯计算节点执行自动回收,控制平面节点和存储节点应默认加入白名单。

三、调度策略与防误删保护机制

自动化回收脚本真正上线前,需要把安全机制放在第一位。通常可以引入三层保护:第一层是资源标注,用户或集群管理员可以在节点上添加recycle-protect=true注解,脚本检测到该注解就跳过;第二层是白名单,例如按节点名称前缀、标签或命名空间排除;第三层是人工确认,脚本默认只输出候选列表,只有带上确认参数才执行真实操作。

调度方面,可以把脚本放入Kubernetes CronJob,也可以使用传统crontab。使用CronJob的好处是可以复用集群的权限控制,脚本容器通过ServiceAccount访问Kubernetes API,而不用在宿主机上配置kubectl凭据。下面的CronJob示例每小时执行一次扫描,并默认以dry-run模式运行,日志可通过kubectl logs查看。

apiVersion: batch/v1
kind: CronJob
metadata:
  name: resource-recycle
spec:
  schedule: "0 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          serviceAccountName: recycle-sa
          restartPolicy: OnFailure
          containers:
            - name: recycle
              image: bitnami/kubectl:latest
              command:
                - /bin/sh
                - -c
                - |
                  kubectl get nodes --show-labels
                  echo "scan completed"

这个CronJob只是一个框架,实际需要挂载包含脚本的ConfigMap,并在命令中执行真实回收脚本。ServiceAccount需要绑定最小权限,只允许get、list节点和Pod,以及cordon、drain、delete操作,且最好限定在特定命名空间或资源标签。不要把cluster-admin权限直接交给回收脚本,否则一旦出现逻辑错误,可能影响整个集群。

另一个容易忽略的点是审计日志。每次回收动作都应当记录候选资源、判断依据、执行时间和操作人。推荐将日志写入标准输出,再通过日志采集系统集中存储。对于需要审批的场景,可以要求脚本在回收前调用外部审批接口,审批通过后才执行。这样即使自动扫描发现大量闲置资源,也不会在没有人工确认的情况下大规模下线。

四、从测试环境到生产环境的落地建议

在测试环境中验证回收脚本时,可以先针对一个低优先级命名空间运行,观察回收后是否出现Pod重建、服务不可用或配额异常。建议使用--dry-run模式连续运行一到两周,确认候选列表稳定,并且没有把正常业务节点列入回收对象。之后再打开执行开关,并从小范围开始,例如只自动回收带有recycle=enabled标注的命名空间。

生产环境还要考虑与弹性伸缩组件的配合。如果使用Cluster Autoscaler,节点回收后可能被自动补充,形成反复回收和扩容的循环。此时需要在节点上添加cluster-autoscaler.kubernetes.io/scale-down-disabled=true注解,或者把回收脚本的动作改为通知Cluster Autoscaler执行缩容,而不是直接删除节点。不同云厂商的节点池管理方式不同,需要根据实际情况调整脚本的下线逻辑。

最后,定期回顾回收策略同样必要。业务形态可能变化,过去的低负载阈值在新的业务周期下不再适用。可以按月汇总回收数量、误判数量和人工回滚次数,根据这些指标调整阈值、观察窗口和白名单范围。只有让回收脚本具备可观测、可调整、可回滚的能力,才能真正降低集群成本而不增加运维风险。

集群资源回收自动化脚本Kubernetes修改时间:2026-08-22 02:51:57

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。