Jenkins搭建及使用
Jenkins介绍
Jenkins,是一个开源的持续集成工具,基于Java开发,可用于持续的软件版本发布/测试项目。
安装JDK(已安装忽略)
Jenkins需要jdk环境
1 检索可用包
yum search java|grep jdk
2 安装
yum install java-1.8.0-openjdk -y
yum install java-devel -y
下载启动
去 https://www.jenkins.io/download/ 下载自己环境对应的war包,我这里是centos7(ip: 192.168.3.101)
下载完后放入自定义目录下,然后启动 java -jar jenkins.war
待启动成功后 访问http://192.168.3.101:8080完成配置
账号 root
密码:看日志得知初始密码
Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation:
e4b7e1ff816e464ea94ebd79189736bd
This may also be found at: /root/.jenkins/secrets/initialAdminPassword
密码文件使用后会自动删除
默认选择推荐插件即可。
准备使用
现在还并不能真正开始使用,先安装一些可能的用到的工具/插件
git安装(已安装忽略)
yum install -y git
Maven安装
下载后放到Jenkins所在服务器解压缩
为了后面打包速度,先配置一下阿里云镜像,修改xxxx/maven/conf/settings.xml
<mirrors>
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
</mirrors>
进入Jenkins,找到Dashboard --> Manage Jenkins --> Global Tool Configuration
拉到最下,找到Maven,Maven安装,点击新增Maven,名字自己取一个,MAVEN_HOME填自己maven全路径
Jenkins安装插件
登录Jenkins,找到Dashboard --> Manage Jenkins --> Manage Plugins --> 可选插件
搜索publish over ssh并安装(勾选单选框,点击下方按钮"Install without restart")
之后按照同样的方法安装Maven Integration plugin
开始使用
一个最小的自动化流程应该包含 代码拉取-->编译打包-->上传目标服务器-->执行重启命令。
我们就以上述流程来举例。
构建一个maven项目
1 回到首页,新建Item,选择"构建一个maven项目"
2 配置项目Git
第一次应该没有凭据(Credentials),点击添加按钮,再弹窗中填写git的用户名密码
3 配置Build,如果你的pom文件在项目根目录下,直接填pom.xml即可,否则填"yourfolder/pom.xml"
4 此时可以试着构建一下,点击项目名-->Build Now
在构建历史中,点击一下数字,再点控制台输出,能看到本次构建结果
不出意外应该是成功的。
目前我们只是打包了项目,接下来我们继续配置后续步骤。
5 配置文件上传
在Post Steps中,点击Add post-build step,下拉框选择Send files or execute commands over SSH
这里配置的是将把打包好的jar文件传到哪里去。但是第一次来,是没有SSH Server可选的。
我们去Dashboard --> Manage Jenkins --> Configure System,页面直接拉到最下方,找到SSH Servers,新增一个。展开高级可配置服务器密码。
配置好服务器后,继续回去配置文件上传
Source files是要上传的文件地址,相对于项目根目录。
Remote directory是远程目标地址,绝对路径。
Remove prefix是指Source files前面那一堆不要,只要最后的xxx.jar文件。
Exec command是指要执行的命令,这里启动了上传的jar包。
6 额外配置说明
上面其实已经完成了最小的流程。不过实际环境往往更复杂,这里其实也还有很多配置项。
比如在Post Steps之前,还有一个Pre Steps,我们可以在这里配置一下,做一些提前工作,如备份,清理服务器等
后面构建设置里也可以邮件通知,构建后操作也可以做更多事情,可自行探索。
构建Pipeline
较之于上面UI的方式,Pipeline脚本的模式可能更适合程序员。
回到首页,新建Item,选择Pipeline。找到流水线一项,编辑脚本,可以选择自带的脚本模板Hello World看看脚本长啥样。
简单介绍下Pipeline脚本语法,后面新出现的再随时说明
pipeline:整条流水线
agent:指定执行器
stages:所有阶段
stage:某一阶段,可有多个
steps:阶段内的每一步,可执行命令
那么,接下来,我们用Pipeline再完成一次前面maven项目的构建。
1 先写一个框架出来
pipeline {
agent any
tools {
maven 'maven3'
}
stages {
stage('git pull') {
steps {
echo 'pull success'
}
}
stage('maven package'){
steps {
echo 'package success'
}
}
stage('upload jar file'){
steps {
echo 'upload jar success'
}
}
stage('restart project'){
steps {
echo 'restart success'
}
}
}
}
tools:配置后面会用到的工具。前面的"maven"是指会用到maven这个工具,后面的"maven3"是指具体用到哪个maven,这个"maven3"是最早在Dashboard --> Manage Jenkins --> Global Tool Configuration里配置的Maven Name。
2 git pull 脚本
这个脚本有的地方写起来比较麻烦,Jenkins帮我们提供了生成工具。
点击脚本编辑框下方的流水线语法,跳转页面,在示例步骤中选择"git:Git",填写好仓库地址,分支,凭据,点击"生成流水线脚本"即可。
将生成好的代码复制到我们的脚本里。现在,我们的脚本看起来像这样
pipeline {
agent any
tools {
maven 'maven3'
}
stages {
stage('git pull') {
steps {
git branch: 'dev', credentialsId: 'c5d3e017-bf92-4369-87bf-229ab230ad2b', url: 'http://192.168.3.112/root/cloud-project-demo.git'
echo 'pull success'
}
}
stage('maven package'){
steps {
echo 'package success'
}
}
stage('upload jar file'){
steps {
echo 'upload jar success'
}
}
stage('restart project'){
steps {
echo 'restart success'
}
}
}
}
3 项目打包脚本
这个比较简单,一句话sh "mvn clean package"
sh用于执行shell命令。如果要执行多行命令,使用三个双引号(""")开头,最后同样以三个双引号结尾,例如
sh """
cd /xxx/yyy
mkdir zzz
echo 'ok'
"""
4 文件上传
再次点击流水线语法,示例步骤中选择"sshPublisher:Send build artifacts over SSH",可以看到这里的配置页面和之前是一样的,填好信息,生成脚本并复制,现在我们的脚本整体如下:
pipeline {
agent any
tools {
maven 'maven3'
}
stages {
stage('git pull') {
steps {
git branch: 'dev', credentialsId: 'c5d3e017-bf92-4369-87bf-229ab230ad2b', url: 'http://192.168.3.112/root/cloud-project-demo.git'
echo 'pull success'
}
}
stage('maven package'){
steps {
sh "mvn clean package"
echo 'package success'
}
}
stage('upload jar file'){
steps {
sshPublisher(publishers: [sshPublisherDesc(configName: 'cloud-project-server-stage', transfers: [sshTransfer(cleanRemote: false, excludes: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '/home/my-project/gateway', remoteDirectorySDF: false, removePrefix: 'gateway/target', sourceFiles: 'gateway/target/gateway.jar')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
echo 'upload jar success'
}
}
stage('restart project'){
steps {
echo 'restart success'
}
}
}
}
5 重启项目
上述配置sshPublisher时,我故意没有配置Exec command,是想让流水线步骤看起来更清晰,单独配置一个stage完成项目重启。
再次点击流水线语法,示例步骤中选择"sshPublisher:Send build artifacts over SSH",这次不配置文件相关的三个选项,只配置Exec command(cd /home/my-project/gateway \n\r ./gateway-restart.sh),生成脚本,复制。
好了,我们的脚本完成了
pipeline {
agent any
tools {
maven 'maven3'
}
stages {
stage('git pull') {
steps {
git branch: 'dev', credentialsId: 'c5d3e017-bf92-4369-87bf-229ab230ad2b', url: 'http://192.168.3.112/root/cloud-project-demo.git'
echo 'pull success'
}
}
stage('maven package'){
steps {
sh "mvn clean package"
echo 'package success'
}
}
stage('upload jar file'){
steps {
sshPublisher(publishers: [sshPublisherDesc(configName: 'cloud-project-server-stage', transfers: [sshTransfer(cleanRemote: false, excludes: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '/home/my-project/gateway', remoteDirectorySDF: false, removePrefix: 'gateway/target', sourceFiles: 'gateway/target/gateway.jar')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
echo 'upload jar success'
}
}
stage('restart project'){
steps {
sshPublisher(publishers: [sshPublisherDesc(configName: 'cloud-project-server-stage', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''cd /home/my-project/gateway
./gateway-restart.sh''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
echo 'restart success'
}
}
}
}
ps: 其中gateway-restart.sh是我自己的项目重启脚本。如果用docker/docker-compose,可以换成docker/docker-compose restart gateway即可。
6 查看构建结果
这里和之前普通maven项目构建一样,也可以在控制台输出里查看结果。不过Pipeline有更方便直观的方式。
构建该Pipeline后,在项目管理页面,右侧的阶段视图,就能直观看到每一次每一个阶段的构建状态。点击某一阶段的logs,可以精准定位到对应的日志上,非常方便。
还可以安装Blue Ocean插件,这样Pipeline左侧就会多一个菜单,"打开Blue Ocean",点击后,可以用更加图形化的页面查看Pipeline构建状态。
Blue Ocean的一个重要功能是可以重新执行某阶段。
7 其他补充
上面已经完成了一个简单的脚本。这里再补充两点。
post
流水线完成后可执行的任务
- always 无论流水线或者阶段的完成状态。
- changed 只有当流水线或者阶段完成状态与之前不同时。
- failure 只有当流水线或者阶段状态为"failure"运行。
- success 只有当流水线或者阶段状态为"success"运行。
- unstable 只有当流水线或者阶段状态为"unstable"运行。例如:测试失败。
- aborted 只有当流水线或者阶段状态为"aborted "运行。例如:手动取消。
agent
指定执行节点。这个是指在Jenkins集群里,指定由哪个节点执行该脚本。关于Jenkins集群,后面会再说明。
label 指定运行job的节点标签,any 表示不指定,由Jenkins自动分配
以下例子展示了pos和label的用法
pipeline {
agent {
node {
label "jenkins-01"
}
}
stages {
stage('git pull') {
steps {
echo 'pull success'
}
}
stage('maven package') {
steps {
echo 'package success'
}
}
}
post {
success {
echo "完成"
}
failure {
echo "失败"
}
}
}
构建多分支流水线
当我们完成了pipeline之后,你可能会想,给测试环境构建写完一套脚本,那给线上环境构建不是又得写一遍,预发布(如果有)环境还得写一遍,而且大体内容差不多,就是改改服务器ip啥的。
当然也不是非常麻烦。在创建Item时,就可以选择复制一个已有的Item,之后改吧改吧就行了
但是,我们可以使用多分支流水线更好的完成这个事情。
1 创建Jenkinsfile
切换到项目dev分支,到项目根目录下创建一个文件Jenkinsfile。Jenkinsfile的内容就是上面我们完成的脚本内容。commit & push Jenkinsfile到远程仓库。
pipeline {
agent any
tools {
maven 'maven3'
}
stages {
stage('git pull') {
steps {
git branch: 'dev', credentialsId: 'c5d3e017-bf92-4369-87bf-229ab230ad2b', url: 'http://192.168.3.112/root/cloud-project-demo.git'
echo 'pull success'
}
}
stage('maven package'){
steps {
sh "mvn clean package"
echo 'package success'
}
}
stage('upload jar file'){
steps {
sshPublisher(publishers: [sshPublisherDesc(configName: 'cloud-project-server-stage', transfers: [sshTransfer(cleanRemote: false, excludes: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '/home/my-project/gateway', remoteDirectorySDF: false, removePrefix: 'gateway/target', sourceFiles: 'gateway/target/gateway.jar')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
echo 'upload jar success'
}
}
stage('restart project'){
steps {
sshPublisher(publishers: [sshPublisherDesc(configName: 'cloud-project-server-stage', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''cd /home/my-project/gateway
./gateway-restart.sh''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
echo 'restart success'
}
}
}
}
之后再另一个分支下,如master也弄一个Jenkinsfile(可以从dev合并过去,然后改内容),为了简单区分,内容就改拉取的分支和目标服务器。
pipeline {
agent any
tools {
maven 'maven3'
}
stages {
stage('git pull') {
steps {
git branch: 'master', credentialsId: 'c5d3e017-bf92-4369-87bf-229ab230ad2b', url: 'http://192.168.3.112/root/cloud-project-demo.git'
echo 'pull success'
}
}
stage('maven package'){
steps {
sh "mvn clean package"
echo 'package success'
}
}
stage('upload jar file'){
steps {
sshPublisher(publishers: [sshPublisherDesc(configName: 'cloud-project-server-prod', transfers: [sshTransfer(cleanRemote: false, excludes: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '/home/my-project/gateway', remoteDirectorySDF: false, removePrefix: 'gateway/target', sourceFiles: 'gateway/target/gateway.jar')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
echo 'upload jar success'
}
}
stage('restart project'){
steps {
sshPublisher(publishers: [sshPublisherDesc(configName: 'cloud-project-server-prod', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''cd /home/my-project/gateway
./gateway-restart.sh''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
echo 'restart success'
}
}
}
}
这里的服务器'cloud-project-server-prod'和之前的'cloud-project-server-stage'一样,如果你没有,需要去Dashboard --> Manage Jenkins --> Configure System 里的SSH Servers处再配置一个,否则后面构建时会报错找不到这个服务器
ps:Jenkinsfile类似docker的dockerfile一样,把一系列命令固化到配置文件,这样更利于团队合作。
2 Jenkins创建多分支流水线任务
我们已经在多个分支(dev和master)下有了Jenkinsfile了,现在可以创建多分支任务了。
到Blue Ocean主页,点击右上方的"创建流水线"
代码仓库选择你自己的类型,我这里选Git,填入项目地址,选择之前配置好的凭证(没有的话新建一个),点击"创建流水线"即可。
这里可能有个bug要提一下。创建完成之后先退出去,到项目配置页面,重新选一下凭证并保存,它有时候没选上(不知道为什么有这个bug)
3 扫描多分支流水线
回到项目,点击"立刻Scan多分支流水线",然后点击"Scan多分支流水线日志",可以看到,有两个分支找到了Jenkinsfile。
回到项目也能看到两个分支构建成功。
在Blue Ocean也能看到
多分支流水线,可以很方便的在多个环境中发布。可以手动只构建某一个分支,某一个阶段,非常灵活。
自动化构建
每次都要自己手动去构建,也是麻烦。Jenkins提供了很多种自动化构建的方式。
触发远程构建
Jenkins可以提供一个接口,外部调用这个接口,就能触发构建。
那么我们可以在代码提交或合并分支时,远程调用该接口,就可以完成自动构建了。
1 配置触发远程构建
为了方便测试,新建一个Pipeline项目,流水线脚本里随便选个Hello World。
在"构建触发器"中勾选"触发远程构建",填一个身份验证令牌,之后保存。
可以看到配置下方提示,通过访问JENKINS_URL/job/test3/build?token=TOKEN_NAME
就能触发这个构建了。
我们立马测试一下。
现在构建历史是空的,在浏览器访问192.168.3.112:8080/job/test3/build?token=abc123
,立刻就能看到构建历史里多了一条记录。
稍等几秒,构建完成。
但是这种方式,远程调用时会有权限问题。我们可以用一个插件解决。
去插件管理中心安装Build Authorization Token Root
。
这样我们上述的url可以改成http://192.168.3.112:8080/buildByToken/build?job=test3&token=abc123
,同样在浏览器访问测试一下,没有问题。
2 webhook调用构建接口
这里以Gitlab为例,利用webhook回调钩子调起Jenkins的构建接口
进入Gitlab项目,找到设置 --> Webhooks,网址填入http://192.168.3.112:8080/buildByToken/build?job=test3&token=abc123
,推送事件可以填一个dev
,这样只有在dev分支推送时才会触发。如果想合并时触发,可以勾上"合并请求事件"。最下面的"启用SSL验证"去掉。最后点击"Add webhook"。
现在,我们有一个项目钩子了。
如果遇到报错:Urlis blocked: Requests to the local network are not allowed
,就去管理中心,找到设置-->网络-->外发请求,勾上"允许Webhook和服务对本地网络的请求",保存修改。
然后我们可以先测试一下
测试成功会在顶部显示Hook executed successfully
3 push代码,触发构建
现在去dev分支,随便改点啥,提交一哈。去Jenkins项目就能看到,已经自动构建了。
定时构建
除了远程触发构建外,还可以定时构建
*/10 * * * *
表示 每隔10分钟执行一次。下方也有提示最近两次的执行时间。
这里和一般的cron表达式类似。如果不知道怎么设置,可以去这个网站 https://crontab.guru
但是我们注意到还有一个黄色感叹号,提示我们用H/10 * * * *
。
这是Jenkins的cron表达式特殊的一点。H表示hash散列值,以job名取值,获取到以job名为入参的唯一值,相同名称值也相同,这个偏移量会和预计时间相加,生成实际的运行时间。
其意义在于:不同的项目在不同的时间运行,即使配置的cron是一样的。比如都是10 * * * *
,表示每个小时的第10分钟开始执行任务,那么会造成同一时间内在Jenkins中启动很多job,对Jenkins造成太大压力。
而换成H/10 * * * *
,那么在首次启动任务时,会有随机值参与进来,可能有的会在11分钟启动 有的会在12分钟启动,随后的启动时间也是这个值。这样就能错开相同cron值的任务执行了。
定期检查源码变更
除了被动构建,Jenkins也可以主动定期检查代码托管服务器上是否有变化,一旦发生变化就执行构建。
新建一个maven项目,其他配置自行补充。勾选Poll SCM,日程表时间规则和上面一样。为了方便测试,设定为每分钟检查一次。
然后去随便改改代码,提交一下,等一下就能看到自动构建了。
邮件通知
在我们构建之后,还可以发送邮件通知。
再配置Jenkins之前,先自行配置好邮件发送方的账号和认证信息。例如163邮箱,在网页端的设置-->POP3/SMTP/IMAP里,找到POP3/SMTP服务,开启它即可。最后会给你生成一串认证码,用于发送邮件校验。
接下来回到Jenkins。
1 配置邮件发送方信息
Dashboard --> Manage Jenkins --> Configure System,找到Jenkins Location
然后找到Extended E-mail Notification
ps:点开高级可添加凭证。
2 构建项目里配置发送邮件
我的例子还是Pipeline项目,maven项目或其它项目也能找到同样的设置地方。
点开"流水线语法",选择"emailext:Extended Email",填写邮件接收方,主题,内容模板。
将生成的脚本复制到Pipeline脚本里,可以给它建一个stage放最后,类似如下:
pipeline {
agent any
stages {
stage('Hello') {
steps {
echo 'Hello World'
}
}
stage('Email') {
steps {
emailext body: '''$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS:
Check console output at $BUILD_URL to view the results.''', subject: '$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!', to: 'youremail@qq.com'
echo 'Send Email Success'
}
}
}
}
3 构建项目
构建项目,就可以收到邮件了
Jenkins集群/并发构建
当项目比较多的时候,我们一个Jenkins构建太慢了。那么我们可以搭建Jenkins集群提高效率。
1 准备服务器
额外准备两台虚拟机,我这里分别是192.168.3.116和192.168.3.117。分别连上去创建一个Jenkins工作目录(例如:/home/jenkins),之后配置Jenkins节点需要用到。
2 创建Jenkins节点
回到Jenkins, Dashboard --> Manage Jenkins --> Manage Nodes And Clouds
可以看到现在只有一个节点,点击"新建节点"。
点击"Create",填写信息后保存。
这里的远程工作目录,填我们之前创建的/home/jenkins。标签可以就填节点名,如果后续某个构建任务需要指定某个节点,就是这个标签。启动方式选"Launch agents via SSH",填上主机ip和远程登录凭证,"Host Key Verification Strategy"选择"Non verifying Verification Strategy"。
保存,我们就有两个节点了。点击右上角刷新状态,就会同步远程机器的磁盘数据了。
ps:此时我们如果去远程机器192.168.3.116的/home/jenkins目录,可以看到jenkins帮我们生成了一点东西
用同样的方式再建立一个节点(再次建立可复制之前的节点)
3 测试并发构建
现在我们有三个节点了。找一个Pipeline项目,连续多次点击构建,就可以看到Jenkins启用了多个节点去构建
值得一提的是,Pipeline项目会默认使用多节点并发构建,除非你勾上"Do not allow concurrent builds"
而普通Item如maven项目则反之,默认不会使用并发构建,需要勾上"在必要的时候并发构建"。
总结
我们先介绍了如何安装Jenkins,并配置工具/插件;然后开始使用,分别演示了maven项目,Pipeline,多分支流水线;接着介绍了自动化构建方式;接着配置邮件通知;最后是搭建Jenkins集群做并发构建。
感谢阅读。