DevOps之CICD服务器Jenkins
DevOps 之 CICD 服务器 Jenkins
Jenkins 部署与基本配置
Jenkins 介绍


官方网站:
https://www.jenkins.io/zh/

官方文档
https://www.jenkins.io/zh/doc/
Jenkins 是基于 Java 开发的一种开源的CI(Continuous integration持续集成)&CD (Continuous
Delivery持续交付,Continuous Deployment持续部署)工具
Jenkins 的前身是商业软件 Hudson
Jenkins 用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能。可用于自动化各种任务,如构建,测试和部署软件。
Jenkins 作为一个可扩展的自动化服务器,可以用作简单的 CI 服务器,或者变成任何项目的持续交付中心。
Jenkins 只是一个调度平台,其本身并不能完成项目的构建部署
Jenkins 需要安装各种插件,可能还需要编写Shell,python脚本等才能调用和集成众多的组件来实现复杂的构建部署功能

主要用途:
- 持续、自动地构建/测试软件项目
- 监控一些定时执行的任务
Jenkins特点:
开源免费
跨平台,支持所有的平台
master/slave支持分布式的build
web形式的可视化的管理页面
安装配置简单
及时快速的提示和帮助
已有的1800+插件
jenkins 版本
https://www.jenkins.io/download/
https://www.jenkins.io/zh/download/
https://mirrors.tuna.tsinghua.edu.cn/jenkins/
jenkins 项目产生两个发行线, 长期支持版本 (LTS) 和定期发布版本。
稳定版 LTS
LTS (长期支持) 版本每12周从常规版本流中选择,作为该时间段的稳定大版本。 每隔 4 周会更新迭
代稳定的小版本,其中包括错误和安全修复反向移植。
下载链接
https://get.jenkins.io/
https://mirrors.tuna.tsinghua.edu.cn/jenkins/
Jenkins 安装和启动
Jenkins 的安装
Jenkins 支持多种部署和运行方式
Jenkins支持多种安装方法
- 包安装
- JAVA的WAR文件
- 容器运行
https://www.jenkins.io/zh/doc/book/installing/
安装前环境准备
系统要求
https://www.jenkins.io/doc/administration/requirements/java/
系统要求
最低推荐配置:
- 256MB可用内存
- 1GB可用磁盘空间(作为一个Docker容器运行jenkins的话推荐10GB)
为小团队推荐的硬件配置:
- 1GB+可用内存
- 50 GB+ 可用磁盘空间
JAVA 软件配置:
- Java 8—无论是Java运行时环境(JRE)还是Java开发工具包(JDK)都可以。
- Jenkins requires Java 11 or 17 since Jenkins 2.357 and LTS 2.361.1.
JAVA 环境
https://www.jenkins.io/doc/book/platform-information/support-policy-java/
jenkins基于JAVA实现,安装jenkins前需要先安装 JDK
#安装openjdk
dnf install java-21-openjdk -y
java --version
包安装 Jenkins
RHEL系统包安装 Jenkins
CentOS 安装jenkins官方说明
https://pkg.jenkins.io/redhat-stable/
包仓库安装过程
wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhatstable/jenkins.repo
rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
yum install jenkins
rpm包国内镜像下载链接
https://mirrors.ustc.edu.cn/jenkins/
https://mirrors.aliyun.com/jenkins/redhat-stable/
https://mirrors.tuna.tsinghua.edu.cn/jenkins/redhat-stable/
https://mirrors.aliyun.com/jenkins/rpm-stable/
192.168.3.60 gitlab.ming.org
192.168.3.63 jenkins.ming.org
范例: CentOS二进制包安装jenkins
wget https://mirrors.aliyun.com/jenkins/rpm-stable/jenkins-2.541.1-1.noarch.rpm
dnf install jenkins-2.541.1-1.noarch.rpm -y
rpm -ql jenkins
systemctl start jenkins.service
systemctl enable jenkins
#查看第一次的登录密码
grep -A2 password /var/log/jenkins/jenkins.log
#查看第一次的登录密码
cat /var/lib/jenkins/secrets/initialAdminPassword
启动 Jenkins
通过脚本或service启动
安装和下载相关文件后,可以通过init脚本或service方式直接启动,
包安装后,直接内置service,可以直接启动
systemctl start jenkins
#查看日志
tail -fn 50 /var/log/jenkins/jenkins.log
首次登录 Jenkins页面初始化
首次登录 Jenkins 页面
用浏览器访问: http://jenkins.ming.org:8080/
默认内置用户admin,其密码为随机字符,可以从如下文件中查到密码

cat /var/lib/jenkins/secrets/initialAdminPassword
输入文件中的密码:

选择安装 Jenkins 插件
因为默认安装插件需要连接国外的网站,会很慢,所以建议选择插件安装,先不安装任何插件,后续做优化配
置后再安装会快很多.

选择安装推荐的插件会安装很慢,可以选择不安装,直接点右上角的X直接完成安装过程,后续再用离线
方式安装插件
插件安装
选 无 ,不安装任何插件

创建 Jenkins 管理员(可选)
用户信息保存在下面目录
ls /var/lib/jenkins/users/
系统默认有一个管理员帐号admin,继续即可
也可以新创建一个管理员帐号
也可以选项下面的 使用admin帐号继续
创建新用户或者给admin修改密码

配置 Jenkins URL
保存完成即可

如果创建新管理员,界面如下

登陆 Jenkins界面
如果没有创建管理员,可以直接使用内置用户 admin 登录

安装中文插件
插件管理--搜索 chinese -- 选中 Location: Chinese (simplified) 进行安装


在浏览器地址栏输入,点击yes 重启jenkins
http://jenkins服务器:8080/restart

修改 Jenkins 的启动用户为 root
默认Jenkins以jenkins的用户身份运行,会导致权限受限,可以修改service文件设为root身份运行解决此问题
#基于RHEL系统安装修改下面文件
vim /etc/sysconfig/jenkins
#JENKINS_USER="jenkins"
JENKINS_USER="root"
systemctl restart jenkins.service
实现 Java 应用源码编译并部署
java 程序需要使用构建工具,如: maven,ant,gradle等进行构建打包才能部署,其中maven比较流行
以下以 maven 为例实现 Java 应用部署
部署环境如下

.jenkins 实现CICD的JAVA应用的自由风格的任务
自由风格的任务构建基于 Spring Boot 的 JAR 包 JAVA 项目
导入项目
新版需要用root用户开启导入项目用到Gitlab功能才支持导入
搜索--管理中心--设置-- 通用--导入和导出设置

https://gitee.com/lbtooth/spring-boot-helloworld.git


192.168.3.64
dnf install git -y
git clone https://gitlab.ming.org/devops/spring-boot-helloworld.git
dnf install -y maven
#镜像加速
vim /etc/maven/settings.xml
<mirror> <id>nexus-aliyun</id> <mirrorOf>*</mirrorOf> <name>Nexus aliyun</name> <url>http://maven.aliyun.com/nexus/content/groups/public</url> </mirror> </mirrors>
测试单元测试
cd /root/spring-boot-helloworld
mvn clean test
编译生成打包文件
mvn clean package -Dmaven.test.skip=true
cp -a /root/spring-boot-helloworld/target/spring-boot-helloworld-0.6-SNAPSHOT.jar /root/
java --version
启动
java -jar spring-boot-helloworld-0.6-SNAPSHOT.jar
curl 192.168.3.64:8080/version


192.168.3.63
vim /etc/ssh/ssh_config
# StrictHostKeyChecking ask 改StrictHostKeyChecking no
dnf install -y maven
dnf install git -y
cat gitlab.ming.org.crt >> /etc/pki/tls/certs/ca-bundle.crt
在Jenkins服务器生成密钥 把公钥加到gitlab的 ssh密钥
su -s /bin/bash jenkins
ssh-keygen -t rsa
cat /var/lib/jenkins/.ssh/id_rsa.pub
把Jenkins Jenkins用户公钥 放到192.168.3.64 authorized_keys
192.168.3.64
vim /root/.ssh/authorized_keys



#!/bin/bash rm -rf spring-boot-helloworld git clone git@gitlab.ming.org:devops/spring-boot-helloworld.git cd spring-boot-helloworld mvn clean package -Dmaven.test.skip=true ssh root@192.168.3.64 killall -9 java scp target/spring-boot-helloworld-*.jar root@192.168.3.64:/root/spring-boot-helloworld.jar ssh root@192.168.3.64 java -jar /root/spring-boot-helloworld.jar &


curl 192.168.3.64:8080/version
mkdir -p /data/jenkins/scripts
vim /data/jenkins/scripts/spring-boot-helloworld.sh
[root@node1 ~]# cat /data/jenkins/scripts/spring-boot-helloworld.sh #!/bin/bash rm -rf spring-boot-helloworld git clone git@gitlab.ming.org:devops/spring-boot-helloworld.git cd spring-boot-helloworld mvn clean package -Dmaven.test.skip=true ssh root@192.168.3.64 killall -9 java scp target/spring-boot-helloworld-*.jar root@192.168.3.64:/root/spring-boot-helloworld.jar ssh root@192.168.3.64 java -jar /root/spring-boot-helloworld.jar &
bash -x /data/jenkins/scripts/spring-boot-helloworld.sh

安装插件之前

安装插件
gitlab

安装插件后




su -s /bin/bash jenkin
cat /var/lib/jenkins/.ssh/id_rsa
粘贴私钥


基于https协议的

需要创建基于https登录 gitlab账号凭据



[root@node1 ~]# cat /data/jenkins/scripts/spring-boot-helloworld.sh #!/bin/bash #rm -rf spring-boot-helloworld #git clone git@gitlab.ming.org:devops/spring-boot-helloworld.git #cd spring-boot-helloworld mvn clean package -Dmaven.test.skip=true ssh root@192.168.3.64 killall -9 java scp target/spring-boot-helloworld-*.jar root@192.168.3.64:/root/spring-boot-helloworld.jar ssh root@192.168.3.64 java -jar /root/spring-boot-helloworld.jar &
实现参数化构建
参数化构建
jenkins支持参数化构建,类似于脚本中的参数,可以实现灵活的构建任务
Jenkins 支持多种参数类型,比如:Boolean,Choice选项,字符串,Multi_line字符串,文件类型等
参数类型说明
参数化构建的目标在于为流水线提供基于参数值的灵活构建机制,从而让一个流水线的定义可以适用于多种需求情形
- 其功能与引用方式与环境变量类似
- 在触发作业运行之时,需要向各参数赋值
- 参数在使用时实际上也表现为变量,可以通过变量的调用方式使用参数
- 注意: 参数化功能无需安装插件即可支持
常用的参数类型
选项参数 Choice Parameter
布尔值参数 Boolean Parameter
字符参数 String Parameter
文本参数 Multi-line String Parameter
凭据参数
密码参数
文件参数
运行时参数


字符参数 String Parameter
字符参数实现实现不同分支的部署

文本参数Multi-line String Parameter

布尔值参数Boolean Parameter和选项参数Choice Parameter





#!/bin/bash #rm -rf spring-boot-helloworld #git clone git@gitlab.ming.org:devops/spring-boot-helloworld.git #cd spring-boot-helloworld TIME=$(date +%F_%H-%M-%S) APP=spring-boot-helloworld TEST_HOST=" 192.168.3.64 " PROD_HOST=" 192.168.3.65 " mvn clean package -Dmaven.test.skip=true [ $ENV = test ] && HOSTS=$TEST_HOST ||HOSTS=$PROD_HOST for i in $HOSTS;do ssh root@$i killall -9 java ssh root@$i mkdir -p /opt/$APP-$TIME scp target/spring-boot-helloworld-*.jar root@$i:/opt/$APP-$TIME/spring-boot-helloworld.jar ssh root@$i "rm -f /root/spring-boot-helloworld.jar && ln -s /opt/$APP-$TIME/spring-boot-helloworld.jar /root/spring-boot-helloworld.jar" ssh root@$i java -jar /root/spring-boot-helloworld.jar & done
新建标签


jenkins实现基于git-parameter插件实现参数化构建CICD的JAVA应用部署和回滚
安装插件
git par

利用 Git Parameter 插件实现拉取指定 Tag


通过git标签回滚指定版本

集成 Ansible 的任务构建

官方参考
https://plugins.jenkins.io/ansible/
在 Jenkins 上安装 Ansible 环境
dnf install -y epel-release
dnf install ansible -y
ansible --version
vim /etc/ansible/hosts
[webservers]
192.168.3.64 ansible_ssh_user=root
[appservers]
192.168.3.65 ansible_ssh_user=root
Jenkins服务器 生成root用户公钥 加入192.168.3.64 192.168.3.65 的/root/.ssh/authorized_key
ssh-keygen -t rsa
ansible all -m ping
ansible -i /etc/ansible/hosts -u root all -m ping

安装 Ansible 插件
安装ansible插件

安装插件后,添加了ansible的构建步骤
使用 Ansible Playbook 实现任务
准备 Playbook文件
cat /data/ansible/test.yml
[root@node1 ~]# cat /data/ansible/test.yml - hosts: webservers remote_user: root tasks: - name: excute cmd shell: cmd: hostname -I register: result - name: show result debug: msg: "{{ result }}" - name: create new file file: path: /tmp/test.log state: touch [root@node1 ~]#
创建任务

使用 Ansible Playbook 基于参数化实现任务测试和生产多套不同环境的部署
上面的任务是固定的,不灵活,利用参数在同一个任务就可以灵活实现测试和生产多套不同环境的部署
准备两个不同环境的主机清单文件
[root@node1 ansible]# cat /etc/ansible/hosts_test [webservers] 192.168.3.64 ansible_ssh_user=root
root@node1 ansible]# cat /etc/ansible/hosts_prod [webservers] 192.168.3.65 ansible_ssh_user=root
创建参数化任务


执行任务

构建后通知
Jenkins通知可以将任务的执行状态、事件或信息推送给相关用户,这些通常发生在pipeline的“构建后处
理(post-processing)”时期
Email是 Jenkins 内置支持的通知方式,它也能够通过 webhook 扩展支持其它的即时通信媒介,例如:钉
钉,Slack等
钉钉通知
插件说明
https://jenkinsci.github.io/dingtalk-plugin/
https://jenkinsci.github.io/dingtalk-plugin/guide/getting-started.html
Jenkins 安装 DingTalk 插件
安装完插后,建议重启,否则可以通知失败

Jenkins 系统配置
新版配置
Manage Jenkins --> 钉钉

输入webhook链接和加签信息

测试查看钉钉收到消息
配置任务实现钉钉通知
创建新的任务,使用钉钉通知

手机号支持多个,每个手机号一行,也可选 atall 即所有群里的人员(不必再输入手机号,如果输入手机
号,仍然会@相关人员)
自定义内容需要使用Markdown格式,比如:
- 构建ID: ${BUILD_ID}
- 部署项目: ${JOB_NAME}
- 项目URL: ${JOB_DISPLAY_URL}
- 部署目录: ${WORKSPACE}

执行任务验证钉钉通知
如果任务成功,显示下面提示
注意: 如果不成功,重启 jenkins服务,再尝试执行构建


自动化构建
两种自动化构建方式
- 周期性定时构建
- Webhook 触发构建
定时和 SCM 构建
周期性构建这是—-种基于 cron 类型的构建机制.按照预定义的时间周期性启动作务
对于期望能够基于代码变更进行触的CI场景来说,周期性构建并非其最佳选项,但对于有些类型的住务,
它却也能够通过精心编排的周期性构建来避免资源冲突;
周期性构建分为定时构建和轮询构建

定时构建: 按时间周期性的触发构建
轮询SCM(Source Code Management):
指的是定期到代码仓库检查代码是否有变更,存在代码变更时就运行pipeline;为了能够从CI中得到
更多的收益,轮询操作越频繁越好;显然,这会给SCM带去无谓的压力,所以构建的触发由SCM负责
通知Jenkins最为理想;但在外部的SCM无法通知到局域网中的Jenkins时,可以采轮询SCM方式倒也
不失为一种选择
首次任务会进行构建,后续查看SCM代码是否变化,来决定是否构建
示例:H/2 * * * * 表示创建任务后2分钟开始每隔2分钟进行构建
每分钟执行一次构建


范例: 每分钟执行SCM 构建

注意:SCM任务会在左侧多出一个“Git 轮询日志”,可以看到轮询的记录信息
观察Git 轮询日志可以看到当有变化时才会构建,否则不会执行构建


构建 Webhook 触发器
构建触发器(webhook),也称为钩子,实际上是一个HTTP回调,其用于在开发人员向gitlab提交代码后
能够触发jenkins自动执行代码构建操作。

常见场景:
只有在开发人员向develop分支提交代码的时候会自动触发代码构建和部署至测试环境,而向主分支提交的代码不会自动构建,需要运维人员手动部署代码到生产环境。

可以使用多种方式实现 Webhook 触发构建
触发远程构建: 此方式无需安装插件
Build when a change is pushed to GitLab. GitLab webhook URL: 需要安装Gitlab插件
Generic Webhook Trigger : 需要安装 Generic Webhook Trigger Plugin 插件
#!/bin/bash #rm -rf spring-boot-helloworld #git clone git@gitlab.ming.org:devops/spring-boot-helloworld.git #cd spring-boot-helloworld TIME=$(date +%F_%H-%M-%S) APP=spring-boot-helloworld TEST_HOST=" 192.168.3.64 " PROD_HOST=" 192.168.3.65 " ENV=test mvn clean package -Dmaven.test.skip=true [ $ENV = test ] && HOSTS=$TEST_HOST ||HOSTS=$PROD_HOST for i in $HOSTS;do ssh root@$i killall -9 java && sleep 1 ssh root@$i mkdir -p /opt/$APP-$TIME scp target/spring-boot-helloworld-*.jar root@$i:/opt/$APP-$TIME/spring-boot-helloworld.jar ssh root@$i "rm -f /root/spring-boot-helloworld.jar && ln -s /opt/$APP-$TIME/spring-boot-helloworld.jar /root/spring-boot-helloworld.jar" ssh root@$i java -jar /root/spring-boot-helloworld.jar & done


触发远程构建
Jenkins配置构建 Webhook 触发器
生成WebHook 触发器链接

生成随机字符串
方法一
cat /dev/urandom |tr -dc 'a-zA-Z0-9'|head -c 20
W3i005qXFQ8NFbjRrJiB
方法二
echo $RANDOM|md5sum|cut -c 1-20
76c11afcfd4b9d448ca3
把随机字符串填入

然后拼起来下面Jenkins webhook地址
http://jenkins.ming.org:8080/job/spring-boot-helloworld-demo01/build?token=76c11afcfd4b9d448ca3
Jenkins 配置生成用户的 API Token
可以直接使用用户密码调用webhook,但是有泄露密码风险
可以对用户生成API Token,使用Token调用webhook,这样更加安全
注意:新版本不再允许用户的密码方式,curl测试成功,但gitlab测式会提示403错误,只支持Token
先用创建用户



用此用户登录后,修改自已用户配置---添加API Token


注意: 此值是一次性的,所以必须立即复制Token
1199c3a2792786c96b3489cfa03811e6b2

curl 命令测试触发并验证远程触发构建
在任意主机使用图形化浏览器比如:chrome直接访问URL地址
http://admin:<token>@jenkins.ming.org:8080/job/spring-boot-helloworld-demo01/build?token=76c11afcfd4b9d448ca3
使用curl命令访问URL
curl http://admin:<token>@jenkins.ming.org:8080/job/spring-boot-helloworld-demo01/build?token=76c11afcfd4b9d448ca3
#如果执行正常,则无任何显示
范例:用token来触发
#格式1
curl http://jenkins:1199c3a2792786c96b3489cfa03811e6b2@jenkins.ming.org:8080/job/spring-boot-helloworld-demo01/build?token=76c11afcfd4b9d448ca3
#格式2
curl -u jenkins:1199c3a2792786c96b3489cfa03811e6b2 http://jenkins.ming.org:8080/job/spring-boot-helloworld-demo01/build?token=76c11afcfd4b9d448ca3 -I

Gitlab配置 Webhook
Gitlab 打开出站请求
以 管理员root身份修改下面配置
管理中心 -- 设置 -- 网络

gitlab配置 webhook
2.11.2.1.5.2 Gitlab配置 Webhook
在gitlab服务器指定项目中创建webhook,输入下面网址
#方法1
#新版格式
http://admin:<token>@jenkins.ming.org:8080/job/hello-world-war/build?token=<项目token>
#旧版格式
http://jenkins.ming.org:8080/job/hello-world-war/build?token=<项目token>
新版格式
#新版只支持token
http://用户:<token>@jenkins.ming.org:8080/job/hello-world-war/build?token=<项目token>
#示例
http://jenkins:1199c3a2792786c96b3489cfa03811e6b2@jenkins.ming.org:8080/job/spring-boot-helloworld-demo01/build?token=76c11afcfd4b9d448ca3



测试调用 webhook



提交代码自动触发 webhook执行
建议选择“标签推送事件”,只有tag推送时才会触发
GitLab Webhook URL
Jenkins 安装 GitLab 插件
需要安装 GitLab 插件,默认无此选项功能
Jenkins 创建和配置任务



http://jenkins.ming.org:8080/project/spring-boot-helloworld-demo01
Gitlab 配置 Webhook
注意:需要确保在Gitlab 能够解析Jenkins的FQDN为IP
在gitlab 上在对应项目代码库--配置 -- Webhooks
将上面的WebHook URL 和 Secret token 分别复制到对应的文本框中
建议选择“标签推送事件”,只有tag推送时才会触发,只push不push tag 则不会触发构建



测试


验证结果
修改代码并提交仓库
git clone git@gitlab.ming.org:devops/spring-boot-helloworld.git
cd /root/spring-boot-helloworld/src/main/java/com/neo/controller
sed -i 's/0.[0-9]/0.9/g' HelloWorldController.java
git add .
git commit -m "v0.9"
git tag v0.9
git push -u origin main
推标签
git push --tags

curl 192.168.3.64:8080/version
实现容器化的 Docker 任务
jenkins 支持 Docker 说明

案例: 实现自由风格任务实现 Docker 镜像制作并运行

Jenkins服务器、业务服务器安装docker
在harbor.ming.org主机上安装Harbor
192.168.3.63 harbor.ming.org
[root@node1 controller]# cat /data/jenkins/scripts/spring-boot-helloworld-docker.sh #!/bin/bash #rm -rf spring-boot-helloworld #git clone git@gitlab.ming.org:devops/spring-boot-helloworld.git #cd spring-boot-helloworld #TIME=$(date +%F_%H-%M-%S) APP=spring-boot-helloworld HUB=harbor.ming.org HOSTS=" 192.168.3.64 " TAG=$(git tag --points-at HEAD) mvn clean package -Dmaven.test.skip=true docker build -t $HUB/example/$APP:$TAG . docker login -u admin -p 123456 $HUB docker push $HUB/example/$APP:$TAG for i in $HOSTS;do ssh root@$i "docker rm -f $APP && docker run -d --name $APP -p 80:8888 --pull always --restart always $HUB/example/$APP:$TAG" done
在 Jenkins 创建自由风格任务
范例:spring-boot-helloworld




在Gitlab 准备项目
项目1: spring-boot-helloworld
准备Dockerfile
[root@node1 spring-boot-helloworld]# cat /root/spring-boot-helloworld/Dockerfile #FROM adoptopenjdk/openjdk11:alpine-jre #FROM openjdk:11.0.16 #FROM openjdk:8u212-jre-alpine3.9 FROM registry.cn-beijing.aliyuncs.com/wangxiaochun/openjdk:8u212-jre-alpine3.9 ADD target/spring-boot-helloworld-*-SNAPSHOT.jar /applications/spring-boot-helloworld.jar #ENTRYPOINT ["/usr/local/openjdk-11/bin/java -jar /applications/spring-boot-helloworld.jar --server.port=80"] #CMD ["java","-jar","/applications/spring-boot-helloworld.jar --server.port=8888"] CMD ["java","-jar","/applications/spring-boot-helloworld.jar","--server.port=8888"]
在 Jenkins主机安装 Docker ,并信任harbor
测试能够拉取镜像推送到harbor
+ docker build -t harbor.ming.org/example/spring-boot-helloworld:1 . ERROR: permission denied while trying to connect to the docker API at unix:///var/run/docker.sock
#默认jenkins用户无法通过docker的socket文件访问docker服务器
ll /var/run/docker.sock srw-rw---- 1 root docker 0 Mar 11 16:56 /var/run/docker.sock=
#方法1:
#修改jenkins用户权限可以访问docker的socket文件
把Jenkins用户加入docker组
usermod -aG docker jenkins
id jenkins
#需要重启jenkins上面的权限才能生效
systemctl restart jenkins
在部署应用的目标主机安装 Docker,并且打开远程连接端口(可选),并且信任harbor
测试能从harbor拉取镜像

curl 192.168.3.64:80/version
使用webhook触发
推送tag 触发
Jenkins url token




验证结果
修改代码并提交仓库
git clone git@gitlab.ming.org:devops/spring-boot-helloworld.git
cd /root/spring-boot-helloworld/src/main/java/com/neo/controller
sed -i 's/0.11/0.12/g' HelloWorldController.java
git add .
git commit -m "v0.12"
git tag v0.12
git push -u origin main
推标签
git push --tags

curl 192.168.3.64:80/version
集成 Kubernetes
https://www.jenkins.io/doc/book/scaling/scaling-jenkins-on-kubernetes/
在 Kubernetes集群的实现基于 Jenkins 实现CICD流程
部署k8s集群
网络插件
mlb
ingres-nginx
192.168.3.64 master
192.168.3.65 node
k8s master node加上域名解析
192.168.3.60 gitlab.ming.org
192.168.3.63 jenkins.ming.org
192.168.3.63 harbor.ming.org

在部署在Kubernetes集群外的Jenkins中执行Kubernetes集群的管理任务有以下三种方式
方法1: 基于ssh协议直接连接kubernetes集群执行
此方式最为简单,只需要打通jenkins 到k8s的master节点的ssh key 验证即可
范例:基于ssh协议直接连接kubernetes集群执行job
Jenkins主机加上k8s master域名解析
cat /var/lib/jenkins/.ssh/id_rsa.pub
把Jenkins Jenkins用户公钥 放到192.168.3.64 authorized_keys
192.168.3.64
vim /root/.ssh/authorized_keys
[root@master1 ingress-nginx]# kubectl get svc -n ingress-nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx-controller LoadBalancer 10.111.149.199 192.168.3.10 80:30080/TCP,443:30121/TCP 19s ingress-nginx-controller-admission ClusterIP 10.101.27.196 <none> 443/TCP 19s
[root@node1 scripts]# cat /root/spring-boot-helloworld/deploy/01-namespace.yaml kind: Namespace apiVersion: v1 metadata: name: test
[root@node1 scripts]# cat /root/spring-boot-helloworld/deploy/02-service.yaml --- kind: Service apiVersion: v1 metadata: name: spring-boot-helloworld namespace: test labels: app: spring-boot-helloworld spec: #type: NodePort type: LoadBalancer ports: - name: http protocol: TCP port: 80 targetPort: 8888 nodePort: 30666 selector: app: spring-boot-helloworld
--- kind: Deployment apiVersion: apps/v1 metadata: name: spring-boot-helloworld namespace: test labels: app: spring-boot-helloworld spec: replicas: 2 selector: matchLabels: app: spring-boot-helloworld template: metadata: name: spring-boot-helloworld labels: app: spring-boot-helloworld spec: containers: - name: spring-boot-helloworld imagePullPolicy: Always image: 'harbor.ming.org/example/spring-boot-helloworld:__TAG__' ports: - name: http containerPort: 80 protocol: TCP
#下面方式直接连接kubernetes集群并使用master节点的工具和kubeconfig实现,无需安装kubectl工具和创建kubeconfig文件
[root@node1 controller]# cat /data/jenkins/scripts/spring-boot-helloworld-k8s.sh #!/bin/bash #rm -rf spring-boot-helloworld #git clone git@gitlab.ming.org:devops/spring-boot-helloworld.git #cd spring-boot-helloworld #TIME=$(date +%F_%H-%M-%S) APP=spring-boot-helloworld HUB=harbor.ming.org TAG=$(git tag --points-at HEAD) mvn clean package -Dmaven.test.skip=true docker build -t $HUB/example/$APP:$TAG . docker login -u admin -p 123456 $HUB docker push $HUB/example/$APP:$TAG sed -i "s#__TAG__#${TAG}#" deploy/03-deployment.yaml cat deploy/*.yaml | ssh root@master1.org "kubectl apply -f -"






[root@master1 ~]# kubectl get pod -n test NAME READY STATUS RESTARTS AGE spring-boot-helloworld-5dd6f7c895-dltnz 1/1 Running 0 2m41s spring-boot-helloworld-5dd6f7c895-tpngv 1/1 Running 0 2m41s
[root@master1 ~]# kubectl get svc -n test NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE spring-boot-helloworld LoadBalancer 10.99.83.85 192.168.3.11 80:30666/TCP 2m54s
curl 192.168.3.11/version
方法2: 基于 kubeconfig 实现
流程说明
- 在Jenkins服务器安装kubectl工具,可以通过复制kubectl 二进制程序文件到Jenkins服务器
- 的/usr/local/bin下实现
- 将Kubernetes集群中的master节点的上的/etc/kubernetes/admin.conf 复制到Jenkins服务器
- ~jenkins/.kube/config
- 修改权限: chmod 644 ~jenkins/.kube/config
- 编写shell,调用kubectl apply -f 执行集群管理操作
在Jenkins主机准备名称解析
cat /etc/hosts
192.168.3.64 kubeapi.org kubeapi
192.168.3.64 master1.org master1
192.168.3.61 master2.org master2
192.168.3.62 master3.org master3
192.168.3.63 node1.org node1
#192.168.3.64 node2.org node2
192.168.3.65 node3.org node3
192.168.3.66 ha1.org ha1
192.168.3.67 ha2.org ha2
192.168.3.60 gitlab.ming.org
192.168.3.63 jenkins.ming.org
192.168.3.63 harbor.ming.org
在 Jenkins主机安装kubectl工具
scp root@master1.wang.org:/usr/bin/kubectl /usr/local/bin/
ll /usr/local/bin/kubectl
ldd /usr/local/bin/kubectl
在 Jenkins主机准备 kubeconfig 文件
scp root@master1.org:/etc/kubernetes/admin.conf .kube/config
mv .kube/config /data/jenkins/scripts/kubeconfig
chmod +r /data/jenkins/scripts/kubeconfig
kubectl --kubeconfig /data/jenkins/scripts/kubeconfig get node
NAME STATUS ROLES AGE VERSION
master1.org Ready control-plane 5d19h v1.35.8
node3.org Ready <none> 5d19h v1.35.8
cat /data/jenkins/scripts/spring-boot-helloworld-k8s.sh #!/bin/bash #rm -rf spring-boot-helloworld #git clone git@gitlab.ming.org:devops/spring-boot-helloworld.git #cd spring-boot-helloworld #TIME=$(date +%F_%H-%M-%S) APP=spring-boot-helloworld HUB=harbor.ming.org TAG=$(git tag --points-at HEAD) mvn clean package -Dmaven.test.skip=true docker build -t $HUB/example/$APP:$TAG . docker login -u admin -p 123456 $HUB docker push $HUB/example/$APP:$TAG sed -i "s#__TAG__#${TAG}#" deploy/03-deployment.yaml #cat deploy/*.yaml | ssh root@master1.org "kubectl apply -f -" kubectl --kubeconfig /data/jenkins/scripts/kubeconfig apply -f deploy/ [root@node1 ~]#
方法3 基于Jenkins配置Kubeconfig的凭据

添加Secet file 类型的凭据用于存放kubeconfig文件的内容
将master节点的.kube/config文件定义为kubeconfig的凭据

创建自由风格的任务
使用前面创建的凭据的内容生成一个临时文件,并将此路径用于定义环境变量KUBECONFIG的值

通过Jenkins凭据实现
cat /data/jenkins/scripts/spring-boot-helloworld-k8s.sh #!/bin/bash #rm -rf spring-boot-helloworld #git clone git@gitlab.ming.org:devops/spring-boot-helloworld.git #cd spring-boot-helloworld #TIME=$(date +%F_%H-%M-%S) APP=spring-boot-helloworld HUB=harbor.ming.org TAG=$(git tag --points-at HEAD) mvn clean package -Dmaven.test.skip=true docker build -t $HUB/example/$APP:$TAG . docker login -u admin -p 123456 $HUB docker push $HUB/example/$APP:$TAG sed -i "s#__TAG__#${TAG}#" deploy/03-deployment.yaml #cat deploy/*.yaml | ssh root@master1.org "kubectl apply -f -" kubectl --kubeconfig $KUBECONFIG apply -f deploy/
方法4:基于 SA 帐号的 Token 实现
创建一个SA帐号并授权
#在k8s的管理节点执行
kubectl create ns jenkins
kubectl create sa jenkins -n jenkins
#方式1:集群级权限
kubectl create clusterrolebinding jenkins-admin --clusterrole=cluster-admin --serviceaccount=jenkins:jenkins
生成 SA 对应的 Secret Token
[root@master1 ~]# cat jenkins-secret.yaml apiVersion: v1 kind: Secret type: kubernetes.io/service-account-token metadata: name: jenkins-secret namespace: jenkins annotations: kubernetes.io/service-account.name: "jenkins"
kubectl apply -f jenkins-secret.yaml
#查看Token
kubectl describe secrets -n jenkins jenkins-secret
将SA对应的Secret Token 保存为Jenkins凭据
在jenkins上创建Secret Text 类型的凭据


在Jenkins的任务中将凭据的值赋值给变量
注意:变量名不能用横线,支持下划线

创建自由风格任务
创建Jenkins 任务,引用 SA对应的 token 进行Kubernetes 集群管理
JENKINS_TOKEN
cat /data/jenkins/scripts/spring-boot-helloworld-k8s.sh #!/bin/bash #rm -rf spring-boot-helloworld #git clone git@gitlab.ming.org:devops/spring-boot-helloworld.git #cd spring-boot-helloworld #TIME=$(date +%F_%H-%M-%S) APP=spring-boot-helloworld HUB=harbor.ming.org TAG=$(git tag --points-at HEAD) mvn clean package -Dmaven.test.skip=true docker build -t $HUB/example/$APP:$TAG . docker login -u admin -p 123456 $HUB docker push $HUB/example/$APP:$TAG sed -i "s#__TAG__#${TAG}#" deploy/03-deployment.yaml #cat deploy/*.yaml | ssh root@master1.org "kubectl apply -f -" #kubectl --kubeconfig $KUBECONFIG apply -f deploy/ kubectl apply -f deploy/ --token=${JENKINS_TOKEN} -s https://kubeapi.org:6443 --insecure-skip-tls-verify=true
配置webhook
推送tag 触发
Jenkins url token
验证结果
修改代码并提交仓库
git clone git@gitlab.ming.org:devops/spring-boot-helloworld.git
cd /root/spring-boot-helloworld/src/main/java/com/neo/controller
sed -i 's/0.14/0.15/g' HelloWorldController.java
git add .
git commit -m "v0.15"
git tag v0.15
git push -u origin main
推标签
git push --tags
Jenkins Pipeline
Pipeline 介绍
流水线生产,又叫流水生产流水作业,指劳动对象按一定的工艺路线和统一的生产速度,连续不断地通过各个
工作地,按顺序地进行加工并生产出产品的一种生产组织形式。它是对象专业化组织形式的进一步发展,是劳
动分工较细、生产效率较高的一种生产组织形式。亨利.福特(Henry Ford)于1913年在密歇根州的
Highland Park,建立的生产系统。
所谓的 Pipeline 流水线,其实就是将之前的一个任务或者一个脚本就做完的工作,用 Pipeline 语法划分
为多个子任务然后分别执行,两者实现的最终效果是一样的,但是由于原始任务划分为多个子任务之
后,以流水线的方式来执行,那么就可以随时查看任意子任务的执行效果,即使在某个阶段出现问题,
我们也可以随时直接定位问题的发生点,大大提高项目的效率,即模块化完成复杂任务的思想体现
Pipeline 是帮助 Jenkins 实现CI到CD转变的重要角色,是运行在 jenkins 2.X 版本的核心插件,简单来说
Pipeline就是一套运行于 Jenkins上的工作流框架,将原本独立运行于单个或者多个节点的任务连接起
来,实现单个任务难以完成的复杂发布流程,从而实现单个任务很难实现的复杂流程编排和任务可视化
官方帮助:
https://www.jenkins.io/zh/doc/book/pipeline/
https://www.jenkins.io/doc/book/pipeline/
https://www.jenkins.io/2.0/
Pipeline基于Groovy DSL(领域特定语言Domain Specific Language )实现,任何发布流程都可以表述为一段Groovy脚本。
Groovy是一种基于JVM虚拟机的敏捷开发语言,它结合了Python、Ruby和Smalltalk的许多强大的特
性,Groovy 是用Java写的 , Groovy语法与Java语法类似,
Groovy 代码不仅能够与 Java 代码很好地结合,也能用于扩展现有代码。由于其运行在 JVM 上的特性,
Groovy也可以使用其他非Java语言编写的库
Groovy官网:http://www.groovy-lang.org/learn.html
Groovy语法:http://groovy-lang.org/syntax.html
流水线和自由风格任务流程比较

Pipeline 优势
所有构建步骤代码化,统一代码语言
代码模块化,清楚显示构建一个个步骤
指定哪个步骤重新执行
临时修改,模拟执行 dry-run
缺点:学习成本

一致性: Pipeline 用统一语法的代码的方式实现各个CICD的阶段的任务,不仅可以被纳入版本控制,还
可以通过编辑代码实现目标效果
直观性: 构建过程中每一步都可以直接的图形化显示输出,比如每个阶段的执行时间,直观友好,pipeline
帮助我们快速的定位哪个阶段的任务出现错误
可持续性:Jenkins的重启或者中断后不影响已经执行的pipeline Job
支持暂停:Pipeline可以选择停止并等待人工输入或批准后再继续执行
支持回放: 如果失败,可以使用回放,进行临时性的修改 job ,再调试执行,如果成功,再真正修改任务即可
可扩展:通过Groovy的编程更容易的扩展插件
并行执行:通过Groovy脚本可以实现step,stage间的并行执行,和更复杂的相互依赖关系
多功能:支持复杂CD要求,包括fork/join子进程,条件判断,循环和并行执行工作的能力
Pipeline 语法
Pipeline 语法介绍和结构
官方文档
https://www.jenkins.io/zh/doc/book/pipeline/syntax/
http://www.jenkins.io/doc/book/pipeline/syntax/
http://www.jenkins.io/doc/pipeline/steps/
#支持docker
https://www.jenkins.io/doc/book/pipeline/docker/
当前 Jenkins 2.X 支持两种语法的流水线: 脚本式(命令式)和声明式
脚本式 Scripted Pipeline语法
此语法是 Jenkins最先支持pipeline语法,采用命令式风格,直接在流水线脚本中定义逻辑和程序流程
声明式 Declarative Pipeline语法
后来CloudBees公司为Jenkins引入的一种“流水线即代码”的pipeline语法
它允许用户在pipeline的定义中将更多的精力关注于期望pipeline的状态和输出之上,而非实现逻辑
声明式和脚本化的流水线从根本上是不同的。 声明式流水线的是 Jenkins 流水线更新一些的特性:
- 相比脚本化的流水线语法,它提供更丰富的语法特性
- 是为了使编写和读取流水线代码更容易而设计的
Pipeline 的基本结构
pipeline
流水线的最外层结构,代表整条pipeline,包含着pipeline的完整逻辑;是声明式流水线语法的关健
特征
node 和 agent
用于定义任务在哪里执行
每个node都是一个 Jenkins 节点,可以是 Jenkins master也可以是 Jenkins agent,node是执行
step的具体服务器。
node 代码块也是脚本式pipeline语法的关健特性,声明式pipeline使用 agent 关健字
stages
用于包含所有stage的定义
stage
属于stages的子语句块
指定 stage 的名称, 用于定义每个阶段 stage 的主要任务
一个pipeline可以划分为若干个stage,每个stage都是一个完整的操作,比如: clone代码、代码编
译、代码测试和代码部署,阶段是一个逻辑分组,可以跨多个node执行。
steps
属于stage的子语句块
每个阶段stage中定义完成该阶段功能所需要经历的一系列步骤
步骤 steps 是jenkins pipeline最基本的操作单元,从在服务器创建目录到构建容器镜像,由各类
Jenkins 插件提供实现,例如: sh “make”
能够把这些步骤steps 同该stage中的其它定义(如环境的定义,Post 等)分隔开
post
用在stage 代码块(和steps 同级)或整个pipeline执行完成后的附加步骤,此指令非必须项
声明式流水线语法
声明式流水线是在"Pipeline plugin"的2.5版本添加到 Jenkins 流水线的 ,它在流水线子系统之上提供了一种更简单,更常见的语法。
所有有效的声明式流水线必须包含在一个 pipeline 块中, 比如
官方说明
https://www.jenkins.io/zh/doc/book/pipeline/syntax/
Pipeline 的基本结构
实线部分为必须段,虚线为可选段

pipeline的定义有一个明确的、必须遵循的结构,它由一些directive和section组成,每一个section又可
包含其它的section、directive和step,以及一些condlition的定义
Section:用于将那些在某个时间点需要一同运行的条目(item)组织在一起
agent section:指定负责运行代码的节点
在pipeline代码块的顶部,必须要有一个agent来指定“默认”的执行节点
而一个stage的顶部也可以有一个agent的定义,用来指定负责运行该stage中的代码的节点
stages section:组织---到多个stage
steps section:组织一至多个DSL格式的步骤
post section:在stage或整个pipeline的尾部封装--些需要被执行的步骤或者检验条件
Directive(指令)︰负责完成特定功能的语句或代码块,如environment、tools、triggers、input和when
等
Steps : steps本身就是一个标识特定section的名称,其内部可以使用任何合法的DSL语句,例如git、
sh、 bat和echo等
Pipeline 的声明式语法要点
steps内部的命令,每一条单独的命令都在当前任务的工作目录下执行。
即使A命令切换到了一个新的目录,接下来的B命令并不会在对应的新目录中执行,而是在当前任务
的工作目录下执行。如果非要在切换后的目录下执行命令B,那么采用she11中的&&符号将多条命
令拼接在一起即可。
默认情况下,不支持shell里面的复杂语法,因为groovy有自己的条件表达式
如果jenkins的工作目录下存在同名目录,则获取失败
Pipeline 常见指令说明
Jenkins 内置语法帮助
http://jenkins-server:8080/job/<job-name>/pipeline-syntax/
#注意:需要安装pipeline插件并且是pipeline风格的任务
agent 可接受多种形式的参数
any:任何可用节点
none:用于pipeline顶端时表示不定义默认的agent,每个stage就需要单独指定
label { label ""}:具有指定的标签的节点均为可用节点
node { label "" }:与label相似,但可以指定额外的参数customWorkspace
docker:在指定的容器中运行pipeline或stage代码,该容器动态创建并运行于预配置的可运行容器
的node上,或能够匹配到指定label的node上;可用参数如下
image、label、args、rgistryUrl和rcgistryCredentialsId
dockerfile:功能上类似于上面docker参数,但容器镜像通过指定的docker进行构建;该参数要求
Jenkinsfile必须从Multibranch Pipeline或者Pipeline from SCM中加载;可用参数如下
filename、dir、label、additionalBuildArgs、args、registryUrl和registryCredentialsId
kubernetes:于Kubernetes集群上指定的Pod中运行stage或pipeline代码,该参数同样要求
Jenkinsfile必须从Multibranch Pipeline或者Pipeline from SCM中加载
需要在kubernetes参数中指定Pod模板
Stages和Stage
stages是pipeline中最重要的section,stages负责描述pipeline中绝大部分的实际工作( work)
Jenkins会按照stages中定义的顺序自卞而后执行各个stage
stages:封装了用于定义pipeline主体和逻辑的所有stage的定义,它包含一个或多个stage
stages中至少需要包含一个stage,每个stage 指令来定义CD过程的每个离散部分,
stage内部还支持再嵌套一个stages或一个parallel代码块,而后在这些代码块内部再嵌套stage,
以指定stage的运行顺序
stage内部嵌套stages,用于指定以顺序(串行)方式依次运行该stages内部的各stage ;而stage内部
嵌套的parallel{}},用于指定以并行方式运行该parallel号内部的各stage
stage内部仅能定义steps、stages、parallel或matrix四者其中之一,且多层嵌套只能用在最后一
个stage中;
对于本身已经嵌套在parallel或matrix内部的stage来说,不支持在其内部再使用parallel或matrix;
但仍能使用agent、tools和when等其它指令,甚至是stages书配置段以顺序运行stage
stage和steps
每个stage都只包含一个steps
Pipeline的基本结构决定了pipeline的整体流程,但真正“做事”是其内部一个个具体的step,因而
steps是pipeline中最核心的组成部分
steps 负责在stage中定义一到多个DSL语句,steps 中的语句负责完成该stage中特定的功能,例如
构建、测试和部署等;但能够同其它的语句分隔开,如environment等
除了script,几乎所有的step在pipeline中都是不可拆分的原子操作
pipeline内置了大量的step,具体请参考https:// www.jenkins.io/doc/pipeline/steps
除此之外,有相当一部分插件可直接当作step来用
steps段中的script{}步骤负责将groovy脚本引入到steps{}配置段中,但它非为必要的步骤,且复杂
的脚本应该单独组织为Shared Libraries,并由Pipeline导入后使用
post section
在stage或pipeline的尾部定义一些step,并根据其所在stage或pipeline的完成情况来判定是否运行这些
step
post section支持的condition如下
always:总是运行
changed:其所处的stage或pipeline同前一次运行具有不同状态时,才运行该post
fixed: stage或pipeline本次运行成功,但前一次为failed或unstable时,才运行该post
regression: stage或pipeline前一次运行成功,但本次为failure、unstable或aborted时,才运行
该post
aborted: stage或pipeline的运行状态为aborted时,才运行该post;在Web UI中灰色显示
failure: stage或pipeline的运行状态为failed时,才运行该post
success: stage或pipeline的运行状态为success时,才运行该post
unstable:因测试失败或代码冲突导致stage或pipeline的运行状态为unstable时,才运行该post;在
Web UI中以黄色显示
unsuccessful: stagc或pipeline的运行不成功时,才运行该post
cleanup:在其它所有的post的条件均被评估后(无论stage或pipeline的状态为何)才运行该post
Jenkins Pipeline 支持常用指令
无论是脚本式语法还是声明式语法,本质上都是执行各种命令,对于不同的命令需要采用专用的语法来
实现指定的功能,常见的语法命令及其样式如下:
echo: 输出信息, echo "Building"
sh: 执行命令,sh 'command' sh([script: 'echo hello']),用三个单号可以支持多行命令,即:sh ''' 多行shell命令 '''
git: 克隆代码,git branch: 'develop', credentialsId: 'd7e3bd', url: 'git@gitlab.wang.org:example/myapp.git'
env: 设置变量, env.PATH="/usr/local/java/bin:$PATH"
environmet:设定环境变量,可用于stage或pipeline代码块中;支持credentialsl)函数,用于通过标识符访问预定义的凭证
tools:指定需要在agent上下载并配置的工具,例如git、maven、jdk等,这些工具可经由PATH环境变量指定的位置访问到;可用于stage或pipeline中
parameters:用户在触发pipeline时应该提供的参数列表;仅可用于pipeline级别
options:仅可用在pipeline级别来配置pipeline自身的选项,支持的参数可由pipeline自身提供,也可由其它插件(例如timestamps)提供
例如“retry(2)”允许在pipeline失败时重试两次
triggers:用于指定负责自动启动pipeline的触发器,对于集成了Github或Gitlab等自带触发机制的系统场景,triggers并非必须的指令;仅可用于pipeline级别
libraries:当前pipeline可以导入的共享库,该共享库内部的代码则可被该pipeline调用
input: stagc中的专用指令,用于暂停pipeline并提示用户输入内容后继续
when: stage中的专用指令,用于设定该stage的运行条件
文件相关指令
isUnix :判断是否为类Unix系统
deleteDir:删除当前目录
dir(" / path/ to/dir"):切换到指定目录
fileExists (" /path/ to/dir"):判断文件是否存在
pwd:打印当前目录
writeFile:将内容写入指定的文件中,支持如下几个参数
file:文件路径,支持相对路径和绝对路径,
text:要写入的内容;
encoding:目标文件的编码,空值为系统默认的编码;支持base64编码格式;可选参数
readFile:读取文件的内容;支持如下几个参数;
file:文件路径,支持相对路径和绝对路径;
encoding:读取文件内容时使用的编码格式;可选参数;
消息或控制指令
echo("message"'):打印指定的消息;
error("message"):主动报错,并中止当前pipeline;
retry(count):重复执行count次在{}中定义的代码块
sleep: 让pipeline休眠一段时间,支持如下参数;
time:整数值,休眠时长
unit:时间单位,支持NANOSECONDS,MICROSECONDS、MILLISECONDS、SECONDS、
MINUTES、HOURS和DAYS,可选参数
timeout:代码块的超时时长,支持如下参数
time:整数值,休眠时长
unit:时间单位,支持NANOSECONDS、MICROSECONDS,MILLISECONDS、SECONDS、
MINUTES、HOURS和DAYS,可选参数
activity:布尔类型,值为true时,表示在该代码块不再有月志活动时才算真正超时;可选参数
waitUntil:等待指定的条件满足时执行定义的代码块
initialRecurrencePeriod:初始的重试周期,即测试条件是否满足的重试周期,默认为250ms;可选
参数
quiet:是否禁止将每次的条件测试都记入日志,默认为false,即记入日志;可选参数
发送通知指令
mail:向指定邮箱发送邮件
subject:邮件标题;√不body:邮件正文
from (optional):发件人地址列表,逗号分隔
cc (optional) : CC email地址列表,逗号分隔
bcc (optional): BCC email地址列表,逗号分隔
charset(optional):编码格式
mimeType (optional): Email正文的MIME类型,默认为text/plain
replyTo (optional):回件地址,默认为Jenkins设置的全局配置中的邮箱地址
Node和Process相关指令
bat: Windows的批处理脚本
powershell:运行指定的PowerShell脚本,支持Microsoft PowerShell 3+
pwsh: PowerShell Core Script
node:在指定的节点上运行后续的脚本
ws :分配工作空间
sh:运行shell脚本,支持的参数如下
script:脚本代码块,支持指定脚本解释器,例如“#!/usr/bin/python3,否则将使用系统默认的解释
器,且使用了-xe选项
encoding (optional):脚本执行后输出的日志信息的编码格式,未定义时使用系统默认编码格式
label(optional):显示在Web UI中的详细信息
returnStdout (optional):布尔型值,true表示任务的标准输出将作为step的返回值,而不是打印到
日志中;若有错误,依然会记入日志
returnStatus (optional):正常情况了命令执行失败会返回非零状态码,设定该参数值为true时,表
示将返回该step的结果,而非状态码
安装 Pipeline 插件
安装 Pipeline(必须安装否则无法显示执行过程) 和 Pipeline Stage View 插件 Blue Ocean 插件

实战案例: 声明式 Pipeline
案例:变量
http://jenkins.wang.org:8080/job/job/pipeline-syntax/globals
nkins环境变量可分为内置变量和用户自定义变量两类
pipeline和stage得中用于定义环境变量的指令是environment,但定义位置的不同,也意味着其作用域
的不同
定义在pipeline顶部的环境变量可被其后的各stage所引用
Jenkins全局环境变量可被所有的pipeline引用,它们以“env.”为前缀
引用全局环境变量格式有四种:
${env.ENV_VAR_NAME}
$env.ENV_VAR_NAME
$ENV_VAR_NAME
${ENV_VAR_NAME}
#注意:变量引用有时要加双引号引起来,如:"${env.<ENV_VAR_NAME>}"
范例:构建和推送Docker镜像
准备环境:
Jenkins 主机或 Agent 需要提前安装 Docker
在Jenkins 创建凭据 credential ID为 gitlab-root-password
范例:基于变量构建spring-boot-helloworld项目和推送Docker镜像

pipeline { agent any //tools { // maven 'maven-3.9.13' //} environment { codeRepo = "git@gitlab.ming.org:devops/spring-boot-helloworld.git" credential = "jenkins" harborServer = 'harbor.ming.org' projectName = 'spring-boot-helloworld' imageUrl = "${harborServer}/example/${projectName}" imageTag = "${BUILD_ID}" harborUserName = "admin" harborPassword = "123456" } stages { stage('Source') { steps { git branch: 'main', credentialsId: "${credential}", url: "${codeRepo}" } } stage('Test') { steps { //注意:不要修改hello()函数,否则会导致下面失败 sh 'mvn test' } } stage('Build') { steps { //sh 'mvn -B -DskipTests clean package' sh 'mvn clean package -Dmaven.test.skip=true' } } stage('Build Docker Image') { steps { sh "docker build -t ${imageUrl}:${imageTag} ." } } stage('Push Docker Image') { steps { sh "echo harborUserName=${harborUserName}" sh "echo harborPassword=${harborPassword}" sh "echo ${harborPassword} | docker login -u ${harborUserName} --password-stdin ${harborServer}" sh "docker login -u ${harborUserName} -p ${harborPassword} ${harborServer}" sh "docker push ${imageUrl}:${imageTag}" } } stage('Run Docker') { steps { sh "ssh root@192.168.3.64 \"docker rm -f ${projectName} ; docker run --name ${projectName} --pull always --restart always -p 80:8888 -d ${imageUrl}:${imageTag}\"" sh "ssh root@192.168.3.65 \"docker rm -f ${projectName} ; docker run --name ${projectName} --pull always --restart always -p 80:8888 -d ${imageUrl}:${imageTag}\"" } } } }
pipeline { agent any environment { codeRepo = "git@gitlab.ming.org:devops/spring-boot-helloworld.git" credential = "jenkins" harborServer = 'harbor.ming.org' projectName = 'spring-boot-helloworld' imageUrl = "${harborServer}/example/${projectName}" // imageTag 不要在这里定义,改为动态获取 harborUserName = "admin" harborPassword = "123456" } stages { stage('Source') { steps { git branch: 'main', credentialsId: "${credential}", url: "${codeRepo}" } } stage('Get Tag') { steps { script { // 获取当前 HEAD 指向的标签,取第一行(如果有多个标签) env.imageTag = sh(script: 'git tag --points-at HEAD', returnStdout: true).trim() echo "当前镜像标签: ${env.imageTag}" } } } stage('Test') { steps { sh 'mvn test' } } stage('Build') { steps { sh 'mvn clean package -Dmaven.test.skip=true' } } stage('Build Docker Image') { steps { sh "docker build -t ${imageUrl}:${env.imageTag} ." } } stage('Push Docker Image') { steps { sh "echo harborUserName=${harborUserName}" sh "echo harborPassword=${harborPassword}" sh "echo ${harborPassword} | docker login -u ${harborUserName} --password-stdin ${harborServer}" sh "docker login -u ${harborUserName} -p ${harborPassword} ${harborServer}" sh "docker push ${imageUrl}:${env.imageTag}" } } stage('Run Docker') { steps { sh "ssh root@192.168.3.64 \"docker rm -f ${projectName} ; docker run --name ${projectName} --pull always --restart always -p 80:8888 -d ${imageUrl}:${env.imageTag}\"" sh "ssh root@192.168.3.65 \"docker rm -f ${projectName} ; docker run --name ${projectName} --pull always --restart always -p 80:8888 -d ${imageUrl}:${env.imageTag}\"" } } } }
范例:基于凭据实现构建和推送Docker镜像
准备环境:
Jenkins 主机或 Agent 需要提前安装 Docker
创建访问harbor的用户和密码的凭据 ID为 harbor-user-credential

范例:利用凭据提取用户名和密码生成变量名构建helloworld-srping-boot项目
pipeline { agent any environment { codeRepo = "git@gitlab.ming.org:devops/spring-boot-helloworld.git" credential = "jenkins" harborServer = 'harbor.ming.org' projectName = 'spring-boot-helloworld' imageUrl = "${harborServer}/example/${projectName}" // imageTag 不要在这里定义,改为动态获取 // harborUserName = "admin" // harborPassword = "123456" } stages { stage('Source') { steps { git branch: 'main', credentialsId: "${credential}", url: "${codeRepo}" } } stage('Get Tag') { steps { script { // 获取当前 HEAD 指向的标签,取第一行(如果有多个标签) env.imageTag = sh(script: 'git tag --points-at HEAD', returnStdout: true).trim() echo "当前镜像标签: ${env.imageTag}" } } } stage('Test') { steps { sh 'mvn test' } } stage('Build') { steps { sh 'mvn clean package -Dmaven.test.skip=true' } } stage('Build Docker Image') { steps { sh "docker build -t ${imageUrl}:${env.imageTag} ." } } stage('Push Docker Image') { steps { withCredentials([usernamePassword(credentialsId: 'harbor-usercredential', passwordVariable: 'harborPassword', usernameVariable: 'harborUserName')]) { sh "echo ${env.harborPassword} | docker login -u ${env.harborUserName} --password-stdin ${harborServer}" // sh "docker login -u ${env.harborUserName} -p ${env.harborPassword} ${harborServer}" sh "docker push ${imageUrl}:${env.imageTag}" echo "username=${env.harborUserName}" echo "password=${env.harborPassword}" } } } stage('Run Docker') { steps { sh "ssh root@192.168.3.64 \"docker rm -f ${projectName} ; docker run --name ${projectName} --pull always --restart always -p 80:8888 -d ${imageUrl}:${env.imageTag}\"" sh "ssh root@192.168.3.65 \"docker rm -f ${projectName} ; docker run --name ${projectName} --pull always --restart always -p 80:8888 -d ${imageUrl}:${env.imageTag}\"" } } } }
案例:构建后操作
流水线也提供了构建后的动作,常可用于消息通知
这个动作就在post 配置段中
post部分定义了一个或多个在管道或阶段运行完成后运行的附加步骤(取决于post 部分在管道中的位
置)。
post可以支持以下任何后置条件块:
always、changed、fixed、regression、aborted、 failure、successunstable、 unsuccessful、
cleanup。
这些条件块允许根据管道或阶段的完成状态在每个条件内执行步骤。条件块按如下所示的顺序执行。
钉钉通知
范例:钉钉通知
注意:需要安装DingTalk插件并配置
pipeline { agent any environment { codeRepo = "git@gitlab.ming.org:devops/spring-boot-helloworld.git" credential = "jenkins" harborServer = 'harbor.ming.org' projectName = 'spring-boot-helloworld' imageUrl = "${harborServer}/example/${projectName}" // imageTag 不要在这里定义,改为动态获取 // harborUserName = "admin" // harborPassword = "123456" } stages { stage('Source') { steps { git branch: 'main', credentialsId: "${credential}", url: "${codeRepo}" } } stage('Get Tag') { steps { script { // 获取当前 HEAD 指向的标签,取第一行(如果有多个标签) env.imageTag = sh(script: 'git tag --points-at HEAD', returnStdout: true).trim() echo "当前镜像标签: ${env.imageTag}" } } } stage('Test') { steps { sh 'mvn test' } } stage('Build') { steps { sh 'mvn clean package -Dmaven.test.skip=true' } } stage('Build Docker Image') { steps { sh "docker build -t ${imageUrl}:${env.imageTag} ." } } stage('Push Docker Image') { steps { withCredentials([usernamePassword(credentialsId: 'harbor-usercredential', passwordVariable: 'harborPassword', usernameVariable: 'harborUserName')]) { sh "echo ${env.harborPassword} | docker login -u ${env.harborUserName} --password-stdin ${harborServer}" // sh "docker login -u ${env.harborUserName} -p ${env.harborPassword} ${harborServer}" sh "docker push ${imageUrl}:${env.imageTag}" echo "username=${env.harborUserName}" echo "password=${env.harborPassword}" } } } stage('Run Docker') { steps { sh "ssh root@192.168.3.64 \"docker rm -f ${projectName} ; docker run --name ${projectName} --pull always --restart always -p 80:8888 -d ${imageUrl}:${env.imageTag}\"" sh "ssh root@192.168.3.65 \"docker rm -f ${projectName} ; docker run --name ${projectName} --pull always --restart always -p 80:8888 -d ${imageUrl}:${env.imageTag}\"" } } } post { success { dingtalk( robot: 'dingtalk', type: 'TEXT', text: [ "Status of pipeline: ${currentBuild.fullDisplayName}", "${env.BUILD_URL} has result ${currentBuild.result}." ] ) } failure { dingtalk( robot: 'dingtalk', type: 'TEXT', text: [ "Status of pipeline: ${currentBuild.fullDisplayName}", "${env.BUILD_URL} has result ${currentBuild.result}." ] ) } } }
pipeline { agent any environment { codeRepo = "git@gitlab.ming.org:devops/spring-boot-helloworld.git" credential = "jenkins" harborServer = 'harbor.ming.org' projectName = 'spring-boot-helloworld' imageUrl = "${harborServer}/example/${projectName}" // imageTag 不要在这里定义,改为动态获取 } stages { stage('Source') { steps { git branch: 'main', credentialsId: "${credential}", url: "${codeRepo}" } } stage('Get Tag') { steps { script { env.imageTag = sh(script: 'git tag --points-at HEAD', returnStdout: true).trim() echo "当前镜像标签: ${env.imageTag}" } } } stage('Test') { steps { sh 'mvn test' } } stage('Build') { steps { sh 'mvn clean package -Dmaven.test.skip=true' } } stage('Build Docker Image') { steps { sh "docker build -t ${imageUrl}:${env.imageTag} ." } } stage('Push Docker Image') { steps { withCredentials([usernamePassword(credentialsId: 'harbor-usercredential', passwordVariable: 'harborPassword', usernameVariable: 'harborUserName')]) { sh "echo ${env.harborPassword} | docker login -u ${env.harborUserName} --password-stdin ${harborServer}" sh "docker push ${imageUrl}:${env.imageTag}" echo "username=${env.harborUserName}" echo "password=${env.harborPassword}" } } } stage('Run Docker') { steps { sh "ssh root@192.168.3.64 \"docker rm -f ${projectName} ; docker run --name ${projectName} --pull always --restart always -p 80:8888 -d ${imageUrl}:${env.imageTag}\"" sh "ssh root@192.168.3.65 \"docker rm -f ${projectName} ; docker run --name ${projectName} --pull always --restart always -p 80:8888 -d ${imageUrl}:${env.imageTag}\"" } } } post { always { script { def statusEmoji = (currentBuild.currentResult == 'SUCCESS') ? '✅' : '❌' def statusColor = (currentBuild.currentResult == 'SUCCESS') ? 'green' : 'red' def atList = (currentBuild.currentResult == 'SUCCESS') ? [] : ['all'] def buildUser = env.BUILD_USER_ID ?: 'admin' def currentTime = new Date().format("yyyy-MM-dd HH:mm:ss") // 构建消息行(List<String>) def textLines = [ "### 🚀 ${env.JOB_NAME} 构建通知", "---", "- **任务**: #${env.BUILD_NUMBER}", "- **状态**: <font color=\"${statusColor}\">${statusEmoji} ${currentBuild.currentResult}</font>", "- **持续时间**: ${currentBuild.durationString}", "- **执行人**: ${buildUser}", "- **部署项目**: #${env.BUILD_NUMBER}", "- **项目 URL**: [点击查看](${env.BUILD_URL})", "- **部署目录**: `${env.WORKSPACE}`", "- **触发时间**: ${currentTime}", "---", "[更改记录](${env.BUILD_URL}changes) | [控制台](${env.BUILD_URL}console)" ] // 失败时追加 @所有人 if (currentBuild.currentResult != 'SUCCESS') { textLines.add(" \n@所有人 **构建失败,请尽快排查!**") } dingtalk( robot: 'dingtalk', type: 'MARKDOWN', title: "${env.JOB_NAME} - 构建通知", text: textLines, at: atList ) } } } }
案例:触发器
https://github.com/jenkinsci/gitlab-plugin
在Pipeline中实现GitLab trigger,注意:需要安装gitlab插件
在Pipeline代码中配置GitLab Trigger会用到如下几个触发条件
triggerOnPush: GitLab仓库推送push事件时是否执行构建
triggerOnMergeRequest:GitLab仓库推送mergeRequest事件时,是否执行构建
branchFilterType:只有符合条件的分支才会被触发;必选配置,否则将无法实现触发,支持如下值
NameBaseFilter:基于分支名进行过滤,多个分支名彼此间以逗号分隔
RegexBaseFilter:基于正则表达式模式对分支名过滤
All:所有分支都会被触发
includeBranchSpec:基于branchFilterType值,输入期望包括的分支的规则
excludeBranchSpec:基于branchFilterType值,输入期望排队的分支的规则
secretToken:在回调时使用的token
GitLab触发器配置格式:
triggers {
gitlab(
triggerOnPush: false,
triggerOnMergeRequest: true,
triggerOpenMergeRequestOnPush: "never",
triggerOnNoteRequest: true,
noteRegex: "Jenkins please retry a build",
skipWorkInProgressMergeRequest: true,
ciSkip: false,
setBuildDescription: true,
addNoteOnMergeRequest: true,
addCiMessage: true,
addVoteOnMergeRequest: true,
acceptMergeRequestOnSuccess: false,
branchFilterType: "NameBasedFilter",
includeBranchesSpec: "release/qat",
excludeBranchesSpec: "",
pendingBuildName: "Jenkins",
cancelPendingBuildsOnUpdate: false,
secretToken: "62dad2cd1d9ae62686ada8dc4cdOae66")
}
执行一次后,会自动生成图形化的构建触发器的配置,效果如下

范例:通过 Gitlab 插件实现
pipeline { agent any triggers { gitlab(triggerOnPush: true, acceptMergeRequestOnSuccess: true, //triggerOnMergeRequest: true, branchFilterType: 'All', secretToken: '62dad2cd1d9ae62686ada8dc4cdOae66') } environment { codeRepo = "git@gitlab.ming.org:devops/spring-boot-helloworld.git" credential = "jenkins" harborServer = 'harbor.ming.org' projectName = 'spring-boot-helloworld' imageUrl = "${harborServer}/example/${projectName}" // imageTag 不要在这里定义,改为动态获取 } stages { stage('Source') { steps { git branch: 'main', credentialsId: "${credential}", url: "${codeRepo}" } } stage('Get Tag') { steps { script { env.imageTag = sh(script: 'git tag --points-at HEAD', returnStdout: true).trim() echo "当前镜像标签: ${env.imageTag}" } } } stage('Test') { steps { sh 'mvn test' } } stage('Build') { steps { sh 'mvn clean package -Dmaven.test.skip=true' } } stage('Build Docker Image') { steps { sh "docker build -t ${imageUrl}:${env.imageTag} ." } } stage('Push Docker Image') { steps { withCredentials([usernamePassword(credentialsId: 'harbor-usercredential', passwordVariable: 'harborPassword', usernameVariable: 'harborUserName')]) { sh "echo ${env.harborPassword} | docker login -u ${env.harborUserName} --password-stdin ${harborServer}" sh "docker push ${imageUrl}:${env.imageTag}" echo "username=${env.harborUserName}" echo "password=${env.harborPassword}" } } } stage('Run Docker') { steps { sh "ssh root@192.168.3.64 \"docker rm -f ${projectName} ; docker run --name ${projectName} --pull always --restart always -p 80:8888 -d ${imageUrl}:${env.imageTag}\"" sh "ssh root@192.168.3.65 \"docker rm -f ${projectName} ; docker run --name ${projectName} --pull always --restart always -p 80:8888 -d ${imageUrl}:${env.imageTag}\"" } } } post { always { script { def statusEmoji = (currentBuild.currentResult == 'SUCCESS') ? '✅' : '❌' def statusColor = (currentBuild.currentResult == 'SUCCESS') ? 'green' : 'red' def atList = (currentBuild.currentResult == 'SUCCESS') ? [] : ['all'] def buildUser = env.BUILD_USER_ID ?: 'admin' def currentTime = new Date().format("yyyy-MM-dd HH:mm:ss") // 构建消息行(List<String>) def textLines = [ "### 🚀 ${env.JOB_NAME} 构建通知", "---", "- **任务**: #${env.BUILD_NUMBER}", "- **状态**: <font color=\"${statusColor}\">${statusEmoji} ${currentBuild.currentResult}</font>", "- **持续时间**: ${currentBuild.durationString}", "- **执行人**: ${buildUser}", "- **部署项目**: #${env.BUILD_NUMBER}", "- **项目 URL**: [点击查看](${env.BUILD_URL})", "- **部署目录**: `${env.WORKSPACE}`", "- **触发时间**: ${currentTime}", "---", "[更改记录](${env.BUILD_URL}changes) | [控制台](${env.BUILD_URL}console)" ] // 失败时追加 @所有人 if (currentBuild.currentResult != 'SUCCESS') { textLines.add(" \n@所有人 **构建失败,请尽快排查!**") } dingtalk( robot: 'dingtalk', type: 'MARKDOWN', title: "${env.JOB_NAME} - 构建通知", text: textLines, at: atList ) } } } }
配置webhook
推送tag 触发
Jenkins url token


验证结果
修改代码并提交仓库
git clone git@gitlab.ming.org:devops/spring-boot-helloworld.git
cd /root/spring-boot-helloworld/src/main/java/com/neo/controller
sed -i 's/0.15/0.16/g' HelloWorldController.java
git add .
git commit -m "v0.16"
git tag v0.16
git push -u origin main
推标签
git push --tags

curl 192.168.3.64:80/version
基于 Pipeline 部署k8s
案例:和 Kubernetes 集成基于docker实现 Jenkins 构建
pipeline { agent any triggers { gitlab(triggerOnPush: true, acceptMergeRequestOnSuccess: true, //triggerOnMergeRequest: true, branchFilterType: 'All', secretToken: '62dad2cd1d9ae62686ada8dc4cdOae66') } environment { codeRepo = "git@gitlab.ming.org:devops/spring-boot-helloworld.git" credential = "jenkins" harborServer = 'harbor.ming.org' projectName = 'spring-boot-helloworld' imageUrl = "${harborServer}/example/${projectName}" // imageTag 不要在这里定义,改为动态获取 } stages { stage('Source') { steps { git branch: 'main', credentialsId: "${credential}", url: "${codeRepo}" } } stage('Get Tag') { steps { script { env.imageTag = sh(script: 'git tag --points-at HEAD', returnStdout: true).trim() echo "当前镜像标签: ${env.imageTag}" } } } stage('Test') { steps { sh 'mvn test' } } stage('Build') { steps { sh 'mvn clean package -Dmaven.test.skip=true' } } stage('Build Docker Image') { steps { sh "docker build -t ${imageUrl}:${env.imageTag} ." } } stage('Push Docker Image') { steps { withCredentials([usernamePassword(credentialsId: 'harbor-usercredential', passwordVariable: 'harborPassword', usernameVariable: 'harborUserName')]) { sh "echo ${env.harborPassword} | docker login -u ${env.harborUserName} --password-stdin ${harborServer}" sh "docker push ${imageUrl}:${env.imageTag}" echo "username=${env.harborUserName}" echo "password=${env.harborPassword}" } } } stage('deploy k8s') { steps { sh "sed -i 's#__TAG__#${env.imageTag}#' deploy/03-deployment.yaml" sh 'cat deploy/*.yaml | ssh root@master1.org "kubectl apply -f -"' } } } post { always { script { def statusEmoji = (currentBuild.currentResult == 'SUCCESS') ? '✅' : '❌' def statusColor = (currentBuild.currentResult == 'SUCCESS') ? 'green' : 'red' def atList = (currentBuild.currentResult == 'SUCCESS') ? [] : ['all'] def buildUser = env.BUILD_USER_ID ?: 'admin' def currentTime = new Date().format("yyyy-MM-dd HH:mm:ss") // 构建消息行(List<String>) def textLines = [ "### 🚀 ${env.JOB_NAME} 构建通知", "---", "- **任务**: #${env.BUILD_NUMBER}", "- **状态**: <font color=\"${statusColor}\">${statusEmoji} ${currentBuild.currentResult}</font>", "- **持续时间**: ${currentBuild.durationString}", "- **执行人**: ${buildUser}", "- **部署项目**: #${env.BUILD_NUMBER}", "- **项目 URL**: [点击查看](${env.BUILD_URL})", "- **部署目录**: `${env.WORKSPACE}`", "- **触发时间**: ${currentTime}", "---", "[更改记录](${env.BUILD_URL}changes) | [控制台](${env.BUILD_URL}console)" ] // 失败时追加 @所有人 if (currentBuild.currentResult != 'SUCCESS') { textLines.add(" \n@所有人 **构建失败,请尽快排查!**") } dingtalk( robot: 'dingtalk', type: 'MARKDOWN', title: "${env.JOB_NAME} - 构建通知", text: textLines, at: atList ) } } } }
curl 192.168.3.11/version
通过 Jenkinsfile 文件实现
可以将pipeline的语句写到代码仓库中的 Jenkinsfile 文件,这样更加方便维护和版本控制
注意:使用Jenkinsfile后无需再执行git clone代码这一步骤,直接执行后面的mvn test即可
创建 Jenkinsfile 文件
git clone git@gitlab.ming.org:devops/spring-boot-helloworld.git
mkdir jenkins
cd jenkins
vim Jenkinsfile
pipeline { agent any triggers { gitlab(triggerOnPush: true, acceptMergeRequestOnSuccess: true, //triggerOnMergeRequest: true, branchFilterType: 'All', secretToken: '62dad2cd1d9ae62686ada8dc4cdOae66') } environment { codeRepo = "git@gitlab.ming.org:devops/spring-boot-helloworld.git" credential = "jenkins" harborServer = 'harbor.ming.org' projectName = 'spring-boot-helloworld' imageUrl = "${harborServer}/example/${projectName}" // imageTag 不要在这里定义,改为动态获取 } stages { stage('Get Tag') { steps { script { env.imageTag = sh(script: 'git describe --tags --abbrev=0', returnStdout: true).trim() echo "当前镜像标签: ${env.imageTag}" } } } stage('Test') { steps { sh 'mvn test' } } stage('Build') { steps { sh 'mvn clean package -Dmaven.test.skip=true' } } stage('Build Docker Image') { steps { sh "docker build -t ${imageUrl}:${env.imageTag} ." } } stage('Push Docker Image') { steps { withCredentials([usernamePassword(credentialsId: 'harbor-usercredential', passwordVariable: 'harborPassword', usernameVariable: 'harborUserName')]) { sh "echo ${env.harborPassword} | docker login -u ${env.harborUserName} --password-stdin ${harborServer}" sh "docker push ${imageUrl}:${env.imageTag}" echo "username=${env.harborUserName}" echo "password=${env.harborPassword}" } } } stage('deploy k8s') { steps { sh "sed -i 's#__TAG__#${env.imageTag}#' deploy/03-deployment.yaml" sh 'cat deploy/*.yaml | ssh root@master1.org "kubectl apply -f -"' } } } post { always { script { def statusEmoji = (currentBuild.currentResult == 'SUCCESS') ? '✅' : '❌' def statusColor = (currentBuild.currentResult == 'SUCCESS') ? 'green' : 'red' def atList = (currentBuild.currentResult == 'SUCCESS') ? [] : ['all'] def buildUser = env.BUILD_USER_ID ?: 'admin' def currentTime = new Date().format("yyyy-MM-dd HH:mm:ss") // 构建消息行(List<String>) def textLines = [ "### ${env.JOB_NAME} 构建通知", "---", "- **任务**: #${env.BUILD_NUMBER}", "- **状态**: <font color=\"${statusColor}\">${statusEmoji} ${currentBuild.currentResult}</font>", "- **持续时间**: ${currentBuild.durationString}", "- **执行人**: ${buildUser}", "- **部署项目**: #${env.BUILD_NUMBER}", "- **项目 URL**: [点击查看](${env.BUILD_URL})", "- **部署目录**: `${env.WORKSPACE}`", "- **触发时间**: ${currentTime}", "---", "[更改记录](${env.BUILD_URL}changes) | [控制台](${env.BUILD_URL}console)" ] // 失败时追加 @所有人 if (currentBuild.currentResult != 'SUCCESS') { textLines.add(" \n@所有人 **构建失败,请尽快排查!**") } dingtalk( robot: 'dingtalk', type: 'MARKDOWN', title: "${env.JOB_NAME} - 构建通知", text: textLines, at: atList ) } } } }
git add .
git commit -m 'add Jenkinsfile'
git push -u origin main
修改 Pipeline job 使用 Jenkinsfile 文件
SCM 软件配置管理(Software Configuration Management)


再次执行构建验证结果

把Jenkinsfile和开发代码 分开两个仓库存放
git clone git@gitlab.ming.org:devops/pipeline-jenkins.git
mkdir jenkins
cd jenkins
vim Jenkinsfile
[root@node1 jenkins]# cat Jenkinsfile pipeline { agent any triggers { gitlab(triggerOnPush: true, acceptMergeRequestOnSuccess: true, //triggerOnMergeRequest: true, branchFilterType: 'All', secretToken: '62dad2cd1d9ae62686ada8dc4cdOae66') } environment { codeRepo = "git@gitlab.ming.org:devops/spring-boot-helloworld.git" credential = "jenkins" harborServer = 'harbor.ming.org' projectName = 'spring-boot-helloworld' imageUrl = "${harborServer}/example/${projectName}" // imageTag 不要在这里定义,改为动态获取 } stages { stage('Source') { steps { git branch: 'main', credentialsId: "${credential}", url: "${codeRepo}" } } stage('Get Tag') { steps { script { env.imageTag = sh(script: 'git describe --tags --abbrev=0', returnStdout: true).trim() echo "当前镜像标签: ${env.imageTag}" } } } stage('Test') { steps { sh 'mvn test' } } stage('Build') { steps { sh 'mvn clean package -Dmaven.test.skip=true' } } stage('Build Docker Image') { steps { sh "docker build -t ${imageUrl}:${env.imageTag} ." } } stage('Push Docker Image') { steps { withCredentials([usernamePassword(credentialsId: 'harbor-usercredential', passwordVariable: 'harborPassword', usernameVariable: 'harborUserName')]) { sh "echo ${env.harborPassword} | docker login -u ${env.harborUserName} --password-stdin ${harborServer}" sh "docker push ${imageUrl}:${env.imageTag}" echo "username=${env.harborUserName}" echo "password=${env.harborPassword}" } } } stage('deploy k8s') { steps { sh "sed -i 's#__TAG__#${env.imageTag}#' deploy/03-deployment.yaml" sh 'cat deploy/*.yaml | ssh root@master1.org "kubectl apply -f -"' } } } post { always { script { def statusEmoji = (currentBuild.currentResult == 'SUCCESS') ? '✅' : '❌' def statusColor = (currentBuild.currentResult == 'SUCCESS') ? 'green' : 'red' def atList = (currentBuild.currentResult == 'SUCCESS') ? [] : ['all'] def buildUser = env.BUILD_USER_ID ?: 'admin' def currentTime = new Date().format("yyyy-MM-dd HH:mm:ss") // 构建消息行(List<String>) def textLines = [ "### ${env.JOB_NAME} 构建通知", "---", "- **任务**: #${env.BUILD_NUMBER}", "- **状态**: <font color=\"${statusColor}\">${statusEmoji} ${currentBuild.currentResult}</font>", "- **持续时间**: ${currentBuild.durationString}", "- **执行人**: ${buildUser}", "- **部署项目**: #${env.BUILD_NUMBER}", "- **项目 URL**: [点击查看](${env.BUILD_URL})", "- **部署目录**: `${env.WORKSPACE}`", "- **触发时间**: ${currentTime}", "---", "[更改记录](${env.BUILD_URL}changes) | [控制台](${env.BUILD_URL}console)" ] // 失败时追加 @所有人 if (currentBuild.currentResult != 'SUCCESS') { textLines.add(" \n@所有人 **构建失败,请尽快排查!**") } dingtalk( robot: 'dingtalk', type: 'MARKDOWN', title: "${env.JOB_NAME} - 构建通知", text: textLines, at: atList ) } } } }
git add .
git commit -m 'add Jenkinsfile'
git push -u origin main
git@gitlab.ming.org:devops/pipeline-jenkins.git



再次执行构建验证结果

浙公网安备 33010602011771号