真的不是我的锅
起步
这是一个客户问题,经过一番排查发现是 node 版本的问题。记录一下这个奇葩的过程。
排查过程
1. 尝试本地复现
在客户机器上出现的问题由于调试不太方便,优先尝试复刻客户运行环境复现问题。
- node 版本:10.18.1
- 运行环境: k8s, docker, 镜像系统 alpine 3.11.3
- 应用依赖: 向客户要了 package.json
结果,本地命令没卡住。未能复现。
2. 远程客户机器进行排查
客户帮咱们创建一个新的负载,我们通过向日葵进行调试。发现 node 的启动命令会发生 coredump 而非卡住,目录有生成 coredump 文件,使用 gdb 查看可以看到:
可得的信息较少,使用 strace 进行跟踪:
客户内核版本 4.4 ,membarrier
内核调用因参数不支持而失败,之后会发送信号到子线程去,然后发生段错误。
我本地也编译了一份 4.4 内核,使用 strace
进行跟踪:
也有发kill,但却没有发生段错误,加载正常的。本地复现再次失败。
3. 进一步排查
从 gdb 入手,我们进入 layout asm
进行调试:
发现是在加载动态库就发生段错误了,其中有个线程在 tss_set,跟线程的栈内存有点关系。
后来发现用公用镜像也会崩:
进一步排查,发现在客户机器上即使不注入探针,仅使用公共镜像也能够复现出这个问题。
左边图是客户机器上。在客户机器上即使不注入探针,仅使用公共镜像也能够复现出这个问题,导致 coredump。但也不是所有机器上都能出现这个问题(右边图是我本地的没这个问题)。
那么问题就极有可能和我们探针没有关系。
4. 定位问题
排查方向就转向 node 的版本。在新版本正常使用,然后通过二分,最后确定了 node 10.22+
版本是首次修复。
注意到 node 10.22+
版本的更新说明哩,有个修复了线程堆栈大小的问题:
线程堆栈太小以至于不能安全地接收信号。
查看对应的 commit 变更,可以看到通过手动修改了 pthread_attr_setstacksize()
解决了问题:
5. 确定问题
那么如何确定这个问题就是造成探针无法载入的罪魁祸首呢?
我们参考 https://github.com/nodejs/docker-node/issues/813#issuecomment-407339011 提供的临时解决方案,然后通过设置 ld_preload 多个值方式运行:
探针载入正常。
得出问题所在,该 node 版本下创建子线程时给的堆栈太小导致无法启动。
解决
建议客户使用 node 10.22+
版本,这是 node 版本问题导致的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。