Jenkins Job中衍生进程存活难题及解决方案
场景介绍
在Jenkins中创建了一个Job,假设你在一系列Build Step之前或之后启动了一个进程,例如启动一个Jboss进程。在Build完成后,你查看Console Output,发现显示启动成功,甚至PID也显示了。然而,当你到后台检查时,发现这个进程实际上并不存在,并没有启动成功。
早期Hudson版本的问题提示
如果你使用的是较早的Hudson版本(Ver 1.136),并且在页面中的Build Session(如Execute Shell)中直接执行命令,你可能会看到如下提示:
Process leaked file descriptors. See http://wiki.hudson-ci.org/display/HUDSON/Spawning+processes+from+build for more information.
问题分析
这个问题的根本原因在于文件描述符的泄漏和继承。Jenkins和子进程通过三根管道(stdin
、stdout
、stderr
)进行通信,以捕获子进程的输出。由于子进程可能向stdout
写入大量数据然后立即退出,Jenkins需要在子进程stdout
的管道上等待EOF
(文件结束符)。
然而,当子进程在后台fork
出另一个进程(如daemon
)时,情况就复杂了。这个daemon
进程会继承父进程的所有文件描述符,包括与Jenkins连接的stdout/stderr
管道的写入端。如果daemon
进程没有关闭这些描述符,即使子进程退出,Jenkins也不会在相应管道上收到EOF
,导致Jenkins认为子进程仍在运行,并在Job结束后将其kill
掉。
解决办法
针对这个问题,有几种解决办法:
使用daemonize工具
在Unix系统上,你可以使用daemonize工具将程序作为实现良好的daemon进程运行。安装daemonize工具包,并使用daemonize命令包装相应进程的启动指令,例如:
daemonize ${your.process} start
此外,对于Windows平台,也有相应的解决办法,具体可参考Jenkins官方Wiki。
重设环境变量BUILD_ID
Jenkins的ProcessTreeKiller会检查进程的环境变量,如果找到它之前设置的环境变量(如BUILD_ID
),就会将其杀掉。因此,你可以通过改变BUILD_ID
的值来避免进程被Jenkins杀掉。例如:
BUILD_ID=dontKillMe
或者,在Job的参数触发器中添加一个字符串参数,变量名为BUILD_ID
,值为dontKillMe
。
启动时添加禁用参数
这是最彻底且不需要逐个配置Job的解决办法。你可以在启动Jenkins时通过Java选项来关闭Jenkins杀掉所有衍生进程的功能:
java -Dhudson.util.ProcessTree.disable=true -jar jenkins.war
这样,Jenkins就不会再杀掉由Job衍生的进程了。
总结
通过以上方法,你可以有效地解决Jenkins在Job构建结束后kill掉衍生进程的问题。选择哪种方法取决于你的具体需求和系统环境。
参考资料
https://wiki.jenkins-ci.org/display/JENKINS/ProcessTreeKiller