本文介绍开发环境持续部署的一种方案,主要借助gitlab仓库、docker容器、jenkins实现环境一键发布,降低发布成本。

部署需求及相关工具

本文是以java-maven项目作为待部署的项目

  1. gitlab仓库作为代码仓库
  2. 服务部署在docker中
  3. 点击jenkins上的构建进行服务发布
  4. 此次部署涉及20个模块,分布在四个服务器上

项目顶级parent-pom文件pom.xml如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>test.ai</groupId>
<artifactId>test-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>test-auth-parent</module>
<module>test-cloud-parent</module>
<module>test-callCenter-parent</module>
<module>test-robot-parent</module>
<module>test-ai-parent</module>
<module>test-nas-parent</module>
<module>test-timerTask-parent</module>
<module>test-common-parent</module>
<module>test-process-parent</module>
<module>test-botstence-parent</module>
<module>test-da-parent</module>
<module>test-sms-parent</module>
<module>test-commlinemarket-parent</module>
<module>test-combination-parent</module>
</modules>
<packaging>pom</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Edgware.RELEASE</spring-cloud.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<!--swagger ui-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>RELEASE</version>
</dependency>

</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

注: 本文是以root用户进行相关环境准备的。

通过本文,让读者了解各组件的使用方式,使用者也可以根据自己的需求灵活配置、灵活实现部署需求

jenkins部署环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Intel(R) Core(TM) i5-7500 CPU @ 3.40GHz 4c-8G
centos 7 x64

# jdk
[root@nginx ~]# java -version
java version "1.8.0_231"
Java(TM) SE Runtime Environment (build 1.8.0_231-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.231-b11, mixed mode)

# docker
[root@nginx ~]# docker -v
Docker version 19.03.4, build 9013bf583a

# git
[root@nginx ~]# git version
git version 1.8.3.1

# maven
[root@nginx ~]# mvn -v
Apache Maven 3.6.2 (40f52333136460af0dc0d7232c0dc0bcf0d9e117; 2019-08-27T23:06:16+08:00)
Maven home: /usr/maven/maven3
Java version: 1.8.0_231, vendor: Oracle Corporation, runtime: /usr/java/jdk1.8.0_231/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.10.0-957.el7.x86_64", arch: "amd64", family: "unix"

gitlab使用公司内部的代码仓库

总体流程

部署简易架构

总体流程如下:

  1. 开发人员提交代码到gitlab
  2. 开发人员在jenkins点击项目构建
  3. jenkins构建
    3.1. 本地maven打包
    3.2. 制作docker镜像
    3.3. 将docker push到私有镜像仓库
  4. jenkins进行服务发布
    4.1 jenkins ssh到相关服务器
    4.2 执行远程脚本命令
    4.3 停止原有容器、删除原有容器、从docker registry中pull最新镜像、docker run

环境安装

git安装
1
[root@nginx ~]# yum install git
jdk安装

jdk下载

  1. 解压jdk压缩包到指定文件夹下

    1
    2
    # 解压压缩包到/usr/java文件夹下
    mkdir -p /usr/java && tar -zxvf jdk-8u231-linux-x64.tar.gz /usr/java/
  2. 配置jdk环境变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    vim /etc/profile

    ########=======在文件尾添加如下内容===start===############

    #java environment
    export JAVA_HOME=/usr/java/jdk1.8.0_231
    export CLASSPATH=.:${JAVA_HOME}/jre/lib/rt.jar:${JAVA_HOME}/lib/dt.jar:${JAVA_HOME}/lib/tools.jar
    export PATH=$PATH:${JAVA_HOME}/bin
    ########=======在文件尾添加如下内容===end===############

    # 保存并退出编辑
  3. 配置生效

    1
    2
    3
    4
    5
    6
    source /etdc/profile

    [root@nginx ~]# java -version
    java version "1.8.0_231"
    Java(TM) SE Runtime Environment (build 1.8.0_231-b11)
    Java HotSpot(TM) 64-Bit Server VM (build 25.231-b11, mixed mode)
maven安装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# maven下载
[root@nginx ~]# wget http://mirror.bit.edu.cn/apache/maven/maven-3/3.6.2/binaries/apache-maven-3.6.2-bin.tar.gz

# 解压
[root@nginx ~]# mkdir -p /usr/maven && tar -zxvf apache-maven-3.6.2-bin.tar.gz -C /usr/maven/maven3

# 配置环境变量
[root@nginx ~]# vim /etc/profile

########=======在文件尾添加如下内容===start===############
#maven environment
export MAVEN_HOME=/usr/maven/maven3
export PATH=$PATH:$JAVA_HOME/bin:$MAVEN_HOME/bin
########=======在文件尾添加如下内容===end===############

# 保存并退出编辑

# 配置生效
[root@nginx ~]# source /etdc/profile
docker安装

官方安装教程,这里就不再详述docker的安装过程

docker registry安装

这里是无证书方式

证书方式

本文为了方便,就采用了无证书方式

本次部署服务的ip如下

ip 服务名
192.168.1.185 xx
192.168.1.190 xx
192.168.1.191 xx
192.168.1.196 xx
192.168.1.198 jenkins、docker registry、maven3

当我们采用无证书的方式时,我们在这几台服务器的作如下配置,这样就可以采用无证书的方式的进行push/pull images

1
2
3
4
5
6
7
8
9
10
# 配置
[root@nginx ~]# vim /etc/docker/daemon.json
############====添加如下内容==start==##############
{
"insecure-registries":["192.168.1.198:5000"]
}
############====添加如下内容==end==##############

# 重启docker
[root@nginx ~]# systemctl restart docker
jenkins安装

本文直接采用yum安装jenkins

1
[root@nginx ~]# yum install jenkins

具体可参照此文进行jenkins安装

jenkins配置

全局工具配置

admin用户登录后,点击左侧 系统管理 -> 全局工具配置,跳转到如下页面

全局工具配置

jdk配置

别名:jdk8,JAVA_HOME就使用前述的/etc/profile中的JAVA_HOME值(/usr/java/jdk1.8.0_231)

Git配置

name:Default,path:git

Maven配置

name: maven,MAVEN_HOME使用前述/etc/profile的MAVEN_HOME值(/usr/maven/maven3)

插件管理

在安装jenkins后,进入jenkins时,已推荐安装了如下插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
Ant Plugin
Apache HttpComponents Client 4.x API Plugin
Authentication Tokens API Plugin
bouncycastle API Plugin
Branch API Plugin
Build Timeout
Command Agent Launcher Plugin
Credentials Binding Plugin
Credentials Plugin
Display URL API
Docker Commons Plugin
Docker Pipeline
Durable Task Plugin
Email Extension Plugin
FindBugs Plug-in
Folders Plugin
Git client plugin
Git plugin
GIT server Plugin
GitHub API Plugin
GitHub Branch Source Plugin
GitHub plugin
Gogs plugin
Gradle Plugin
Jackson 2 API Plugin
Javadoc Plugin
JavaScript GUI Lib: ACE Editor bundle plugin
JavaScript GUI Lib: Handlebars bundle plugin
JavaScript GUI Lib: jQuery bundles (jQuery and jQuery UI) plugin
JavaScript GUI Lib: Moment.js bundle plugin
JSch dependency plugin
JUnit Plugin
LDAP Plugin
Mailer Plugin
MapDB API Plugin
Matrix Authorization Strategy Plugin
Matrix Project Plugin
Maven Integration plugin
OWASP Markup Formatter Plugin
PAM Authentication plugin
Pipeline
Pipeline Graph Analysis Plugin
Pipeline: API
Pipeline: Basic Steps
Pipeline: Build Step
Pipeline: Declarative
Pipeline: Declarative Agent API
Pipeline: Declarative Extension Points API
Pipeline: GitHub Groovy Libraries
Pipeline: Groovy
Pipeline: Input Step
Pipeline: Job
Pipeline: Milestone Step
Pipeline: Model API
Pipeline: Multibranch
Pipeline: Nodes and Processes
Pipeline: REST API Plugin
Pipeline: SCM Step
Pipeline: Shared Groovy Libraries
Pipeline: Stage Step
Pipeline: Stage Tags Metadata
Pipeline: Stage View Plugin
Pipeline: Step API
Pipeline: Supporting APIs
Plain Credentials Plugin
Resource Disposer Plugin
SCM API Plugin
Script Security Plugin
SSH Credentials Plugin
SSH Slaves plugin
Static Analysis Utilities
Structs Plugin
Subversion Plug-in
Timestamper
Token Macro Plugin
Windows Slaves Plugin
Workspace Cleanup Plugin

点击 系统管理 -> 插件管理

还需要安装如下插件:

插件名 作用
Extended Choice Parameter Plug-In 参数化构建时,可提供丰富的选择支持
SSH Pipeline Steps pipeline模式下,采用ssh执行文件传输
或执行远程主机命令
凭据添加
  1. gitlab的ssh认证

我们采用gitlab作为代码仓库,我们直接在服务器上生成公私钥,并将公钥配置在gitlab上,这样就可实现无登录clone/pull代码

git凭据

  1. 各服务器的登录账号密码,用于ssh远程登录执行命令

ssh账号密码

注:在脚本中使用凭据时,我们使用的是这里的唯一标识

凭据唯一标识

任务创建

回到主页,点击新建任务

新建任务

输入任务名称,选择流水线方式

流水线

任务配置

任务配置最终的结果如下:

任务配置结果

简单点,我们这里不允许并发构建。

参数化构建过程

git分支这里我们写死,没有做读取gitlab仓库的分支

git分支

待构建模块(支持多选,顺序发布)

模块选择

注:这里的all模块,表示在顶级pom上进行全量install(即全量打包)

是否发布选择项(单选)

是否发布

当选择否时,表示仅做mvn install,而不作服务发布,是表示会将jar包发布到指定的服务器

是否通过更新docker镜像方式部署(单选)
是否更新镜像

是:将会打jar包,并将jar包scp到服务对应的服务器上,然后使用docker sp jar && docker restart的方式更新服务
否:将会重新制作docker镜像,并将镜像上传至docker registry,在ssh执行远程服务器命令,更新镜像重启服务

jenkins-pipline脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
pomMap = [
"all" : "test-parent/pom.xml",
"ai_web" : "test-parent/test-ai-parent/pom.xml",
"auth_web" : "test-parent/test-auth-parent/pom.xml",
"botstence" : "test-parent/test-botstence-parent/pom.xml",
"callinserver" : "test-parent/test-callCenter-parent/pom.xml",
"ccmanager" : "test-parent/test-callCenter-parent/pom.xml",
"combination" : "test-parent/test-combination-parent/pom.xml",
"fsagent" : "test-parent/test-callCenter-parent/pom.xml",
"fsmanager" : "test-parent/test-callCenter-parent/pom.xml",
"toagentserver" : "test-parent/test-callCenter-parent/pom.xml",
"monitor" : "test-parent/test-cloud-parent/pom.xml",
"registeration" : "test-parent/test-cloud-parent/pom.xml",
"zuul" : "test-parent/test-cloud-parent/pom.xml",
"linemarket" : "test-parent/test-commlinemarket-parent/test-linemarket-parent/pom.xml",
"voipgateway" : "test-parent/test-commlinemarket-parent/test-voipgateway-parent/pom.xml",
"common" : "test-parent/test-common-parent/pom.xml",
"datadic" : "test-parent/test-common-parent/test-datadic/pom.xml",
"sysoperalog" : "test-parent/test-common-parent/test-sysoperalog-parent/pom.xml",
"wss" : "test-parent/test-common-parent/test-wsserver-parent/pom.xml",
"da_web" : "test-parent/test-da-parent/.pom.xml",
"nas_web" : "test-parent/test-nas-parent/pom.xml",
"notice" : "test-parent/test-notice-parent/pom.xml",
"process_agent" : "test-parent/test-process-parent/pom.xml",
"process" : "test-parent/test-process-parent/pom.xml",
"robot" : "test-parent/test-robot-parent/pom.xml",
"sms" : "test-parent/test-sms-parent/pom.xml",
"timertask" : "test-parent/test-timerTask-parent/pom.xml"
]

ipMap = [
"all" : "test-parent/pom.xml",
"ai_web" : "192.168.1.196",
"auth_web" : "192.168.1.196",
"botstence" : "192.168.1.190",
"callinserver" : "192.168.1.190",
"ccmanager" : "192.168.1.190",
"combination" : "192.168.1.185",
"fsagent" : "192.168.1.190",
"fsmanager" : "192.168.1.190",
"toagentserver" : "192.168.1.190",
"monitor" : "192.168.1.185",
"registeration" : "192.168.1.185",
"zuul" : "192.168.1.185",
"linemarket" : "192.168.1.191",
"voipgateway" : "192.168.1.191",
"datadic" : "192.168.1.185",
"sysoperalog" : "192.168.1.191",
"wss" : "192.168.1.191",
"da_web" : "192.168.1.191",
"nas_web" : "192.168.1.196",
"notice" : "192.168.1.185",
"process_agent" : "192.168.1.190",
"process" : "192.168.1.190",
"robot" : "192.168.1.190",
"sms" : "192.168.1.191",
"timertask" : "192.168.1.196"
]

dockerfileMap = [
"ai_web" : "/tmp/test/ai-web",
"auth_web" : "/tmp/test/auth-web",
"botstence" : "/tmp/test/botstence",
"callinserver" : "/tmp/test/calloutserver",
"ccmanager" : "/tmp/test/ccmanager",
"combination" : "/tmp/test/combination",
"fsagent" : "/tmp/test/fsagent",
"fsmanager" : "/tmp/test/fsmanager",
"toagentserver" : "/tmp/test/toagentserver",
"monitor" : "/tmp/test/monitor",
"registeration" : "/tmp/test/registeration",
"zuul" : "/tmp/test/zuul",
"linemarket" : "/tmp/test/linemarket",
"voipgateway" : "/tmp/test/voipgateway",
"datadic" : "/tmp/test/datadic",
"sysoperalog" : "/tmp/test/sysoperalog",
"wss" : "/tmp/test/wss",
"da_web" : "/tmp/test/da-web",
"nas_web" : "/tmp/test/nas-web",
"notice" : "/tmp/test/notice",
"process_agent" : "/tmp/test/process-agent",
"process" : "/tmp/test/process",
"robot" : "/tmp/test/robot",
"sms" : "/tmp/test/sms",
"timertask" : "/tmp/test/timertask"
]

jarFileMap = [
"ai_web" : "test-ai-web-1.0-SNAPSHOT.jar",
"auth_web" : "test-auth-web-1.0-SNAPSHOT.jar",
"botstence" : "test-botstence-web-1.0-SNAPSHOT.jar",
"callinserver" : "test-callCenter-callinserver-web-1.0-SNAPSHOT.jar",
"ccmanager" : "test-callCenter-ccmanager-web-1.0-SNAPSHOT.jar",
"combination" : "test-combination-web-1.0-SNAPSHOT.jar",
"fsagent" : "test-callCenter-fsagent-web-1.0-SNAPSHOT.jar",
"fsmanager" : "test-callCenter-fsmanager-web-1.0-SNAPSHOT.jar",
"toagentserver" : "test-callCenter-toagentserver-web-1.0-SNAPSHOT.jar",
"monitor" : "test-cloud-monitor-1.0-SNAPSHOT.jar",
"registeration" : "test-cloud-registeration-1.0-SNAPSHOT.jar",
"zuul" : "test-cloud-zuul-1.0-SNAPSHOT.jar",
"linemarket" : "test-linemarket-web-1.0-SNAPSHOT.jar",
"voipgateway" : "test-voipgateway-web-1.0-SNAPSHOT.jar",
"datadic" : "test-datadic-web-1.0-SNAPSHOT.jar",
"sysoperalog" : "test-sysoperalog-web-1.0-SNAPSHOT.jar",
"wss" : "test-wsserver-web-1.0-SNAPSHOT.jar",
"da_web" : "test-da-web-1.0-SNAPSHOT.jar",
"nas_web" : "test-nas-web-1.0-SNAPSHOT.jar",
"notice" : "test-notice-web-1.0-SNAPSHOT.jar",
"process_agent" : "test-process-agent-1.0-SNAPSHOT.jar",
"process" : "test-process-web-1.0-SNAPSHOT.jar",
"robot" : "test-robot-web-1.0-SNAPSHOT.jar",
"sms" : "test-sms-web-1.0-SNAPSHOT.jar",
"timertask" : "test-timerTask-admin-1.0-SNAPSHOT.jar"
]

jarMap = [
"ai_web" : "test-parent/test-ai-parent/test-ai-web/target/test-ai-web-1.0-SNAPSHOT.jar",
"auth_web" : "test-parent/test-auth-parent/test-auth-web/target/test-auth-web-1.0-SNAPSHOT.jar",
"botstence" : "test-parent/test-botstence-parent/test-botstence-web/target/test-botstence-web-1.0-SNAPSHOT.jar",
"callinserver" : "test-parent/test-callCenter-parent/test-callCenter-callinserver/test-callCenter-callinserver-web/target/test-callCenter-callinserver-web-1.0-SNAPSHOT.jar",
"ccmanager" : "test-parent/test-callCenter-parent/test-callCenter-ccmanager/test-callCenter-ccmanager-web/target/test-callCenter-ccmanager-web-1.0-SNAPSHOT.jar",
"fsagent" : "test-parent/test-callCenter-parent/test-callCenter-fsagent/test-callCenter-fsagent-web/target/test-callCenter-fsagent-web-1.0-SNAPSHOT.jar",
"combination" : "test-parent/test-combination-parent/test-combination-web/target/test-combination-web-1.0-SNAPSHOT.jar",
"fsmanager" : "test-parent/test-callCenter-parent/test-callCenter-fsmanager/test-callCenter-fsmanager-web/target/test-callCenter-fsmanager-web-1.0-SNAPSHOT.jar",
"toagentserver" : "test-parent/test-callCenter-parent/test-callCenter-toagentserver/test-callCenter-toagentserver-web/target/test-callCenter-toagentserver-web-1.0-SNAPSHOT.jar",
"monitor" : "test-parent/test-cloud-parent/test-cloud-monitor/target/test-cloud-monitor-1.0-SNAPSHOT.jar",
"registeration" : "test-parent/test-cloud-parent/test-cloud-registeration/target/test-cloud-registeration-1.0-SNAPSHOT.jar",
"zuul" : "test-parent/test-cloud-parent/test-cloud-zuul/target/test-cloud-zuul-1.0-SNAPSHOT.jar",
"linemarket" : "test-parent/test-commlinemarket-parent/test-linemarket-parent/test-linemarket-web/target/test-linemarket-web-1.0-SNAPSHOT.jar",
"voipgateway" : "test-parent/test-commlinemarket-parent/test-voipgateway-parent/test-voipgateway-web/target/test-voipgateway-web-1.0-SNAPSHOT.jar",
"datadic" : "test-parent/test-common-parent/test-datadic/test-datadic-web/target/test-datadic-web-1.0-SNAPSHOT.jar",
"sysoperalog" : "test-parent/test-common-parent/test-sysoperalog-parent/test-sysoperalog-web/target/test-sysoperalog-web-1.0-SNAPSHOT.jar",
"wss" : "test-parent/test-common-parent/test-wsserver-parent/test-wsserver-web/target/test-wsserver-web-1.0-SNAPSHOT.jar",
"da_web" : "test-parent/test-da-parent/test-da-web/target/test-da-web-1.0-SNAPSHOT.jar",
"nas_web" : "test-parent/test-nas-parent/test-nas-web/target/test-nas-web-1.0-SNAPSHOT.jar",
"notice" : "test-parent/test-notice-parent/test-notice-web/target/test-notice-web-1.0-SNAPSHOT.jar",
"process_agent" : "test-parent/test-process-parent/test-process-agent/target/test-process-agent-1.0-SNAPSHOT.jar",
"process" : "test-parent/test-process-parent/test-process-server/test-process-web/target/test-process-web-1.0-SNAPSHOT.jar",
"robot" : "test-parent/test-robot-parent/test-robot-web/target/test-robot-web-1.0-SNAPSHOT.jar",
"sms" : "test-parent/test-sms-parent/test-sms-web/target/test-sms-web-1.0-SNAPSHOT.jar",
"timertask" : "test-parent/test-timerTask-parent/test-timerTask-admin/target/test-timerTask-admin-1.0-SNAPSHOT.jar"
]

dockerRegistryUrl = "192.168.1.198:5000"

def compileProject(pom, jar){
try {
withMaven(jdk: 'jdk8',maven: 'maven') {
echo "====++++start compile++++===="
sh "mvn -f ${pom} clean install -DskipTests"
// archiveArtifacts jar
}
}catch (err){
echo "Failed: ${err}"
error "maven package failed"
}
}

def buildImage(jar, dockerfile, module, commit_ID){
echo "====++++get latest commitID++++===="
echo "====++++commitID:${commit_ID}++++===="
sh "cp ${jar} ${dockerfile}"
dir(dockerfile) {
echo "====++++build docker images++++===="
try {
sh "docker build -t ${dockerRegistryUrl}/huru/${module}:${commit_ID} ."
sh "docker tag ${dockerRegistryUrl}/huru/${module}:${commit_ID} ${dockerRegistryUrl}/huru/${module}:pre"
}catch(err) {
echo "Failed: ${err}"
error "build docker image failed"
}
}
}

def pushImage(module, commit_ID){
echo "====++++push docker images++++===="
try{
//TODO:docker golbal variable
withDockerRegistry(url: dockerRegistryUrl) {
//sh "docker push 192.168.1.198:5000/huru/${module}:${commit_ID}"
sh "docker push ${dockerRegistryUrl}/huru/${module}:pre"
}
}catch (err){
echo "Failed: ${err}"
error "push docker image failed"
}
}

def cleanImages(module, commit_ID){
echo "====++++clean local docker images++++===="
try{
sh "docker rmi ${dockerRegistryUrl}/huru/${module}:${commit_ID}"
sh "docker rmi ${dockerRegistryUrl}/huru/${module}:pre"
}catch(err){
echo "Failed:${err}"
error "clean local docker images failed"
}
}

def getServer(remoteIp){

echo "ip:==============="

def remote = [:]
echo "remote ip : ${remoteIp}"
remote.name = "server-${remoteIp}"
remote.host = remoteIp
remote.port = 22
remote.allowAnyHosts = true
withCredentials([usernamePassword(credentialsId: 'huru_ssh', passwordVariable: 'password', usernameVariable: 'username')]) {
remote.user = "${username}"
remote.password = "${password}"
}
return remote
}

def publishOnlyJar(jar, module) {

def ip = ipMap.get(module)
def server = getServer(ip)
def sourceFile = jarFileMap.get(module);

//writeFile file: '$jar', text: 'ls -lrt'
sshPut remote: server, from: jar, into: '/tmp/'

sshCommand remote: server, command: "docker cp /tmp/${sourceFile} ${module}:/home/apps/jars/"

sshCommand remote: server, command: "docker restart ${module}"

sshCommand remote: server, command: "rm -f /tmp/${sourceFile}"
}

//对于botsentence发布,注意要处理文件夹
def botsentenceDirCreate() {

echo "========处理botsentence相关文件夹=============="

def ip = ipMap.get('botstence')
def server = getServer(ip)
def botsentenceDir = dockerfileMap.get('botstence')


def filePwd = botsentenceDir + '/botsentence-dir.tar.gz'

sshPut remote: server, from: filePwd, into: '/tmp/'

sshCommand remote: server, command: '[ ! -d /home/botsentence] && tar -zxvf botsentence-dir.tar.gz -C /home/ || echo "文件夹已存在"'

}



def restart(module, ip) {
echo "====++++restart docker++++===="


def sshServer = getServer(ip)

try{
// 更新或下载镜像
sshCommand remote: sshServer, command: "docker pull ${dockerRegistryUrl}/huru/${module}:pre"
// 停止容器
sshCommand remote: sshServer, command: "docker stop ${module}"
// 删除容器
sshCommand remote: sshServer, command: "docker rm ${module}"


}catch(err){
echo "Failed:${err}"
error "restart failed"
}

try{

def publishCommand = "docker run -d --cpus=2 --restart=always --network=host --name=${module} -v /home/apps/logs:/home/apps/logs ${dockerRegistryUrl}/huru/${module}:pre";

if (module.equalsIgnoreCase("botstence")) {

botsentenceDirCreate()

publishCommand = "docker run -d --cpus=2 --restart=always --network=host --name=${module}\
-v /home/apps/logs:/home/apps/logs \
-v /home/botsentence:/home/botsentence \
-v /home/sellbot/dist/app/cfgs/:/home/sellbot/dist/app/cfgs/ \
-v /home/sellbot/dist/app/wav/:/home/sellbot/dist/app/wav/ \
${dockerRegistryUrl}/huru/${module}:pre"
}

if (module.equalsIgnoreCase("robot")) {

publishCommand = "docker run -d --cpus=2 --restart=always --network=host --name=${module} \
-v /home/botstence_robot_tmpl/:/home/botstence_robot_tmpl/ \
-v /home/apps/logs:/home/apps/logs \
${dockerRegistryUrl}/huru/${module}:pre"

}

if (module.equalsIgnoreCase("process_agent")) {
publishCommand = "docker run -d --cpus=2 --restart=always --network=host --name=${module} \
-v /home/apps/logs:/home/apps/logs \
-v /home/sellbot/dist/app/cfgs/:/home/sellbot/dist/app/cfgs/ \
-v /home/sellbot/dist/app/wav/:/home/sellbot/dist/app/wav/ \
-v /home/botstence_robot_tmpl/:/home/botstence_robot_tmpl/ \
-v /home/botstence_tmpl/:/home/botstence_tmpl/ \
${dockerRegistryUrl}/huru/${module}:pre"
}


if (module.equalsIgnoreCase("robot")) {

publishCommand = "docker run -d --cpus=2 --restart=always --network=host --name=${module} -v /home/botstence_robot_tmpl/:/home/botstence_robot_tmpl/ -v /home/apps/logs:/home/apps/logs ${dockerRegistryUrl}/huru/${module}:pre"

}

// 启动容器
sshCommand remote: sshServer, command: publishCommand

// 清理none镜像
def clearNoneSSH = "n=`docker images | grep '<none>' | wc -l`; if [ \$n -gt 0 ]; then docker rmi `docker images | grep '<none>' | awk '{print \$3}'`; fi"
sshCommand remote: sshServer, command: "${clearNoneSSH}"

}catch(err){
echo "Failed:${err}"
error "restart failed"
}

}

def publish(moduleName, commitID) {
def pomDir = pomMap.get(moduleName)
def dockerfileDir
def jarDir

def remoteIp = ipMap.get(moduleName)

if('all'.equals(moduleName)){

stage("compile") {
compileProject(pomDir,"")
}

if(params.publish == "是") {
for(keyName in jarMap.keySet()){
jarDir = jarMap.get(keyName)
dockerfileDir = dockerfileMap.get(keyName)
println(jarDir+":"+dockerfileDir)

stage("build docker image"){
buildImage(jarDir, dockerfileDir, keyName, commitID)
}
stage("push docker image"){
pushImage(keyName, commitID)
}
stage("clean local docker images"){
cleanImages(keyName, commitID)
}
}
}
}else if('common'.equals(moduleName)){
stage("compile") {
compileProject(pomDir,"")
}
}else{
dockerfileDir = dockerfileMap.get(moduleName)
jarDir = jarMap.get(moduleName)
archiveArtifacts jarDir
stage("compile") {
compileProject(pomDir,jarDir)
}
echo "publish: ${params.publish}"
if (moduleName.equalsIgnoreCase("fsagent") || params.publish == "否") {
sh "cp ${jarDir} ${dockerfileDir}"
echo "jar file directory : ${dockerfileDir}"
return
}

if ("否".equalsIgnoreCase(params.docker)) {
publishOnlyJar(jarDir, moduleName)
return
}

stage("build docker image"){
buildImage(jarDir, dockerfileDir, moduleName, commitID)
}

stage("push docker image"){
pushImage(moduleName, commitID)
}
stage("clean local docker images"){
cleanImages(moduleName, commitID)
}
stage("restart server") {
restart(moduleName, remoteIp)
}
}
}

node("master") {

//获取参数化构建的参数module的值,即具体到某个模块名
currentBuild.description = params.module

//多选的话,模块名间是以‘,’分割的
def modules = params.module.split(",")

def gitUrl = 'git@192.168.1.68:si-talk/si-talk.git'

//检出分支
echo "modules: ${modules}"

stage('chechout'){
echo "====++++start checkout++++===="
echo "branch: ${params.branch}"
git branch: params.branch, credentialsId: 'huru_admin', url: gitUrl
}

def commitID = sh(returnStdout: true, script: "git rev-parse --short HEAD").trim()

//all时,表示对顶级pom进行mvn install
if (modules.contains("all")) {

stage('publish') {
publish("all", commitID);
publish("common", commitID)
}
}

//对待install或发布的模块逐一进行操作
for(String module : modules) {
//滤掉"all"模块名
if (!module.equalsIgnoreCase("all")) {
stage('publish') {
publish(module, commitID);
}
}
}

}

脚本代码可以使用 流水线语法 -> 片断生成器 来生成相关操作的脚本代码

脚本采用的是groovy语言编写的。

关键词介绍

stage,表示构建时的一个一个阶段,比如:mvn install/build image/push image/restart server等,这样在构建时,日志层次更清晰。

总结

jenkins提供了丰富的功能,可以支持我们各种特殊化的发布需求。这里仅仅介绍了如何使用脚本对一个项目多模块进行指定模块发布,通过脚本我们可以灵活的实现发布部署的需求,当然示例的项目还存在诸多可优化的地方,包括dockerfile,模块配置文件等如果更新的问题,后续可以逐步优化。

 评论