Skip to content
个人学习网站
3 分钟阅读

每个任务一个容器,隔离解决了,用户要干等 45 秒

Agent 沙盒系列第二篇。一任务一容器听起来理所当然,代价是用户提交后要等 45 到 90 秒。这一篇拆开这几十秒到底花在哪,以及为什么"管理系统的接口"不能放在用户点击的那条路上。

上一篇的结论是:所有任务挤在一个程序里,一个出事全体陪葬,而且谁都能读到别人的数据和钥匙。

修法很自然——每个任务发一个自己的容器。用完就扔,互不干扰。

这一步在 Kubernetes 上写起来顺手得不像话,顺手到让人怀疑上一篇是不是小题大做。

代码:来一个任务,现造一个容器

# 收到请求 → 让 K8s 造一个新容器
v1.create_namespaced_pod(namespace="default", body=pod_manifest)

# 然后不停地问:"好了吗?好了吗?"——这就是用户干等的那段时间
while True:
    pod = v1.read_namespaced_pod(name=task_id, namespace="default")
    if pod.status.phase == "Running":
        break
    time.sleep(0.5)          # 问得太勤会把管理系统问垮,所以每半秒问一次

return {"time_to_interactive_seconds": round(time.time() - start_time, 2)}

隔离是真拿到了:一个任务一个容器,内存超标只杀自己,钥匙也不再共用。上一篇那两堵墙确实没了。

然后看最后那行返回值——它记录的是用户从点下按钮到真正开始干活等了多久

从半秒变成 45–90 秒

这不是代码写得慢,是这段时间里排队要办的事本来就多:

这几十秒花在哪 谁在忙 能不能优化
登记这个新容器 集群的“总账本”要记一笔并确认写牢了 基本不能
挑一台机器放它 调度程序筛选、打分、选中一台 有限
下载程序镜像 那台机器去仓库拉安装包 提前拉好可以,但只对已知的镜像有效
创建容器、分配网络 底层运行时开容器、网络插件分 IP 基本不能
程序自己启动 Python 解释器起来、加载依赖 有限

很多人第一反应是怪镜像太大。但镜像拉过一次就存在那台机器上了,再快也快不到能接受的程度——真正的大头是“登记 + 挑机器 + 初始化”这几步,它们是这套系统固有的节奏,不是你的代码慢。这就是“优化镜像体积”这条路走不远的原因:你优化的只是能优化的那一小块。

更麻烦的是那个“不停地问”

注意代码里那句注释:问得太勤会把管理系统问垮。写下这行的时候,问题的性质已经变了。

打个比方:K8s 有两套东西,一套是总务处(负责登记、审批、分配资源),一套是车间(真正干活的地方)。造容器、查状态,走的都是总务处。而总务处这种部门,设计出来就是给内部流程用的——不着急、可以重来、慢一点没关系。

现在的写法,等于把每一个客户都领到总务处窗口前排队,而且每半秒催一次。

一个客户还好。人多起来就是:

  • 每个在等的请求每 0.5 秒查一次,100 个人同时来就是每秒 200 次查询;
  • 每来一个新任务就要登记一笔,而登记要写进总账本、要确认写牢;
  • 总务处先忙不过来,总账本的写入跟着变慢——这两个一慢,整个集群都开始慢,包括跟你的 Agent 毫无关系的其他业务。

这条分界值得记住:管理系统的接口是给管理流程用的,不是给用户点击用的。 冷启动只是它开给你的第一张账单,把整个集群拖慢才是真正的风险。

这一步定下来的事

两件事同时成立:

  • 隔离是对的,不用回头;
  • 临时去造是错的,错在时机——不能等客人到了才开始盖房子。

所以下一步只有一个方向:把“造”这件事挪到用户到达之前。提前准备一批容器闲着待命,请求来了直接认领一个。

等待会从 45 秒掉到零点几秒。但那批一直空转的容器,从此每分钟都在烧钱。下一篇算这笔账。

一句话版本:隔离没错,错的是等用户来了才开始准备。

代码:agent-sandbox-oss/lab2,一个文件,自己跑一次比看数字有说服力。