devops Jenkins pipeline 踩坑集合

among · 2017年09月29日 · 最后由 among 回复于 2019年07月09日 · 2567 次阅读
本帖已被设为精华帖!

前言

最近由于项目需要,接触到了 Jenkins 2.0 版本,其中最重要的特性就是提供了对 pipeline 的支持。
简单的来说,就是把 Jenkins1.0 版本中,Project 中的相关配置信息,如 SVN/Git 的配置,Parameter 的配置等都变成 Code,即 Pipeline as Code。
这样的优势为可以通过写代码的形式配置 Project,且 Jenkins 中内置了常用的 steps。实现了构建步骤代码化、构建过程视图化。
其他的 Jenkins 基础这里不多说了,这里主要介绍最近遇到的问题及其处理方法。一方面是自己总结和整理一下,另一方面也可以供他人参考,少踩坑。

选择 Declarative Pipeline 还是 Scripted Pipeline

最开始的 Pipeline plugin,支持的只有一种脚本类型,就是 Scripted Pipeline;
Declarative Pipeline 为 Pipeline plugin 在 2.5 版本之后新增的一种脚本类型,与原先的 Scripted Pipeline 一样,都可以用来编写脚本。

使用哪一种脚本格式呢,我又纠结了,也查询了些资料。
https://stackoverflow.com/questions/43484979/jenkins-scripted-pipeline-or-declarative-pipeline
http://jenkins-ci.361315.n4.nabble.com/Declarative-pipelines-vs-scripted-td4891792.html

最后,我还是选择了 Declarative Pipeline,这也是后续 Open Blue Ocean 所支持的类型。
相对而言,Declarative Pipeline 比较简单,如果 Groovy 很熟的,用 Scripted Pipeline 可能更顺手。
另外,Declarative Pipeline 中,是可以内嵌 Scripted Pipeline 代码的。

设置和获取执行参数

原先在 Jenkins 1.0 的时候,常用的一个设置就是 “ "This build is parameterized",通过获取参数值,执行后续相关的判断及操作。
在 pipeline 中,可以这样设置:

#!/usr/bin/env groovy
pipeline{
    agent none
    options{
        disableConcurrentBuilds()
        skipDefaultCheckout()
        timeout(time: 1, unit: 'HOURS')
        timestamps()
    }
    parameters{
        string(name: 'PERSON', defaultValue: 'among中文', description: '请输入中文')
        booleanParam(name: 'YESORNO', defaultValue: true, description: '是否发布')
    }
    stages{
    stage('test stage')
    {
      agent
      {
          label 'master' 
      }
      steps
       {
          echo 'Hello, stage1'
          echo "Hello ${params.PERSON}"
          echo "Hello ${env.PERSON}"
      scrip
          {
            def input = params.YESORNO
            if (input)
            {
              echo "you input is ${input},to do sth"
            }
            else
            {
              echo "you input is ${input},nothing to do"
            }
          }
       }
    }
}

环境变量的问题

通过 Jenkins 执行相关 sh 的时候,环境变量中,不会默认继承/etc/profile 和 ~/.profile 等环境变量。
这个时候就很麻烦了,尤其在一些依赖环境变量操作的 sh 脚本时。

可以这样来做,一是在增加 node 节点时,自己设置环境变量,如:

也可以在代码中这么写。写 withEnv ,或是直接在 shell 中先 source profile 文件。然后在执行相关命令。

steps
{
    withEnv(['TPS=amtps']) {
  // do sth
  }
 // 
    sh 'source /etc/profile && source ~/.bash_profile && env'
    dir('/root')
    {
      sh '(source /etc/profile;source ~/.bash_profile;sh ./ee.sh)'
    }
}

Jenkins 中 nohup 后进程还是起不来的问题

在普通的 shell 环境中,nohup,并且& 某个程序后,会抛到后台执行,在退出当前 shell 环境后,程序依然可以执行。
但是在 Jenkins 中,通过 nohup,且使用&之后,step 结束后,执行的程序还是会退出,导致程序起不来。

尝试和验证了很多方法,后面都是这样解决的。
修改 JENKINS_NODE_COOKIE 的值,这样后续结束的时候,后面的 sh 程序就不会被 kill 掉了。
适用版本:Jenkins 2.46 版本,版本如差异较大,可能不一致。当时为了解决这个问题,折腾了很久,找的资料也比较老了,很多都没用,特定记录一下。

steps
  {
    sh 'JENKINS_NODE_COOKIE=dontKillMe nohup python3 /home/among/pj/my_py/monitor/amon/amon.py >/tmp/run.log 2>&1 &'
  }

shell 出错后继续,取 shell 输出值。

这 2 个比较简单,看例子就知道了。

 steps
{
 sh returnStatus: true, script: "ps -ef|grep amon|grep -v grep|awk '{print \$2}'|xargs kill -9"
 script
 {
   def pid = sh returnStdout: true ,script: "ps -ef|grep amon|grep -v grep|awk '{print \$2}'"
   pid = pid.trim()
   echo "you input pid is ${pid},to do sth"
   sh "kill -9 ${pid}"
 }

}

以上就是最近遇到的一些问题,后续遇到了,我再补充吧。
一些地方有可能存在问题或有更好的解决方法,欢迎大家提出和完善。

共收到 8 条回复 时间 点赞
思寒_seveniruby 将本帖设为了精华贴 09月30日 15:24

再更新一个,目前的 pipeline 关于 parameters 的说明中,没有说明支持多选。
实际是可以支持的。

parameters{
        string(name: 'Time_Out', defaultValue: '200', description: '超时时间')
        choice(name: 'Server_addr', choices: '10.114.24.192:7722\n10.129.14.147:7722\n10.114.4.208:6005\n127.0.0.1:6005', description: '被测系统ip及端口')
}

多选的数据类型还是 string 类型,里面使用\n 来区分多个候选值。

among 回复

hi 关于多选问题,想问下如果我想取第二个值该怎么写 ${params.Server_addr} 这个只能去第一个值

jackie 回复

原来我写的有歧义来,上面那个是单选,不是多选。

among 回复

能否帮我看下 https://testerhome.com/topics/11202这个帖子里我提及的几个问题,你是不是知道怎么处理

simple 专栏文章:[精华帖] 社区历年精华帖分类归总 中提及了此贴 12月13日 14:44
simple [精彩盘点] TesterHome 社区 2018 年 度精华帖 中提及了此贴 01月07日 12:08
among jenkins 中利用 cmd 命令启动 tomcat,进程被杀 中提及了此贴 02月27日 21:15

hi! 能否帮看一下,在用 pipeline 向远程服务部署 jeesite 项目时,使用 “JENKINS_NODE_COOKIE=dontKillMe” 进程还是会被杀掉,运行 Jenkinsfile 的一段脚本如下

sshCommand remote:remote,command:"cd /usr/local/jeeweb/web/WEB-INF && JENKINS_NODE_COOKIE=dontKillMe nohup ./startup.sh >/tmp/jeeweb.log 2>&1 &"
among #10 · 2019年07月05日 Author
王蕾 回复

你用的应该是:https://github.com/jenkinsci/ssh-steps-plugin 这个插件。

这个 plugin 相当于通过 ssh 连接到远程机器去执行,不需要用 JENKINS_NODE_COOKIE 这个参数。

只要手工通过那个命令可以成功,通过 plugin 就可以成功,你可以在那个机器上写一个 sh,这样 ssh 过去,只需要执行执行 sh 就行了,看看日志,是不是其他原因导致执行失败了。

嗯嗯,现在可以远程执行并在后台继续运行进程了。确实不需要 JENKINS_NODE_COOKIE 这个参数了。用现在是定义了 build_id,用了 ssh-steps 这个这个插件中的 sshScript 方法。

environment{
BUILD_ID='dontKillMe'
}
stage('Remote SSH'){
writeFile file:'abc.sh', text:'nohup /usr/local/jeeweb/web/WEB-INF/startup.sh >/tmp/jeeweb.log 2>&1 &'
sshScript remote:remote, script:"abc.sh"
}

among #12 · 2019年07月09日 Author
王蕾 回复

你这个 environment 也没什么用吧,writefile 也不需要。只需要一个 sshScript,sh 文件先在远程放好。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册