jenkins
-
1、jenkins pipeline实践
* (1)调用salt进行os初始化
* (2)SOA发版jenkins pipeline配置:
* (3)生产上有几个服务要经常重启,因此做了一个如下的东西,让开发自己去弄!
* (4)search发版:
* (5)别人复杂的例子 -
2、使用注意事项
* (1)jenkins编译打包java时,要在jenkins中配置jdk
* (2)颜色相关
* (3)有一次jenkins构建pipeline任务时,清空了workspace导致构建任务失败:
* (4)jenkins的input用法:
* (5)lock方法: -
3、自动化开发相关的东西
* (1)jenkins job builder可以将job创建变得模板化和脚本化,给你不一样的job规模化job管理
* (2)自动化相关
* a、获取构建结果(SUCCESS/FAIL)
* b、jenkins的cli客户端
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/