Jenkins CI&CD 自动化发布项目实战(下篇)
作者 |
刘畅 |
时间 |
2020-12-04 |
实验环境
centos7.5
主机名 |
ip |
服务配置 |
软件 |
gitlab |
172.16.1.71 |
2核/4G/60G |
docker、gitlab |
jenkins-master |
172.16.1.72 |
2核/4G/60G |
docker、jdk、maven、jenkins |
jenkins-slave01 |
172.16.1.73 |
2核/2G/60G |
jdk、maven、ansible |
java-web01 |
172.16.1.74 |
2核/2G/60G |
jdk、tomcat |
java-web02 |
172.16.1.75 |
2核/2G/60G |
jdk、tomcat |
说明:本文docker、gitlab的安装就不赘述了,可以参考其它专题文档。
目录
5.2
在Jenkins项目里配置触发器生成URL和Token 9
1 Pipeline介绍
1Jenkins Pipeline是一套运行工作流框架,将原本独立运行单个或者多个节点的任务链接起来,实现单个任务难以完成的复杂流程编排和可视化。
2 Jenkins Pipeline是一套插件,支持在Jenkins中实现持续集成和持续交付。
3 Pipeline通过特定语法对简单到复杂的传输管道进行建模。
4 Jenkins Pipeline的定义被写入一个文本文件,称为Jenkinsfile。
5 安装pipeline插件
2 Pipeline语法
2.1
声明式流水线语法
Jenkinsfile(Declarative Pipeline)
pipeline {
agent any
stages {
stage('Build') {
steps
{
echo
'Build'
}
}
stage('Test')
{
steps
{
echo
'Test'
}
}
stage('Deploy')
{
steps
{
echo
'Deploy'
}
}
}
}
说明:
支持大部分Groovy,具有丰富的语法特性,易于编写和设计。
(1) Stages:
阶段,是Pipeline中最主要的组成部分,Jenkins将会按照Stages中描述的顺序从上往下的执行。
包含一系列一个或多个stage指令, stages部分是流水线描述的大部分"work" 的位置。
(2) Stage:
阶段,一个Pipeline可以划分为若干个 Stage,每个Stage代表一组操作,比如:Build、Test、Deploy。
(3) Steps:
步骤,Steps是最基本的操作单元,可以是打印一句话,也可以是构建一个Docker镜像,由各类Jenkins插件提供,比如命令:sh
'mvn',就相当于我们平时
shell终端中执行mvn命令一样。
steps 部分在给定的 stage
指令中执行的定义了一系列的一个或多个steps。
2.2
脚本式流水线语法
Jenkinsfile(Scripted Pipeline)
node {
stage('Build')
{
echo
'Build'
}
stage('Test')
{
echo
'Test'
}
stage('Deploy')
{
echo
'Deploy'
}
}
说明:
遵循与Groovy相同语法。
2.3
生成Pipeline脚本
1 创建Pipeline项目,在底部生成示例。
2 最后使用生成器生成Pipeline片段,完善Pipeline脚本。
3 Pipeline指令
3.1
指令示例网址
https://www.jenkins.io/zh/doc/book/pipeline/syntax/#parameters-example
3.2
常用指令
指令 |
描述 |
定义位置 |
生成方法(Declarative Directive Generator) |
agent |
指定流水线的执行节点 |
在pipeline块的顶层被定义 |
agent: Agent |
options |
流水线选项,一般用于设置阶段超时时间 |
在pipeline块的顶层或stage定义 |
options: Options |
environment |
定义所有步骤的环境变量,或者特定步骤 |
在pipeline块的顶层被定义 |
environment: Environment |
parameters |
触发流水线时提供的参数列表 |
在pipeline块的顶层被定义 |
parameters: Parameters |
triggers |
自动化触发 |
在pipeline块的顶层被定义 |
triggers: Triggers |
tools |
使用配置的工具,只支持maven、jdk、gradle |
在pipeline块的顶层被定义 |
tools: Tools |
input |
用户交互输入 |
在stage定义 |
input: Input |
post |
流水线执行完成后执行 |
在pipeline块的底层被定义 |
post: Post Stage or Build |
指令 |
描述 |
定义位置 |
生成方法(片段生成器) |
script |
使用脚本式语法 |
在steps定义 |
script: Run arbitrary Pipeline |
sh |
shell命令 |
在steps定义 |
sh: Shell Script |
withCredentials |
从凭据中读取数据并赋值变量 |
在steps定义 |
withCredentials: Bind credentials to |
checkout |
从版本仓库种拉取代码 |
在steps定义 |
checkout: Check out from version |
ansiblePlaybook |
ansiblePlaybook: Invoke an ansible |
||
sshPublisher |
通过ssh方式拷贝构建文件到远程服务器 |
在steps定义 |
sshPublisher: Send build artifacts over |
(1) pipeline引用变量一定要使用双引号,与shell中引用变量方法相同"$变量名"。
(2)
pipeline配置的parameters参数会将功能解析到页面上显示。
(3)
script的if语句在括号内引用变量时可以不加$。
(4)
流水线写好后第一次构建(pipeline初始化)时可能因没有构建参数可选,会出现构建错误,第二次
再构建时就好了。
(5)
pipeline shell命令中,如果自己定义了变量,在引时,$需要进行转义,引用jenkins中的
变量不需要加转义。
3.3
流水线脚本与源代码一起版本管理
Jenkinsfile文件建议与源代码一起版本管理,实现流水线即代码(Pipeline as
Code)。
这样做的好处:
(1) 自动为所有分支创建流水线脚本。
(2) 方便流水线代码复查、追踪、迭代。
(3) 可被项目成员查看和编辑。
4 流水线发布Java网站项目
基于ssh远程拷贝项目文件发布到远程服务器
4.1
前提条件
1 配置通过免交互,秘钥方式连接远程应用服务器(公钥要发送到远程应用服务器上)
Manage Jenkins -> Configure System -> Publish over
SSH
2 配置连接gitlab的的全局凭证(用户名、密码方式)
Manage Jenkins -> Manage Credentials ->
凭据
3 创建项目名为java-mall-pipeline的jenkins项目,gitlab依然使用上篇实验的java项目。
4.2
流水线代码
pipeline {
agent {
label
'jenkins-slave01'
}
environment
{
gitlab_address
= "http://172.16.1.71/java-project/java-mall.git"
gitlab_auth
= "c1ba4aa8-f310-45e7-9939-bc1967a32d79"
publish_host
= "java-web01"
}
parameters
{
gitParameter
branch: '', branchFilter: 'origin/(.*)', defaultValue: 'master', description:
'请选择要发布的分支名称',
name: 'Branch', quickFilterEnabled: false, selectedValue: 'NONE', sortMode:
'NONE', tagFilter: '*', type: 'PT_BRANCH'
}
stages {
stage('拉取代码') {
steps
{
checkout([$class:
'GitSCM', branches: [[name: "${params.Branch}"]],
doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [],
userRemoteConfigs: [[credentialsId: "${gitlab_auth}", url:
"${gitlab_address}"]]])
}
}
stage('编译构建') {
steps
{
sh
"/usr/local/maven/bin/mvn clean package
-Dmaven.test.skip=true"
}
}
stage('拷贝构建文件到远程主机并部署')
{
steps
{
sshPublisher(publishers:
[sshPublisherDesc(configName: "${publish_host}", transfers:
[sshTransfer(cleanRemote: false, excludes: '', execCommand:
'''#!/bin/bash
#
备份已部署的程序
tomcat=/usr/local/tomcat/webapps
BACKUP_DIR=/data/backup
[
! -d $BACKUP_DIR ] && mkdir -p $BACKUP_DIR
[
-f $tomcat/*.war ] && mv $tomcat/*.war $BACKUP_DIR/$(date
+"%F_%T")_ROOT.war
#
部署新程序并重启Tomcat
[
-d $tomcat/ROOT ] && rm -rf $tomcat/ROOT
mv
/tmp/$JOB_NAME/*.war $tomcat/ROOT.war
pid=$(ps
-ef |grep $tomcat |egrep -v \'grep\' |awk \'{print $2}\')
[
-n "$pid" ] && kill -9 $pid
export
JAVA_HOME=/usr/local/jdk
/usr/local/tomcat/bin/startup.sh''',
execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes:
false, patternSeparator: '[, ]+', remoteDirectory: 'tmp/$JOB_NAME',
remoteDirectorySDF: false, removePrefix: 'target', sourceFiles:
'target/*.war')], usePromotionTimestamp: false, useWorkspaceInPromotion: false,
verbose: false)])
}
}
}
}
// 说明:
// 以上脚本只要修改环境变量(environment)中的内容即可。
4.3 Build with Parameters
4.4
查看控制台输出
5 Gitlab
Webhooks自动触发发布
5.1
安装gitlab插件
5.2
在Jenkins项目里配置触发器生成URL和Token
java-mall-pipeline项目
5.3
在gitlab项目Webhooks中配置URL和Token
java-mall项目
报错说明:
解决办法:
管理中心
->
设置 ->
网络 -> 外发请求
5.4
测试
在gitlab中修改java-mall项目的代码并提交后,发现jenkins的java-mall-pipline项目自动进行
构建及发布项目的操作,说明配置成功。
构建历史显示为gitlab推送
6 集成Ansible发布多台服务器及项目回滚
6.1
前提条件
1 配置通过免交互,秘钥方式连接远程应用服务器(公钥发送到远程应用服务器)的凭据,供ansiable使用,在172.16.1.73节点上安装ansiable。
2 配置连接gitlab的全局凭证(用户密码方式),方便连接gitlab
3 配置连接jenkins的全局凭证(用户密码方式),方便应用服务器发送代码版本文件。
4 远程应用服务器上要安装sshpass包,方便应用服务器免交互发送代码版本文件。
6.2
发布项目
1 说明:
创建项目名为java-mall-pipeline的jenkins项目,gitlab依然使用上篇实验的java项目。
2 pipeline脚本
pipeline {
agent {
label
"jenkins-slave01"
}
environment
{
gitlab_address
= "http://172.16.1.71/java-project/java-mall.git"
//
gitlab的地址
gitlab_auth
= "c1ba4aa8-f310-45e7-9939-bc1967a32d79"
//
连接gitlab的凭证
ansible_ssh_auth
= "d0651139-bbea-403a-9543-4f404e4778dd"
//
ansiable免交互连接远程服务的凭证
jenkins_auth
= "4061843c-c6dd-4b97-b986-94cbaa72fa3e"
//
远程服务器向jenkins发送代码版本的凭证
}
parameters
{
gitParameter
branch: '', branchFilter: 'origin/(.*)', defaultValue: 'master', description:
'请选择要发布的分支名称',
name: 'Branch', quickFilterEnabled: false, selectedValue: 'NONE', sortMode:
'NONE', tagFilter: '*', type: 'PT_BRANCH'
choice
choices: ['java-web01', 'java-web02'], description: '''请选择要发布的服务器组
[java-web01]
172.16.1.74
[java-web02]
172.16.1.75''',
name: 'Servers'
}
stages {
stage('拉取代码') {
steps
{
checkout([$class:
'GitSCM', branches: [[name: "${params.Branch}"]],
doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [],
userRemoteConfigs: [[credentialsId: "${gitlab_auth}", url:
"${gitlab_address}"]]])
}
}
stage('编译构建') {
steps
{
sh
"/usr/local/maven/bin/mvn clean package
-Dmaven.test.skip=true"
}
}
stage('拷贝构建文件到远程主机并部署')
{
steps
{
//
读取连接Jenkins服务器用户名和密码
withCredentials([usernamePassword(credentialsId:
"${jenkins_auth}", passwordVariable: 'password', usernameVariable: 'username')])
{
//
===========================================
sh
"""
###################### 主机清单文件
############################
cat > /opt/jenkins_home/.hosts <<
EOF
[java-web01]
172.16.1.74
[java-web02]
172.16.1.75
EOF
###################### Playbook文件
############################
cat > /opt/jenkins_home/.playbook.yaml <<
"EOF"
- hosts: $Servers # Jenkins参数化变量
gather_facts: no
vars: # 定义playbook变量,下面{{}}引用这里的变量
workspace: $WORKSPACE # WORKSPACE和BUILD_NUMBER引用Jenkins变量
job_name:
$JOB_NAME
tomcat_dir:
"/usr/local/tomcat/webapps" # 自定义变量
tomcat:
"/usr/local/tomcat"
backup_dir:
"/data/backup"
backup_filename: "{{ job_name
}}_ROOT_\$(date +%F)_${BUILD_NUMBER}.war" # 格式:项目名_文件名_日期_构建编号
jenkins_ip:
"172.16.1.72"
username:
$username
password:
$password
tasks:
- name: 在远程服务器上创建项目目录
file:
dest: /tmp/{{
job_name }}
state:
directory
- name: 推送部署包到远程服务器
copy: src="{{ item }}"
dest=/tmp/{{ job_name }}
with_fileglob:
- "{{ workspace
}}/target/*.war"
- name: 部署新程序并重启Tomcat
shell: |
# 备份已部署的程序
[ ! -d {{
backup_dir }} ] && mkdir -p {{ backup_dir }}
[ -f {{ tomcat_dir
}}/*.war ] && mv {{ tomcat_dir }}/*.war {{ backup_dir }}/{{
backup_filename }}
# 部署新程序并重启Tomcat
[ -d {{ tomcat_dir
}}/ROOT ] && rm -rf {{ tomcat_dir }}/ROOT
mv /tmp/{{
job_name }}/*.war {{ tomcat_dir }}/ROOT.war
pid=\$(ps -ef
|grep {{ tomcat }} |egrep -v 'grep' |awk '{print \$2}')
[ -n "\$pid" ]
&& kill -9 \$pid
export
JAVA_HOME=/usr/local/jdk
nohup
/usr/local/tomcat/bin/startup.sh &
ls {{ backup_dir
}} |sort -t '_' -k 4 -rn |head -n5 |awk 'BEGIN{printf "version="}{printf
\$0","}' > /tmp/{{ job_name }}/backup_version.txt
sshpass -p{{
password }} scp -o StrictHostKeychecking=no -p /tmp/{{ job_name
}}/backup_version.txt {{ username }}@{{ jenkins_ip
}}:/opt/jenkins_home
EOF
"""
//
===========================================
}
ansiblePlaybook(
playbook:
'/opt/jenkins_home/.playbook.yaml',
inventory:
'/opt/jenkins_home/.hosts',
credentialsId:
"${ansible_ssh_auth}"
)
}
}
}
}
// 说明:
// 以上脚本只要修改环境变量(environment)中的内容和要发布的主机组清单即可。
3 Build with Parameters
6.3
回滚项目
1 说明:
创建项目名为java-mall-pipeline_rollback的jenkins项目,回滚的是备份目录/data/backup下的备
份代码。
2 pipeline脚本
pipeline {
agent {
label
"jenkins-slave01"
}
environment
{
ansible_ssh_auth
= "d0651139-bbea-403a-9543-4f404e4778dd"
//
ansiable免交互连接远程服务的凭证
}
parameters
{
extendedChoice
description: '请选择回滚的备份版本',
multiSelectDelimiter: ',', name: 'BackupVersion', propertyFile:
'/var/jenkins_home/backup_version.txt', propertyKey: 'version', quoteValue:
false, saveJSONParameterToFile: false, type: 'PT_SINGLE_SELECT',
visibleItemCount: 5
choice choices: ['java-web01',
'java-web02'], description: '''请选择要回滚的服务器组
[java-web01]
172.16.1.74
[java-web02]
172.16.1.75''',
name: 'Servers'
}
stages {
stage('回滚') {
steps
{
sh
"""
###################### 主机清单文件
############################
cat > /opt/jenkins_home/.hosts <<
EOF
[java-web01]
172.16.1.74
[java-web02]
172.16.1.75
EOF
###################### Playbook文件
###########################
cat > /opt/jenkins_home/.playbook.yaml <<
"EOF"
- hosts: $Servers
gather_facts: no
vars:
tomcat_dir:
"/usr/local/tomcat/webapps"
tomcat:
"/usr/local/tomcat"
backup_dir:
"/data/backup"
rollbak_dir:
"/data/backup/rollback"
backup_filename:
$BackupVersion
tasks:
- name: 使用备份文件重新部署
shell: |
# 备份已部署的程序
[ ! -d {{
rollbak_dir }} ] && mkdir -p {{ rollbak_dir }}
mv {{ tomcat_dir
}}/ROOT.war {{ rollbak_dir }}/ROOT-\$(date +%F-%T).war
# 部署备份程序并重启Tomcat
[ -d {{ tomcat_dir
}}/ROOT ] && rm -rf {{ tomcat_dir }}/ROOT
cp {{ backup_dir
}}/{{ backup_filename }} {{ tomcat_dir }}/ROOT.war
pid=\$(ps -ef
|grep {{ tomcat }} |egrep -v 'grep' |awk '{print \$2}')
[ -n "\$pid" ]
&& kill -9 \$pid
export
JAVA_HOME=/usr/local/jdk
nohup {{ tomcat
}}/bin/startup.sh &
EOF
"""
//
===========================================
ansiblePlaybook(
playbook:
'/opt/jenkins_home/.playbook.yaml',
inventory:
'/opt/jenkins_home/.hosts',
credentialsId:
"${ansible_ssh_auth}"
)
}
}
}
}
// 说明:
// 以上脚本只要修改环境变量(environment)中的内容和要发布的主机组清单即可。
3 Build with Parameters
6.4
测试
1 发布项目
先发布两次项目(第一次因为webapps中没有项目,所以/data/backup目录下没有备份项目)。
2 回滚项目
可以看到参数化构建中有备份的项目可供选择。
7 Jenkins备份与恢复
7.1
方式一、备份数据目录
1 如果是基于jenkins war包安装的jenkins,备份目录为"/root/.jenkins/"
2 如果是基于docker方式安装的jenkins,备份目录为jenkins的持久化家目录(/opt/jenkins_home/)
7.2
方式二、Thinbackup
1 安装插件
2 Manage Jenkins -> ThinBackup
3 备份设置(Settings)
创建jenkins的备份目录,该目录必须存在
# mkdir -p
/opt/jenkins_home/backup
jenkins备份的主要是"系统配置"、"job"、"jenkins插件"即可。
参考文档:https://wiki.jenkins.io/pages/diffpagesbyversion.action?pageId=49512461&originalVersion=19&revisedVersion=48
4 备份测试
(1)
点击Backup
Now手动进行备份
(2)
查看备份内容
# ls -ld /opt/jenkins_home/jenkins_backup/
drwxr-xr-x 6 root root 4096 Dec 3 14:35
FULL-2020-12-03_14-35
(3)
还原备份到新的jenkins服务器上
1) 在thinBackup插件中设置备份文件所在的目录。
2) 点击Restore还原备份(注意:要耐心等待备份的完成)。
3) 备份还原后重启jenkins后生效,jenkins的系统设置和创建的jenkins项目都在。
重启:Manage
Jenkins -> Reload Configuration from disk
注意:
A 此时jenkins-slave可能会出现断联的状态,需要进行从新配置连接。
B 插件可能会报依赖问题,安装缺少的依赖。
C 拉取gitlab代码的配置会报如下错误,需要重新配置gitlab的全局用户凭证信息即可。
以上jenkins备份后还原到的是新的jenkins服务器,如果是还原到原有的jenkins服务器上不会
出现以上注意点。
8 补充
8.1 Blue
Ocean
1 安装插件
2 什么是Blue Ocean
https://www.jenkins.io/zh/doc/book/blueocean/
(1) Blue Ocean 重新思考Jenkins的用户体验,从头开始设计Jenkins Pipeline, 但仍然与*式作业兼容,Blue Ocean减少了混乱而且进一步明确了团队中每个成员。
(2)
Blue Ocean 的主要特性包括
1) 持续交付(CD)Pipeline的复杂可视化,可以让您快速直观地理解管道状态。
2) Pipeline 编辑器 -
引导用户通过直观的、可视化的过程来创建Pipeline,从而使Pipeline的创建变得平易近人。
3) 个性化以适应团队中每个成员不同角色的需求。
4) 在需要干预或出现问题时精确定位。 Blue
Ocean展示Pipeline中需要关注的地方,简化异常处理,提高生产力。
5) 本地集成分支和合并请求, 在与GitHub和Bitbucket中的其他人协作编码时实现最大程度的开发人员生产力。
3 视图
8.2
修改jenkins时区的方法
查看系统设置:http://172.16.1.72:8080/systemInfo
jenkins默认使用UTC时间,想要修改为CST时间,按如下步骤操作:
1 临时生效
(1) 打开【系统管理】->【脚本命令行】运行下面的命令
(2) System.setProperty('org.apache.commons.jelly.tags.fmt.timeZone',
'Asia/Shanghai')
2 永久生效
docker run ... -e
JAVA_OPTS=-Duser.timezone=Asia/Shanghai
示例:
docker run -d --name jenkins -p 8080:8080 -p
50000:50000 -u root \
-v /opt/jenkins_home:/var/jenkins_home
\
-v /var/run/docker.sock:/var/run/docker.sock
\
-v /usr/bin/docker:/usr/bin/docker \
-v /usr/local/maven:/usr/local/maven
\
-v /usr/local/jdk:/usr/local/jdk \
-v /etc/localtime:/etc/localtime \
--restart=always \
-e JAVA_OPTS=-Duser.timezone=Asia/Shanghai
\
--name jenkins jenkins/jenkins:lts
3 补充
(1)
通过jenkins RPM包安装的jenkins
# vim /etc/sysconfig/jenkins
JENKINS_JAVA_OPTIONS="-Duser.timezone=Asia/Shanghai"
(2)
通过jenkins war包安装的jenkins
# vim
/usr/local/tomcat/bin/catalina.sh
JAVA_OPTS="-Duser.timezone=Asia/Shanghai"
补充:
# jenkins以root用户运行,也可以不写
export JENKINS_USER="root"
(3)
通过系统设置时区
参考文档:https://www.jenkins.io/doc/book/using/change-time-zone/
# 您可能需要更改显示的时区以匹配您自己的时区。通过转到用户配置页面,可以将
# 设置User Defined Time
Zone为与您自己的匹配。