jenkins介绍
jenkins官网:https://www.jenkins.io
摘自百度百科的介绍:"Jenkins是一个开源软件,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件项目可以进行持续集成。"
简单说就是一个Java Web程序,一个开放的软件平台,可根据不同需求选择不同插件来实现不同的功能,有点类似eclipse,也是一个开放的软件平台,通过集成插件的形式实现不同的功能。
java程序发布步骤
一般情况下,java程序手动发布,要经过Git拉取代码、本地maven或gradle编译打包、发送程序包到服务器指定位置、远程服务器执行命令启动并备份这几步,这些步骤其实都已经可以直接通过shell脚本来自动化搞定,只是在jenkins上可以用界面更好的呈现步骤和统一管理这些功能,而jenkins是通过集成插件来工作的,每一步对应的步骤几乎都有特定的插件来完成,下面列一下用到的插件。
1.Git拉取代码:Git Parameter、Git
2.maven编译打包:本地mvn命令(需安装mvn和jdk)
3.发送程序包到服务器指定位置: Publish Over SSH(注:已被弃用)
4.远程服务器执行命令启动并备份:SSH
需要注意的是,Publish Over SSH在本周刚刚宣布被弃用,或许全网还没有人说过这个事情,在jenkins的可选插件仓库中的确已经搜索不到这个插件了,所以今天的记录步骤会以expect和scp命令shell脚本的方式实现文件发送到服务器的功能
实现方法
jenkins实现这个流程可以有两种方法:
1.FreeStyle Project:通过插件提供的图形界面直接配置,是最直观的实现方法,但是要是换jenkins需要重新配置,比较麻烦。
2.Pipeline:管道流水线的方法,步骤比较直观,支持Pipeline Script脚本配置,可以把脚本集成在项目里放在git上,这样不管是在哪个jenkins上都可以很容易的生成构建任务。
安装Jenkins
清晰了上面的这些基本概念,就可以安装jenkins了, 下面以Ubuntu为例:
安装jenkins是比较简单的,毕竟是个Java Web程序,和大多启动java程序的步骤类似。
1.安装JDK8或JDK11
sudo apt install openjdk-11-jdk
java -version
jdk默认安装路径为/lib/jvm/java-11-openjdk-amd64/
2.安装Maven
sudo apt install maven
mvn -v
maven默认安装路径为/usr/share/maven
修改conf文件夹中的settings.xml,指定本地maven仓库和远程阿里库:
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<!--
| This is the configuration file for Maven. It can be specified at two levels:
|
| 1. User Level. This settings.xml file provides configuration for a single user,
| and is normally provided in ${user.home}/.m2/settings.xml.
|
| NOTE: This location can be overridden with the CLI option:
|
| -s /path/to/user/settings.xml
|
| 2. Global Level. This settings.xml file provides configuration for all Maven
| users on a machine (assuming they're all using the same Maven
| installation). It's normally provided in
| ${maven.conf}/settings.xml.
|
| NOTE: This location can be overridden with the CLI option:
|
| -gs /path/to/global/settings.xml
|
| The sections in this sample file are intended to give you a running start at
| getting the most out of your Maven installation. Where appropriate, the default
| values (values used when the setting is not specified) are provided.
|
|-->
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<!-- localRepository
| The path to the local repository maven will use to store artifacts.
|
| Default: ${user.home}/.m2/repository
<localRepository>/path/to/local/repo</localRepository>
-->
<localRepository>/home/zhaohy/.m2/repository</localRepository>
<!-- interactiveMode
| This will determine whether maven prompts you when it needs input. If set to false,
| maven will use a sensible default value, perhaps based on some other setting, for
| the parameter in question.
|
| Default: true
<interactiveMode>true</interactiveMode>
-->
<!-- offline
| Determines whether maven should attempt to connect to the network when executing a build.
| This will have an effect on artifact downloads, artifact deployment, and others.
|
| Default: false
<offline>false</offline>
-->
<!-- pluginGroups
| This is a list of additional group identifiers that will be searched when resolving plugins by their prefix, i.e.
| when invoking a command line like "mvn prefix:goal". Maven will automatically add the group identifiers
| "org.apache.maven.plugins" and "org.codehaus.mojo" if these are not already contained in the list.
|-->
<pluginGroups>
<!-- pluginGroup
| Specifies a further group identifier to use for plugin lookup.
<pluginGroup>com.your.plugins</pluginGroup>
-->
</pluginGroups>
<!-- proxies
| This is a list of proxies which can be used on this machine to connect to the network.
| Unless otherwise specified (by system property or command-line switch), the first proxy
| specification in this list marked as active will be used.
|-->
<proxies>
<!-- proxy
| Specification for one proxy, to be used in connecting to the network.
|
<proxy>
<id>optional</id>
<active>true</active>
<protocol>http</protocol>
<username>proxyuser</username>
<password>proxypass</password>
<host>proxy.host.net</host>
<port>80</port>
<nonProxyHosts>local.net|some.host.com</nonProxyHosts>
</proxy>
-->
</proxies>
<!-- servers
| This is a list of authentication profiles, keyed by the server-id used within the system.
| Authentication profiles can be used whenever maven must make a connection to a remote server.
|-->
<servers>
<!-- server
| Specifies the authentication information to use when connecting to a particular server, identified by
| a unique name within the system (referred to by the 'id' attribute below).
|
| NOTE: You should either specify username/password OR privateKey/passphrase, since these pairings are
| used together.
|
<server>
<id>deploymentRepo</id>
<username>repouser</username>
<password>repopwd</password>
</server>
-->
<!-- Another sample, using keys to authenticate.
<server>
<id>siteServer</id>
<privateKey>/path/to/private/key</privateKey>
<passphrase>optional; leave empty if not used.</passphrase>
</server>
-->
</servers>
<!-- mirrors
| This is a list of mirrors to be used in downloading artifacts from remote repositories.
|
| It works like this: a POM may declare a repository to use in resolving certain artifacts.
| However, this repository may have problems with heavy traffic at times, so people have mirrored
| it to several places.
|
| That repository definition will have a unique id, so we can create a mirror reference for that
| repository, to be used as an alternate download site. The mirror site will be the preferred
| server for that repository.
|-->
<mirrors>
<!-- mirror
| Specifies a repository mirror site to use instead of a given repository. The repository that
| this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used
| for inheritance and direct lookup purposes, and must be unique across the set of mirrors.
|
<mirror>
<id>mirrorId</id>
<mirrorOf>repositoryId</mirrorOf>
<name>Human Readable Name for this Mirror.</name>
<url>http://my.repository.com/repo/path</url>
</mirror>
-->
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>;
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
<!-- profiles
| This is a list of profiles which can be activated in a variety of ways, and which can modify
| the build process. Profiles provided in the settings.xml are intended to provide local machine-
| specific paths and repository locations which allow the build to work in the local environment.
|
| For example, if you have an integration testing plugin - like cactus - that needs to know where
| your Tomcat instance is installed, you can provide a variable here such that the variable is
| dereferenced during the build process to configure the cactus plugin.
|
| As noted above, profiles can be activated in a variety of ways. One way - the activeProfiles
| section of this document (settings.xml) - will be discussed later. Another way essentially
| relies on the detection of a system property, either matching a particular value for the property,
| or merely testing its existence. Profiles can also be activated by JDK version prefix, where a
| value of '1.4' might activate a profile when the build is executed on a JDK version of '1.4.2_07'.
| Finally, the list of active profiles can be specified directly from the command line.
|
| NOTE: For profiles defined in the settings.xml, you are restricted to specifying only artifact
| repositories, plugin repositories, and free-form properties to be used as configuration
| variables for plugins in the POM.
|
|-->
<profiles>
<!-- profile
| Specifies a set of introductions to the build process, to be activated using one or more of the
| mechanisms described above. For inheritance purposes, and to activate profiles via <activatedProfiles/>
| or the command line, profiles have to have an ID that is unique.
|
| An encouraged best practice for profile identification is to use a consistent naming convention
| for profiles, such as 'env-dev', 'env-test', 'env-production', 'user-jdcasey', 'user-brett', etc.
| This will make it more intuitive to understand what the set of introduced profiles is attempting
| to accomplish, particularly when you only have a list of profile id's for debug.
|
| This profile example uses the JDK version to trigger activation, and provides a JDK-specific repo.
<profile>
<id>jdk-1.4</id>
<activation>
<jdk>1.4</jdk>
</activation>
<repositories>
<repository>
<id>jdk14</id>
<name>Repository for JDK 1.4 builds</name>
<url>http://www.myhost.com/maven/jdk14</url>
<layout>default</layout>
<snapshotPolicy>always</snapshotPolicy>
</repository>
</repositories>
</profile>
-->
<!--
| Here is another profile, activated by the system property 'target-env' with a value of 'dev',
| which provides a specific path to the Tomcat instance. To use this, your plugin configuration
| might hypothetically look like:
|
| ...
| <plugin>
| <groupId>org.myco.myplugins</groupId>
| <artifactId>myplugin</artifactId>
|
| <configuration>
| <tomcatLocation>${tomcatPath}</tomcatLocation>
| </configuration>
| </plugin>
| ...
|
| NOTE: If you just wanted to inject this configuration whenever someone set 'target-env' to
| anything, you could just leave off the <value/> inside the activation-property.
|
<profile>
<id>env-dev</id>
<activation>
<property>
<name>target-env</name>
<value>dev</value>
</property>
</activation>
<properties>
<tomcatPath>/path/to/tomcat/instance</tomcatPath>
</properties>
</profile>
-->
</profiles>
<!-- activeProfiles
| List of profiles that are active for all builds.
|
<activeProfiles>
<activeProfile>alwaysActiveProfile</activeProfile>
<activeProfile>anotherAlwaysActiveProfile</activeProfile>
</activeProfiles>
-->
</settings>
3.去官网下载jenkins.war程序包
在家目录创建jenkins文件夹,将下载好的jenkins.war放到这个文件夹中执行如下启动命令:
nohup java -jar jenkins.war --ajp13Port=-1 --httpPort=8085 >/dev/null 2>&1 &
jenkins默认启动端口是8080,因为我本地的8080端口已经被其他程序占用了,所以自定义为了8085,这里可以根据自己想要运行的端口自定义。
启动之后,jenkins会初始化一个密码,密码默认存储在/home/zhaohy/.jenkins/secrets/initialAdminPassword
访问8085端口,把这个密码复制填进去
点击安装推荐的插件
等待安装推荐的插件安装完毕
创建管理员账户
实例配置保存
如此,jenkins就安装成功并初始化完成了。
准备工作
进来jenkins之后点击Manage Jenkins->Manage Plugins去下载所需插件,把上面提到的插件全部安装。
插件安装完毕后,点击Manage Jenkins->Global Tool Configuration,把jdk和maven的路径配置好。
点击Manage Jenkins->Configure System 把要部署的远程服务器信息填好
其他保持默认就可以,点击保存。
上面打算把java应用部署到192.168.0.115这台机上的/home/zhaohy/tmp这个文件夹下,把备份java程序放到/home/zhaohy/tmp/backup文件夹下,没有的话新建一下。
到这里jenkins上的准备配置工作就已完成。
考虑到这次没有用Publish Over SSH插件,所以要在安装jenkins机器上,安装expect,用shell脚本实现把文件发送到服务器,后面Pipeline的时候还会用到远程ssh的脚本,这里一并新建了:
安装expect
sudo apt install tcl
sudo apt install expect
新建一个test文件夹 新建expect_scp脚本文件
#!/usr/bin/expect
set timeout 10
set host [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
set src_file [lindex $argv 3]
set dest_file [lindex $argv 4]
spawn scp $src_file $username@$host:$dest_file
expect {
"(yes/no)?"
{
send "yes\n"
expect "*assword:" { send "$password\n"}
}
"*assword:"
{
send "$password\n"
}
}
expect "100%"
expect eof
给此文件执行权限
chmod +x expect_scp
如上所示,通过脚本依次传入主机 用户名 密码 源文件路径 目标文件路径就可以自动填充密码通过scp命令把文件发送到目标服务器路径
新建expect_ssh脚本文件
#!/usr/bin/expect
set timeout 10
set host [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
set shellinfo [lindex $argv 3]
spawn ssh $username@$host
expect {
"(yes/no)?"
{
send "yes\n"
expect "*assword:" { send "$password\n"}
}
"*assword:"
{
send "$password\n"
}
}
expect "*]#"
send "$shellinfo\r"
expect "*]#"
send "exit\r"
expect eof
给expect_ssh执行权限
chmod +x expect_ssh
如上所示,脚本中依次传入主机 用户名 密码 远程命令 就可以在远程服务器上执行命令了
至此,全部准备工作完成。
FreeStyle project任务自动化部署
用图形化页面先建一个自由风格项目实现一下
比如在Gitee上已经有我一个java源码,项目目录结构如下:
比如我要部署springbootSSM-imageSave这个项目的话:
jenkins上点击新建Item,输入一个任务名称,这里和gitee上的项目名称保持一致了,可以随便命名:springbootSSM-imageSave,之后选择Freestyle project点击确定
这里会有General、源码管理、构建触发器、构建环境、构建、构建后操作等步骤
General
这里有一个This project is parameterized选项,这个选项可以构建key value参数,在这里添加的参数在后续步骤中都可以用$参数名的方式直接引用参数的值,是个自己声明的变量,有boolean、string、text、password等常用类型的参数,这里主要用到Git Parameter插件带的Git Parameter、string和password这几种类型的参数。
点击This project is parameterized添加几个参数,可根据自身需要灵活声明:
Git Parameter类型的branch,默认值是master
string类型的jarName, 默认值是springbootSSM-imageSave-0.0.1-SNAPSHOT.jar(项目pom.xml里打包出来的默认名称)
string类型的projectName,默认值是springbootSSM-imageSave(git路径下的项目名称)
string类型的expectScpPath,默认值是/home/zhaohy/test/expect_scp(上面新建的expect_scp可运行脚本地址)
string类型的scpHost,默认值是192.168.0.115(scp发送文件的服务器地址)
string类型的scpUserName,默认值是zhaohy(scp发送服务器的用户名)
password类型的scpPwd,默认值是远程服务器的密码(scp发送服务器的密码)
string类型的scpTargetPath,默认值是/home/zhaohy/tmp(scp远程服务器目标路径地址)
源码管理
配置好自己的git仓库以及分支信息,构建时会从远程自动拉取代码到/home/zhaohy/workspace/任务名
文件夹下
构建触发器
这里可以什么都不选,手动点击构建就可以了,可以配置定时构建(Poll SCM)或其他触发构建
构建环境
这里可以配置远程服务器构建之前或之后的操作,这里把构建完成之后对服务器jar包的备份和重启shell命令配置在这里
cd $scpTargetPath
cp $jarName backup/$projectName_$(date +%F-%T).jar
pid=$(ps -ef|grep $jarName |egrep -v grep|awk '{print $2}')
kill -9 $pid
nohup java -jar $jarName >/dev/null 2>&1 &
构建
把maven打包命令和打包完毕之后把包发布到服务器的shell命令配置在这
pwd
cd $projectName/
mvn clean package -Dmaven.test.skip=true
$expectScpPath $scpHost $scpUserName $scpPwd target/*.jar $scpTargetPath
构建后操作
构建完成后删除workspace下本任务下载的代码文件,视需要而定,也可以不删除
这样就配置完成了,保存后点击Build with Parameters构建看看效果,第一次构建应该会比较久,maven要从仓库下载jar包打包等操作比较费时间,点击左下角的任务可以进入控制台输出日志页面。
如此,程序自动部署成功。
Pipeline
pipeline的方法就比较简单了,不需要一个个去点,直接用脚本写好就可以。
jenkins上点击新建Item,选择Pipeline ,任务名:pipeline-springbootSSM-imageSave
点击确定之后直接点击最后一部流水线,这里有两种方法,一种是直接在里面写脚本,一种是Pipeline script from SCM可以把脚本上传到git上的项目目录下,跟着项目走,推荐第二种,这样就不怕换jenkins了。
选择Pipeline script from SCM,配置好git仓库链接,选择好分支,填好脚本路径,这里为:springbootSSM-imageSave/Jenkinsfile
去git仓库springbootSSM-imageSave目录下新建Jenkinsfile,写入以下脚本:
pipeline {
agent any
parameters{
//choice(choices:"master", description:"请填写git分支",name:"branch")
string(defaultValue:"springbootSSM-imageSave-0.0.1-SNAPSHOT.jar", description:"jar包名称",name:"jarName")
string(defaultValue:"springbootSSM-imageSave", description:"项目名称",name:"projectName")
string(defaultValue:"/home/zhaohy/test/expect_scp", description:"expect_scp可运行脚本地址",name:"expectScpPath")
string(defaultValue:"/home/zhaohy/test/expect_ssh", description:"expect_ssh可运行脚本地址",name:"expectSsh")
string(defaultValue:"192.168.0.115", description:"scp发送文件的服务器地址",name:"scpHost")
string(defaultValue:"zhaohy", description:"scp发送服务器的用户名",name:"scpUserName")
password(defaultValue:"password", description:"scp发送服务器的密码",name:"scpPwd")
string(defaultValue:"/home/zhaohy/tmp/", description:"scp发送服务器的目标路径",name:"scpTargetPath")
}
stages() {
stage('下载') {
steps {
// Get some code from a GitHub repository
git branch:'master', credentialsId: 'xxx', url: 'https://gitee.com/xxx.git'
}
// post {
// If Maven was able to run the tests, even if some of the test
// failed, record the test results and archive the jar file.
//success {
// junit '**/target/surefire-reports/TEST-*.xml'
//archiveArtifacts 'target/*.jar'
// }
// }
}
stage('打包') {
steps {
sh "pwd"
sh "cd ${params.projectName}/ && mvn clean package -Dmaven.test.skip=true"
}
}
stage('部署') {
steps {
sh "pwd"
sh "cd ${params.projectName}/ && ${params.expectScpPath} ${params.scpHost} ${params.scpUserName} ${params.scpPwd} target/*.jar ${params.scpTargetPath}"
}
}
stage('备份') {
steps {
sh "${params.expectSsh} ${params.scpHost} ${params.scpUserName} ${params.scpPwd} \'cd ${params.scpTargetPath};cp ${params.jarName} backup/${params.projectName}_\$(date +%F-%T).jar\'"
}
}
stage('重启') {
steps {
sh "${params.expectSsh} ${params.scpHost} ${params.scpUserName} ${params.scpPwd} \'cd ${params.scpTargetPath};pid=\$(ps -ef|grep $jarName |egrep -v grep|awk '{print \$2}');kill -9 \$pid;nohup java -jar ${params.jarName} >/dev/null 2>&1 &\'"
sh "rm -rf *"
}
}
}
}
上面可以看到上面除了“下载”这一步其他都是shell脚本的形式,那么Pipeline script怎么写呢,保存刚刚jenkins上创建的pipeline任务之后可以看到左边有个流水线语法的按钮:
点击这个按钮就可以进入流水线语法片段生成器,下拉选择git,填好仓库地址和分支信息点击生成流水线脚本就可以生成那个git的语法脚本了,下拉选择sh:shell script就可以把shell脚本转换成流水线脚本了,其他的stage结构是固定的。
可以看到上面脚本中的参数也是用脚本的形式声明的具体可参考博文:jenkins pipeline的parameters参数介绍
脚本里声明好的参数可以用${params.参数名}
的方法直接引用,注意,shell脚本中的$
要加\
转义才能识别。
如此,直接点击构建也是可以成功的,而且流水线的方法会比较直观美观。
至此,完结撒花~