Jenkins小坑之执行Shell

背景

  1. 之前使用jenkins的大概率是作为远程部署,通常使用over ssh plugin作部署

问题

我们正常使用ssh时当Java应用运行时没有问题 但是在Execute Bash执行时发现输出如下

Starting ...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
waitting for f6-insurance start...
2017-12-01 17:05:25.031  INFO 10067 --- [           main] b.c.e.u.UndertowEmbeddedServletContainer : Undertow started on port(s) 8078 (http)
f6-insurance start success!

但是当访问该进程是发现并没有该Java进程【初步怀疑脚本出现问题】

分析

  1. 手动通过执行脚本发现启动正常
  2. 通过jenkins调用发现输出正常但是当jenkins结束后java进程消失
  3. 首先怀疑脚本中没有使用后台命令启动【以前ssh碰到】可以参考 解决Linux关闭终端(关闭SSH等)后运行的程序自动停止
  4. 但是通过检查脚本使用了nohup 没有问题
  5. 多次启动jenkins任务发现都是在任务结束时java进程消失【排除oomkiller&nohup】
  6. 经过‘细心’观察发现如下日志Process leaked file descriptors. See https://jenkins.io/redirect/troubleshooting/process-leaked-file-descriptors for more information
  7. 经过相关文档说明总结如下
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。

  1. 我们找到一个简单的工具称之为daemonize
  2. 安装daemonize
$ git clone http://github.com/bmc/daemonize.git
$ ./configure
$ make
$ make install
  1. 执行脚本时修改为如下
daemonize -E BUILD_ID=dontKillcenter  /mnt/shell/restart-f6-insurance-web-prod-trial.sh

方案

#!/bin/bash
cp f6-insurance-web/target/f6-insurance-web-0.0.1-SNAPSHOT.jar /mnt/f6_jar/f6-insurance-web-0.0.1-SNAPSHOT-prod-trial.jar
cd /mnt/f6-insurance-8078-prod-trial/
daemonize -E BUILD_ID=dontKillcenter  /mnt/shell/restart-f6-insurance-web-prod-trial.sh

这样就可以成功运行!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏腾讯移动品质中心TMQ的专栏

从 Ant 到 Gradle 的迁移之路

前一段时间项目组打算将原来的 Ant 编译打包方式迁移到 Gradle 编译打包方式。现在迁移基本完成,我这里将迁移过程遇到的坑以及经验做一个总结,希望能给大家...

4560
来自专栏dalaoyang

使用Admin监控

在springboot中,也提供了很全面的监控系统。这篇文章介绍一下springboot—admin监控springboot项目。 原来大致是这样的,sprin...

3567
来自专栏24K纯开源

Android Studio导入项目非常慢的解决办法

问题     Android Studio目前已经更新到2.0 Preview 6了,作为Google大力推崇的开发工具,相对于Eclipse ADT有着不可比...

1979
来自专栏王磊的博客

react native一键分享功能实现&原理和注意点(支持微信、qq、新浪微博等)

前言 目前使用一键分享比较主流的两个SDK:ShareSDK、友盟; 又因为友盟功能比较多且比较全,比如说友盟统计、友盟推送等,所以本文重点介绍的是友盟分享功能...

4389
来自专栏张伟博客

CentOS 7下搭建配置SVN服务器

subversion默认以/var/svn作为数据根目录,可以通过/etc/sysconfig/svnserve修改这个默认位置。

1722
来自专栏瞎说开发那些事

[微服务系列] 3. 服务治理 2 --- Eureka集群

24110
来自专栏王肖的UT

Assimp Android 编译

2764
来自专栏运维一切

moosefs的master单节点热备 原

目前还没有弄大清楚mfsmetarestore这里命令参数到底是什么意思,总体的思路是: 1、安装一个mfsmaster 2、利用同样的配置来配置这台mfsma...

943
来自专栏令仔很忙

SQL server 2000 安装问题

安装SQL server 2000 的时候,运行autorun.exe时,显示不兼容

3711
来自专栏KaliArch

快捷安装不同版本Python

1285

扫码关注云+社区