1480 字
7 分钟
3.79 倍只在一个前提下成立:模拟节点把镜像下载从分母里删掉了

同一个控制面、同一套配置,我测出过两个结论:一个说它慢 3.79 倍,一个说它快慢差不到 5%。两个数都没测错,差别在于快的那一组把真实负载里最慢的一段从分母里删掉了。我的判断是:拿到一个性能倍数,先问分母里缺了什么,再决定它能不能用来改架构。

两组基准的分母差异

两组数字#

被测的是一个沙箱服务。调用方通过它建沙箱、在沙箱里执行命令。建沙箱这件事交给一个开源引擎:引擎在 Kubernetes 里写一个自己的沙箱对象,它的控制器看到后去建 pod、回写状态。要决定的是这一层保不保留。

第一组:1000 个建沙箱请求一次提交,50 路并发发出。真实算力只有 1 台 8 核机器,另外登记了 50 台 KWOK 模拟节点——这些节点只在 Kubernetes 里存在,不下载镜像、不运行容器。这一组只算建 pod 这一跳。经引擎全部可用 69.17 秒,测试脚本直接建 pod 18.24 秒。3.79 倍。

第二组:60 个真实沙箱同时建,每个一个镜像,含下载。引擎按默认配置,就绪时间中位数 45.0 秒;精简版服务原型直接建 pod 22.6 秒。约 2 倍。把引擎的三处配置调好之后,经引擎 21.7 秒,同一轮直接建 21.1 秒。

三处配置是:

  1. 引擎给每个沙箱加的 init 容器没写 resources,被命名空间的 LimitRange 按 2 核填上,而主容器只要 0.5 核。ResourceQuota 只认声明量不认实际用量,配额提前判满,并发度从 53 掉到 27。
  2. 引擎控制器自己的 CPU 上限只给了 0.5 核。
  3. 命名空间给引擎沙箱对象设的个数配额是 40 个,这一轮要建 60 个。

三条都在 YAML 层,不碰引擎代码。

模拟节点把镜像下载从分母里删掉了#

节点不拉镜像,建 pod 这一跳就成了第一组的全部耗时。真实负载里它只是开头的一小截,后面压着几十秒的镜像下载。

69.17 秒本身是讲得通的。新版控制器每建一个沙箱要改写引擎自己的沙箱对象约 6.6 次,这类对象在客户端限速里单独限每秒 100 次,折合每秒最多建约 15 个沙箱。1000 ÷ 15 ≈ 66 秒,实测 69.17 秒,重跑 64.99 秒。

按比例改参数也对得上。上限减半到每秒 50 次,时间几乎严格翻倍,141.55 秒;控制器 CPU 从 0.5 核加到 2 核、上限不变,时间完全没动——它绝大部分时间在限速器里等令牌,不在算。

3.79 倍是引擎在控制面上的放大系数。调用方等的是沙箱可用,这段时间的大头是镜像下载,倍数摊上去就稀释了。命令执行和文件传输占这个服务全部请求的 97%,建沙箱只在会话开头发生一次。

一千多行代码的三条理由#

实验阶段我自己定的方案是把引擎的建沙箱、预建池、执行转发全搬进服务,服务直接建 pod,引擎只留作退路。这条路要多写一千多行代码,而且引擎每升一版都得重新对一遍。

回头问「为什么引擎一定要搬进服务」,底下是三条理由:

  • 经引擎慢 3.79 倍。只在 50 台模拟节点、1000 个同时建、只算建 pod 时成立。真实规模下调好配置差不到 5%。
  • 引擎的预建池会把用过的沙箱还回池里。这条只在旧版上测过,新版默认用过即删,当时没测。
  • 引擎不能指定把沙箱建在哪台机器上,所以「先把镜像拉到那台机器、再在那台建」做不了。当时也没测。后来确认这条成立,但对应的做法是按镜像清单把镜像铺到每台机器、不指定落点,用不着把引擎搬进服务。

一条有前提,两条没测。三条理由撑不起一千多行代码,方案退回薄适配:服务只做认证分户、准入排队、防串号、防重复、寿命上限、转发前核对;建沙箱和执行命令直接用开源引擎,跟着上游升级;引擎确实做不了的事才交给每台机器上的节点组件。

阈值要在测之前定#

测之前我写死了判据:60 个那组经引擎比直接建慢不到 10%、1000 个那组不到 20%,就不构成去掉引擎的理由。

先看到 3.79 倍再决定「多少算慢」,那个阈值一定会被这个数字拉过去。一千多行代码的方案就是这么长出来的——数字先出现,判据后补上,中间那句「它慢得不可接受」是我自己填进去的。

这几个数还有三条边界:

  • 引擎对照每组只跑 1 次。实验集群的 etcd 写盘常停顿 5 到 12 秒,最长卡过 18.9 秒,而被测时间在几十秒量级。噪声和 5% 的判据同量级,所以 21.7 对 21.1 只能说「没测出差异」,不能说「慢 3%」。
  • 1000 个那组的对照方是压测脚本,不是服务原型。服务自己从队列取请求的速度没测,所以 3.79 倍只是上界。
  • 把引擎访问 Kubernetes 的上限从每秒 100 次放到 400 次、CPU 给 2 核后,1000 个全部可用降到 43.91 秒,仍是直接建的 2.35 倍(跨时段比,只看量级),而且对 Kubernetes 的写冲突翻了一倍。余下这段差距是不是引擎结构造成的,没查清,只记为风险。