判断镜像是否“过期”应检查是否被任何pod引用,包括各命名空间中非running态pod、daemonset、job、cronjob及replicaset历史版本;优先用imageid匹配,次选digest,避免仅比对tag。

怎么判断一个镜像是否“过期”
不能只看 Created 时间戳——Docker 镜像本身没生命周期,真正要盯的是它是否还在被任何 Pod 引用。Kubernetes 里镜像可能被多个命名空间的 Pod 使用,也可能被 DaemonSet、Job 或 CronJob 持有,甚至存在未运行但仍在 ReplicaSet 历史版本里的残留引用。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 遍历所有命名空间下的
Pod列表(包括phase != Running的),提取spec.containers[].image和status.containerStatuses[].imageID - 对每个节点执行
crictl images(或docker images,取决于容器运行时),拿到本地镜像列表和RepoDigests - 用
imageID匹配最可靠;若无imageID,回退到比对带@sha256:的完整 digest;避免仅比对 tag,因为 tag 可能被覆盖重推 - 注意:
imagePullPolicy: Never的 Pod 不会触发拉取,但其image字段仍应计入引用
如何安全识别并删除“孤儿 Pod”
“孤儿 Pod”不是 Kubernetes 官方概念,而是指那些不再属于任何控制器(Deployment、StatefulSet 等)管理、且 phase 为 Succeeded 或 Failed 的 Pod。它们常因 Job 完成、手动创建后忘记清理、或控制器被删但 Pod 残留而产生。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
clientset.CoreV1().Pods(ns).List()获取 Pod 列表,检查每个Pod的OwnerReferences字段是否为空 - 过滤掉
phase == "Running"或"Pending"的 Pod——即使无 owner,正在运行也不能删 - 特别注意
Job类型:Job 控制器默认会保留成功完成的 Pod(ttlSecondsAfterFinished未设时),这类 Pod 是“合法孤儿”,但可删;需区分是用户主动保留还是配置缺失 - 删除前加 dry-run 模式,输出将删的
Pod名称 + 命名空间 + age,人工确认阈值(比如只删 >72h 的)
用 Golang 调 Kubernetes API 时怎么避免 429 Too Many Requests
Kubernetes API Server 对未认证/低权限客户端有默认限速(如 20 QPS),批量扫描节点镜像或全量 List Pod 时极易触发。错误信息是 429 Too Many Requests,响应头含 Retry-After: 1,但直接 sleep 会拖慢整个流程。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 给
rest.Config显式设置QPS: 5.0和Burst: 10,比默认更保守;尤其在多节点轮询时,每个节点调crictl是本地操作,但 List Pod 是集中打 API Server - 对
List类请求加分页:用Limit+Continue参数,避免单次拉几万条 Pod 导致超时或内存暴涨 - 把“查 Pod 引用”和“查节点镜像”拆成两个阶段,并发控制分开:Pod 列表用 clientset 限速;节点镜像用
ssh或exec调crictl,走各自节点本地,不走 API Server - 不要在一个循环里连续
Get数百个 Pod —— 改用List+ selector 过滤,例如fieldSelector=spec.nodeName=xxx
清理镜像前为什么必须先 drain 节点上的 Pod
直接在节点上执行 crictl rmi 删除镜像,如果该镜像正被某个运行中容器使用,命令会失败(错误信息类似 cannot remove image: image is being used by running container)。但更危险的是:你删了镜像,而某个刚被调度的 Pod 正准备启动,此时 kubelet 拉取失败,Pod 卡在 ImagePullBackOff,形成雪球效应。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 清理前必须确保目标节点处于
SchedulingDisabled状态(即已kubectl cordon),且所有非 DaemonSet Pod 已被驱逐(kubectl drain --ignore-daemonsets) - DaemonSet Pod 无法驱逐,但它的镜像通常不该删——除非你明确知道该 DS 已下线且无残留实例;可用
clientset.AppsV1().DaemonSets(ns).List()核对 - 清理脚本里加入前置检查:调
nodes().Get(name)拿node.Spec.Unschedulable和node.Status.Conditions,确认不是Ready状态才继续 - 删完镜像后,别忘了
uncordon——自动化工具最容易漏这步,导致节点长期不可调度
真正的难点不在代码怎么写,而在判断“这个镜像到底还能不能删”:它可能被一个 annotation 里藏着的旧 CronJob 引用,也可能被某次 kubectl run 临时 Pod 持有却没设 ownerRef。每次清理前,得有人盯着 diff 结果看三秒。










