持续集成及Jenkins介绍
软件开发生命周期叫SDLC(Software Development Life Cycle),集合了计划、开发、测试、部署过程。
在平常的开发过程中, 需要频繁地(一天多次)将代码集成到主干,这个叫持续集成(Continuous Integration,简称CI),有个持续集成,还有持续部署(Continuous Deployment)。
在没有Jenkins的时候,需要本地打包,或者本地编译,然后把改动过的文件上传到服务器,然后重启服务,这样是比较麻烦的,特别是这次的修改文件比较多的时候,有了Jenkins,只需要点一下,就可以实现这个功能了。
持续集成的好处:
- 降低风险,由于持续集成不断去构建,编译和测试,可以很早期发现问题,所以修复的代价就少
- 对系统健康持续检查,减少发布风险带来的问题
- 减少重复性工作
- 持续部署,提供可部署单元包
- 持续交付可供使用的版本
- 增强团队信心
Jenkins介绍
Jenkins是一款流行的开源持续集成(Continuous Integration)工具,广泛用于项目开发,具有自动化构建测试和部署等功能。中文官网:https://www.jenkins.io/zh/。
Jenkins的特征:
- 开源的Java语言开发持续集成工具,支持持续集成,持续部署
- 易于安装部署配置:可通过yum安装,或下载war包以及通过docker容器等快速实现安装部署,可方便web界面配置管理
- 消息通知及测试报告:集成RSS/E-mail通过RSS发布构建结果,或当构建完成时通过E-mail通知,生成JUnit/TestNG测试报告
- 分布式构建:支持Jenkins能够让多台计算机一起构建/测试
- 文件识别:Jenkins能够跟踪哪次构建生成哪些jar,哪次构建使用哪个版本的jar等
- 丰富的插件支持:支持扩展插件,你可以开发适合自己团队使用的工具,如git,svn,maven,docker等
Jenkins安装和持续集成环境配置
流程:
- 开发人员每天进行代码提交,提交到Git仓库
- Jenkins作为持续集成工具,使用Git工具到Git仓库拉取代码到集成服务器,再配合JDK,Maven等软件完成代码编译,代码测试与审查,测试,打包等工作,在这个过程中每一步出错,都重新再执行一次整个流程
- Jenkins把生成的jar或war包分发到测试服务器或者生产服务器,测试人员或用户就可以访问应用
这里不得不说到一个东西:GitLab。
GitLab是一个用于仓库管理系统的开源项目,使用Git作为代码管理工具,并在此基础上搭建起来的web服务。
GitLab和GitHub一样属于第三方基于Git开发的作品,免费且开源(基于MIT协议),与Github类似,可以注册用户,任意提交你的代码,添加SSHKey等等。不同的是,GitLab是可以部署到自己的服务器上,数据库等一切信息都掌握在自己手上,适合团队内部协作开发,简单来说可把GitLab看作个人版的GitHub。
# 安装相关依赖
[root@localhost ~]# yum -y install policycoreutils openssh-server openssh-clients postfix
# 启动ssh服务,设置开机启动
[root@localhost ~]# systemctl enable sshd && sudo systemctl start sshd
# 设置postfix开机自启,并启动postfix支持gitlab发信功能
[root@localhost ~]# systemctl enable postfix && systemctl start postfix
# 关闭防火墙
[root@localhost ~]# systemctl disable firewalld
# 在线下载安装包
[root@localhost ~]# wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el6/gitlab-ce-12.4.2-ce.0.el6.x86_64.rpm --no-check-certificate
# 安装GitLab
[root@localhost ~]# rpm -i gitlab-ce-12.4.2-ce.0.el6.x86_64.rpm
# 提示错误,说是缺少依赖,那我们安装依赖,如果没有报错,就不需要执行
[root@localhost ~]# yum -y install policycoreutils-python
# 再次执行安装命令,稍微等一会儿,就会提示安装成功了,在它给出的信息里,告诉我们,要去设置external_url并启动服务才能访问
[root@localhost ~]# rpm -i gitlab-ce-12.4.2-ce.0.el6.x86_64.rpm
# 修改域名和端口
[root@localhost ~]# vim /etc/gitlab/gitlab.rb
# 重载配置启动GitLab,reconfigure操作需要等待5分钟左右
[root@localhost ~]# gitlab-ctl reconfigure
[root@localhost ~]# gitlab-ctl restart
修改后的gitlab.rb文件
external_url 'http://192.168.216.123:8081'
nginx['listen_port'] = 8081
等GitLab启动起来,浏览器访问http://192.168.216.123:8081/即可看到我们的GitLab服务了。
GitLab添加组、创建用户、创建项目
创建组
点击Create a group就可以创建一个组,这里我取名叫test1_group,再点击Create group按钮。
创建项目
进入刚刚创建的test1_group,再点击New project创建项目,取名test1_project,最后点击Create project按钮创建项目。
创建用户
点击最上面的Admin Area,再点击Users,就进到了用户界面,这里可以添加用户,输入必要信息,其他保持默认即可,点击创建即可。在新创建用户界面右上角有个Edit,可以设置用户的密码。
将新建用户添加到组里
打开刚刚创建的test1_group,点击左侧的Members,在搜索框里搜索刚刚创建的用户,后面有个权限设置,这里无关紧要,后面会说,随便选一个,点击Add to group即可将用户加到组里。
- Guest:可以创建issue、发表评论,不能读写版本库
- Reporter:可以克隆代码,不能提交,QA、PM可以赋予这个权限
- Developer:可以克隆代码、开发、提交、push,普通开发可以赋予这个权限
- Maintainer:可以创建项目、添加tag、保护分支、添加项目成员、编辑项目,核心开发可以赋予这个权限
- Owner:可以设置项目访问权限 - Visibility Level、删除项目、迁移项目、管理组成员,开发组组长可以赋予这个权限
源码上传到GitLab仓库
- 首先在本地IDEA里创建一个Web项目,只有一个index.html页面
- 开启版本控制:点击VCS-Enable Version Control Integration,选择Git
- 在项目上右键,选择Git-Add,将代码提交到缓存区
- 点击Commit,将代码提交到本地仓库
- 此时会提示定义一个远程仓库,我们点击Define remote
- 来到GitLab,找到之前创建的test1_project,点击右上角Clone,选择Clone with SSH的地址,粘贴到IDEA中,最后点击Push
- 来到GitLab,打开test1_project项目就可以看到刚刚提交的文件了
# 解压jdk-8u231-linux-x64.tar.gz到/opt/module/下
[root@localhost ~]# tar -xzvf /opt/software/jdk-8u231-linux-x64.tar.gz -C /opt/module/
# 设置环境变量
[root@localhost ~]# vim /etc/profile
# 添加如下内容到最后
# JAVA_HOME
export JAVA_HOME=/opt/module/jdk1.8.0_231
export PATH=$PATH:$JAVA_HOME/bin
# 刷新环境变量
[root@localhost /]# source /etc/profile
# 测试Java环境
[root@localhost /]# java -version
# 下载Jenkins,可能会很慢,我用其他工具下载下来后,再上传到服务器上的
[root@localhost software]# wget http://archives.jenkins-ci.org/redhat-stable/jenkins-2.190.3-1.1.noarch.rpm
# 安装Jenkins
[root@localhost software]# rpm -ivh jenkins-2.190.3-1.1.noarch.rpm
# 修改Jenkins配置,将JENKINS_USER改成root,JENKINS_PORT改成8083(8082端口被GitLab的一个服务占用了)
[root@localhost software]# vim /etc/sysconfig/jenkins
# 告诉jenkins服务,Java服务的位置,将“/opt/module/jdk1.8.0_231/bin/java”加到candidates里面
[root@localhost software]# vim /etc/init.d/jenkins
# 重载jenkins启动配置
[root@localhost software]# systemctl daemon-reload
# 启动Jenkins
[root@localhost software]# systemctl start jenkins
浏览器访问http://192.168.216.123:8083/即可看到Jenkins的界面了,按照提示,需要解锁Jenkins,通过这个地址/var/lib/jenkins/secrets/initialAdminPassword获取密码,输入密码后,因为网络问题,安装插件会很耗时,这里跳过插件安装(选择“选择插件来安装”,然后选择“无”),后面根据需要再安装就行。
创建一个管理员账户,保存并完成,此时就可以看到熟悉的Jenkins界面了。
我们可以利用Role-based Authorization Strategy插件来管理Jenkins用户,如果是高版本的Jenkins,是不需要插件的,因为高版本直接集成进来了。
这里可以添加角色、用户、项目,然后就是给用户添加角色,给角色赋予权限,角色可以访问哪些项目。
Jenkins凭证管理
凭据可以用来存储需要密文保护的数据库密码、Gitlab密码信息、Docker私有仓库密码等,以便Jenkins可以和这些第三方的应用进行交互。
常用的凭证类型有:Username with password(用户密码)和SSH Username with private key(SSH 密钥)。
Maven安装和配置
在Jenkins服务器上,我们需要Maven来编译和打包项目,首先安装Maven。
[root@localhost ~]# cd /opt/software/
# 下载Maven
[root@localhost software]# wget https://dlcdn.apache.org/maven/maven-3/3.8.5/binaries/apache-maven-3.8.5-bin.tar.gz --no-check-certificate
# 解压Maven
[root@localhost software]# tar -zxvf apache-maven-3.8.5-bin.tar.gz -C /opt/module/
# 配置环境变量
[root@localhost software]# vim /etc/profile
# 使环境变量生效
[root@localhost software]# source /etc/profile
# 测试Maven是否安装成功
[root@localhost software]# mvn -v
在/etc/profile文件的最后加上如下内容:
# JAVA_HOME
export JAVA_HOME=/opt/module/jdk1.8.0_231
# MAVEN_HOME
export MAVEN_HOME=/opt/module/apache-maven-3.8.5
# 将JAVA_HOME和MAVEN_HOME加到PATH里
export PATH=$PATH:$JAVA_HOME/bin:$MAVEN_HOME/bin
让Jenkins关联JDK和Maven,Manage Jenkins-Global Tool Configuration-JDK-Add JDK,填入Name和JAVA_HOME的值,同理,在下面的Maven也是填入Name和MAVEN_HOME的值,取消勾选Install automatically,最后保存。
添加全局变量,Manage Jenkins-Config System-Global properties,添加3个全局变量。
Tomcat安装和配置
[root@localhost ~]# cd /opt/software/
# 下载Tomcat
[root@localhost software]# wget https://dlcdn.apache.org/tomcat/tomcat-8/v8.5.78/bin/apache-tomcat-8.5.78.tar.gz --no-check-certificate
# 解压缩
[root@localhost software]# tar -zxvf apache-tomcat-8.5.78.tar.gz -C /opt/module/
# 启动Tomcat
[root@localhost ~]# cd /opt/module/apache-tomcat-8.5.78/bin/
[root@localhost bin]# ./startup.sh
# 此时访问8080会发现访问不到,好像是其台服务把8080给占用了,进入conf/server.xml,将8080端口改成8085就可以访问了
配置Tomcat角色权限
默认情况下,Tomcat是没有配置用户角色权限的,点击这里,会提示403错误,但是后序Jenkins部署到Tomcat,需要用到Tomcat的用户,所以修改Tomcat的tomcat-users.xml配置文件,添加用户及权限,这里添加了一个用户名、密码是tomcat的用户,并赋予它一些角色。
<tomcat-users>
<role rolename="tomcat"/>
<role rolename="role1"/>
<role rolename="manager-script"/>
<role rolename="manager-gui"/>
<role rolename="manager-status"/>
<role rolename="admin-gui"/>
<role rolename="admin-script"/>
<user username="tomcat" password="tomcat" roles="manager-gui,manager-script,tomcat,admin-gui,admin-script"/>
</tomcat-users>
修改context.xml文件,注释掉Value这一行。
<!--
<Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
-->
重启Tomcat后,访问http://192.168.216.123:8085/manager/html,输入刚才添加的用户名和密码(这里都是tomcat),就可以看到管理界面了。
Jenkins构建Maven项目
Jenkins中自动构建类型有很多,常用的有以下三种:
- 自由风格软件项目(FreeStyle Project)
- Maven项目(Maven Project)
- 流水线项目(Pipeline Project)
自由风格项目构建
拉取代码
在Jenkins里,新建一个FreeStyle Project。Source Code Management选择从GitLab拉取代码,并指定Credentials。
编译打包
在Build里找到Add build step,选择“Execute shell”,并输入以下内容,Apply,Save。
echo "开始编译和打包"
mvn clean package
echo "编译和打包结束"
部署
需要安装Deploy to container插件,添加Tomcat用户凭证(用户名密码都是tomcat)。
再回到FreeStyle Project的配置里,找到Post-build Actions,选择“Deploy war/ear to a container”,填写Tomcat URL。
最后,试一下Build Now。
Maven项目构建
首先需要安装一个Maven Integration的插件,然后创建一个Maven项目。设置好Source Code Management,和FreeStyle Project不同的是,在Build里需要指定pom.xml并设置Maven命令,在Goals and options里输入clean package。在Post-build Actions里选择Deploy war/ear to a container,指定war和Context path,最后保存并开始构建,查看结果。
Pipeline流水线项目构建
Pipeline,简单来说,就是一套运行在Jenkins上的工作流框架,将原来独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂流程编排和可视化的工作。
Pipeline脚本是由Groovy语言实现的,但是我们没必要单独去学习Groovy
Pipeline支持两种语法:Declarative(声明式)和Scripted Pipeline(脚本式)语法
Pipeline也有两种创建方法:可以直接在Jenkins的Web UI界面中输入脚本;也可以通过创建一个Jenkinsfile脚本文件放入项目源码库中(一般我们都推荐在Jenkins中直接从源代码控制(SCM)中直接载入Jenkinsfile Pipeline这种方法)
Declarative声明式-Pipeline
pipeline {
agent any
stages {
stage('拉取代码') {
steps {
echo '拉取代码'
}
}
stage('编译构建') {
steps {
echo '编译构建'
}
}
stage('项目部署') {
steps {
echo '项目部署'
}
}
}
}
点击Build Now,就完成了一个Pipeline项目的构建,要想看到Pipeline视图,还需要安装一个Pipeline: Stage View的插件。
Scripted Pipeline脚本式-Pipeline
node {
def mvnHome
stage('Preparation') { // for display purposes
// Get some code from a GitHub repository
git 'https://github.com/jglick/simple-maven-project-with-tests.git'
// Get the Maven tool.
// ** NOTE: This 'M3' Maven tool must be configured
// ** in the global configuration.
mvnHome = tool 'M3'
}
stage('Build') {
// Run the maven build
withEnv(["MVN_HOME=$mvnHome"]) {
if (isUnix()) {
sh '"$MVN_HOME/bin/mvn" -Dmaven.test.failure.ignore clean package'
} else {
bat(/"%MVN_HOME%\bin\mvn" -Dmaven.test.failure.ignore clean package/)
}
}
}
stage('Results') {
junit '**/target/surefire-reports/TEST-*.xml'
archiveArtifacts 'target/*.jar'
}
}
node代表一个Jenkins结点,Master或者是Agent,是执行Step的具体运行环境,后续Jenkins的Master-Slave架构会用到。
Stage代表一个阶段,Pipeline执行可以划分为多个Stage,每个Stage代表一组操作,比如:Build、Test、Deploy,Stage是一个逻辑分组的概念。
Step代表一个步骤,是最基本的操作单元,可以是一个Shell脚本命令。
在Pipeline项目里,有一个Pipeline语法的超链接,点击过去,可以看到有一些常用的Steps,我们可以选择对应的模板,填充上需要的参数,这个Step就生成了,对于不会Pipeline语法的人来说,还是比较方便的。
拉取代码
编译打包
部署
将脚本复制出来。放到Pipeline脚本里的steps里,应用,保存。
最终的脚本如下:
pipeline {
agent any
stages {
stage('pull code') {
steps {
checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: 'e689f397-c855-4783-9600-b990ea39ab83', url: 'git@192.168.216.123:test1_group/test1_project.git']]])
}
}
stage('build projct') {
steps {
sh 'mvn clean package'
}
}
stage('deploy project') {
steps {
deploy adapters: [tomcat8(credentialsId: 'f6769281-ec81-4b0b-bf5e-8921f8e7e67a', path: '', url: 'http://192.168.216.123:8085/')], contextPath: null, war: 'target/*.war'
}
}
}
}
最后,应用,保存,Build Now进行测试。
Pipeline Script from SCM
在Jenkins里编写脚本,不方便维护,有一种方式款已解决这个问题,那就是把Pipeline Script放到项目里,用版本控制来跟踪记录。
在项目根目录新建一个Jenkinsfile文件,文件名是Jenkinsfile,将刚才的内容复制进来,提交代码后,来到Jenkins。
Apply,Save,Build Now,进行测试。
常见的构建触发器
Jenkins有4种构建触发器:
- 触发远程构建
- 其他工程构建后触发(Build after other projects are build)
- 定时构建(Build periodically)
- 轮询SCM(Poll SCM)
触发远程构建
打开一个项目的配置,找到Build Triggers,勾选“Trigger builds remotely”,并输入一个Authentication Token,也就是一个加密串,Apply,Save。
然后浏览器通过访问JENKINS_URL/job/pipeline01/build?token=TOKEN_NAME,注意把JENKINS_URL换成Jenkins的地址,把TOKEN_NAME换成刚才设置的加密串,把就可以发起一个Jenkins项目的构建。
其他工程构建后触发
找到Build Trigger,选择“Build after other projects are build”,输入其他项目的项目名,Apply,Save。
定时构建
定时构建,就需要我们配置一个表达式,Jenkins就会按照规则进行定时构建。找到Build Trigger,勾选“Build periodically”,这里以每分钟构建一次为例,Apply,Save。
轮询SCM
意思是Jenkins定时扫描代码仓库是否有变更,如果有变更,自动触发一次构建,但是这种方式对系统的开销比较大,不建议使用。
Git hook自动触发构建
前面介绍的轮询SCM方式,对系统开销比较大,不建议使用,其实还有其他的方案,可以在push代码后,自动触发构建操作,那就是利用GitLab的webhook来实现。
它们的区别是:
轮询SCM:Jenkins每隔多久去问GitLab:你那边有没有代码变动
webhook:GitLab代码变更后,通知Jenkins:你该触发构建了
首先需要安装:GitLab Hook插件。
找到Build Triggers,勾选新出现的这个选项,其他默认就可以,这里需要记住webhook URL,Apply,Save。
使用root登录GitLab,依次点击:Admin Area-Settings-Network,勾选“Allow requests to the local network from web hooks and services”,最后Save changes。
给项目添加webhook,依次点击项目-Settings-Integrations,输入刚才的webhook URL,最后Save changes。
再来到Jenkins里,Manage Jenkins-Configure System,取消“Enable authentication for ‘/project’ end-point”的勾选,Apply,Save。
来到刚才GitLab添加webhook的地方,找到Test,模拟发送一个Push事件,此时Jenkins里的项目就可以构建了,说明配置成功。
Jenkins的参数化构建
这里定义的参数,可以在Pipeline Script里使用,将'*/master'
换成'*/${branch}'
,这里的${branch}
就是动态的了。
配置邮箱服务器发送构建结果
安装Email Extension和Email Extension Template插件。设置邮箱相关参数:Manage Jenkins-Configure System。
设置发件人:
设置插件信息:
设置发件人的SMTP地址和密码等信息:
最下面有一个Test configuration,可以点一下试试,如果配置正确的话,可以成功发出邮件的。
对应Pipeline Script,这里用到的是post标签,关于post,可以在Pipeline Syntax-Declarative Directive Generator,选择“post: Post Stage or Build Conditions”,可以根据一些条件生成post语法。
在Pipeline Syntax-片段生成器,选择“emailext: Extended Email”,可以生成一些脚本。
pipeline {
agent any
stages {
stage('pull code') {
steps {
checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: 'e689f397-c855-4783-9600-b990ea39ab83', url: 'git@192.168.216.123:test1_group/test1_project.git']]])
}
}
stage('build projct') {
steps {
sh 'mvn clean package'
}
}
stage('deploy project') {
steps {
deploy adapters: [tomcat8(credentialsId: 'f6769281-ec81-4b0b-bf5e-8921f8e7e67a', path: '', url: 'http://192.168.216.123:8085/')], contextPath: null, war: 'target/*.war'
}
}
}
post {
always {
emailext(
subject: '构建通知:${PROJECT_NAME} - Build # ${BUILD_NUMBER} - ${BUILD_STATUS}!',
body: '${FILE,path="email.html"}',
to: '收件人的邮箱'
)
}
}
}