场景描述
最近在测试环境上搭建了一套jenkins构建集群,然后在这个集群中的某个静态节点上跑一个Jenkins构建任务,构建任务里面的工作是这样的:在静态节点上编译构建好该服务,生成可执行文件,再执行可执行文件,并期望该静态节点在构建任务结束后该服务不随构建任务的结束而停止运行,现在挑重点说明一下,
最后一个步骤是采用shell经典的做法,使用nohup COMMAND &的方式来处理,
但实际情况是当构建任务停止后,静态节点也跟着停止。在各种尝试后,仍然没有按预期的方式运行。
jenkins内部原理分析
先了解下jenkins构建流程的基本原理:
jenkins官方给出解释,Jenkins每次构建完了后都会自动杀掉在执行过程中启动的子进程。Jenkins使用processTreeKiller杀掉了所有子进程,而且这是Jenkins的默认行为。当一次build异常结束,或被人终止时,必然需要结束所有这次build启动的子进程。如果要避免jenkins按照默认的方式处理子进程,官方提供了如下的解决方案:
- Pipeline job解决办法
设置JENKINS_NODE_COOKIE 参数的值,使用方法如下
在pipeline的job中,执行脚本的时候,用下面的方法即可
withEnv([‘JENKINS_NODE_COOKIE=dontkillme’]) {
sh ‘sh xxx’
}
- jenkins普通job(非Pipeline job)
在执行 shell输入框中加入BUILD_ID=dontKillMe ,即可防止jenkins杀死启动的进程
临时改变BUILD_ID值,使得jenkins不会找到并结束掉run.sh启动的后台进程
样例如下:
OLD_BUILD_ID=$BUILD_ID
echo $OLD_BUILD_ID
export BUILD_ID=dontKillMe
#执行tomcat启动脚本
sh ${tomcatHome}/bin/startup.sh
#改回原来的BUILD_ID值
export BUILD_ID=$OLD_BUILD_ID
echo $BUILD_ID
如想深入理解,可参考原文链接
为了验证,我们给出如下几个例子
jenkins版本:2.150.1
- 尝试一:修改BUILD_ID
经验证,该方法在jenkins 2.xxx版本中已失效
- 尝试二:修改JENKINS_NODE_COOKIE
在shell脚本中添加如下脚本即可
export JENKINS_NODE_COOKIE=dontkillme
nihup ./xxx &
或者添加groovy脚本
withEnv(['JENKINS_NODE_COOKIE=dontkillme']){
xxxx
}
除了官方给出的方案,还可以通过插件的方式来避免该问题
- 尝试三:使用sshCommand
脚本如下:
node {
def remote = [:]
remote.name = 'test'
remote.host = 'test.domain.com'
remote.user = 'root'
remote.password = 'password'
remote.allowAnyHosts = true
stage('Remote SSH') {
sshCommand remote: remote, command: "ls -lrt"
sshCommand remote: remote, command: "for i in {1..5}; do echo -n \"Loop \$i \"; date ; sleep 1; done"
}
}
使用插件来完成该操作,可参考https://github.com/jenkinsci/ssh-steps-plugin
- 其他尝试:在pipeline中调用sh
这种方式未尝试,就是不直接使用sh命令,而是通过pipeline间接调用sh,感兴趣的同学可以尝试下