fix(k8s): own sandbox pods with session Service so GC reclaims them on SIGKILL - #57
Open
qiuqionglin wants to merge 1 commit into
Open
fix(k8s): own sandbox pods with session Service so GC reclaims them on SIGKILL#57qiuqionglin wants to merge 1 commit into
qiuqionglin wants to merge 1 commit into
Conversation
…n SIGKILL The K8s provider creates sandbox pods as bare pods (no ownerReference), so cleanup relies entirely on teardown()'s Python finally path. When the process is SIGKILLed — OOM, node eviction, force-delete, or a hard deadline — teardown never runs and the pods leak permanently, exhausting per-node pod slots. The SIGTERM handler added in #56 only covers graceful shutdown, not SIGKILL. Set each pod's ownerReference to the per-session headless Service (already created in setup()). Kubernetes' garbage collector then reclaims every pod when the Service is deleted, independent of whether this process runs its own cleanup. Deleting the Service in teardown() now cascades to the pods; if the process dies first, the pods are still owned and get collected once the Service is removed. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题
K8s sandbox provider 创建的每个 pod 都是裸 pod——没有
ownerReference、没有 controller。清理完全依赖teardown()的 Pythonfinally路径去删除 pod。当进程被非优雅杀掉时——OOM、节点驱逐、force-delete、硬超时——
teardown()根本不执行,pod 会永久泄漏,堆积在节点上耗尽 per-node pod 槽位。#56 加的 SIGTERM handler 只覆盖优雅关闭(cancel job 让
teardown()有机会跑),对 SIGKILL 无能为力——按定义,SIGKILL 下任何用户态清理都无法执行。生产上已观察到:数百个终止态、无
ownerReference的 sandbox pod,谁都回收不了(裸 pod 没有 owner 可级联)。修复
把每个 pod 的
ownerReference指向setup()本就会创建的 session headlessService。这样 Service 一被删,Kubernetes 垃圾回收器就会级联回收所有 pod——与进程是否跑完自己的清理无关。setup()接住创建 Service 的返回值,存下metadata.uid。create()给 pod 挂ownerReference(kind: Service+ 该 uid,controller=False、blockOwnerDeletion=False——纯 GC 归属链,不是 controller 托管关系)。teardown()删 Service 时会级联删 pod;如果进程先死,pod 仍有主,等 Service 被清时照样被 GC。本 PR 与 #56 正交互补:#56 优化优雅路径,本 PR 让非优雅路径的清理也稳。
def setup(self) -> None: ... - self._v1.create_namespaced_service(namespace=self._namespace, body=svc) + created_svc = self._v1.create_namespaced_service(namespace=self._namespace, body=svc) ... + self._service_uid = created_svc.metadata.uid def create(self, spec: SandboxSpec) -> Sandbox: ... + owner_references = None + if self._service_uid is not None: + owner_references = [client.V1OwnerReference( + api_version="v1", kind="Service", + name=f"terrarium-{self._session_id}", uid=self._service_uid, + block_owner_deletion=False, controller=False, + )] pod = client.V1Pod( metadata=client.V1ObjectMeta( name=pod_name, labels={...}, + owner_references=owner_references, ),为什么安全
ownerReference若指向一个不存在的 owner,K8s 会把 pod 当孤儿立即删除(有误删风险)。本修复永远不会走到这条路:Service 在setup()中创建,严格早于任何create()调用,且 uid 来自真实 API 返回——所以 pod 引用 owner 时,owner 一定已存在。验证
单元测试全是 mock,只能证明 pod 对象被构造出带
ownerReference。为证明 Kubernetes 真的会据此级联 GC,我在本地 kind 集群(K8s v1.31.0)上做了端到端测试,精确复刻了本 PR 生成的ownerReference字段。测试 A —— 级联 GC(owned)vs 泄漏(bare): 建 headless Service + 一个 owned pod(本 PR 行为)+ 一个裸 pod(现状),然后删 Service。
API server 接受并持久化了 ownerReference,字段与生成的完全一致:
{"apiVersion":"v1","kind":"Service","name":"terrarium-<session>", "uid":"bf713f89-...","blockOwnerDeletion":false,"controller":false}测试 B —— 无误删:
这同时证明了:owner 存在时级联 GC 生效,以及"owner 必须先存在"这个不变式(本修复恰好满足)正是保护存活 pod 不被误删的关键。
(kind 集群里 pod 显示
ErrImagePull(拉不到busybox)是因节点离线,与 GC 无关——GC 只看ownerReference,不看 pod phase。)现有测试套件:
tests/environment/—— 371 passed,含新增的test_create_owns_pod_with_session_service。