共计 2953 个字符,预计需要花费 8 分钟才能阅读完成。
呈现问题
其余小组在客户现场部署咱们的零碎时遇到了问题,公司环境测试失常的一个 Docker 到客户服务器上无奈失常应用。Docker 外围是运行 theia 程序,能够在网页里与 VSCode 一样开发代码。在客户服务器启动后,网页拜访失常关上,但在外面开启终端时就卡死,而后 theia 的后盾服务就解体断连了。
收集信息
通过沟通了解到,客户现场是离线环境,没方法近程,没方法下载工具调试,传输内部文件也不能轻易传送。简略的让共事做了下测试,并排除了权限问题、操作谬误、配置不正确等问题。剩下的很可能就是镜像自身问题了。
把雷同镜像拿到客户的其余机器上,发现能失常应用。询问后通晓,不能应用的服务器是 麒麟 V10
零碎,失常应用的是 CentOS
,以及本人测试服务器的Ubuntu
。麒麟零碎咱们没思考过,有些意料之外问题也是能承受的。这种运行终端最外围的性能也没定制过,很大概率是兼容性问题,手头没复现的环境很是头疼。
再次询问后发现一个十分费解的事件,他们组是有部署一台 麒麟 V10
零碎的,这台跑同样的镜像就十分失常,为此还专门过来试了试,的确如此。只能一边让共事比照两台服务器的差别,一边持续收集错误信息。
最初膨胀到两个谬误上,一个是程序终止后最初会输入终端 id 是 - 1 与其余环境不统一,另一个是稍早一些有抛出一个错误信息Error: Unexpected SIGPIPE
。
推断排查
翻翻代码,从 @theia/terminal
开始,终端 id - 1 是终端初始化值,接着调用 @theia/process
模块,@theia/process
模块创立终端是调用 node-pty
包(版本 0.11.0-beta17)的 spawn
办法。node-pty
作用就是创立一个过程,返回能够读写的终端对象,spawn
就是他的创立办法。
抛出的错误信息是在 @theia/core
中的 src/node/backend-application.ts
,调用 process.on 监听了“SIGPIPE”事件,解决逻辑就仅仅是抛出错误信息。
在这段代码有附带一个 electron 的 issues,工夫很早曾经修复敞开了。issues 里是 electron 环境,咱们当初是在浏览器环境,并不太一样。并且 Node.js 中有对 SIGPIPE 信号做疏忽解决,应该不至于过程被终止。持续搜寻其余相干探讨也没更多有用的材料了。只能回头,再从提示信息 SIGPIPE
下手了。
信号 signal
SIGPIPE
是什么?这是 linux 过程通信的形式之一,是惟一的异步通信形式。
linux 零碎定义了 64 种信号值,其中 1 -31 是不牢靠信号,大部分是一些错误信息。
当过程接管到信号,能够有三种解决形式:1. 捕捉信号本人解决。2. 疏忽信号不做任何解决。3. 缺省解决,应用零碎默认的解决形式,有终止过程、疏忽信号、挂起等。
除了谬误产生的,软硬件也能被动产生信号,像是键盘触发 ctrl+ c 时,就是发送的 2 信号标识码是 SIGINT。kill 命令也能借助零碎向其余过程发送信号,以达到杀死过程的成果。
SIGPIPE
的缺省解决就是终止过程,是管道操作产生的谬误,产生在管道敞开后,但过程仍然在写入,会触发这个信号产生。
所以 theia 捕捉并抛出了错误信息也合乎对信号的解决。
管道 pipe
由以上得悉,这个标记阐明过程执行时遇到了谬误,这个谬误是在操作“管道”产生的。
什么是“管道”呢?管道与信号一样,也是过程间的通信形式。过程间通信(Inter Process Communication)缩写为 IPC,形式一共有四种:管道(pipe)、信号(signal)、共享映射区(mmap)、套接字(socket)。管道还有辨别为 pipe 匿名管道,FIFO 为命名管道,个别用在血缘关系的过程之间(父子过程、兄弟的子过程)实现数据传递。
推断
依据以上信息做出一个大抵推断,node-pty
在创立终端时要向相终端过程读写数据,但指标过程不明起因未启动或者意外敞开了,导致后续对管道读写数据时呈现了谬误,零碎产生了SIGPIPE
信号。
可能 node-pty
中的错误处理在麒麟下未失效,或是并没对此有做错误处理,导致过程异样连带 theia 一起被终止。
陷入纳闷
没有复现环境很麻烦,好在最初又整了套麒麟零碎,胜利了复现了问题。那这样就搞个最小化的验证。
在这期间还发现了,此前旧版的 theia 在客户服务器上能够失常应用。查问 theia 历史,最开始应用的是 @theia/node-pty
也就是定制的 node-pty
,在 1.22.0 版本更换了包,应用了原版的 node-pty
。
搞了两个版本放到测试环境上做验证,但奇怪的是,两个包都胜利运行,开启了终端失常读写,并没有中断。
这,难不成是 Docker 环境有什么包导致的?但此前也有打过纯正的 theia 根底依赖镜像,也是无奈运行,与 Docker 环境中其余依赖没什么关系。最初索性在麒麟零碎上间接装置了 theia 测试,发现雷同版本的确是能够间接跑起来的。
Docker?
Docker?剩下的差别只有 Docker 了。于是在麒麟服务器上将能够运行的我的项目打包成 Docker 间接跑,发现打成了 Docker 就呈现了终端打不开的状况。
这就很奇怪了,Docker 是一个容器,并不是真正的虚拟机,是利用 linux 内核提供的 Namespace
、CGroup
个性来对程序进行隔离和资源限度。而咱们装置的 Docker 零碎镜像只是预装了各种零碎的根底初始工具,其内核还是与宿主机应用的是同一套,所以实质上在容器中跑的程序还是在麒麟零碎的 内核
上跑的。
在此前也有与公司内服务器核查过版本的,大版本统一,只有小版本不同,客户的测试服务器也换过版本,仍然无奈跑起镜像来。要说区别能跑起来的那台麒麟装置的 Docker 其中 runc 版本会高一些。
但即便是 Docker 问题,真正上生产环境也没方法换 Docker,还是只能在本人的程序上找解决计划。
长期补救
目前已知的是降 theia 版本是可行的,因为降 theia 版本等于是降 node-pty 版本。但这个计划问题较多,因为新旧版本差别以及自定的功能模块差别较多,降级的话不容易保障其余性能没有问题,同时打包后的镜像过大,打包以及传输也挺耗时的,对于筹备上线我的项目来说工夫太紧张了。但还是没方法,最初仍然着手开始搞这个计划,心愿尽早弄出新的测试镜像。
之后后端共事发现,既然是 node-pty 出问题,那么何不试试间接替换镜像中的 node-pty 版本呢?共事替换的是更高的版本,重启一下镜像中的 theia,居然也能失常工作了。这样的话就不必从新搞大镜像了。
后续我将明确的操作步骤整顿了进去,并传好资源,让客户那儿的共事进入 theia 容器,拿给出的资源文件替换目录下的文件,而后应用 Docker commit 命令生成新的镜像,启动,测试,胜利!至此性能应用问题算是解决了。
至于问题定位,后续去翻了翻 node-pty
源码搜寻与 PIPE 相干的段落,没找到什么无效线索。至于 Docker 的调试,则无能为力,雷同版本 Docker 在其余零碎失常,在麒麟下不失常,按排除法应该是麒麟零碎的差别造成的问题,这块临时啃不动。最初在现有的 theia 根底上筹备了一份锁死 node-pty 版本为 1.0.0 的代码,算是为当前再次碰见相似情况做了筹备。