先介绍下场景:

在Jenkins中新建了一个Job,假设你在一些列Build Step之前/之后,启动了一个进程,打个比方说启动一个Jboss进程。等到Build完成,你去Console Output中查看显示启动成功,甚至PID也有了。但是当你去后台查看的时候,发现其实这个进程根本不存在,并没有启动成功。

不过如果你使用的是较早的Hudson版本(Ver 1.136),并且是直接在页面中的Build Session中(如Excute Shell)执行命令的话,你可能会得到如下的提示:

Process leaked file descriptors. See http://wiki.hudson-ci.org/display/HUDSON/Spawning+processes+from+build for more information

如果你看过这篇文章,那么你就会明白造成这种问题的原因是什么:Jenkins默认会在Build结束后Kill掉所有的衍生进程,用官方的话来说就是:

To reliably kill processes spawned by a job during a build, Jenkins contains a bit of native code to list up such processes and kill them.

问题分析:

The reason this problem happens is because of file descriptor leak and how they are inherited from one process to another. Jenkins and the child process are connected by three pipes (stdin/stdout/stderr.) This allows Jenkins to capture the output from the child process. Since the child process may write a lot of data to the pipe and quit immediately after that, Jenkins needs to make sure that it drained the pipes before it considers the build to be over. Jenkins does this by waiting for EOF.

When a process terminates for whatever reasons, the operating system closes all the file descriptors it owned. So even if the process didn't close stdout/stderr, Jenkins will nevertheless get EOF.

The complication happens when those file descriptors are inherited to other processes. Let's say the child process forks another process to the background. The background process (AKA daemon) inherits all the file descriptors of the parent, including the writing side of the stdout/stderr pipes that connect the child process and Jenkins. If the daemon forgets to close them, Jenkins won't get EOF for pipes even when the child process exits, because daemon still have those descriptors open. That's how this problem happens.

从官方的说明中可以看出,造成这种问题的原因,是由于文件描述符的丢失以及子进程是如何继承的。

Jenkins和子进程之间的通信使用三根管道进行:stdin,stdout,stderr,由于Jenkins可以捕捉到子进程的输出,而子进程可能往stdout中写入大量数据然后立即退出,Jenkins为了完整的读取子进程的输出,会在用于子进程stdout的管道上等待EOF。这样,无论子进程由于什么原因退出,系统将关闭进程使用的文件描述符,因而Jenkins总是可以收到EOF。

但是当子进程在后台fork出另一个进程的时候(比如A fork出了B,启动一个daemon进程),情况就出现一些变化。由于进程B会继承进程A的文件描述 符,如果进程B没有关闭这些描述符,即使进程A退出,这些描述符依然是打开的,Jenkins将不会在相应管道上收到EOF。

通常一个实现良好的daemon会关闭所有文件描述符以避免这个问题,但是总有一些实现没有遵循这个规则。在更旧版本的Hudson上,这个问题甚至将导致Job无法结束,Jenkins一直在那等待EOF。

解决办法:

现在我们知道,Jboss之所以没有启动成功,是因为它没有关闭继承的文件描述符,Jenkins在Job构建过程结束后认为Jboss进程未终止,因而将其kill掉了。

1. 使用daemonize工具

在这个页面中https://wiki.jenkins-ci.org/display/JENKINS/Spawning+processes+from+build介绍了一种方法:

On Unix, you can use a wrapper like this to make the daemon behave.

在Unix上,你可以使用daemonize工具将程序作为实现良好的daemon进程运行以避免这个问题:

安装daemonize工具包,使用daemonize命令wrap 相应进程的启动指令,类似于:

  1. daemonize ${your.process} start

另外,页面中也给出了如何在Windows平台解决这个问题的方法,具体的看页面中的介绍就可以了。

2. 重设环境变量BUILD_ID

在研究这个问题的时候,找到了另外一篇文章:https://wiki.jenkins-ci.org/display/JENKINS/ProcessTreeKiller,这篇文章进一步描述了Hudson杀掉衍生进程的情况:

The ProcessTreeKiller takes advantage of the fact that by default a new process gets a copy of the environment variables of its spawning/creating process.

It sets a specific environment variable in the process executing the build job. Later, when the user requests to stop the build job's process it gets a list of all processes running on the computer and their environment variables, and looks for the environment variable that it initially set for the build job's process.

Every job with that environment variable in its environment is then terminated.

意思就是说,Jenkins会在执行Job时设置一系列的环境变量,这些环境变量将被Job衍生出的进程所继承。Jenkins在Kill掉衍生进程的时候会查看进程的环境变量,如果找到它之前设置的环境变量,就将其杀掉。

因此,Wiki中也给出了另外一个简单的方法来避免进程被杀掉:

A convenient way to achieve that is to change the environment variable BUILD_ID which Jenkins's ProcessTreeKiller is looking for. This will cause Jenkins to assume that your daemon is not spawned by the Jenkins build.

修改Hudson设置的环境变量BUILD_ID的值,从而让Jenkins认为此进程不是由Job的构建过程衍生的,如:

  1. BUILD_ID=dontKillMe /usr/apache/bin/httpd

后面的"/usr/apache/bin/httpd"可以省略,即只需要在parameter build trigger中加入一个string parameter,变量名为BUILD_ID,值为dontKillMe即可。

3. 启动时添加禁用参数

除以上两种方式以外,上面的Wiki页面中还提到了第三种解决办法,个人觉得这种方案最靠谱最彻底,不需要再去配置每个Job了:

you can disable this feature by setting a Java property named "hudson.util.ProcessTree.disable" to the value "true". This can be done as a parameter to the "java" binary when starting Jenkins:

在启动Jenkins的时候直接通过Java选项来关闭Jenkins杀掉所有衍生进程的这个功能:

  1. java -Dhudson.util.ProcessTree.disable=true -jar jenkins.war

修改Jenkins启动衍生进程的生命周期相关推荐

  1. 禁止jenkins杀死衍生进程

    现象 最近公司CI jenkins框架加上了sonarqube组件进行代码检测,但是jenkins运行sonarqube后,偶尔会出现下面的错误: Process leaked file descri ...

  2. UNIX 进程揭秘--进程的生命周期

    探索运行在 UNIX 操作系统下的进程的生命周期 Sean A. Walberg (sean@ertw.com), 高级网络工程师 2007 年 7 月 16 日 研究进程的生命周期,以便您能将所看到 ...

  3. 修改jenkins启动的默认用户

    # 背景 通过yum命令安装的jenkins,通过service jenkins去启动jenkins的话,默认的用户是jenkins,但jenkins这个用户是无法通过su切换过去的 ,在某些环节可能 ...

  4. Android Activity的启动模式及对生命周期的影响

    Activity的启动模式 官网解释链接 (tips:在阅读此文章前,应先对Activity生命周期掌握) 在每一个程序的main目录下有一个AndroidManifest.xml文件,这个文件是用来 ...

  5. Activity的生命周期和启动模式--Activity的生命周期的全面分析

    本节将Activity的生命周期分为两部分内容,一部分是典型情况下的生命周期,另一部分是异常情况下的生命周期.所谓典型情况下的生命周期,是指在有用户参与的情况下,Activity所经过的生命周期的改变 ...

  6. Spring容器启动流程+Bean的生命周期【附源码】

    如果对SpringIoc与Aop的源码感兴趣,可以访问参考:https://javadoop.com/,十分详细. 文章目录 Spring容器的启动全流程 Spring容器关闭流程 Bean 的生命周 ...

  7. (四)进程的生命周期——起源

    操作系统:linux 处理器:arm 内核版本:4.x 目录: 0号进程 1号进程.2号进程 提到进程生命周期就不得不说一说进程的起源:进程是怎么来的?第一个进程是谁? 0号进程 实际上计算机中第一个 ...

  8. (五)进程的生命周期——诞生:fork、vfork、clone、内核线程(待续)

    操作系统:linux 处理器:arm 内核版本:4.x fork.vfork.clone cow _do_fork copy_process 内核线程 自然界中的每一个生命都需要经历出生.成长.死亡, ...

  9. AndroidStudio安卓原生开发_Activity_生命周期_单activity的生命周期_多activity启动关闭的时候生命周期关系---Android原生开发工作笔记85

    暂时不写内容,后边补上,因为工作太忙,先把图,以及重要的难点说明写出来,后边会修改成详细的文章

最新文章

  1. cvelist.jsp
  2. LeetCode 236. 二叉树的最近公共祖先
  3. 切换 uniapp_万能前端框架uni app初探03:底部导航开发
  4. whois老域名挖掘技术
  5. 新鲜出炉!大规模神经网络最新综述!
  6. echarts里面的参数解释_Echarts适用小技巧:适用参数详细说明及示例-TS文件
  7. shellinabox基于web浏览器的终端模拟器
  8. PHP编写shell
  9. linux重启ipv6_Linux关闭、开启、配置IPv6
  10. escape encodeURI 和encodeURIComponent JS编码
  11. 马斯克再谈“AI威胁论”,吴恩达也看不下去了
  12. JavaScript我学之七数组
  13. The proxy server is refusing connections – Fix for Firefox Browser
  14. 大学c语言怎么应付考试,二级c语言考试应对技巧
  15. ASM的普通盘转AFD
  16. Word文档进行XXE攻击
  17. C语言:质数和合数的判断
  18. 即将打破x86和ARM垄断地位的RISC-V,你了解吗?
  19. 将mp4文件转换为flv
  20. 对比不同子载波数量下的OFDM和FBMC频谱matlab仿真

热门文章

  1. JQuery_003_事件绑定与解绑
  2. 全屏背景图片动画插件 vegas.min.js
  3. 书评Python神经网络编程——相当友好的入门书
  4. 微信小程序真机测试 Provisional headers are shown 问题解决办法
  5. 提高幸福感的一些方法
  6. 网络工程毕业设计----基于ensp华为校园网双出口网络仿真设计
  7. java request 方法_Request常用方法
  8. 深入了解HDMI接口
  9. 关于用fiddler抓包后iPhone手机连不上网
  10. 迭代与递归的区别(斐波那契数列和小猴子摘桃)