前言
最近不知道写点什么,想着补充一下自己的以前的文档,有时间就补充一下jenkins,nginx。。。
以前个人总结的jenkins文档:
Jenkins官方文档:
https://www.jenkins.io/zh/doc/book/pipeline/
下面介绍一下Jenkins Pipeline的基本用法
由于内容较多,为了讲解编辑了大量的补充性的内容,建议首次看的可以不看补充内容
Pipeline简介
Pipeline是Jenkins 2.x的核心的特性,帮助Jenkins实现从CI到CD与DevOps的转变。其中文翻译为管道,也可以叫做流水线
它是一套运行于Jenkins上的工作流框架,将原本独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂流程编排与可视化
简单的来说就是将一套上线或者发布变得流程化和可视化
和传统的扔代码到线上以及使用jenkins简单的调用脚本的上线方式的区别:
更加的流程化和安全。可以清楚的显示在哪个流程中有问题,并自动停止执行。如果是之前文档演示的几种方式,如果出问题了,需要翻整篇的日志,去查询哪个地方有问题。并且最后页面上的显示,只有2种结果,要么最后成功要么最后失败,比较浪费时间
更加的直观显示流程的状态,可视化的界面更加的令人舒适
基本思路
将一个上线的过程进行分解和流程化,分成多个环节。例如:清理空间,拉取代码,发布到环境,备份,重启服务,验证,通知等环节,然后我们需要将这些步骤套用pipeline的语法
按照以前的方式也许一个脚本里就蕴含了这些流程,但是这实际上并不算是配合jenkins实现了流程化,仅仅只算是使用jenkins进行了简单的发布。虽然完成了发布,但是看起来并不”高级“和舒适
直白的说,使用这个的目的不仅是为了方便自己和规范流程,对于部分领导并不喜欢你看上去很闲,简单的认为越复杂越专业。配置时越复杂越看不懂,图形界面越好看才是”他的需求“,效率并不会让他舒服,只会认为你工作不饱和,
Pipeline的内容
实际上Pipeline的语法较多,有很大部分并不一定能用到
因此这里只演示几种常见的配置和创建方式,更多的需要研究官方文档
Pipeline关键概念
Stage 阶段,一个Pipeline可以划分为若干个Stage,每个Stage代表一组操作
你可以把你在此阶段后要配置的过程进行一个总结,将这个阶段叫做 构建 测试 发布 或者其他
Stage是一个逻辑分组的概念,可以跨多个Node
Node 节点,一个Node就是一个Jenkins节点,或者是Master,或者是slave,是执行Step的具体运行期环境
Step 步骤,Step是最基本的操作单元,小到创建一个目录,大到构建一个Docker镜像,由各类Jenkins Plugin提供
Pipeline语法
Pipeline支持两种语法:声明式语法(Declarative Pipeline) 和脚本化语法(Scripted Pipeline)
两者都能够使用pipeline内置的插件或者插件提供的steps,两者都可以利用共享库扩展
从官网上来说,声明式流水线相比脚本化的流水线语法,它提供更丰富的语法特性,是为了使编写和读取流水线代码更容易而设计的。其语法更严格,有固定的组织结构,更容易生成代码段,可以Blue Ocean插件进行简单的编排(实际上部分工作者还是喜欢类似于脚本化语法的方法,在声明式流水线中去调用脚本)
但是对于一些使用较老版本jenkins的用户,更习惯使用脚本化的语法。实际上脚本化的语法要更加灵活方便,用户可以根据自己的业务进行灵活的实现和扩展实现其他需求,例如 配合编写的python或其他脚本,完成一些复杂的任务
声明式语法由于语法严格,有固定的,严格的格式
声明式由块,章节,指令,步骤,节点组成;必须包含在固定格式pipeline{}块内;每个声明必须独立一行,行尾无需使用分号
块(blocks{})
由大括号括起来的语句,如pipeline{},Section{},parameters{},script{}
章节(Sections)
通常包含一个或多个指令或步骤。如 agent 、post、stages、steps
指令(Directives)
environment、options、parameters、triggers(触发)、stage、tools、when
步骤(Steps)
agent
必须存在,agent必须在pipeline块内的顶层定义,但stage内是否使用使可选的
Pipeline的配置
预计在这里演示一下Pipeline的创建方法,然后再演示几个常用的配置
实际上还有Blue Ocean的创建pipeline的方法,但是我这边更常用一些老的方法
Pipeline的常见配置
<1>新建一个item 项目
<2>根据你的项目的需求选择要创建的格式
由于要演示Pipeline,可以选择流水线 或者自由风格的软件项目
<3>设置上线记录和描述(可跳过)
和之前jenkins文档里配置方式没区别
也可省略,到pipeline里配置
<4>配置流水线
选择2种语法格式的任意一个即可,点击右侧j进行选择 可以显示jenkins自带的模板
可以点击流水线语法,选择想要的步骤,生成流水线脚本
接着页面就会显示这个步骤要使用的语法
<5>创建dingding机器人的robot
创建dingding机器人
""
钉钉插件网址:
"http://updates.jenkins-ci.org/download/plugins/dingding-notifications/"
实际上从dingding插件的2.0及其以上的版本开始,钉钉通知的设置方式产生了较大的变化,要在系统管理里设置,之后的版本适应了不同的生产环境,通知的配置更加的灵活
建议在jenkins版本允许的基础上,使用2.0以上插件(钉钉2.0插件最低对应jenkins 2.176.4及其以上)
以下演示的是dingding插件2.0及其以上版本的配置方法
演示用的jenkins是2.3.44
id不用填,让其自动生成(这个就是后面pipeline中的robot)
填入webhook
和
认证方式(和钉钉创建机器人的设置保持一致)
2.0以前的版本,不能在系统设置中设置dingding
它是在安装后,直接在该项目的 构建后操作 中配置 钉钉通知,根据要求填入即可
声明式Pipeline
pipeline { //首先表明这是一个声明式的pipeline
agent any //指定整个管道或特定阶段在Jenkins环境中的执行位置,选择在jenkins的哪个节点执行,常见any, none, label, node, docker, dockerfile。此处的意思是 在任意jenkins环境中可用的机器上执行pipeline
也可以单独指定某个节点agent{ label 'slave1'}
options { //用于配置Jenkins pipeline本身有的选项
buildDiscarder(logRotator(numToKeepStr: '30')) //表示保留30次构建历史
timestamps() //打印日志带上对应时间
timeout(time: 10, unit: 'MINUTES') //流水线构建超过10分钟,终止构建
}
# environment { //设置设置环境变量,可定义在stage或pipeline部分。例如可以设置IP的变量,便于之后的阶段中调用
# TST = /data/workspace2
# }
# triggers{ //设置触发器,格式类似Centos计划任务
# pollSCM('H/5 * * * *')
# }
stages { //声明这是一个多个阶段的流水线
stage('清理工作空间') { //声明这是一个什么阶段(根据自己的情况创建不同的阶段)
steps { //下面需要填入这个阶段的具体步骤
script{
println env.WORKSPACE
dir("${env.WORKSPACE}/"){
sh "pwd"
}
sh("ls -al ${env.WORKSPACE}")
deleteDir()
sh("ls -al ${env.WORKSPACE}")
}
}
stage('拉取代码') {
steps {
git credentialsId: '89e62xxxx-xxxxxx', url: 'git@gitlab.xxx.cn:xxxx.git'
}
}
stage('mvn构建'){
withMaven(maven: 'maven') {
sh "cd $WORKSPACE/"
sh "mvn clean install -Dmaven.test.skip=true"
}
}
stage(部署){
steps{
sh 'scp $WORKSPACE/target/xxx.jar root@业务服务器的ip:/jar_dir_path/'
sh 'ssh root@你业务服务器的ip "/script/deploy.sh"' //执行远程服务器上的脚本
}
}
}
post { //post指的是构建后操作,success成功后执行,failure失败后执行
success {
dingtalk (
robot: "你的机器人的id不是token",
type:'ACTION_CARD', //通知的消息类型,即采用的格式
atAll: false, //是否@所有人
title: "构建成功:${env.JOB_NAME}", //通知的标题
text: [
"### xx平台上线 ${env.JOB_NAME} ",
'---',
"- 任务:[${currentBuild.displayName}](${env.BUILD_URL})",
'- 状态:<font color=#00CD00 >上线成功</font>',
"- 持续时间:${currentBuild.durationString}".split("and counting")[0],
"- 执行人:${BUILD_USER}",
]
)
}
failure{
dingtalk (
robot: "你的机器人的id不是token",
type:'ACTION_CARD',
atAll: false,
title: "构建失败:${env.JOB_NAME}",
text: [
"### xx平台上线 ${env.JOB_NAME}",
'---',
"- 任务:[${currentBuild.displayName}](${env.BUILD_URL})",
'- 状态:<font color=#EE0000 >上线失败</font>',
"- 持续时间:${currentBuild.durationString}".split("and counting")[0],
"- 执行人:${BUILD_USER}"
]
)
}
}
}
}
<1>
实际上,上面的示例可根据自己需求决定是否采用
本示例stages下的,
第一个阶段是上线前清理jenkins服务器上该项目的工作空间
WORKSPACE 是这个项目的工作目录的绝对路径,为了避免编译时产生干扰,所以上线前清理旧代码
dir() 改变当前的工作目录,相当于Centos 的 cd(为了演示所以加了一句dir)
deleteDir() 不加参数,默认递归删除WORKSPACE下的文件和文件夹
script 该script步骤需要一个脚本式Pipeline,并在声明性Pipeline中执行。用这个语法,后面可以用当前环境中的命令的执行操作
sh 用这个方法,后面可以执行shell命令,有时也会经常用来调用jenkins服务器上的脚本;bat是windows命令
<2>
第二个阶段是拉取代码
git的语法可根据需求使用流水线语法生成
可以选择分支,默认是master
<3>
第三个阶段是编译
withMaven需要装一个插件Pipeline Maven Integration,会根据后面填写的变量指定maven,默认读取的是jenkins服务器的环境变量中的maven
如不使用这个语法,去除后,编译命令直接写绝对路径也可以
sh "/data/bin/mvn clean install -Dmaven.test.skip=true"
<4>
第四个阶段是部署
可以用命令行、ansible、调用脚本等方式。构建镜像或k8s的apply都可以
目标是将代码弄到线上环境,并使服务生效
(别忘了产生代码备份,根据需求)
随着需求的不同,方法也不同,所以示例仅列举了部分常用的
# stage(部署){
# steps{
# ansiblePlaybook installation: 'ansible-playbook', inventory: '/etc/ansible/online1/hosts', playbook: '/etc/ansible/online1/project.yml'
# }
# }
# stage(部署){
# steps{
# sh "sh $WORKSPACE/../script/online.sh"
# }
# }
补充:
实际上通常部署或其他阶段还能细分,使用when和当上线时工作人员手动从web界面填入的变量,产生不同的结果
创建文本或其他格式的环境变量,配置后,在上线时会要求工作人员去填入参数
stages {
stage('编译'){
when { //当下方的条件满足,这个阶段才执行
environment name: 'GITENV', value: 'uat' //如果环境变量的值与给定的值相同就执行
}
withMaven(maven: 'maven') {
sh "cd $WORKSPACE/"
sh "mvn clean package -Puat -Dmaven.test.skip=true"
}
}
<5>
第五个阶段是通知
也可以通过python或shell,使用邮箱或钉钉及其他方式,对上线结果进行通知
如果是使用相关插件的,注意jenkins版本和插件版本
补充1
2.0以前的dingding插件,项目如果用pipline的配置方法
post {
success {
dingTalk accessToken:'https://oapi.dingtalk.com/robot/send?access_token=你的token',
imageUrl:'http:/xxxxxx/success.png', //要在通知中插入的图片
jenkinsUrl:'http://localhost/jenkins/', //要跳转的url
message:'更新提示: xxxxxxxxx上线成功', //要发布的通知
notifyPeople:''
}
failure {
dingTalk accessToken:'https://oapi.dingtalk.com/robot/send?access_token=你的token',
imageUrl:'http://xxxxxx/failure.png',
jenkinsUrl:'http://localhost/jenkins/',
message:'更新提示: xxxxxxxx上线失败',
notifyPeople:''
}
}
补充2
通过curl或python的请求并发送报警,无需安装钉钉插件
但是这个方式报警需要 符合配置钉钉机器人时 设置的关键字或ip等限制
stage("钉钉通知"){
sh """
curl -H 'Content-Type: application/json' "https://oapi.dingtalk.com/robot/send?access_token=你的token" \
-d '
{
"actionCard": {
"title": "上线通知",
"text": "xx平台上线 \n\n ${env.JOB_NAME} 上线成功 \n\n",
"hideAvatar": "0",
"btnOrientation": "0",
"singleTitle" : "查看详情",
"singleURL" : "http://jenkins.com/" //你的jenkins地址
},
"msgtype": "actionCard"
}'
"""
}
补充3
如果是一个邮件的脚本,脚本中的报警内容等参数可设置成变量,pipeline在通知这一阶段导入变量,即可实现报警内容的定制
钉钉的pipeline语法
"https://jenkinsci.github.io/dingtalk-plugin/guide/pipeline.html#参数说明"
脚本式pipeline
先创建参数化构建,后面示例的pipelien中有用到
node {
stage("清理工作空间"){
deleteDir()
}
}
node("slave1"){ //指定整个管道或特定阶段在Jenkins环境中的执行的节点,不填默认是master节点执行
也可以单独指定某个节点agent{ label 'slave1'}
stage('变量配置') {
project_work_list=["192.168.1.1","192.168.1.2"] //要上线的IP
TOKEN='xxx' //dingding机器人的token
}
stage('拉取代码') {
checkout([$class: 'GitSCM', branches: [[name: '*/${branch}']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'xxxx', url: 'http://xxxx.git']]]) //和声明式一样,可用流水线语法生成
}
stage('构建') {
sh "cd $WORKSPACE/"
sh "mvn clean install -U -Puat -Dmaven.test.skip=true"
}
stage('部署') {
for (i <project_work_list.size();i++){
def ip=project_work_list[i]
if ( "${project_name}" == "bigdata" ){ //这里的project_name需要你去开启参数化构建,创建一个参数project_name。示例含义:当项目是大数据的时候,使用一种上线脚本,当项目是其他的时候,使用其他的上线脚本
sh "scp $WORKSPACE/target/xxx.jar root@${ip}:/jar_dir_path/"
sh '"ssh root@${ip} "/script/start_project.sh"'
}else {
sh "scp $WORKSPACE/target/xxx.jar root@${ip}:/jar_dir_path/"
sh 'ssh root@${ip} "/script/deploy.sh"'
}
}
}
stage("钉钉通知"){
sh """
curl -H 'Content-Type: application/json' "https://oapi.dingtalk.com/robot/send?access_token=${TOKEN}" \
-d '
{
"actionCard": {
"title": "上线通知",
"text": "xx平台上线 \n\n ${env.JOB_NAME} 上线成功 \n\n",
"hideAvatar": "0",
"btnOrientation": "0",
"singleTitle" : "查看详情",
"singleURL" : "http://jenkins.com/" //你的jenkins地址
},
"msgtype": "actionCard"
}'
"""
}
能看懂声明式的,应该能看懂脚本式,基本不用解释
虽然脚本式看着简单,但是如果要深入,用的更花,需要去官方网址再研究
脚本式区别于声明式pipeline的明显的一点是,只支持stage,像stages、steps更复杂的划分则不支持,因此在脚本中仅仅看到了stage,示例中的if和for用着很方便,但是如果要在声明式用循环需要script