blog
blog copied to clipboard
Pod 中获取到错误的 CPUS

背景
同学 a 在 gitlab CI 上的单测运行失败了, 错误信息如下。于是他按照 jest/issues/8769 的建议,在运行单测的命令上加上了 --maxWorkers=2 的参数,发现不止没有报错了,单测运行的时间还快了 5 倍 ?! 所以询问我这是发生了什么化学反应🤔
● Test suite failed to run
Call retries were exceeded
at ChildProcessWorker.initialize (../node_modules/jest-worker/build/workers/ChildProcessWorker.js:193:21)
jest --maxWorkers

--maxWorkers 参数表示的是 jest 会开启多少个线程去完成所有的测试任务,默认值是 50% * os.cpus().length,相关的文档可见 Jest docs/cli#--maxworker。
其实早在之前, 同学 b 就告诉了我,咱们的机器规格很高,比如 xxx 服务就有 96 核。所以我首先打印了一下单测节点的机器的配置,发现有 64 核。那么本次单测线程数就有了 32 个。
分析
我的猜想是 Jest 比如并行跑两个用例时,由于 Node 线程间内存不共享,每个线程中的用例如果 import 了同样的文件都会通过 tsTransform、babelTransform 去转译一次,做了大量的重复工作而导致的耗时严重。
Node 线程的实现可见之前写的一篇文章 【node 源码学习笔记】worker_threads 工作线程 ,另外关于如何提升 Node 线程之间大量数据交换的效率也是我近期一直关注学习的点,后面有机会整理分享一次。
接着我又去咨询运维的大佬,大佬说这里的 64 核是宿主机的,分配给单测节点的可能只有 5核。
那么这样看来, 多线程在需要大量共享数据的情况下会变慢,而现在更是运超了分到的 CPU 资源的 6 倍之多,造成速度如此之缓慢就是情理之中的。
可行的方案
接着我尝试搜索了一下 Node 如何能获取到 k8s 分配到的给容器的资源数量,没有搜索到可直接使用的方案, 仅下面的方案看上去比较接近一点。
该方案是把设置的 resources 参数通过环境变量 MY_CPU_LIMIT 的形式传递给容器,详细可见 Kubernetes 用 Container 字段作为环境变量的值
apiVersion: v1
kind: Pod
metadata:
name: dapi-envars-resourcefieldref
spec:
containers:
- name: test-container
image: k8s.gcr.io/busybox:1.24
command: [ "sh", "-c"]
args:
- while true; do
echo -en '\n';
printenv MY_CPU_REQUEST MY_CPU_LIMIT;
printenv MY_MEM_REQUEST MY_MEM_LIMIT;
sleep 10;
done;
resources:
requests:
memory: "32Mi"
cpu: "125m"
limits:
memory: "64Mi"
cpu: "250m"
env:
- name: MY_CPU_REQUEST
valueFrom:
resourceFieldRef:
containerName: test-container
resource: requests.cpu
- name: MY_CPU_LIMIT
valueFrom:
resourceFieldRef:
containerName: test-container
resource: limits.cpu
- name: MY_MEM_REQUEST
valueFrom:
resourceFieldRef:
containerName: test-container
resource: requests.memory
- name: MY_MEM_LIMIT
valueFrom:
resourceFieldRef:
containerName: test-container
resource: limits.memory
restartPolicy: Never
有趣的是本地 Docker 容器中 os.cpus().length 不是宿主机中的核数,而是 Docker Desktop 中 Resources 设置的 CPUS。

小结
同理现在的 Node 服务通过集群模式启动直接使用 os.cpus().length 也是有问题的,暂时写死并发的数量去解决。也在 CNode 社区提了一个问答 如何在容器内获取分配到的 cpu 资源数量, 看看有没有踩过坑的大佬有更好的办法 ~
2022-01-29 补充
经过评论区 @Kaijun 大佬指点, 借鉴 Go 的解决方案 https://github.com/uber-go/automaxprocs , 实现了一版 Node.js 方案 https://github.com/xiaoxiaojx/get_cpus_length ,多方测试后是可行的 ✅
竟然从zhihu一路过来看到凯多大佬的 blog
go 社区有自动探测 container 内,通过 procfs 找到容器进程 cgroup 的信息,推算出容器自身的 cpu 限制 ,可以参考: https://github.com/uber-go/automaxprocs
node 社区可能没有相关的自适应的工具和包,可以尝试搞一个
竟然从zhihu一路过来看到凯多大佬的 blog
go 社区有自动探测 container 内,通过 procfs 找到容器进程 cgroup 的信息,推算出容器自身的 cpu 限制 ,可以参考: https://github.com/uber-go/automaxprocs
node 社区可能没有相关的自适应的工具和包,可以尝试搞一个
哈哈,看来是熟人 😁 ,感谢大佬指点,我们再研究一下
竟然从zhihu一路过来看到凯多大佬的 blog go 社区有自动探测 container 内,通过 procfs 找到容器进程 cgroup 的信息,推算出容器自身的 cpu 限制 ,可以参考: https://github.com/uber-go/automaxprocs node 社区可能没有相关的自适应的工具和包,可以尝试搞一个
哈哈,看来是熟人 😁 ,感谢大佬指点,我们再研究一下
也可以脑壳喊我 @chiji ✋
竟然从zhihu一路过来看到凯多大佬的 blog go 社区有自动探测 container 内,通过 procfs 找到容器进程 cgroup 的信息,推算出容器自身的 cpu 限制 ,可以参考: https://github.com/uber-go/automaxprocs node 社区可能没有相关的自适应的工具和包,可以尝试搞一个
哈哈,看来是熟人 😁 ,感谢大佬指点,我们再研究一下
也可以脑壳喊我 @chiji ✋
原来是 chiji
竟然从zhihu一路过来看到凯多大佬的 blog go 社区有自动探测 container 内,通过 procfs 找到容器进程 cgroup 的信息,推算出容器自身的 cpu 限制 ,可以参考: https://github.com/uber-go/automaxprocs node 社区可能没有相关的自适应的工具和包,可以尝试搞一个
哈哈,看来是熟人 😁 ,感谢大佬指点,我们再研究一下
也可以脑壳喊我 @chiji ✋
原来是 chiji
瞻仰各位大佬们