[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实例时,系统会要求您使用自动生成的密码对其进行解锁。
- 浏览到 http://localhost:1080(或安装时为Jenkins配置的任何端口),并等待 解锁 Jenkins 页面出现。
-
从Jenkins控制台日志输出中,复制自动生成的字母数字密码(在两组星号之间
安装插件
通过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 部署私钥
-
在凭据-->系统中找到“全局凭据”
-
添加一个全局凭据,类型选择为“SSH Username with private key”
-
将私钥粘贴到“private Key”中,并将ID设置为 root10.168.1.42 供后续使用,然后保存
Gogs 仓库准备
-
新建 Spring Boot 项目
-
在项目中增加 Jenkinsfile 与 Launch 文件,并推送到Gogs仓库
-
根据项目实际情况修改 Jenkinsfile 配置
Note:
- token 必须根据不同项目设置为不同的值,供 Jenkins 识别不同项目
- 如需对应用设置不同端口或profile,可通过 java_opts 配置
- 将端口设置为 1999 则 java_opts = "-Dserver.port=1999"
- 使用 dev profile 则 java_opts = "-Dspring.profiles.active=dev"
部署项目
-
新建一个任务
-
输入自己任务的名称,然后选择流水线,流水线才是pipeline。
-
确认之后管理界面,如果做过了解的应该知道,piepeline有两种方式,一种是直接写script另一种是引入SCM。这里使用第二种方式
-
保存配置后进入项目界面,点击立即构建,稍等片刻待项目构建完成,并查看项目构建日志。
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
-
登录目标服务器通过PS查看进程已成功运行,并通过 get 8080端口获得程序输出。
设置 Gogs 钩子
-
添加Web钩子
登录代码管理系统,进入仓库设置→管理Web钩子→添加Web钩子。
- 设置web钩子的详情
这里的推送地址是http://jenkins地址:端口号/generic-webhook-trigger/invoke?token=密钥,这里的密钥是要与前面在Jenkinsfile里面的密钥设置一致。触发web钩子的事件可以自己设置什么样的事件去触发jenkins的构建。
这里设置完了之后可以点击测试推送,点击测试记录可以看到发送给jenkins的推送详情了。其中ref就是推送过去的分支啦。以下的推送的内容都是可以通过设置jenkins变量来获得的。(底下红色圈出的部分是Jenkinsfile中配置的变量)
-
验证钩子触发 Jenkins 自动构建
在项目目录提交代码变更,并将提交信息填写为"ci test"便于查看,并推送。
片刻后 Jenkins 便构建完成,查看提交信息及日志发现项目由 Gogs 钩子触发构建完成。
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