欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

jenkins

程序员文章站 2022-05-08 18:34:26
...


1、jenkins pipeline实践

(1)调用salt进行os初始化

依赖后端python脚本和成体系的salt状态管理

properties([
    parameters([
        choice(choices: 'ali\ntengxun\naws', description: '选择数据中心', name: 'idc_name')
    ])
])

node {
    if ( params.idc_name == 'ali' ) {
        env.HOST = '10.0.0.1'
        env.PORT = 56578
        env.IDC_NAME = 'ali'
    } else if ( params.idc_name == 'tengxun' ) {
        env.HOST = '10.0.0.2'
        env.PORT = 56578
        env.IDC_NAME = 'tengxun'
    } else if ( params.idc_name == 'aws' ) {
        env.HOST = '10.0.0.3'
        env.IDC_NAME = 'aws'
        env.PORT = 22
    } else {
        echo "input error !!!!!!"
        sh "exit 1"
    }

    stage("upload") {
        //上传excel文件,记录主机名、ip、登录账号及密码等信息
        def inputFile = input message: 'Upload file', parameters: [file(name: 'salt-minion.xlsx')]
        new hudson.FilePath(new File("/srv/salt/base/salt-minion.xlsx")).copyFrom(inputFile)
        inputFile.delete()
    }
    stage("check_file") {
        echo fileExists('/srv/salt/base/salt-minion.xlsx').toString()
        echo "upload excel success !"
    }

    stage ('cp_excel_to_master') {
        sh "scp -P ${env.PORT} /srv/salt/base/salt-minion.xlsx [email protected]${env.HOST}:/tmp/ "
    }
    stage ('create_roaster') {
        sh "ssh -p ${env.PORT} [email protected]${env.HOST} 'sudo /root/.pyenv/shims/python /data/scripts/jenkins/autosysinit/1_check_ssh.py --idc ${env.IDC_NAME}'"
    }
    //stage ('check_certifi') {
    //    sh "salt-ssh ${env.MASTER_NAME} cmd.run '/root/.pyenv/shims/python /data/scripts/jenkins/autosysinit/2_install_certifi.py'"
    //}
    stage ('salt-minion-install') {
        sh "/root/.pyenv/shims/python /data/scripts/jenkins/autosysinit/2_salt-ssh.py --idc ${env.IDC_NAME}"
    }

    stage ('salt-minion-check') {
        sh "/root/.pyenv/shims/python /data/scripts/jenkins/autosysinit/3_check_minion.py --idc ${env.IDC_NAME}"
    }

    stage ('salt-state-sls') {
        sh "/root/.pyenv/shims/python /data/scripts/jenkins/autosysinit/4_check_sls.py --idc ${env.IDC_NAME}"
    }

}

参考:
https://*.com/questions/38080876/jenkins-pipeline-job-with-file-parameter

(2)SOA发版jenkins pipeline配置:

依赖后端python+saltstack发版系统和lua+redis+nginx实现的流量自动切换,实现soa框架精细化发版

properties([
    parameters([
        choice(choices: '01-Commit Package to SVN\n02-Archive Package to Local DataCenter\n03-Group01-Getfile\n04-Group01-Unpack\n05-Group01-Dubbo-Disable\n06-Group01-Stop\n07-Group01-Symlink\n08-Group01-Start\n09-Group01-Dubbo-Enable\n10-Group02-Getfile\n11-Group02-Unpack\n12-Group02-Dobbo-Disable\n13-Group02-Stop\n14-Group02-Symlink\n15-Group02-Start\n16-Group02-Dubbo-Enable', description: '选择开始步骤', name: 'start_step')
    ])
])


node
{
  if (1 >= params.start_step.substring(0,2).toInteger()) {
    //提交开发上传的工程包至发布SVN库
    stage ('1-Commit Package to SVN') {
        wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
            sh 'salt-ssh --force-color "xxxxxxx.ali" cmd.run "python commit.py --app=soa" cwd="/data/deploy/apps/sbin" runas=root --out=txt |awk -F\': \' \'{$1="";print}\''
        }
        input '继续还是退出?'
    }
  }

  if (2 >= params.start_step.substring(0,2).toInteger()) {
    //从发布SVN库更新至本地数据中心
    stage ('2-Archive Package to Local DataCenter') {
        wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
            sh 'salt-ssh --force-color "xxxxxxx.ali" cmd.run "python archive.py --app=soa" cwd="/data/deploy/apps/sbin" runas=root --out=txt |awk -F\': \' \'{$1="";print}\''
        }
    }
  }

  if (3 >= params.start_step.substring(0,2).toInteger()) {
    //分发第一组应用到对应的机器上
    stage ('3-Group01-Getfile') {
        wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
            sh 'salt-ssh --force-color "xxxxxxx.ali" cmd.run "python release.py --app=soa --group=1 --type=getfile" cwd="/data/deploy/apps/sbin" runas=root --out=txt |awk -F\': \' \'{$1="";print}\''
            input '继续还是退出?'
        }
    }
  }


  if (4 >= params.start_step.substring(0,2).toInteger()) {
    //解压第一组应用
    stage ('4-Group01-Unpack') {
        wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
            sh 'salt-ssh --force-color "xxxxxxx.ali" cmd.run "python release.py --app=soa --group=1 --type=unpack" cwd="/data/deploy/apps/sbin" runas=root --out=txt |awk -F\': \' \'{$1="";print}\''
        }
    }
  }

  if (5 >= params.start_step.substring(0,2).toInteger()) {
    //在dubbo-admin中禁用第一组应用
    stage ('5-Group01-Dubbo-Disable') {
        wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
            sh 'salt-ssh --force-color "xxxxxxx.ali" cmd.run "python release.py --app=soa --group=1 --type=dubbo --action=disable" cwd="/data/deploy/apps/sbin" runas=root --out=txt |awk -F\': \' \'{$1="";print}\''
            input '继续还是退出?'
        }
    }
  }

  if (6 >= params.start_step.substring(0,2).toInteger()) {
    //停止、打连接、启动第一组应用
    stage ('6-Group01-Stop') {
        wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
            sh 'salt-ssh --force-color "xxxxxxx.ali" cmd.run "python release.py --app=soa --group=1 --type=stop" cwd="/data/deploy/apps/sbin" runas=root --out=txt |awk -F\': \' \'{$1="";print}\''
        }
    }
  }

  if (7 >= params.start_step.substring(0,2).toInteger()) {
    //打连接第一组应用
    stage ('7-Group01-Symlink') {
        wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
            sh 'salt-ssh --force-color "xxxxxxx.ali" cmd.run "python release.py --app=soa --group=1 --type=symlink" cwd="/data/deploy/apps/sbin" runas=root --out=txt |awk -F\': \' \'{$1="";print}\''
        }
    }
  }

  if (8 >= params.start_step.substring(0,2).toInteger()) {
    //启动第一组应用
    stage ('8-Group01-Start') {
        wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
            sh 'salt-ssh --force-color "xxxxxxx.ali" cmd.run "python release.py --app=soa --group=1 --type=start" cwd="/data/deploy/apps/sbin" runas=root --out=txt |awk -F\': \' \'{$1="";print}\''
            input '继续还是退出?'
        }
    }
  }

  if (9 >= params.start_step.substring(0,2).toInteger()) {
    //在dubbo-admin中开启第一组应用
    stage ('9-Group01-Dubbo-Enable') {
        wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
            sh 'salt-ssh --force-color "xxxxxxx.ali" cmd.run "python release.py --app=soa --group=1 --type=dubbo --action=enable" cwd="/data/deploy/apps/sbin" runas=root --out=txt |awk -F\': \' \'{$1="";print}\''
            input '继续还是退出?'
        }
    }
  }


  if (10 >= params.start_step.substring(0,2).toInteger()) {
    //分发第二组应用包到对应对的机器上
    stage ('10-Group02-Getfile') {
        wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
            sh 'salt-ssh --force-color "xxxxxxx.ali" cmd.run "python release.py --app=soa --group=2 --type=getfile" cwd="/data/deploy/apps/sbin" runas=root --out=txt |awk -F\': \' \'{$1="";print}\''
            input '继续还是退出?'
        }
    }
  }

  if (11 >= params.start_step.substring(0,2).toInteger()) {
    //解压第二组应用
    stage ('11-Group02-Unpack') {
        wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
            sh 'salt-ssh --force-color "xxxxxxx.ali" cmd.run "python release.py --app=soa --group=2 --type=unpack" cwd="/data/deploy/apps/sbin" runas=root --out=txt |awk -F\': \' \'{$1="";print}\''
        }
    }
  }

  if (12 >= params.start_step.substring(0,2).toInteger()) {
    //在dubbo-admin中禁用第二组应用
    stage ('12-Group02-Dobbo-Disable') {
        wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
            sh 'salt-ssh --force-color "xxxxxxx.ali" cmd.run "python release.py --app=soa --group=2 --type=dubbo --action=disable" cwd="/data/deploy/apps/sbin" runas=root --out=txt |awk -F\': \' \'{$1="";print}\''
            input '继续还是退出?'
        }
    }
  }

  if (13 >= params.start_step.substring(0,2).toInteger()) {
    //停止第二组应用
    stage ('13-Group02-Stop') {
        wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
            sh 'salt-ssh --force-color "xxxxxxx.ali" cmd.run "python release.py --app=soa --group=2 --type=stop" cwd="/data/deploy/apps/sbin" runas=root --out=txt |awk -F\': \' \'{$1="";print}\''
        }
    }
  }

  if (14 >= params.start_step.substring(0,2).toInteger()) {
    //打连接第二组应用
    stage ('14-Group02-Symlink') {
        wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
            sh 'salt-ssh --force-color "xxxxxxx.ali" cmd.run "python release.py --app=soa --group=2 --type=symlink" cwd="/data/deploy/apps/sbin" runas=root --out=txt |awk -F\': \' \'{$1="";print}\''
        }
    }
  }

  if (15 >= params.start_step.substring(0,2).toInteger()) {
    //启动第二组应用
    stage ('15-Group02-Start') {
        wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
            sh 'salt-ssh --force-color "xxxxxxx.ali" cmd.run "python release.py --app=soa --group=2 --type=start" cwd="/data/deploy/apps/sbin" runas=root --out=txt |awk -F\': \' \'{$1="";print}\''
            input '继续还是退出?'
        }
    }
  }

    if (16 >= params.start_step.substring(0,2).toInteger()) {
    //在dubbo-admin中开启第二组应用
    stage ('16-Group02-Dubbo-Enable') {
        wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
            sh 'salt-ssh --force-color "xxxxxxx.ali" cmd.run "python release.py --app=soa --group=2 --type=dubbo --action=enable" cwd="/data/deploy/apps/sbin" runas=root --out=txt |awk -F\': \' \'{$1="";print}\''
        }
    }
  }
}

说明:

当执行到7步的时候,由于网络或者其他原因,导致在第七步失败了。
人工干预:
    先确定第七步是否执行?
        如果第七步已经执行了,只是突然断网导致pipeline退出,第二次手动执行pipeline的时候从选择第7+1步开始
        如果第七步并没有执行就断网了,第二次手动执行pipeline的时候还是从第七步开始执行
(3)生产上有几个服务要经常重启,因此做了一个如下的东西,让开发自己去弄!

注意:

    借助python+saltstack+jenkins可以让很多动作实现半自动化,包括发版、系统初始化、扩容、服务启停等等,这些动作完全可以交给开发自己去操作,从而将我们运维的时间解放。
    如果要实现k8s的那种完全自动化扩容,在saltstack的基础上,写一个Daemon来收集性能数据,并根据一定的算法,也可以实现一个简单的定制的自动化扩容方案。
    本篇博文中pipeline是否专业的groovy编写,复杂用法有很多,可以参考groovy语法。

#!/usr/bin/env groovy
#coding:utf-8

properties([
    parameters([
        choice(choices: 'ali\ntengxun', description: '选择数据中心', name: 'idc_name'),
        choice(choices: '10.0.0.1\n10.0.0.2\n10.0.0.3\n10.0.0.4\n10.0.0.5\n10.0.0.6', description: '选择目标主机', name: 'target_host'),
        choice(choices: 'biz\ncanal\nhadoop\nindex\nsearch\nadmin\nrestapi', description: '选择要操作的服务名', name: 'target_service'),
        choice(choices: 'start\nstop\nrestart\nstatus', description: '选择操作类型', name: 'type_name')
    ])
])

node {
    if ( params.idc_name == 'ali' ) {
        env.HOST = 'xxxxxxx.ali'
    } else if ( params.idc_name == 'tengxun' ) {
        env.HOST = 'yyyyyyy.tengxun'
    } else {
        echo "input error !!!!!!"
        sh "exit 1"
    }

    if ( params.target_host == '10.0.0.1' ) {
        env.TARGET_HOST = 'xxxxxxx.ali'
    } else if ( params.target_host == '10.0.0.2' ){
        env.TARGET_HOST = 'yyyyyyy.ali'
    } else if ( params.target_host == '10.0.0.3' ) {
        env.TARGET_HOST = 'zzzzzzz.ali'
    } else if ( params.target_host == '10.0.0.4' ) {
        env.TARGET_HOST = 'aaaaaaa.ali'
    } else if ( params.target_host == '10.0.0.5' ) {
        env.TARGET_HOST = 'bbbbbbb.tengxun'
    } else if ( params.target_host == '10.0.0.6' ) {
        env.TARGET_HOST = 'ccccccc.tengxun'
    } else {
        echo "input error !!!!!!"
        sh "exit 1"
    }

    //下面的转换其实不需要
    if ( params.target_service == "biz" ) {
        env.TARGET_SERVICE = "biz"
    } else if ( params.target_service == "canal" ) {
        env.TARGET_SERVICE = "canal"
    } else if ( params.target_service == "hadoop" ) {
        env.TARGET_SERVICE = "hadoop"
    } else if ( params.target_service == "index" ) {
        env.TARGET_SERVICE = "index"
    } else if ( params.target_service == "search" ) {
        env.TARGET_SERVICE = "search"
    } else if ( params.target_service == "admin" ) {
        env.TARGET_SERVICE = "admin"
    } else if ( params.target_service == "restapi" ) {
        env.TARGET_SERVICE = "restapi"
    } else {
        echo "input error !!!!!!"
        sh "exit 1"
    }


    if ( params.type_name == "start" ) {
        env.TYPE_NAME = "start"
    } else if ( params.type_name == "restart" ) {
        env.TYPE_NAME = "restart"
    } else if ( params.type_name == "stop" ) {
        env.TYPE_NAME = "stop"
    } else if ( params.type_name == "status" ) {
        env.TYPE_NAME = "status"
    } else {
        echo "input error !!!!!!"
        sh "exit 1"
    }

    stage ('tool') {
        wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
            #echo "${env.HOST}"
            #echo "${env.TARGET_HOST}"
            #echo "${env.TARGET_SERVICE}"
            #echo "${env.TYPE_NAME}"
            def type_cmd = "systemctl " + "${env.TYPE_NAME}" + " " + "${env.TARGET_SERVICE}"
            def remote_cmd = '"' + "salt" + " " + "${env.TARGET_HOST}" + " cmd.run " + "'${type_cmd}'" + '"'
            #echo "${type_cmd}"
            #echo "${remote_cmd}"
            sh "salt-ssh ${env.HOST} cmd.run ${remote_cmd}"
        }
    }

}

由于命令中层层嵌套,同时每层中都有变量,因此用字符串拼接的方式

salt-ssh yyyyyyy.tengxun cmd.run 'xxxxxxx.ali cmd.run '\''systemctl status canal'\'''
(4)search发版:

如下代码,当要进行流量切换时,想做成子流程,有待完善;另外下面的处理也比较粗糙。

#!/usr/bin/env groovy
#coding:utf-8

properties([
    parameters([
        choice(choices: 'yes\nno', description: '选择是否要进行流量切换?', name: 'net_flow'),
        choice(choices: 'ali\ntengxu', description: '选择要操作的机房?', name: 'idc_name'),
        choice(choices: '01-Group01-Close-Net_flow\n02-Commit Package to SVN\n03-Archive Package to Local DataCenter\n04-Group01-Getfile\n05-Group01-Unpack-Tarfile\n06-Group01-Stop_services\n07-Group01-Symlink\n08-Group01-Config\n09-Group01-Start_services\n10-Group01-Open-Net_flow\n11-Group02-Close-Net_flow\n12-Group02-Getfile\n13-Group02-Unpack-Tarfile\n14-Group02-Stop_services\n15-GroGroup02-Symlink\n16-Group02-Config\n17-Group02-Start_services\n18-Group02-Open-Net_flow', description: '选择开始步骤', name: 'start_step')
    ])
])

node {
    //根据用户选择参数,判断salt-master主机名
    if ( params.idc_name == 'ali' ) {
        env.HOST = 'xxxxxxx.ali'
        env.REMOTE_PATH = '/root/tools/deploy/apps/sbin/apps'
    } else if ( params.idc_name == tengxun' ) {
        env.HOST = 'yyyyyyy.tengxun'
        env.REMOTE_PATH = '/data/deploy/apps/sbin/apps'
    } else {
        echo "input error !!!!!!"
        sh "exit 1"
    }

    if (1 >= params.start_step.substring(0,2).toInteger()) {
        //如果需要关闭第一组流量,这里关闭,否则此步不会进行实质性操作
        stage ('01-Group01-Close-Net_flow') {
            if (params.net_flow == "no") {
                echo "不用进行流量切换,此步将被忽略..."
            } else {
                //...待补充
                input '继续还是退出?'
            }
        }
    }


    if (2 >= params.start_step.substring(0,2).toInteger()) {
        //提交开发上传的工程包至发布SVN库,eg: python commit.py search sl
        stage ('02-Commit Package to SVN') {
            wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
                def remote_cmd = "'" + "python commit.py search ${params.idc_name}" + "'"
                sh "salt-ssh ${env.HOST} --force-color cmd.run ${remote_cmd} cwd=${env.REMOTE_PATH} runas=root --out=txt"
                input '继续还是退出?'
            }
        }
    }

    if (3 >= params.start_step.substring(0,2).toInteger()) {
        //从发布SVN库更新至本地数据中心,eg: python archive.py --app=search --idc=sl
        stage ('03-Archive Package to Local DataCenter') {
            wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
                def remote_cmd = "'" + "python archive.py --app=search --idc=${params.idc_name}" + "'"
                sh "salt-ssh ${env.HOST} --force-color cmd.run ${remote_cmd} cwd=${env.REMOTE_PATH} runas=root --out=txt"
                input '继续还是退出?'
            }
        }
    }

    if (4 >= params.start_step.substring(0,2).toInteger()) {
        //第一组拉取包到本地,eg: python release.py --app=search --idc=sl --group=1 --type=getfile
        stage ('04-Group01-Getfile') {
            wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
                def remote_cmd = "'" + "python release.py --app=search --idc=${params.idc_name} --group=1 --type=getfile" + "'"
                sh "salt-ssh ${env.HOST} --force-color cmd.run ${remote_cmd} cwd=${env.REMOTE_PATH} runas=root --out=txt"
                input '继续还是退出?'
            }
        }
    }

    if (5 >= params.start_step.substring(0,2).toInteger()) {
        //第一组解压拉取到本地的包,eg: python release.py --app=search --idc=sl --group=1 --type=unpack
        stage ('05-Group01-Unpack-Tarfile') {
            wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
                def remote_cmd = "'" + "python release.py --app=search --idc=${params.idc_name} --group=1 --type=unpack" + "'"
                sh "salt-ssh ${env.HOST} --force-color cmd.run ${remote_cmd} cwd=${env.REMOTE_PATH} runas=root --out=txt"
                input '继续还是退出?'
            }
        }
    }

    if (6 >= params.start_step.substring(0,2).toInteger()) {
        //第一组停止本地服务,eg:salt -N 'search-group1' state.sls release.search.stop saltenv=prod
        stage ('06-Group01-Stop_services') {
            wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
                def remote_cmd = '"' + "salt -N 'search-group1' state.sls release.search.stop saltenv=prod" + '"'
                sh "salt-ssh ${env.HOST} --force-color cmd.run ${remote_cmd} cwd=${env.REMOTE_PATH} runas=root --out=txt"
                input '继续还是退出?'
            }
        }
    }

    if (7 >= params.start_step.substring(0,2).toInteger()) {
        //第一组打软链,eg:python release.py --app=search --idc=sl --group=1 --type=symlink
        stage ('07-Group01-Symlink') {
            wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
                def remote_cmd = "'" + "python release.py --app=search --idc=${params.idc_name} --group=1 --type=symlink" + "'"
                sh "salt-ssh ${env.HOST} --force-color cmd.run ${remote_cmd} cwd=${env.REMOTE_PATH} runas=root --out=txt"
                input '继续还是退出?'
            }
        }
    }

    if (8 >= params.start_step.substring(0,2).toInteger()) {
        //第一组修改配置,eg:salt -N 'search-group1' state.sls release.search.config saltenv=prod
        stage ('08-Group01-Config') {
            wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
                def remote_cmd = '"' + "salt -N 'search-group1' state.sls release.search.config saltenv=prod" + '"'
                sh "salt-ssh ${env.HOST} --force-color cmd.run ${remote_cmd} cwd=${env.REMOTE_PATH} runas=root --out=txt"
                input '继续还是退出?'
            }
        }
    }

    if (9 >= params.start_step.substring(0,2).toInteger()) {
        //第一组服务启动,eg:salt -N 'search-group1' state.sls release.search.start saltenv=prod
        stage ('09-Group01-Start_services') {
            wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
                def remote_cmd = '"' + "salt -N 'search-group1' state.sls release.search.start saltenv=prod" + '""'
                sh "salt-ssh ${env.HOST} --force-color cmd.run ${remote_cmd} cwd=${env.REMOTE_PATH} runas=root --out=txt"
                input '继续还是退出?'
            }
        }
    }

    if (10 >= params.start_step.substring(0,2).toInteger()) {
        //如果第一组流量关闭,此时打开
        stage ('10-Group01-Open-Net_flow') {
            if (params.net_flow == "no") {
                echo "不用进行流量切换,此步将被忽略..."
            } else {
                //...待补充
                input '继续还是退出?'
            }
        }
    }

    if (11 >= params.start_step.substring(0,2).toInteger()) {
        //如果需要切流量,此时关闭第二组流量
        stage ('11-Group02-Close-Net_flow') {
            if (params.net_flow == "no") {
                echo "不用进行流量切换,此步将被忽略..."
            } else {
                //...待补充
                input '继续还是退出?'
            }
        }
    }

    if (12 >= params.start_step.substring(0,2).toInteger()) {
        //第二组拉取包到本地,eg: python release.py --app=search --idc=sl --group=2 --type=getfile
        stage ('12-Group02-Getfile') {
            wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
                def remote_cmd = "'" + "python release.py --app=search --idc=${params.idc_name} --group=2 --type=getfile" + "'"
                sh "salt-ssh ${env.HOST} --force-color cmd.run ${remote_cmd} cwd=${env.REMOTE_PATH} runas=root --out=txt"
                input '继续还是退出?'
            }
        }
    }

    if (13 >= params.start_step.substring(0,2).toInteger()) {
        //第二组解压拉取到本地的包,eg: python release.py --app=search --idc=sl --group=2 --type=unpack
        stage ('13-Group02-Unpack-Tarfile') {
            wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
                def remote_cmd = "'" + "python release.py --app=search --idc=${params.idc_name} --group=2 --type=unpack" + "'"
                sh "salt-ssh ${env.HOST} --force-color cmd.run ${remote_cmd} cwd=${env.REMOTE_PATH} runas=root --out=txt"
                input '继续还是退出?'
            }
        }
    }

    if (14 >= params.start_step.substring(0,2).toInteger()) {
        //第二组停止本地服务,eg:salt -N 'search-group2' state.sls release.search.stop saltenv=prod
        stage ('14-Group02-Stop_services') {
            wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
                def remote_cmd = '"' + "salt -N 'search-group2' state.sls release.search.stop saltenv=prod" + '"'
                sh "salt-ssh ${env.HOST} --force-color cmd.run ${remote_cmd} cwd=${env.REMOTE_PATH} runas=root --out=txt"
                input '继续还是退出?'
            }
        }
    }

    if (15 >= params.start_step.substring(0,2).toInteger()) {
        //第二组打软链,eg:python release.py --app=search --idc=sl --group=2 --type=symlink
        stage ('15-GroGroup02-Symlink') {
            wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
                def remote_cmd = "'" + "python release.py --app=search --idc=${params.idc_name} --group=2 --type=symlink" + "'"
                sh "salt-ssh ${env.HOST} --force-color cmd.run ${remote_cmd} cwd=${env.REMOTE_PATH} runas=root --out=txt"
                input '继续还是退出?'
            }
        }
    }

    if (16 >= params.start_step.substring(0,2).toInteger()) {
        //第二组修改配置,eg:salt -N 'search-group2' state.sls release.search.config saltenv=prod
        stage ('16-Group02-Config') {
            wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
                def remote_cmd = '"' + "salt -N 'search-group2' state.sls release.search.config saltenv=prod" + '"'
                sh "salt-ssh ${env.HOST} --force-color cmd.run ${remote_cmd} cwd=${env.REMOTE_PATH} runas=root --out=txt"
                input '继续还是退出?'
            }
        }
    }

    if (17 >= params.start_step.substring(0,2).toInteger()) {
        //第二组服务启动,eg:salt -N 'search-group2' state.sls release.search.start saltenv=prod
        stage ('17-Group02-Start_services') {
            wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
                def remote_cmd = '"' + "salt -N 'search-group2' state.sls release.search.start saltenv=prod" + '"'
                echo "${remote_cmd}"
                //sh "salt-ssh ${env.HOST} --force-color cmd.run ${remote_cmd} cwd=${env.REMOTE_PATH} runas=root --out=txt"
                echo "salt-ssh ${env.HOST} --force-color cmd.run ${remote_cmd} cwd=${env.REMOTE_PATH} runas=root --out=txt"
                input '继续还是退出?'
            }
        }
    }

    if (18 >= params.start_step.substring(0,2).toInteger()) {
        //如果第二组流量关闭,打开
        stage ('18-Group02-Open-Net_flow') {
            if (params.net_flow == "no") {
                echo "不用进行流量切换,此步将被忽略..."
            } else {
                //待补充,暂时不进行自动流量切换
                input '继续还是退出?'
            }
        }
    }

}
(5)别人复杂的例子

另外附上github中的例子url:https://github.com/jenkinsci/pipeline-examples
官方基础steps的文档url:https://jenkins.io/doc/pipeline/steps/workflow-basic-steps/#catcherror-catch-error-and-set-build-result

pipeline {
    agent none

    parameters {
        string(name: 'BRANCH_TO_BUILD', defaultValue: "develop", description: 'GIT branch to build')
    }

    environment {
        GIT_URL = 'ssh://[email protected]/repo.git'
        BRANCH_TO_BUILD_DEFAULT = 'develop'
        BRANCH_TO_BUILD_REQUESTED = "${params.BRANCH_TO_BUILD}"
    }


    stage('Configure the build') {
        agent none
        steps {
            echo "Prompt user for a branch to build (default: ${BRANCH_TO_BUILD_DEFAULT})"
            script {
                try {
                    timeout(time:30, unit:'SECONDS') {
                    BRANCH_TO_BUILD_REQUESTED = input(
                        message: 'Input branch to build',
                        parameters: [
                                [$class: 'TextParameterDefinition',
                                 defaultValue: BRANCH_TO_BUILD_DEFAULT,
                                 description: 'Branch name', name: 'Enter branch name (or leave default) and press [Proceed]:']
                            ])
                        echo ("User has entered the branch name: " + BRANCH_TO_BUILD_REQUESTED)
                    }
                } catch(err) { // timeout reached or input Aborted
                    def user = err.getCauses()[0].getUser()
                        if('SYSTEM' == user.toString()) { // SYSTEM means timeout
                            echo ("Input timeout expired, default branch will be used: " + BRANCH_TO_BUILD_DEFAULT)
                            BRANCH_TO_BUILD_REQUESTED = BRANCH_TO_BUILD_DEFAULT
                        } else {
                            echo "Input aborted by: [${user}]"
                            error("Pipeline aborted by: [${user}]")
                        }
                }
            }
        }
    }

    stage('Checkout') {
        agent{node {label 'worker-1'}}

        steps {
            echo "Checkout will be done for Git branch: ${BRANCH_TO_BUILD_REQUESTED}"
            checkout([$class: 'GitSCM', branches: [[name: "*/${BRANCH_TO_BUILD_REQUESTED}"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[url: ${GIT_URL]}]])
            }
    }

}

2、使用注意事项

(1)jenkins编译打包java时,要在jenkins中配置jdk

另外jenkins和sonar集成时也要依赖这个配置

(2)颜色相关

由于结果有一些是有颜色的,这时可以使用jenkins的AnsiColor插件
参考:
https://wiki.jenkins.io/display/JENKINS/AnsiColor+Plugin
https://github.com/jenkinsci/ansicolor-plugin

pipeline中:   #将输出结果的命令放入wrap命令块中
wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
    sh 'salt-ssh "xxxxxxx.ali" cmd.run "python commit.py --app=soa" cwd="/data/deploy/apps/sbin" runas=root'
    //这里我们当时用的是febric的颜色控制,返回的不是标准的格式??还是怎么回事呢???
    //标准的格式是echo -e "\033[31m ERROR: 当前已是最新版,请确认是否上传工程包! \033[0m"因此也就这样了
    //或者echo -e "\e[31m ERROR: 当前已是最新版,请确认是否上传工程包! \e[0m"
}
或者
ansiColor('xterm') {
    echo 'something that outputs ansi colored stuff'
}

当时设置经过:

加上salt-ssh命令加上--force-color参数,可以显示salt-ssh执行后的颜色,但是无法正常显示远程终端的颜色
# salt-ssh "xxxxxxx.ali" cmd.run "echo -e \"\033[31mERROR: 当前已是最新版,请确认是否上传工程包!\033[0m\"" runas=root
xxxxxxx.ali:
    ?[31mERROR: 当前已是最新版,请确认是否上传工程包!?[0m
结果是salt-ssh执行成功的绿色,而不是命令的结果红色

salt-ssh 加一个参数--out=txt这样可以jenkins pipeline中可以显示颜色,但是格式又不是很好,blue ocean还是没有颜色。
最后结合shell脚本的管道,过滤掉第一列数据,就好很多了。
(3)有一次jenkins构建pipeline任务时,清空了workspace导致构建任务失败:

jenkins启动相关属性,必须在-jar之前,才能生效,否则会被忽略
https://wiki.jenkins.io/display/JENKINS/Features+controlled+by+system+properties

hudson.model.WorkspaceCleanupThread.disabled
hudson.model.WorkspaceCleanupThread.recurrencePeriodHours
hudson.model.WorkspaceCleanupThread.retainForDays
看/etc/sysconfig/jenkins中配置:
JENKINS_JAVA_OPTIONS="-Djava.awt.headless=true -Dhudson.DNSMultiCast.disabled=true -Dhudson.model.WorkspaceCleanupThread.recurrencePeriodHours=168"

jenkins启动参数查看
java -jar jenkins.jar --help

注意:
如果是关闭了workspace清空线程,那么如何清空工作目录呢
pipeline {
    agent any
    stages {
        stage('No-op') {
            steps {
                sh 'ls'
            }
        }
    }
    post {
        always {
            echo 'One way or another, I have finished'
            deleteDir() /* clean up our workspace */
        }
    }
}

另外有一个workspace清空插件可以用

(4)jenkins的input用法:

https://*.com/questions/37831386/jenkins-pipeline-input-step-blocks-executor

node() {
  stage 'Build to Stage' {
    sh '# ...'
  }

  stage 'Promotion' {
    timeout(time: 1, unit: 'HOURS') {
      input 'Deploy to Production?'
    }
  }

  stage 'Deploy to Production' {
    sh '# ...'
  }
}
(5)lock方法:

You have to use the input step outside any node block, so it does not hold any executor:

stage 'Build'
node('build-node') {
  sh 'call you build tool'
  stash includes: 'target/my-output-artifact.whatever', name: 'built'
}

input 'Continue to deploy stage?'

stage 'Deploy'
node('deploy-node') {
  unstash 'built'
  sh 'scp target/my-output-artifact.whatever user@deploy-server:/deploy'
}
And you can lock the deploy stage if you want only one deploy at time:

lock ('deploy-server') {
  stage 'Deploy'
  node('deploy-node') {
    unstash 'built'
    sh 'scp target/my-output-artifact.whatever user@deploy-server:/deploy'
  }
}

3、自动化开发相关的东西

(1)jenkins job builder可以将job创建变得模板化和脚本化,给你不一样的job规模化job管理

待补充

(2)自动化相关
a、获取构建结果(SUCCESS/FAIL)
获取某一个构建版本号为6的构建结果:
curl http://192.168.165.214:8081/job/pro-ljj_mir-ljj/6/api/xml --user admin:123456
获取最后一次构建的构建结果:
curl http://192.168.165.214:8081/job/pro-ljj_mir-ljj/lastBuild/api/xml --user admin:123456
获取最后一次构建的版本号:
curl  http://192.168.165.214:8081/job/laijinjie-pro_laijinjie-mir/lastBuild/buildNumber --user admin:123456
获取某个项目所以构建版本号的结果;
curl  http://192.168.165.214:8081/job/pro-ljj_mir-ljj/api/xml --user admin:123456
以上pro-ljj_mir-ljj为项目名称,最后的是账号密码。
b、jenkins的cli客户端

参考:https://www.w3cschool.cn/jenkins/jenkins-zh3e28mj.html

wget https://JENKINS_URL/jnlpJars/jenkins-cli.jar   #jenkins服务器自己就提供了
使用:java -jar jenkins-cli.jar [-s JENKINS_URL] [global options...] command [command options...] [arguments...]
客户端连接模式
有三种基本模式,其中可以使用本2.54+ / 2.46.2+客户端,通过全局选项可选择的: -http; -ssh; 和-remoting。

http模式:
java -jar jenkins-cli.jar [-s JENKINS_URL] -auth kohsuke:abc1234ffe4a command ...
获取单个job的输出:
java -jar ./jenkins-cli.jar -s http://10.4.4.67:8080/ -auth admin:xKHIBxHkrTE console java-ejob 28

# java -jar ./jenkins-cli.jar -s http://10.4.4.67:8080/ -auth admin:xKHIBxHkrTE console --help
ERROR: "--help" is not a valid option
java -jar jenkins-cli.jar console JOB [BUILD] [-f] [-n N] [--username VAL] [--password VAL] [--password-file VAL]
Produces the console output of a specific build to stdout, as if you are doing 'cat build.log'
 JOB                 : Name of the job
 BUILD               : Build number or permalink to point to the build.
                       Defaults to the last build
 -f                  : If the build is in progress, stay around and append     //正在执行的job
                       console output as it comes, like 'tail -f'
 -n N                : Display the last N lines
 --username VAL      : User name to authenticate yourself to Jenkins
 --password VAL      : Password for authentication. Note that passing a
                       password in arguments is insecure.
 --password-file VAL : File that contains the password
当日志中有颜色时,可以用sed进行处理:
# cat 76.log |sed 's/ha.*==//g'|sed 's/ha.*=//g'
eg:
# java -jar ./jenkins-cli.jar -s http://10.4.4.67:8080/ -auth admin:xKHIBxHkrTE console SOA-Deploy-Level01 |sed 's/ha.*==//g'|sed 's/ha.*=//g'|sed 's/ha.*AAAA//g'

更多详细用法:

# java -jar ./jenkins-cli.jar -s http://10.4.4.67:8080/ -auth admin:xKHIBxHkrTE help
  add-job-to-view
    Adds jobs to view.
  build
    Builds a job, and optionally waits until its completion.
  cancel-quiet-down
    Cancel the effect of the "quiet-down" command.
  clear-queue
    Clears the build queue.
  connect-node
    Reconnect to a node(s)
  console
    Retrieves console output of a build.
  copy-job
    Copies a job.
  create-credentials-by-xml
    Create Credential by XML
  create-credentials-domain-by-xml
    Create Credentials Domain by XML
  create-job
    Creates a new job by reading stdin as a configuration XML file.
  create-node
    Creates a new node by reading stdin as a XML configuration.
  create-view
    Creates a new view by reading stdin as a XML configuration.
  declarative-linter
    Validate a Jenkinsfile containing a Declarative Pipeline
  delete-builds
    Deletes build record(s).
  delete-credentials
    Delete a Credential
  delete-credentials-domain
    Delete a Credentials Domain
  delete-job
    Deletes job(s).
  delete-node
    Deletes node(s)
  delete-view
    Deletes view(s).
  disable-job
    Disables a job.
  disconnect-node
    Disconnects from a node.
  enable-job
    Enables a job.
  get-credentials-as-xml
    Get a Credentials as XML (secrets redacted)
  get-credentials-domain-as-xml
    Get a Credentials Domain as XML
  get-job
    Dumps the job definition XML to stdout.
  get-node
    Dumps the node definition XML to stdout.
  get-view
    Dumps the view definition XML to stdout.
  groovy
    Executes the specified Groovy script.
  groovysh
    Runs an interactive groovy shell.
  help
    Lists all the available commands or a detailed description of single command.
  install-plugin
    Installs a plugin either from a file, an URL, or from update center.
  install-tool
    Performs automatic tool installation, and print its location to stdout. Can be only called from inside a build. [deprecated]
  keep-build
    Mark the build to keep the build forever.
  list-changes
    Dumps the changelog for the specified build(s).
  list-credentials
    Lists the Credentials in a specific Store
  list-credentials-context-resolvers
    List Credentials Context Resolvers
  list-credentials-providers
    List Credentials Providers
  list-jobs
    Lists all jobs in a specific view or item group.
  list-plugins
    Outputs a list of installed plugins.
  login
    Saves the current credentials to allow future commands to run without explicit credential information. [deprecated]
  logout
    Deletes the credentials stored with the login command. [deprecated]
  mail
    Reads stdin and sends that out as an e-mail.
  offline-node
    Stop using a node for performing builds temporarily, until the next "online-node" command.
  online-node
    Resume using a node for performing builds, to cancel out the earlier "offline-node" command.
  quiet-down
    Quiet down Jenkins, in preparation for a restart. Don’t start any builds.
  reload-configuration
    Discard all the loaded data in memory and reload everything from file system. Useful when you modified config files directly on disk.
  reload-job
    Reload job(s)
  remove-job-from-view
    Removes jobs from view.
  replay-pipeline
    Replay a Pipeline build with edited script taken from standard input
  restart
    Restart Jenkins.
  safe-restart
    Safely restart Jenkins.
  safe-shutdown
    Puts Jenkins into the quiet mode, wait for existing builds to be completed, and then shut down Jenkins.
  session-id
    Outputs the session ID, which changes every time Jenkins restarts.
  set-build-description
    Sets the description of a build.
  set-build-display-name
    Sets the displayName of a build.
  set-build-parameter
    Update/set the build parameter of the current build in progress. [deprecated]
  set-build-result
    Sets the result of the current build. Works only if invoked from within a build. [deprecated]
  set-external-build-result
    Set external monitor job result.
  shutdown
    Immediately shuts down Jenkins server.
  update-credentials-by-xml
    Update Credentials by XML
  update-credentials-domain-by-xml
    Update Credentials Domain by XML
  update-job
    Updates the job definition XML from stdin. The opposite of the get-job command.
  update-node
    Updates the node definition XML from stdin. The opposite of the get-node command.
  update-view
    Updates the view definition XML from stdin. The opposite of the get-view command.
  version
    Outputs the current version.
  wait-node-offline
    Wait for a node to become offline.
  wait-node-online
    Wait for a node to become online.
  who-am-i
    Reports your credential and permissions.

另外jenkins本身提供xml api,json api,python api
参考:https://www.cnblogs.com/zjsupermanblog/archive/2017/07/26/7238422.html

http://10.0.0.7:8080/api/
某个job的api借口:http://10.0.0.7:8080/job/ejob/api/
eg:curl 'http://10.0.0.7:8080/job/ejob/lastBuild/api/json?pretty=true&tree=result' --user admin:12345678
tree是用来过滤结果的
或者直接使用第三方模块https://pypi.python.org/pypi/python-jenkins/
相关标签: jenkins pipeline