Kubernetes 能告诉你节点是否就绪、带有哪些污点、运行着哪些 Pod,却一直缺少一种 Kubernetes 原生、跨组件通用的方式来表达“节点正在排空”“维护已经开始”或“正在优雅关机”。Kubernetes v1.37 引入五个标准 Node Condition,为这些运维状态提供统一的发布位置。
五个条件分别解决什么问题
新增的生命周期条件都是标准化的 NodeConditionType:
| Condition | 含义 |
|---|---|
DrainInProgress |
正在按照管理员选择的排空标准驱逐工作负载 |
Drained |
已达到管理员定义的排空完成标准 |
MaintenancePlanned |
节点预计在未来接受变更或维护 |
MaintenanceInProgress |
节点正在维护,例如升级、修复、退役或调试 |
GracefulNodeShutdownInProgress |
kubelet 判断节点正在执行优雅关机 |
每个条件仍遵循 Kubernetes Condition 的常规语义:
True:当前观察到该状态。False:当前未观察到该状态。Unknown:无法确定该状态是否存在。reason:供程序判断的稳定原因代码。message:供操作人员阅读的补充说明。lastTransitionTime:状态最后一次发生变化的时间。
维护和排空不是同一件事。升级 Kubernetes 或更换硬件通常需要先排空节点,而内核热补丁可能不需要。因此,控制器不应仅因为看见 MaintenancePlanned=True 就推断节点已经被 cordon,或者 Pod 正在被驱逐。
v1.37 中它是状态信号,不是操作开关
v1.37 同时引入 Alpha 级别的 NodeLifecycleConditions feature gate,但它默认关闭,而且在这一版本中实际上不会限制条件写入,也没有核心组件读取这些条件。当前即使不启用 feature gate,获得授权的管理员或控制器也可以发布它们。
这意味着条件本身不会产生以下行为:
- 不会阻止新 Pod 调度到节点。
- 不会自动驱逐已有 Pod。
- 不会替代
kubectl cordon或kubectl drain。 - 不会让 DaemonSet、Job、调度器或自动伸缩器在 v1.37 中自动改变决策。
正确的职责划分是:用现有机制执行操作,用生命周期条件报告操作状态。例如,使用 cordon 和 drain 改变调度与驱逐行为,同时让控制器更新 DrainInProgress 和 Drained,供仪表盘、告警以及其他自动化系统消费。
可以这样发布维护状态
下面的命令使用 strategic merge patch 更新 Node 的 status 子资源。运行前把 worker-01 改为实际节点名,并确认当前身份拥有更新 nodes/status 的权限。
NODE=worker-01
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
kubectl patch node "$NODE" \
--subresource=status \
--type=strategic \
--patch "{
\"status\": {
\"conditions\": [
{
\"type\": \"MaintenancePlanned\",
\"status\": \"True\",
\"reason\": \"MaintenanceWindow\",
\"message\": \"Hardware maintenance is scheduled for this node\",
\"lastTransitionTime\": \"$NOW\"
}
]
}
}"
检查发布结果:
kubectl get node "$NODE" \
-o jsonpath='{range .status.conditions[?(@.type=="MaintenancePlanned")]}{.type}{"\t"}{.status}{"\t"}{.reason}{"\t"}{.message}{"\n"}{end}'
维护流程可以把条件更新与现有节点操作串起来:
NODE=worker-01
# 1. 阻止普通工作负载继续调度到节点
kubectl cordon "$NODE"
# 2. 控制器应在此处将 DrainInProgress 设置为 True
kubectl drain "$NODE" --ignore-daemonsets --delete-emptydir-data
# 3. 达到组织定义的排空标准后,将 Drained 设置为 True
# 4. 开始维护时,将 MaintenanceInProgress 设置为 True
维护结束后应将活动条件设置为 False,或者删除相应条件。设置为 False 的好处是消费者仍能看到最近一次状态、原因和转换时间;删除条件更适合不需要保留该信息的场景。不要让过期的 True 长期留在节点上,否则它会成为比缺少信号更危险的错误信号。
控制器实现需要明确所有权
生产环境不宜让多个脚本随意写同一种 Condition。集群管理员应明确每个条件的唯一所有者,例如:
- 维护编排系统负责
MaintenancePlanned和MaintenanceInProgress。 - 排空控制器负责
DrainInProgress和Drained。 - 节点侧组件负责
GracefulNodeShutdownInProgress。
RBAC 也应限制在 nodes/status,避免维护控制器获得不必要的节点规格修改权限。下面是一个可改造的最小授权示例;请将 ServiceAccount 名称和命名空间替换为实际值。由于 Node 是集群级资源,这里必须使用 ClusterRole:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: node-lifecycle-condition-writer
rules:
- apiGroups: [""]
resources: ["nodes/status"]
verbs: ["get", "patch", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: node-lifecycle-condition-writer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: node-lifecycle-condition-writer
subjects:
- kind: ServiceAccount
name: maintenance-controller
namespace: operations
应用前可以先做服务端校验:
kubectl apply --dry-run=server -f node-lifecycle-rbac.yaml
kubectl auth can-i patch nodes/status \
--as=system:serviceaccount:operations:maintenance-controller
控制器还需要处理并发更新、API 冲突和进程重启。应根据 type 合并条件,只在 status 真正变化时更新 lastTransitionTime,并在任务恢复时核对实际维护状态,不能简单地把内存中的流程阶段重新写回 API。
为什么统一上下文值得尽早采用
NotReady 只能说明节点当前不可用,无法区分意外故障、计划维护和优雅关机;污点可以影响调度或驱逐,却不能证明排空是否已经开始或达到完成标准;Pod 状态也只能提供局部结果。缺少共同上下文时,各个组件即使分别做出合理判断,也可能形成冲突。
例如,一个处于维护状态的节点可能长期占用 DaemonSet rollout 的可用性预算。控制器能看到 Pod 不可用,却无法判断这是新版本故障,还是管理员主动将节点移出服务。MaintenanceInProgress 为未来的 rollout 排序、可用性统计和状态展示提供了 Kubernetes 自有的权威位置。不过,这些消费行为仍属于后续设计,不能当作 v1.37 已经具备的能力。
落地时可以采用一份简短检查表:为每种条件指定唯一写入方;保持 reason 值稳定且可枚举;让 message 描述具体维护活动;将条件更新纳入失败恢复流程;继续使用 cordon、drain、污点和工作负载专用控制执行真实操作;在接入自动决策前,将未知条件和过期时间视为显式风险。这样既能立即改善运维可见性,也为未来生命周期感知型控制器留下清晰接口。