Jenkins代码发布流程(三)
前面已经对jenkins界面有了一些大概的介绍,jenkins可以干很多事情,当前其中很重要的一项就是发布代码,当然发布代码的方式也有一些不同,下面我们就一个开源项目做一次发布。
一、job的简单创建
1.1 实现代码的构建
#我们先什么都jenkins页面化操作,所以先需要安装一个Maven Integration插件,因为我们先拿一个MVC框架的java程序入手。
创建job:
配置job:
#如果出现下面的报错,就是git地址没有代码拉取权限或者git地址真心配置的不对:
Failed to connect to repository : Command "git.exe ls-remote -h https://github.com/youzan/bugCatcher/tree/master.git HEAD" returned status code 128: stdout: stderr: remote: Not Found fatal: repository 'https://github.com/youzan/bugCatcher/tree/master.git/' not found
#注意这里一般就是mvn clean install,这样能保证每次编译的时候依赖包是最新的。这里有一篇关于mvn clean install和mvn install的区别的文章:
https://blog.csdn.net/qq_38245537/article/details/78563284?locationNum=4&fps=1
#当然一般都会跳过单元测试:
mvn clean install -DskipTests 或者 mvn clean install -Dmaven.test.skip=true
#当然也可以在pom.xml文件中标注跳过单元测试:
<plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <skip>true</skip> </configuration> </plugin> </plugins>
#很多时候环境分开发测试环境、qa测试环境、正式上线环境,所以呢可能连接的数据库啊等一些上游地址是不同的,如何实现呢,就是maven的profile构建不同环境配置.
#例如在pom.xml文件中配置:
<profiles> <!-- 开发 --> <profile> <!-- profile的id --> <id>dev</id> <properties> <env>src/main/resources/dev</env> <env.name>develop</env.name> </properties> <activation> <!-- 默认激活此配置 --> <activeByDefault>true</activeByDefault> </activation> </profile> <!-- 测试 --> <profile> <id>test</id> <properties> <env>src/main/resources/test</env> <env.name>test</env.name> </properties> </profile> <!-- 线上 --> <profile> <id>online</id> <properties> <env>src/main/resources/online</env> <env.name>online</env.name> </properties> </profile> </profiles>
#上面pom.xml这样配置以后,然后在源代码的src/main/resources目录下面分别创建dev,test,online这三个不同的目录,然后目录里面分别创建同名文件但是里面的db连接之类的不同。
#然后在编译的时候执行:
线上环境:mvn clean install -P online -Dmaven.test.skip=true 测试环境:mvn clean install -P test -Dmaven.test.skip=true
编译测试:
#上面就是一个最简单的编译,编译一下。
#再看下界面的数据,太长只截取部分(点击控制台输出就可以查看完整的构建过程)。
#先看错误的输出:
#再看成功的输出:
博文来自:www.51niux.com
查看下载jar包的位置目录:
#因为我们是115节点执行的编译操作,所以要上115上面去查看。
# vim /usr/local/apache-maven/conf/settings.xml
<!-- localRepository | The path to the local repository maven will use to store artifacts. | | Default: ${user.home}/.m2/repository <localRepository>/path/to/local/repo</localRepository> --> #从上面的内容可以看出来走的默认${user.home}/.m2/repository
# ls -l /home/work/.m2/repository/ #所有下载的jar包都在这个目录下面
总用量 0 drwxrwxr-x. 3 work work 25 11月 3 18:55 aopalliance drwxrwxr-x. 4 work work 35 11月 3 19:20 asm drwxrwxr-x. 3 work work 38 11月 3 19:21 backport-util-concurrent drwxrwxr-x. 3 work work 17 11月 3 18:56 ch drwxrwxr-x. 3 work work 25 11月 3 18:57 classworlds drwxrwxr-x. 5 work work 54 11月 3 19:20 com drwxrwxr-x. 3 work work 31 11月 3 18:56 commons-beanutils drwxrwxr-x. 3 work work 25 11月 3 18:57 commons-cli drwxrwxr-x. 3 work work 33 11月 3 19:20 commons-collections drwxrwxr-x. 3 work work 30 11月 3 19:20 commons-digester drwxrwxr-x. 3 work work 24 11月 3 19:20 commons-io drwxrwxr-x. 3 work work 26 11月 3 19:20 commons-lang drwxrwxr-x. 3 work work 29 11月 3 18:55 commons-logging drwxrwxr-x. 3 work work 31 11月 3 19:20 commons-validator drwxrwxr-x. 3 work work 28 11月 3 19:21 doxia drwxrwxr-x. 6 work work 72 11月 3 18:56 javax drwxrwxr-x. 3 work work 19 11月 3 18:56 junit drwxrwxr-x. 3 work work 34 11月 3 18:55 mysql drwxrwxr-x. 3 work work 16 11月 3 18:56 net drwxrwxr-x. 12 work work 165 11月 3 18:57 org drwxrwxr-x. 3 work work 17 11月 3 19:20 oro drwxrwxr-x. 3 work work 22 11月 3 19:20 xml-apis drwxrwxr-x. 3 work work 22 11月 3 19:00 xpp3
# ls -l /home/work/.m2/repository/org/slf4j/slf4j-api/1.7.5/ #这是根据上面截图最后一个jar包的url找的其所在位置,可以看到其目录结构就跟URL一样。
总用量 96 -rw-rw-r--. 1 work work 235 11月 3 19:27 _remote.repositories -rw-rw-r--. 1 work work 26084 11月 3 18:57 slf4j-api-1.7.5.jar -rw-rw-r--. 1 work work 40 11月 3 18:57 slf4j-api-1.7.5.jar.sha1 -rw-rw-r--. 1 work work 2689 11月 3 18:56 slf4j-api-1.7.5.pom -rw-rw-r--. 1 work work 40 11月 3 18:56 slf4j-api-1.7.5.pom.sha1 -rw-rw-r--. 1 work work 47186 11月 3 19:27 slf4j-api-1.7.5-sources.jar -rw-rw-r--. 1 work work 40 11月 3 19:27 slf4j-api-1.7.5-sources.jar.sha1
#为什么要展示这里,因为java编译就是依赖于各种各样的jar包,你不了解这层关系以后在编译报错排错的时候就稍微有点费劲,很多时候可能是依赖包问题导致编译失败。
查看编译出来的jar包:
# ls -l /data/jenkins_workspace/workspace/bugCatcher----Permission-sandbox-mvc-single/ #首先先看这个目录,这就是我们创建的job名称,下面直接就是代码了,所以没有git组的概念你也看不出下面是什么git地址的代码
总用量 32 -rw-rw-r--. 1 work work 18804 11月 3 18:16 pom.xml drwxrwxr-x. 2 work work 228 11月 3 18:16 README -rw-rw-r--. 1 work work 4891 11月 3 18:16 README.md -rwxrwxr-x. 1 work work 167 11月 3 18:16 setup.sh drwxrwxr-x. 4 work work 30 11月 3 18:16 src drwxrwxr-x. 10 work work 231 11月 3 21:14 target
# cd /data/jenkins_workspace/workspace/bugCatcher----Permission-sandbox-mvc-single/
# ls -lh target/
总用量 12M drwxrwxr-x. 9 work work 134 11月 3 21:14 classes drwxrwxr-x. 2 work work 4.0K 11月 3 21:14 dependency-maven-plugin-markers drwxrwxr-x. 3 work work 25 11月 3 21:14 generated-sources drwxrwxr-x. 3 work work 30 11月 3 21:14 generated-test-sources drwxrwxr-x. 2 work work 28 11月 3 21:14 maven-archiver drwxrwxr-x. 5 work work 71 11月 3 21:14 pfcase -rw-rw-r--. 1 work work 34K 11月 3 21:14 pfcase-sources.jar -rw-rw-r--. 1 work work 12M 11月 3 21:14 pfcase.war #这就是最后编译出来的war包,要丢到tomcat的webapps目录下面的。 drwxrwxr-x. 2 work work 6 11月 3 21:15 surefire drwxrwxr-x. 3 work work 17 11月 3 21:14 test-classes
#为什么是pfcase.war而不是ROOT.war包呢,就得看下面的pom.xml文件了。
# vim pom.xml
<build> <finalName>pfcase</finalName> </build>
1.2 将构建好的war包发布到指定机器上面去
将war包发到指定机器上面去:
#直接看通知台数据看最后的结果吧,都是文字就不截图了省点带宽:
channel stopped [bugCatcher----Permission-sandbox-mvc-single] $ /bin/bash /tmp/jenkins4595475323755118241.sh inet 192.168.1.115 netmask 255.255.255.0 broadcast 192.168.1.255 Set build name. New build name is '#13' Finished: SUCCESS
#通过测试我们可以明确如果job里面制定了从节点,那么shell里面的操作发起者就是哪台机器。
被发布机查看一下:
#登录192.168.1.151这台机器。
$ ls -l /usr/local/tomcat/webapps/
总用量 11832 drwxr-x---. 14 work work 4096 11月 3 19:21 docs drwxr-x---. 6 work work 83 11月 3 19:21 examples drwxr-x---. 5 work work 87 11月 3 19:21 host-manager drwxr-x---. 5 work work 103 11月 3 19:21 manager drwxr-x---. 5 work work 71 11月 3 22:07 pfcase #可以看到已经将war解压成指定的目录了。 -rw-rw-r--. 1 work work 12109266 11月 3 22:07 pfcase.war #可以看到这是做好的war包 drwxr-x---. 3 work work 283 11月 3 19:21 ROOT
#访问url:192.168.1.151:8080/pfcase/
#通过web结果看到代码发布成功了,页面也正常的显示出来了,当然是否完全OK不确定,还要查看日志或者访问指定的探测接口之类的。
二、针对上面的job做一些优化
2.1 发布主机应该改成参数的形式
#上面scp主机写死了IP的形式,要发布哪个主机应该是由发布者来定的。而且war包所在的目录写死了明显也不合适。
#先来个参数化构建
#这样的话基本都是变量适用性就会好很多。
2.2 优化Build History的输出
现在构建历史哪里只是数字号码排序,也看不出来是谁发布的,发布的哪个主机等等,好的那就再改一改。
#下面分别用两个用户发布,然后看看效果:
#这就很明显了哪个用户做的什么事一眼就看出来了,当然还可以继续加变量,系统不自带就自定义就行。
2.3 可以选择指定的版本发布而不是发布最新的代码
先设置一下:
#首先要安装插件:Git parameter,不然参数化构建哪里没有这一项。
#要注意如果你只是这样设置了,不拿着$release_tag这个变量去做点事情发布的依旧是最新的代码,所以还要进入到工作空间里面进行git checkout操作。
下面验证一下:
下面直接在115机器上面看一下:
$ pwd
/data/jenkins_workspace/workspace/admobadapter----Permission-sandbox-java-single
$ git status
# 头指针分离于 v1.4.5 无文件要提交,干净的工作区
2.4 jenkins来点提示信息
jenkins发布出问题该找谁,出现的报错信息应该看什么链接等等,一些提示信息怎么展示呢?
博文来自:www.51niux.com
2.5 Jenkins发布邮件通知
jenkins发布时发布了,但是发布是否成功并且谁发布的什么时候发布的,只有操作jenkins的人知道,其他人并不知情,怎么让发布通知出来呢?
#这里先来一个最简单的: Jenkins配置邮件通知。(当然做技术的都知道邮箱基本被各种信息填满所以发邮件并不是一个多好的方式)
配置邮箱服务器:
系统管理=》系统设置
#注意上图中的Reply-To-Address如果只是用于job发布失败报警可以不填。
#上面有个验证报错,要注意的是我这里其实都填OK了但是还提示下面的报错:
Failed to send out e-mail com.sun.mail.smtp.SMTPSenderFailedException: 553 Mail from must equal authorized user at com.sun.mail.smtp.SMTPTransport.mailFrom(SMTPTransport.java:1587) Caused: com.sun.mail.smtp.SMTPSendFailedException: 553 Mail from must equal authorized user ; nested exception is: com.sun.mail.smtp.SMTPSenderFailedException: 553 Mail from must equal authorized user at com.sun.mail.smtp.SMTPTransport.issueSendCommand(S80) at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1097)
#上面的报错是因为好少了一个管理员邮箱,那里没有填写,当然填写的邮箱用户跟上图中的是一致的:
job配置邮件发送:
构建后操作==》E-mail Notification
找个job发布测试一下:
看眼邮件的大概内容(jenkins地址以及发布信息):
See <http://192.168.1.104:8080/jenkins/job/admobadapter----Permission-sandbox-java-single/13/display/redirect> ------------------------------------------ Started by user admin Building remotely on jdk_salve_115 in workspace <http://192.168.1.104:8080/jenkins/job/admobadapter----Permission-sandbox-java-single/ws/> > /usr/bin/git rev-parse --is-inside-work-tree # timeout=10 Fetching changes from the remote Git repository > /usr/bin/git config remote.origin.url https://github.com/clockbyte/admobadapter.git # timeout=10 Fetching upstream changes from https://github.com/clockbyte/admobadapter.git > /usr/bin/git --version # timeout=10 > /usr/bin/git fetch --tags --progress https://github.com/clockbyte/admobadapter.git +refs/heads/*:refs/remotes/origin/*
2.6 Jenkins使用email-ext替换Jenkins的默认邮件通知
#先要安装插件:Email Extension
#还有管理员邮箱还是要继续设置的不然依旧是不能发送邮件的。
全局配置:
#上面要注意的是收件人都是可以写多个收件人的就是逗号隔开就行
Default Content(这里就是一个HTML页面了,下面为具体内容):
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志</title> </head> <body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0"> <table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif"> <tr> <td>(本邮件由程序自动下发,无需回复!)</td> </tr> <tr> <tr> <td> <h2><font color="#FF0000">构建结果 - ${BUILD_STATUS}</font></h2> </td> </tr> <tr> <td><br /> <b><font color="#0B610B">构建信息</font></b> <hr size="2" width="100%" align="center" /> </td> </tr> <tr><a href="${PROJECT_URL}">${PROJECT_URL}</a> <td> <ul> <li>项目名称:${PROJECT_NAME}</li> <li>GIT路径:<a href="${GIT_URL}">${GIT_URL}</a></li> <li>构建编号:${BUILD_NUMBER}</li> <li>触发原因:${CAUSE}</li> <li>构建日志:<a href="${BUILD_URL}console">${BUILD_URL}console</a></li> </ul> </td> </tr> <tr> <td> <b><font color="#0B610B">变更信息:</font></b> <hr size="2" width="100%" align="center" /> </td> </tr> <tr> <td> <ul> <li>上次构建成功后变化 : ${CHANGES_SINCE_LAST_SUCCESS}</a></li> </ul> </td> </tr> <tr> <td> <ul> <li>上次构建不稳定后变化 : ${CHANGES_SINCE_LAST_UNSTABLE}</a></li> </ul> </td> </tr> <tr> <td> <ul> <li>历史变更记录 : <a href="${PROJECT_URL}changes">${PROJECT_URL}changes</a></li> </ul> </td> </tr> <tr> <td> <ul> <li>变更集:${JELLY_SCRIPT,template="html"}</a></li> </ul> </td> </tr> <hr size="2" width="100%" align="center" /> </table> </body> </html>
博文来自:www.51niux.com
单个job里面的配置:
增加构建后操作步骤=》Editable Email Notification
#在弹出的界面大部分保持默认就可以
#注意,这里设置的收件人冰不会替换job点开Editable Email Notification首先出现的那个Project Recipient List那里的值,而是累加的关系,所以如果你想实现完全的成功发送一波人失败发送一波人的话,Project Recipient List这里就要继续引用变量而不是单独设置一个具体的收件人邮箱。这样通过高级里面这个收件人列表就可以实现发布成功发送给谁,发布失败发送给谁。
查看一下:
#上面是邮件构建失败的部门邮件内容,如果构建成功最下面就不是打印构建的错误信息而是打印打印的jar包之类的。
三、Jenkins使用钉钉发通知
#现在大家办公使用钉钉的很多,钉钉机器人是个很好的东西.
#首先要创建一个钉钉群,然后就可以创建钉钉机器人了,这样在钉钉群里面的人就可以收到钉钉通知了。创建自定义的机器人,然后就会或者一个群机器人的WebHook地址。
#安装dingding插件那种方式就不介绍了,当选择不管是成功还是错误都会触发钉钉通知的时候,因为最终调用钉钉是OK的,结果导致发布结果显示是成功的,这样不太好容易造成困惑。
首先编写一个python脚本:
#vim /data/jenkins_workspace/dingding.py
#!/usr/bin/python #-*-coding:utf-8-*- import urllib2 import json import sys import commands BUILD_USER_ID = sys.argv[1] online_ip = sys.argv[2] JOB_NAME = sys.argv[3] BUILD_STATUS = sys.argv[4] msgdata='''上线通知: 上线发布用户: %s 上线发布机器: %s 上线发布job: %s 上线发布状态: %s'''%(BUILD_USER_ID,online_ip,JOB_NAME,BUILD_STATUS) def dingding(msgdata):
url = "https://oapi.dingtalk.com/robot/send?access_token=d06fc58838f5125aa76205a09515c73190e942b26ca3f2ddf92d72b27a6bf36b" #这是机器人的hook
data = { "msgtype":"text", "text":{"content": msgdata}, "at":{ "atMobiles":[ 1234567890, #这是手机号,可以写多个,钉钉不是有@人吗,这里就是@谁 ], "isAtAll":"False" } } json_data=json.dumps(data) req=urllib2.Request(url,json_data) req.add_header('Content-type','application/json') response=urllib2.urlopen(req) if __name__ == '__main__': if msgdata: dingding("%s" % msgdata )
job那里调用python脚本:
#这个调用写的比较简单,为什么要加if判断呢,因为之前没有加调用python脚本的时候编译错了就会显示错误,但是你加了这个调用python脚本,最后一步操作确实调用成功的就会导致jenkins的job显示是成功的,这样又造成了错误的信息产出,所以就加个判断。
构建测试一下:
#这样只要你处在一个上线钉钉群里面,当有上线的时候你也会知道,当然这里写的比较简单,但是意思已经达到了,你可以根据自己的需求去改进他。
#下面是通过curl传参的方式:
content=" # 上线发布: \n\n 部署用户: ${BUILD_USER} \n\n 部署项目: $JOB_NAME \n\n Jenkins构建地址:${BUILD_URL} \n\n" body1='{"msgtype":"markdown","markdown":{"title":"JAVA部署","text":"' body2="${content}" body3='"}, "at":{"atMobiles":[], "isAtAll":false}}' curl --header "Content-Type: application/json" \ --request POST \ --data "$body1$body2$body3" \ https://oapi.dingtalk.com/robot/send?access_token=d06fc58838f5125aa76205a09515c73190e942b26ca3f2ddf92d72b27a6bf36b
#另外还可以通过自建webhook API得方式,通过执行shell哪里传参来进行通知:
curl -S -m 5 -X POST --header "Content-Type:application/x-www-form-urlencoded" \ --data "git_pro=${git_pro}&version=${tags}&online_ip=${online_ip}&build_url=${BUILD_URL}&ding_token=d06fc58838f5125aa76205a09515c73190e942b26ca3f2ddf92d72b27a6bf36b" \ http://test.51niux.com/jenkins_hook || fail="fail"
#另外再说一嘴,钉钉给出的那个text的例子有点不能用,这里写了个能用的,不过text基本都是静态的东西,如果只是单纯的一个curl的话可以搞成定时提醒任务来用:
curl -H "Content-type: application/json" -X POST -d '{"msgtype": "text","text": {"content": "小伙伴该看一波报警了"}, "at": {"atMobiles": ["0123456789"], "isAtAll": false}}' https://oapi.dingtalk.com/robot/send?access_token=d06fc58838f5125aa76205a09515c73190e942b26ca3f2ddf92d72b27a6bf36b
#这样可以做一个定时提醒任务定时的在群里@人提醒他去干啥,或者当我们做了什么事的时候也可以钉钉群里通知一下。