kubectl delete pod 不是驱逐,而是强制删除:它绕过 PDB 检查、跳过优雅终止(terminationGracePeriodSeconds)、不触发控制器重建计数,且无审计级 Eviction 事件,等同于暴力中断。

直接调用 Eviction API 是唯一安全、受控、可审计的驱逐方式;用 kubectl delete pod 或直接删 Pod 对象,绕过了 PDB 检查和优雅终止流程,等同于强制中断,不是“驱逐”。
为什么不能用 kubectl delete pod 代替驱逐
很多人误以为删 Pod 就是驱逐,其实二者语义和行为完全不同:
-
kubectl delete pod是对Pod资源的 DELETE 操作,不经过Eviction准入链,完全无视PodDisruptionBudget—— 即使你配了minAvailable: 3,删第 4 个 Pod 也照删不误 - 它跳过
terminationGracePeriodSeconds的协商机制,容器可能被 SIGKILL 立即终止,来不及 flush 日志或保存状态 - 不会触发控制器(如 Deployment)的“自愿中断计数器”,导致
kubectl drain或 Descheduler 后续判断失准 - API Server 不会产生
Eviction事件,审计日志里只记 “DELETE pod”,无法区分是运维操作还是故障丢失
policy/v1 Eviction API 的正确调用姿势
从 v1.22 起必须用 policy/v1 版本,否则返回 404 或静默降级失败。关键点不是“能不能发”,而是“发完谁来拦、怎么拦”:
- 请求路径必须是
/api/v1/namespaces/{ns}/pods/{name}/eviction,不是 POST 到/apis/policy/v1/...下的任意位置 - body 必须是完整对象,
name和namespace必须与 URL 中一致,否则返回500 Internal Server Error - 若该 Pod 所属的控制器(如 ReplicaSet)没匹配任何
PDB,API 直接返回200 OK并开始驱逐;若有匹配但违反预算,返回429 Too Many Requests - 客户端必须处理
429:不能重试,应先检查kubectl get pdb -n {ns}和kubectl describe pdb {pdb-name} -n {ns},确认当前可用副本数是否真低于minAvailable
示例(Go 客户端片段):
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
evict := &policyv1.Eviction{
TypeMeta: metav1.TypeMeta{Kind: "Eviction", APIVersion: "policy/v1"},
ObjectMeta: metav1.ObjectMeta{
Name: "my-pod",
Namespace: "default",
},
}
err := client.PolicyV1().Evictions("default").Create(context.TODO(), evict, metav1.CreateOptions{})
Go 代码里容易踩的三个坑
用 client-go 调用时,错误往往不报在 Create(),而藏在配置或上下文里:
- 忘记设置
RestConfig.QPS和Burst:Eviction 是写敏感操作,若集群启用了 API 速率限制(如--max-requests-inflight=500),高频调用会卡在429,且错误信息模糊,需加client.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(5, 10) - 用错 client 实例:必须用
clientset.PolicyV1().Evictions(ns),不是CoreV1().Pods(ns)—— 后者调Create()会 panic 报 “no kind ‘Eviction’ is registered for version ‘v1’” - 忽略
Eviction的 namespace 绑定:即使 Pod 在 default 命名空间,Eviction对象的metadata.namespace字段也不能省略或填错,否则 API Server 认为是跨命名空间非法操作,返回403 Forbidden
驱逐后如何确认是否真正生效
别只看 200 OK 就认为成功——那是“允许驱逐”的响应,不是“已经终止”。真实状态要查两处:
- 查 Pod phase:
kubectl get pod my-pod -o jsonpath='{.status.phase}',正常流程中会经历Running → Terminating → (消失);如果卡在Terminating超过terminationGracePeriodSeconds,说明容器未响应 SIGTERM,得查kubectl describe pod里的Events是否有FailedKillPod - 查节点 kubelet 日志(登录节点执行):
journalctl -u kubelet -n 100 --no-pager | grep -i "evict\|my-pod",能看到 kubelet 是否收到驱逐通知、按什么顺序选中该 Pod、是否因 QoS 或资源超限被提前跳过 - 特别注意:如果 Pod 被
NodeController因NotReady驱逐(非 Eviction API),它的reason是NodeLost,不是Evicted—— 这类驱逐不受 PDB 约束,也查不到对应Eviction事件
真正难的不是发请求,而是理解哪个组件在哪个环节插手了这次驱逐:PDB 拦在 API 层,QoS 排序发生在 kubelet 内部,而 NodeController 的驱逐根本绕开了整个流程。混用不同机制时,日志和事件来源必须分清。

















