Jenkins Pipeline + Gogs 实现 Spring Boot 项目 CI/CD

[TOC]

前言

CI/CD是什么呢?在软件开发过程中存在这繁琐的重复的“构建”、“发布”、“测试”工作,把这些工作进行流程化自动化的实践过程则可称之为CI/CD。而这个过程种可能需要到Kuberneters、Docker in Docker 、Pipeline等丰富的工具链和知识储备,而团队中又没有Kuberneters实现自动部署?我们团队成员也没有太多时间学习掌握Pipeline呢?完成自动构建后又如何实现jar的自动上传和优雅地重启呢?面对这些问题该如何解决呢?而这里将通过 SSH 将应用部署到远程主机以满足非容器化的场景需求。通过Jenkinsfile以及scm模式实现项目零配置。最后通过sh脚本实现程序地发布和优雅地重启。

Jenkins 部署

环境准备

安装 Docker
配置 yum 源
## 配置默认源
## 备份
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup

## 下载阿里源
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

## 刷新
yum makecache fast



## 重建yum缓存
yum clean all
yum makecache fast
yum -y update
安装 Docker

下载docker的yum源文件

yum -y install yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

这里指定 Docker 版本,可以先查看支持的版本

[root@localhost ~]# yum list docker-ce --showduplicates |sort -r               
 * updates: mirrors.aliyun.com
Loading mirror speeds from cached hostfile
Loaded plugins: fastestmirror
 * extras: mirrors.aliyun.com
 * epel: hkg.mirror.rackspace.com
docker-ce.x86_64            3:19.03.2-3.el7                     docker-ce-stable
docker-ce.x86_64            3:19.03.1-3.el7                     docker-ce-stable
docker-ce.x86_64            3:19.03.0-3.el7                     docker-ce-stable
docker-ce.x86_64            3:18.09.9-3.el7                     docker-ce-stable
...
 * base: mirrors.aliyun.com

目前最新版本为19.03,指定下载18.09

yum install -y docker-ce-18.09.9-3.el7
systemctl enable docker
systemctl start docker
新建 Jenkins 专用用户组和用户
groupadd -g 1000 jenkins
useradd -u 1000 -g 1000 -s /sbin/nologin jenkins
安装 Maven 工具
## 下载 Maven
wget http://mirror.bit.edu.cn/apache/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz

## 解压至指定目录供后续调用
tar xzf apache-maven-3.6.3-bin.tar.gz /opt

安装 Jenkins

通过 Docker 安装 Jenkins
docker run -d --name jenkinsci --restart=always -p 1080:8080 -v /home/jenkins:/var/jenkins_home -v /opt/apache-maven-3.6.3:/opt/maven dockerhub.azk8s.cn/jenkins/jenkins:2.204.2
解锁 Jenkins

当您第一次访问新的Jenkins实例时,系统会要求您使用自动生成的密码对其进行解锁。

  1. 浏览到 http://localhost:1080(或安装时为Jenkins配置的任何端口),并等待 解锁 Jenkins 页面出现。
    avatar
  2. 从Jenkins控制台日志输出中,复制自动生成的字母数字密码(在两组星号之间


    avatar
安装插件

通过Jenkins中的Manage Jenkins > Manage Plugins 安装本案用到得插件 。

  • Timestamper
  • Pipeline
  • Pipeline: Stage View
  • Generic Webhook Trigger
  • Git
  • Email Extension
  • Mailer
  • SSH Pipeline Steps
  • Gogs

部署项目

环境准备

部署 Java 运行环境
## 安装 OpenJDK
yum install -y java-1.8.0-openjdk

## 配置 JAVA_HOME 环境变量

cat <<EOF >  /etc/profile
JAVA_HOME=/usr/lib/jvm/java
CLASSPATH=.:${JAVA_HOME}/lib/dt.jar:${JAVA_HOME}/lib/tools.jar
PATH=${PATH}:${JAVA_HOME}/bin:/usr/local/bin
export JAVA_HOME CLASSPATH PATH
EOF
source /etc/profile
配置 SSH 证书访问

生成 SSH 证书

ssh-keygen -t rsa -b 4096

修改服务器 ssh 配置文件

vi /etc/ssh/sshd_config

## 是否允许用户自行使用成对的密钥系统进行登入行为,仅针对 version 2。
## 至于自制的公钥数据就放置于用户家目录下的 .ssh/id_rsa.pub
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile %h/.ssh/id_rsa.pub

重启 SSH

systemctl restart sshd

验证 SSH 证书登录过

ssh -h localhost -i ~/.ssh/id_rsa -l root
为 Jenkins 配置 SSH 部署私钥
  1. 在凭据-->系统中找到“全局凭据”


    avatar
  2. 添加一个全局凭据,类型选择为“SSH Username with private key”


    avatar
  3. 将私钥粘贴到“private Key”中,并将ID设置为 root10.168.1.42 供后续使用,然后保存


    avatar

    avatar
Gogs 仓库准备
  1. 新建 Spring Boot 项目


    avatar
  2. 在项目中增加 Jenkinsfile 与 Launch 文件,并推送到Gogs仓库


    avatar
  3. 根据项目实际情况修改 Jenkinsfile 配置

    avatar

    Note:

  • token 必须根据不同项目设置为不同的值,供 Jenkins 识别不同项目
  • 如需对应用设置不同端口或profile,可通过 java_opts 配置
    • 将端口设置为 1999 则 java_opts = "-Dserver.port=1999"
    • 使用 dev profile 则 java_opts = "-Dspring.profiles.active=dev"

部署项目

  1. 新建一个任务


    avatar
  2. 输入自己任务的名称,然后选择流水线,流水线才是pipeline。


    avatar
  3. 确认之后管理界面,如果做过了解的应该知道,piepeline有两种方式,一种是直接写script另一种是引入SCM。这里使用第二种方式


    avatar
  4. 保存配置后进入项目界面,点击立即构建,稍等片刻待项目构建完成,并查看项目构建日志。


    avatar

    avatar
Started by user admin
Obtained Jenkinsfile from git http://10.168.1.21:3000/dtseaci/hello.git
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/demo
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Declarative: Checkout SCM)
[Pipeline] checkout
using credential d6b004da-0be9-4d1b-945a-8ccdc42ad6f4
Cloning the remote Git repository
Cloning repository http://10.168.1.21:3000/dtseaci/hello.git
 > git init /var/jenkins_home/workspace/demo # timeout=10
Fetching upstream changes from http://10.168.1.21:3000/dtseaci/hello.git
 > git --version # timeout=10
using GIT_ASKPASS to set credentials 
 > git fetch --tags --progress -- http://10.168.1.21:3000/dtseaci/hello.git +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url http://10.168.1.21:3000/dtseaci/hello.git # timeout=10
 > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url http://10.168.1.21:3000/dtseaci/hello.git # timeout=10
Fetching upstream changes from http://10.168.1.21:3000/dtseaci/hello.git
using GIT_ASKPASS to set credentials 
 > git fetch --tags --progress -- http://10.168.1.21:3000/dtseaci/hello.git +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git rev-parse refs/remotes/origin/dev^{commit} # timeout=10
 > git rev-parse refs/remotes/origin/origin/dev^{commit} # timeout=10
Checking out Revision adc94be7293c9c894a7122934d1be2c6c6f89953 (refs/remotes/origin/dev)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f adc94be7293c9c894a7122934d1be2c6c6f89953 # timeout=10
Commit message: "ci"
First time build. Skipping changelog.
[Pipeline] }
[Pipeline] // stage
[Pipeline] withEnv
[Pipeline] {
[Pipeline] withEnv
[Pipeline] {
[Pipeline] timestamps
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Prepare)
[Pipeline] echo
14:16:39  1. Prepare Stage
[Pipeline] checkout
14:16:39  using credential d6b004da-0be9-4d1b-945a-8ccdc42ad6f4
14:16:39   > git rev-parse --is-inside-work-tree # timeout=10
14:16:39  Fetching changes from the remote Git repository
14:16:39   > git config remote.origin.url http://10.168.1.21:3000/dtseaci/hello.git # timeout=10
14:16:39  Fetching upstream changes from http://10.168.1.21:3000/dtseaci/hello.git
14:16:39   > git --version # timeout=10
14:16:39  using GIT_ASKPASS to set credentials 
14:16:39   > git fetch --tags --progress -- http://10.168.1.21:3000/dtseaci/hello.git +refs/heads/*:refs/remotes/origin/* # timeout=10
14:16:39   > git rev-parse refs/remotes/origin/dev^{commit} # timeout=10
14:16:39   > git rev-parse refs/remotes/origin/origin/dev^{commit} # timeout=10
14:16:39  Checking out Revision adc94be7293c9c894a7122934d1be2c6c6f89953 (refs/remotes/origin/dev)
14:16:39   > git config core.sparsecheckout # timeout=10
14:16:39   > git checkout -f adc94be7293c9c894a7122934d1be2c6c6f89953 # timeout=10
14:16:39  Commit message: "ci"
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Maven)
[Pipeline] echo
14:16:39  2. Maven Stage
[Pipeline] script
[Pipeline] {
[Pipeline] sh
14:16:39  + /opt/maven/bin/mvn package
14:16:41  [INFO] Scanning for projects...
14:16:41  [INFO] 
14:16:41  [INFO] -----------------------< net.dtsea.dtseac:hello >-----------------------
14:16:41  [INFO] Building demo 0.0.1-SNAPSHOT
14:16:41  [INFO] --------------------------------[ jar ]---------------------------------
14:16:41  [INFO] 
14:16:41  [INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ hello ---
14:16:42  [INFO] Using 'UTF-8' encoding to copy filtered resources.
14:16:42  [INFO] Copying 1 resource
14:16:42  [INFO] Copying 0 resource
14:16:42  [INFO] 
14:16:42  [INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ hello ---
14:16:42  [INFO] Changes detected - recompiling the module!
14:16:42  [INFO] Compiling 1 source file to /var/jenkins_home/workspace/demo/target/classes
14:16:42  [INFO] 
14:16:42  [INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ hello ---
14:16:42  [INFO] Using 'UTF-8' encoding to copy filtered resources.
14:16:42  [INFO] skip non existing resourceDirectory /var/jenkins_home/workspace/demo/src/test/resources
14:16:42  [INFO] 
14:16:42  [INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ hello ---
14:16:42  [INFO] Changes detected - recompiling the module!
14:16:42  [INFO] Compiling 1 source file to /var/jenkins_home/workspace/demo/target/test-classes
14:16:42  [INFO] 
14:16:42  [INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ hello ---
14:16:43  [INFO] 
14:16:43  [INFO] -------------------------------------------------------
14:16:43  [INFO]  T E S T S
14:16:43  [INFO] -------------------------------------------------------
14:16:43  [INFO] Running net.dtsea.dtseac.hello.DemoApplicationTests
14:16:43  06:16:43.770 [main] DEBUG org.springframework.test.context.junit4.SpringJUnit4ClassRunner - SpringJUnit4ClassRunner constructor called with [class net.dtsea.dtseac.hello.DemoApplicationTests]
14:16:43  06:16:43.776 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating CacheAwareContextLoaderDelegate from class [org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate]
14:16:43  06:16:43.784 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating BootstrapContext using constructor [public org.springframework.test.context.support.DefaultBootstrapContext(java.lang.Class,org.springframework.test.context.CacheAwareContextLoaderDelegate)]
14:16:43  06:16:43.808 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating TestContextBootstrapper for test class [net.dtsea.dtseac.hello.DemoApplicationTests] from class [org.springframework.boot.test.context.SpringBootTestContextBootstrapper]
14:16:43  06:16:43.822 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Neither @ContextConfiguration nor @ContextHierarchy found for test class [net.dtsea.dtseac.hello.DemoApplicationTests], using SpringBootContextLoader
14:16:43  06:16:43.826 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [net.dtsea.dtseac.hello.DemoApplicationTests]: class path resource [net/dtsea/dtseac/hello/DemoApplicationTests-context.xml] does not exist
14:16:43  06:16:43.826 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [net.dtsea.dtseac.hello.DemoApplicationTests]: class path resource [net/dtsea/dtseac/hello/DemoApplicationTestsContext.groovy] does not exist
14:16:43  06:16:43.827 [main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [net.dtsea.dtseac.hello.DemoApplicationTests]: no resource found for suffixes {-context.xml, Context.groovy}.
14:16:43  06:16:43.827 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils - Could not detect default configuration classes for test class [net.dtsea.dtseac.hello.DemoApplicationTests]: DemoApplicationTests does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
14:16:43  06:16:43.876 [main] DEBUG org.springframework.test.context.support.ActiveProfilesUtils - Could not find an 'annotation declaring class' for annotation type [org.springframework.test.context.ActiveProfiles] and class [net.dtsea.dtseac.hello.DemoApplicationTests]
14:16:44  06:16:43.972 [main] DEBUG org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider - Identified candidate component class: file [/var/jenkins_home/workspace/demo/target/classes/net/dtsea/dtseac/hello/DemoApplication.class]
14:16:44  06:16:43.973 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Found @SpringBootConfiguration net.dtsea.dtseac.hello.DemoApplication for test class net.dtsea.dtseac.hello.DemoApplicationTests
14:16:44  06:16:44.079 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - @TestExecutionListeners is not present for class [net.dtsea.dtseac.hello.DemoApplicationTests]: using defaults.
14:16:44  06:16:44.080 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
14:16:44  06:16:44.090 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Skipping candidate TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener] due to a missing dependency. Specify custom listener classes or make the default listener classes and their required dependencies available. Offending class: [org/springframework/transaction/TransactionDefinition]
14:16:44  06:16:44.091 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Skipping candidate TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener] due to a missing dependency. Specify custom listener classes or make the default listener classes and their required dependencies available. Offending class: [org/springframework/transaction/interceptor/TransactionAttribute]
14:16:44  06:16:44.091 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@636be97c, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@50a638b5, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@1817d444, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@6ca8564a, org.springframework.test.context.support.DirtiesContextTestExecutionListener@50b472aa, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@31368b99, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@1725dc0f, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@3911c2a7, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@4ac3c60d, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@4facf68f]
14:16:44  06:16:44.099 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [net.dtsea.dtseac.hello.DemoApplicationTests]
14:16:44  06:16:44.100 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [net.dtsea.dtseac.hello.DemoApplicationTests]
14:16:44  06:16:44.101 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [net.dtsea.dtseac.hello.DemoApplicationTests]
14:16:44  06:16:44.101 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [net.dtsea.dtseac.hello.DemoApplicationTests]
14:16:44  06:16:44.102 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [net.dtsea.dtseac.hello.DemoApplicationTests]
14:16:44  06:16:44.102 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [net.dtsea.dtseac.hello.DemoApplicationTests]
14:16:44  06:16:44.105 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - Before test class: context [DefaultTestContext@1ea9f6af testClass = DemoApplicationTests, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@6a192cfe testClass = DemoApplicationTests, locations = '{}', classes = '{class net.dtsea.dtseac.hello.DemoApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@68bbe345, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@4bbfb90a, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@56aac163, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@536aaa8d], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true]], class annotated with @DirtiesContext [false] with mode [null].
14:16:44  06:16:44.106 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [net.dtsea.dtseac.hello.DemoApplicationTests]
14:16:44  06:16:44.106 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [net.dtsea.dtseac.hello.DemoApplicationTests]
14:16:44  06:16:44.126 [main] DEBUG org.springframework.test.context.support.TestPropertySourceUtils - Adding inlined properties to environment: {spring.jmx.enabled=false, org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true, server.port=-1}
14:16:44  
14:16:44    .   ____          _            __ _ _
14:16:44   /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
14:16:44  ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
14:16:44   \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
14:16:44    '  |____| .__|_| |_|_| |_\__, | / / / /
14:16:44   =========|_|==============|___/=/_/_/_/
14:16:44   :: Spring Boot ::       (v2.1.12.RELEASE)
14:16:44  
14:16:44  2020-02-07 06:16:44.754  INFO 12207 --- [           main] n.d.dtseac.hello.DemoApplicationTests    : Starting DemoApplicationTests on 0a9deba97ed1 with PID 12207 (started by jenkins in /var/jenkins_home/workspace/demo)
14:16:44  2020-02-07 06:16:44.755  INFO 12207 --- [           main] n.d.dtseac.hello.DemoApplicationTests    : No active profile set, falling back to default profiles: default
14:16:47  2020-02-07 06:16:46.686  INFO 12207 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
14:16:47  2020-02-07 06:16:46.966  INFO 12207 --- [           main] n.d.dtseac.hello.DemoApplicationTests    : Started DemoApplicationTests in 2.833 seconds (JVM running for 3.681)
14:16:47  [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.586 s - in net.dtsea.dtseac.hello.DemoApplicationTests
14:16:47  2020-02-07 06:16:47.206  INFO 12207 --- [       Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
14:16:47  [INFO] 
14:16:47  [INFO] Results:
14:16:47  [INFO] 
14:16:47  [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
14:16:47  [INFO] 
14:16:47  [INFO] 
14:16:47  [INFO] --- maven-jar-plugin:3.1.2:jar (default-jar) @ hello ---
14:16:47  [INFO] Building jar: /var/jenkins_home/workspace/demo/target/hello-0.0.1-SNAPSHOT.jar
14:16:48  [INFO] 
14:16:48  [INFO] --- spring-boot-maven-plugin:2.1.12.RELEASE:repackage (repackage) @ hello ---
14:16:48  [INFO] Replacing main artifact with repackaged archive
14:16:48  [INFO] ------------------------------------------------------------------------
14:16:48  [INFO] BUILD SUCCESS
14:16:48  [INFO] ------------------------------------------------------------------------
14:16:48  [INFO] Total time:  7.420 s
14:16:48  [INFO] Finished at: 2020-02-07T06:16:48Z
14:16:48  [INFO] ------------------------------------------------------------------------
[Pipeline] }
[Pipeline] // script
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Deploy)
[Pipeline] echo
14:16:48  3. Deploy Stage
[Pipeline] script
[Pipeline] {
[Pipeline] readMavenPom
[Pipeline] sh
14:16:48  + sed -i s/<API_NAME>/hello-0.0.1-SNAPSHOT/ Launch
[Pipeline] sh
14:16:49  + sed -i s/<JAVA_OPTS>/-Xms256m -Xmx512m/ Launch
[Pipeline] withCredentials
14:16:49  Masking supported pattern matches of $_keyfile or $_uname
[Pipeline] {
[Pipeline] sshCommand
14:16:49  Executing command on dest[10.168.1.42]: mkdir -p /opt/dst**** sudo: false
[Pipeline] sshPut
14:16:49  Sending a file/directory to dest[10.168.1.42]: from: /var/jenkins_home/workspace/demo/Launch into: /opt/dst****
[Pipeline] sshPut
14:16:49  Sending a file/directory to dest[10.168.1.42]: from: /var/jenkins_home/workspace/demo/target/hello-0.0.1-SNAPSHOT.jar into: /opt/dst****
[Pipeline] sshCommand
14:16:50  Executing command on dest[10.168.1.42]: mv -f /opt/dst****/Launch /opt/dst****/Launch.sh sudo: false
[Pipeline] sshCommand
14:16:50  Executing command on dest[10.168.1.42]: chmod +x /opt/dst****/Launch.sh sudo: false
[Pipeline] sshCommand
14:16:50  Executing command on dest[10.168.1.42]: cd /opt/dst**** && ./Launch.sh restart sudo: false
14:16:51  >>> api PID = 27892 begin kill 27892 <<<
14:16:53  >>> /opt/dst****/hello-0.0.1-SNAPSHOT.jar is not running <<<
14:16:53  >>> start /opt/dst****/hello-0.0.1-SNAPSHOT.jar successed PID=28031 <<<
[Pipeline] }
[Pipeline] // withCredentials
[Pipeline] }
[Pipeline] // script
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // timestamps
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS
  1. 登录目标服务器通过PS查看进程已成功运行,并通过 get 8080端口获得程序输出。


    avatar

设置 Gogs 钩子

  1. 添加Web钩子
    登录代码管理系统,进入仓库设置→管理Web钩子→添加Web钩子。


    avatar
  2. 设置web钩子的详情
    这里的推送地址是http://jenkins地址:端口号/generic-webhook-trigger/invoke?token=密钥,这里的密钥是要与前面在Jenkinsfile里面的密钥设置一致。触发web钩子的事件可以自己设置什么样的事件去触发jenkins的构建。
    avatar

这里设置完了之后可以点击测试推送,点击测试记录可以看到发送给jenkins的推送详情了。其中ref就是推送过去的分支啦。以下的推送的内容都是可以通过设置jenkins变量来获得的。(底下红色圈出的部分是Jenkinsfile中配置的变量)


avatar
  1. 验证钩子触发 Jenkins 自动构建
    在项目目录提交代码变更,并将提交信息填写为"ci test"便于查看,并推送。


    avatar

    片刻后 Jenkins 便构建完成,查看提交信息及日志发现项目由 Gogs 钩子触发构建完成。


    avatar

    avatar

Note

  • Jenkinsfile
pipeline{
    agent any
    environment {
        // 目标主机
        dsthost = "10.168.1.42"
        // 目标目录
        dstroot = "/opt/dstroot"
        // 凭证ID 
        credentials = 'root10.168.1.42'
        // Java 运行参数
        java_opts = "-Xms256m -Xmx512m"
    }
    options {
        // 不允许同时执行流水线, 防止同时访问共享资源等
        disableConcurrentBuilds()
        // 显示具体的构建流程时间戳
        timestamps()
    }
    triggers {
        GenericTrigger(
            genericVariables: [
              [key: 'ref', value: '$.ref']
            ],
            // 钩子密钥
            token: "abcd" ,
            causeString: ' Triggered on $ref' ,
            printContributedVariables: true,
            printPostContent: true,
            regexpFilterText: '$ref',
            regexpFilterExpression: '^(refs/heads/dev)$'
        )
    }



    stages{
        stage('Prepare') {
            steps{
                echo "1. Prepare Stage"
                //git branch: 'dev', url: 'http://chenkai:chenkai@10.168.1.21:3000/dtseaci/hello.git'
                checkout scm
                //sh "echo $ref"
                //sh "printenv"
                //script {
                    //sh 'ls -l'
                //}
            }
        }
        stage('Maven'){
            steps{
                echo "2. Maven Stage"
                script{
                    //sh "cat /etc/os-release"
                    sh "/opt/maven/bin/mvn package"
                }
            }
        }
        stage('Deploy'){
            steps{
                echo "3. Deploy Stage"
                script{
                    def _pom = readMavenPom file: 'pom.xml'
                    _version = _pom.getVersion()
                    _artifactId = _pom.getArtifactId()
                    sh "sed -i 's/<API_NAME>/${_artifactId}-${_version}/' Launch"
                    sh "sed -i 's/<JAVA_OPTS>/${java_opts}/' Launch"
                    //sh "scp hello-0.0.1-SNAPSHOT.jar"
                    withCredentials([sshUserPrivateKey(credentialsId: credentials,keyFileVariable: '_keyfile',usernameVariable:'_uname')]) {
                        def remote = [:]
                        remote.name = "dest"
                        remote.host = dsthost
                        remote.allowAnyHosts = true
                        remote.user = _uname
                        remote.identityFile = _keyfile
                        sshCommand remote: remote, command: "mkdir -p ${dstroot}"
                        sshPut remote: remote, from: 'Launch', into: "${dstroot}"
                        sshPut remote: remote, from: "target/${_artifactId}-${_version}.jar", into: "${dstroot}"
                        sshCommand remote: remote, command: "mv -f ${dstroot}/Launch ${dstroot}/Launch.sh"
                        sshCommand remote: remote, command: "chmod +x ${dstroot}/Launch.sh"
                        sshCommand remote: remote, command: "cd ${dstroot} && ./Launch.sh restart"
                        
                    }
                }   
            }
        }

    }
}
  • Launch
#!/bin/sh
CWD=$(cd $(dirname $0); pwd)


API_NAME=<API_NAME>
JAR_NAME=$CWD/$API_NAME\.jar
#PID  代表是PID文件
PID=$CWD/$API_NAME\.pid

#使用说明,用来提示输入参数
usage() {
    echo "Usage: sh 执行脚本.sh [start|stop|restart|status]"
    exit 1
}

#检查程序是否在运行
is_exist(){
  pid=`ps -ef|grep $JAR_NAME|grep -v grep|awk '{print $2}' `
  #如果不存在返回1,存在返回0     
  if [ -z "${pid}" ]; then
   return 1
  else
    return 0
  fi
}

#启动方法
start(){
  is_exist
  if [ $? -eq "0" ]; then 
    echo ">>> ${JAR_NAME} is already running PID=${pid} <<<" 
  else 
    nohup java <JAVA_OPTS> -jar $JAR_NAME >/dev/null 2>&1 &
    echo $! > $PID
    echo ">>> start $JAR_NAME successed PID=$! <<<" 
   fi
  }

#停止方法
stop(){
  #is_exist
  pidf=$(cat $PID)
  #echo "$pidf"  
  echo ">>> api PID = $pidf begin kill $pidf <<<"
  kill $pidf
  rm -rf $PID
  sleep 2
  is_exist
  if [ $? -eq "0" ]; then 
    echo ">>> api 2 PID = $pid begin kill -9 $pid  <<<"
    kill -9  $pid
    sleep 2
    echo ">>> $JAR_NAME process stopped <<<"  
  else
    echo ">>> ${JAR_NAME} is not running <<<"
  fi  
}

#输出运行状态
status(){
  is_exist
  if [ $? -eq "0" ]; then
    echo ">>> ${JAR_NAME} is running PID is ${pid} <<<"
  else
    echo ">>> ${JAR_NAME} is not running <<<"
  fi
}

#重启
restart(){
  stop
  start
}

#根据输入参数,选择执行对应方法,不输入则执行使用说明
case "$1" in
  "start")
    start
    ;;
  "stop")
    stop
    ;;
  "status")
    status
    ;;
  "restart")
    restart
    ;;
  *)
    usage
    ;;
esac
exit 0