Jenkins构建nodejs项目
之前自己好像在一个项目的构建当中说过这样的话,“而如果是其他的诸如前端啊,PHP 之类的项目,则就更简单了”,说出去之后好像这些项目的构建操作真的非常简单一般的,也就没有在对此进行任何表述。
但是有不少公司其实是纯 web 端服务的,这个时候构建前端项目则显得成为一个主要的事儿了,那么究竟该如何来构建一个完整前端项目呢?
今天我就借以 nodejs 项目举例,不仅仅把 node 项目完整配置流程讲清楚,更借此机会把如何利用 git 分支的办法进行项目的回滚之操作给表述一下,这次表述是一个引子,以后或许会专门写一篇关于回滚的文章吧。
首先说 nodejs 是一个跑起来的服务,需要一些依赖,而每个开发所定义的或者使用的命令之类的又不一样,因此在做 nodejs 的准备工作的时候就很有必要与开发者结合着来做。
接下来进入正题,我们先来在 Jenkins 上做一些配置,先把代码给搞到目标服务器去。
首先创建一个新的项目 test-node。
进入到项目的配置里,添加两项参数。
提示
注意,此处定义了一个名为 mode 的变量,到时候就通过这个变量的传递来实现整个流程是部署,还是回滚,或者是纯粹的项目重启一下。
字符参数定义发布代码的分支。
源码管理依旧使用 git 的地址。
在构建这里先简单写几条命令,让新鲜的代码推到远程服务器去。注意,这里所写的并不是最终脚本哦。因此也就不贴出来了。
然后执行一下构建先:
真的是超级简陋的,刚刚开始,什么都没有呢。
好了,现在去远程服务器上做一下环境的准备工作。
# 1,安装 node。
来波小小的操作。
cd /opt
wget https://nodejs.org/dist/v6.10.1/node-v6.10.1-linux-x64.tar.xz #下载源码包
tar xf node-v6.10.1-linux-x64.tar.gz
mv /opt/node-v6.10.1-linux-x64 /urs/local/node
ln -s /usr/local/node/bin/* /usr/local/bin/
#做软链接让命令可用,也可以写入到/etc/profile里,如果写入配置,那么内容如下:
export NODE_HOME=/usr/local/node
export PATH=$NODE_HOME/bin:$PATH
export NODE_PATH=$NODE_HOME/lib/node_modules:$PATH
node -v #验证安装是否成功。
2
3
4
5
6
7
8
9
10
11
12
然后去到刚才同步过来的目录当中。也就是 node 项目的根目录中。
# 2,配置 node 的一些工具。
确保此时是在项目根目录下。
[root@localhost h5-node]# pwd
/usr/local/h5-node
2
然后安装项目中的依赖。
[root@localhost h5-node]# npm install --registry=https://registry.npm.taobao.org
这一步的操作因为我也不十分明白其中意义,百度一下还真有雷同的问题:https://segmentfault.com/q/1010000012142031
其实上边这一步是安装好本项目所有的依赖。
因为我这里的项目开发者用的是 gulp 进行管理的。因此来安装一下 gulp 与 pm2(每次看到这个词儿就想到开发大哥操着小口音说“嘎破”的样子)命令。
[root@localhost h5-node]# npm i -g gulp
[root@localhost h5-node]# npm i -g pm2
2
其中 i 是 install,-g 表示全局安装的意思。这两条命令安装都比较慢,请耐心等待。
成功的样子如下边:
[root@localhost h5-node]# npm i pm2 -g
[ ..............] - fetchMetadata: sill mapToRegistry uri https://registry.npmjs.org/yamljs
npm ERR! fetch failed https://tgz.pm2.io/gkt-1.0.0.tgz
npm WARN retry will retry, error on last attempt: Error: connect ETIMEDOUT 163.172.101.189:443
npm ERR! fetch failed https://tgz.pm2.io/gkt-1.0.0.tgz
npm WARN retry will retry, error on last attempt: Error: connect ETIMEDOUT 163.172.101.189:443
npm ERR! fetch failed https://tgz.pm2.io/gkt-1.0.0.tgz
/usr/local/node/bin/pm2 -> /usr/local/node/lib/node_modules/pm2/bin/pm2
/usr/local/node/bin/pm2-dev -> /usr/local/node/lib/node_modules/pm2/bin/pm2-dev
/usr/local/node/bin/pm2-docker -> /usr/local/node/lib/node_modules/pm2/bin/pm2-docker
/usr/local/node/bin/pm2-runtime -> /usr/local/node/lib/node_modules/pm2/bin/pm2-runtime
/usr/local/node/lib
└─┬ pm2@2.10.4
├─┬ async@2.6.1
│ └── lodash@4.17.10
├── blessed@0.1.81
├─┬ chalk@1.1.3
│ ├── ansi-styles@2.2.1
│ ├── escape-string-regexp@1.0.5
│ ├─┬ has-ansi@2.0.0
│ │ └── ansi-regex@2.1.1
│ ├── strip-ansi@3.0.1
│ └── supports-color@2.0.0
├─┬ chokidar@2.0.3
│ ├─┬ anymatch@2.0.0
│ │ └─┬ micromatch@3.1.10
│ │ ├── arr-diff@4.0.0
│ │ ├─┬ define-property@2.0.2
│ │ │ └─┬ is-descriptor@1.0.2
│ │ │ ├── is-accessor-descriptor@1.0.0
│ │ │ └── is-data-descriptor@1.0.0
│ │ ├─┬ extend-shallow@3.0.2
│ │ │ ├── assign-symbols@1.0.0
│ │ │ └─┬ is-extendable@1.0.1
│ │ │ └── is-plain-object@2.0.4
│ │ ├─┬ extglob@2.0.4
│ │ │ ├─┬ define-property@1.0.0
│ │ │ │ └─┬ is-descriptor@1.0.2
│ │ │ │ ├── is-accessor-descriptor@1.0.0
│ │ │ │ └── is-data-descriptor@1.0.0
│ │ │ ├─┬ expand-brackets@2.1.4
│ │ │ │ ├── debug@2.6.9
│ │ │ │ ├── define-property@0.2.5
│ │ │ │ ├── extend-shallow@2.0.1
│ │ │ │ └── posix-character-classes@0.1.1
│ │ │ └── extend-shallow@2.0.1
│ │ ├── fragment-cache@0.2.1
│ │ ├── kind-of@6.0.2
│ │ ├─┬ nanomatch@1.2.9
│ │ │ ├─┬ is-odd@2.0.0
│ │ │ │ └── is-number@4.0.0
│ │ │ └── is-windows@1.0.2
│ │ ├── object.pick@1.3.0
│ │ └── regex-not@1.0.2
│ ├── async-each@1.0.1
│ ├─┬ braces@2.3.2
│ │ ├── arr-flatten@1.1.0
│ │ ├── array-unique@0.3.2
│ │ ├─┬ extend-shallow@2.0.1
│ │ │ └── is-extendable@0.1.1
│ │ ├─┬ fill-range@4.0.0
│ │ │ ├── extend-shallow@2.0.1
│ │ │ ├─┬ is-number@3.0.0
│ │ │ │ └─┬ kind-of@3.2.2
│ │ │ │ └── is-buffer@1.1.6
│ │ │ ├── repeat-string@1.6.1
│ │ │ └── to-regex-range@2.1.1
│ │ ├── isobject@3.0.1
│ │ ├── repeat-element@1.1.2
│ │ ├─┬ snapdragon@0.8.2
│ │ │ ├─┬ base@0.11.2
│ │ │ │ ├─┬ cache-base@1.0.1
│ │ │ │ │ ├─┬ collection-visit@1.0.0
│ │ │ │ │ │ ├── map-visit@1.0.0
│ │ │ │ │ │ └── object-visit@1.0.1
│ │ │ │ │ ├── get-value@2.0.6
│ │ │ │ │ ├─┬ has-value@1.0.0
│ │ │ │ │ │ └─┬ has-values@1.0.0
│ │ │ │ │ │ └── kind-of@4.0.0
│ │ │ │ │ ├─┬ set-value@2.0.0
│ │ │ │ │ │ └── extend-shallow@2.0.1
│ │ │ │ │ ├─┬ to-object-path@0.3.0
│ │ │ │ │ │ └── kind-of@3.2.2
│ │ │ │ │ ├─┬ union-value@1.0.0
│ │ │ │ │ │ └─┬ set-value@0.4.3
│ │ │ │ │ │ └── extend-shallow@2.0.1
│ │ │ │ │ └─┬ unset-value@1.0.0
│ │ │ │ │ └─┬ has-value@0.3.1
│ │ │ │ │ ├── has-values@0.1.4
│ │ │ │ │ └── isobject@2.1.0
│ │ │ │ ├─┬ class-utils@0.3.6
│ │ │ │ │ ├── arr-union@3.1.0
│ │ │ │ │ ├── define-property@0.2.5
│ │ │ │ │ └─┬ static-extend@0.1.2
│ │ │ │ │ ├── define-property@0.2.5
│ │ │ │ │ └─┬ object-copy@0.1.0
│ │ │ │ │ ├── copy-descriptor@0.1.1
│ │ │ │ │ ├── define-property@0.2.5
│ │ │ │ │ └── kind-of@3.2.2
│ │ │ │ ├── component-emitter@1.2.1
│ │ │ │ ├─┬ define-property@1.0.0
│ │ │ │ │ └─┬ is-descriptor@1.0.2
│ │ │ │ │ ├── is-accessor-descriptor@1.0.0
│ │ │ │ │ └── is-data-descriptor@1.0.0
│ │ │ │ ├─┬ mixin-deep@1.3.1
│ │ │ │ │ ├── for-in@1.0.2
│ │ │ │ │ └── is-extendable@1.0.1
│ │ │ │ └── pascalcase@0.1.1
│ │ │ ├── debug@2.6.9
│ │ │ ├─┬ define-property@0.2.5
│ │ │ │ └─┬ is-descriptor@0.1.6
│ │ │ │ ├─┬ is-accessor-descriptor@0.1.6
│ │ │ │ │ └── kind-of@3.2.2
│ │ │ │ ├─┬ is-data-descriptor@0.1.4
│ │ │ │ │ └── kind-of@3.2.2
│ │ │ │ └── kind-of@5.1.0
│ │ │ ├── extend-shallow@2.0.1
│ │ │ ├── map-cache@0.2.2
│ │ │ ├── source-map@0.5.7
│ │ │ ├─┬ source-map-resolve@0.5.2
│ │ │ │ ├── atob@2.1.1
│ │ │ │ ├── decode-uri-component@0.2.0
│ │ │ │ ├── resolve-url@0.2.1
│ │ │ │ ├── source-map-url@0.4.0
│ │ │ │ └── urix@0.1.0
│ │ │ └── use@3.1.0
│ │ ├─┬ snapdragon-node@2.1.1
│ │ │ ├─┬ define-property@1.0.0
│ │ │ │ └─┬ is-descriptor@1.0.2
│ │ │ │ ├── is-accessor-descriptor@1.0.0
│ │ │ │ └── is-data-descriptor@1.0.0
│ │ │ └─┬ snapdragon-util@3.0.1
│ │ │ └── kind-of@3.2.2
│ │ ├── split-string@3.1.0
│ │ └─┬ to-regex@3.0.2
│ │ └─┬ safe-regex@1.1.0
│ │ └── ret@0.1.15
│ ├─┬ glob-parent@3.1.0
│ │ ├── is-glob@3.1.0
│ │ └── path-dirname@1.0.2
│ ├── inherits@2.0.3
│ ├─┬ is-binary-path@1.0.1
│ │ └── binary-extensions@1.11.0
│ ├─┬ is-glob@4.0.0
│ │ └── is-extglob@2.1.1
│ ├─┬ normalize-path@2.1.1
│ │ └── remove-trailing-separator@1.1.0
│ ├── path-is-absolute@1.0.1
│ ├─┬ readdirp@2.1.0
│ │ ├── graceful-fs@4.1.11
│ │ ├─┬ minimatch@3.0.4
│ │ │ └─┬ brace-expansion@1.1.11
│ │ │ ├── balanced-match@1.0.0
│ │ │ └── concat-map@0.0.1
│ │ ├─┬ readable-stream@2.3.6
│ │ │ ├── core-util-is@1.0.2
│ │ │ ├── isarray@1.0.0
│ │ │ ├── process-nextick-args@2.0.0
│ │ │ ├── safe-buffer@5.1.2
│ │ │ ├── string_decoder@1.1.1
│ │ │ └── util-deprecate@1.0.2
│ │ └── set-immediate-shim@1.0.1
│ └── upath@1.1.0
├── cli-table-redemption@1.0.1
├── commander@2.13.0
├─┬ cron@1.3.0
│ └── moment-timezone@0.5.17
├─┬ debug@3.1.0
│ └── ms@2.0.0
├── eventemitter2@1.0.5
├── fclone@1.0.11
├─┬ mkdirp@0.5.1
│ └── minimist@0.0.8
├── moment@2.22.1
├─┬ needle@2.2.1
│ ├── debug@2.6.9
│ ├─┬ iconv-lite@0.4.23
│ │ └── safer-buffer@2.1.2
│ └── sax@1.2.4
├─┬ nssocket@0.6.0
│ ├── eventemitter2@0.4.14
│ └── lazy@1.0.11
├── pidusage@1.2.0
├─┬ pm2-axon@3.1.0
│ ├── amp@0.3.1
│ ├── amp-message@0.1.2
│ └── escape-regexp@0.0.1
├── pm2-axon-rpc@0.5.1
├─┬ pm2-deploy@0.3.9
│ ├── async@1.5.2
│ └── tv4@1.3.0
├─┬ pm2-multimeter@0.1.2
│ └── charm@0.1.2
├─┬ pmx@1.6.4
│ ├── deep-metrics@0.0.1
│ ├── json-stringify-safe@5.0.1
│ └─┬ vxx@1.2.2
│ ├─┬ continuation-local-storage@3.2.1
│ │ ├── async-listener@0.6.9
│ │ └── emitter-listener@1.1.1
│ ├── debug@2.6.9
│ ├── extend@3.0.1
│ ├── is@3.2.1
│ ├── lodash.findindex@4.6.0
│ ├── lodash.isequal@4.5.0
│ ├── lodash.merge@4.6.1
│ ├── methods@1.1.2
│ ├── shimmer@1.2.0
│ └── uuid@3.2.1
├─┬ promptly@2.2.0
│ └─┬ read@1.0.7
│ └── mute-stream@0.0.7
├── semver@5.5.0
├─┬ shelljs@0.7.8
│ ├─┬ glob@7.1.2
│ │ ├── fs.realpath@1.0.0
│ │ ├─┬ inflight@1.0.6
│ │ │ └── wrappy@1.0.2
│ │ └── once@1.4.0
│ ├── interpret@1.1.0
│ └─┬ rechoir@0.6.2
│ └─┬ resolve@1.7.1
│ └── path-parse@1.0.5
├─┬ source-map-support@0.5.6
│ ├── buffer-from@1.0.0
│ └── source-map@0.6.1
├── sprintf-js@1.1.1
├── v8-compile-cache@1.1.2
├─┬ vizion@0.2.13
│ └── async@1.5.2
└─┬ yamljs@0.3.0
└─┬ argparse@1.0.10
└── sprintf-js@1.0.3
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: gkt@https://tgz.pm2.io/gkt-1.0.0.tgz (node_modules/pm2/node_modules/gkt):
npm WARN network SKIPPING OPTIONAL DEPENDENCY: connect ETIMEDOUT 163.172.101.189:443
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.1.2 (node_modules/pm2/node_modules/chokidar/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
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
此时基本上构建 node 项目的准备工作就已经完备了,但是此时有一个问题,那就是之前自己安装完上边两条命令之后就以为可以使用了,然后就在 Jenkins 上开始构建,发现总是会报错,说找不到这个命令,改成绝对路径执行就能执行了,后来明白了。
提示
Linux 有不少类似的情况,就像这个 node 一样,解压安装之后创建了软链接,此时只是将源码中带着的命令链接到了/usr/bin 目录下,接下来安装的新命令,并没有被链接过去,因此执行的时候会报找不到这个命令的错误。
那么重新执行一下软链接命令就行了,或者是写入了/etc/profile 中的话,记得在脚本当中及时使用 source 命令对环境进行加载。
ln -s /usr/local/node/bin/* /usr/local/bin/
现在就可以通过准备好了的环境,先在远程服务器上跑一下服务,如果本地可以跑起来,那么接下来的事儿就好办了。
用启动命令跑一下:
第一次启动会有如下输出:
[root@localhost h5-node]# npm run testEnv &
[1] 3854
> 51fanbei-web@0.0.0 testEnv /usr/local/h5-node
> pm2 start ecosystem.json --watch --env test
-------------
__/\\\\\\\\\\\\\____/\\\\____________/\\\\____/\\\\\\\\\_____
_\/\\\/////////\\\_\/\\\\\\________/\\\\\\__/\\\///////\\\___
_\/\\\_______\/\\\_\/\\\//\\\____/\\\//\\\_\///______\//\\\__
_\/\\\\\\\\\\\\\/__\/\\\\///\\\/\\\/_\/\\\___________/\\\/___
_\/\\\/////////____\/\\\__\///\\\/___\/\\\________/\\\//_____
_\/\\\_____________\/\\\____\///_____\/\\\_____/\\\//________
_\/\\\_____________\/\\\_____________\/\\\___/\\\/___________
_\/\\\_____________\/\\\_____________\/\\\__/\\\\\\\\\\\\\\\_
_\///______________\///______________\///__\///////////////__
Community Edition
Production Process Manager for Node.js applications
with a built-in Load Balancer.
Start and Daemonize any application:
$ pm2 start app.js
Load Balance 4 instances of api.js:
$ pm2 start api.js -i 4
Monitor in production:
$ pm2 monitor
Make pm2 auto-boot at server restart:
$ pm2 startup
To go further checkout:
http://pm2.io/
-------------
[PM2] Spawning PM2 daemon with pm2_home=/root/.pm2
[PM2] PM2 Successfully daemonized
[PM2][WARN] Applications 51web not running, starting...
[PM2] App [51web] launched (1 instances)
┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────┬───────────┬──────┬──────────┐
│ App name │ id │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │
├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────┼───────────┼──────┼──────────┤
│ 51web │ 0 │ fork │ 3880 │ online │ 0 │ 0s │ 3% │ 13.1 MB │ root │ enabled │
└──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────┴───────────┴──────┴──────────┘
Use `pm2 show <id|name>` to get more details about an app
[1]+ 完成 npm run testEnv
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
好啦,起来啦,使用 pm2 list 可以看到服务此刻的状态。
# 3,部署脚本。
那么,现在是时候完善一波脚本了。
首先是 Jenkins 项目配置处的脚本:此处的脚本任务非常简单,那就是调用一下服务器上的真实脚本,咱有事儿服务器上说,大概就是这么个意思。
内容也非常简单:
source /etc/profile && /bin/bash /usr/local/scripts/deploy.sh $mode $branch
注意
注意此处两个变量的来源是上头两个参数化构建出所定义的名字,这里的先后顺序可以自定义,但是要注意上下文调用的时候别搞混了就行。
现在来到 Jenkins 服务器本机完善重要的中转脚本,此脚本主要完成三件事儿:其一是将代码从 Git 仓库拉过来然后推送到远程服务器;其二就是依据选项参数中的变量内容来传递不同的操作内容;其三就是记录每次部署的 git 版本号,从而实现后来的回滚。
或许此时你可以根据我上边给出的思路,先尝试着思索一下这个脚本该怎么完成,我不希望你直接看了我的文章照搬照抄之后什么都没懂,这种废了功夫一无所获的情况,不是我想看到的。
闲话不多说,上脚本。
#!/bin/bash
set -e
source /etc/profile
MODE=$1
GIT_BRANCH=$2
function MVN-SCP(){
rsync -vzrtopg --progress -e 'ssh -p 22' --delete $WORKSPACE/* root@192.168.96.81:/usr/local/h5-node
ssh -p 22 root@192.168.96.81 "/bin/bash /usr/local/scripts/deploy_node.sh"
}
function deploy()
{
cd $WORKSPACE
git checkout $GIT_BRANCH
[ $? -ne 0 ] && echo -e '\033[31m[ error ] Failed to checkout the branch\033[0m' && exit 1
git pull
[ $? -ne 0 ] && echo -e '\033[31m[ error ] Failed to pull the branch\033[0m' && exit 1
git rev-parse HEAD >> $WORKSPACE/version.log
MVN-SCP
}
function rollback()
{
[ $HEAD == "0" ] && Head=`tail -n 2 $WORKSPACE/version.log | head -n 1`
git reset --hard $Head
MVN-SCP
}
function reboot()
{
ssh -p 22 root@192.168.96.81 "/bin/bash /usr/local/scripts/deploy_node.sh"
}
case $1 in
deploy)
deploy
;;
rollback)
rollback
;;
reboot)
reboot
;;
*)
echo $"Usage: $0 {deploy|rollback|reboot}"
exit 1
;;
esac
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
脚本写的比较简单,只是作为一个引子,诸位在自己公司使用的时候,请注意各处的环境变化以及上下文变量的调用等问题。
这里边的操作也都是常规操作,只有其中对于回滚的操作可以说一说,这里是利用 git 自身的特性,然后在每次构建的时候保留下来其commit id
到一个固定的 log 文件中,那么如果此时需要回滚,我们只用取出上次构建的commit id
并把代码回滚到那个版本,随后再按常规的路数传代码,部署就好啦。就这么简单的一个回滚就完成啦,如果回滚的版本更靠前一些,那么只需要到 Jenkins 服务器上 cd 到$WORKSPACE 目录下 git log 一下,随便挑选哪次的,然后进行回滚就行啦。毕竟,往前回滚好几个版本的事情,基本上发生的几率为 0 吧。
细心的朋友可能注意到了在 rollback 的函数中,使用 HEAD 的时候,前边有一个$HEAD 做的一个判断,那么这个判断的变量名称是从哪里来的呢,其实是在项目配置当中添加的又一个字符参数来实现的。添加效果如下图:
这里的两个参数其实是分属两个工作方向的:
branch
是服务于部署的时候直接输入部署的分支,那么就会部署想要部署的分支了。
HEAD
是服务于回滚的时候被脚本调用,如果你什么都不操作,只选择了 rollback,那么默认会回滚到上一次部署的版本,如果想要回滚更靠前的版本,那么需要手动输入版本号,即可实现。
配置完成之后构建界面如下:
这样就实现了部署回滚这两个功能了。
好啦,上边所有的操作都指向最后的那个远程脚本了,那也别客气了,来亮个相吧。
#!/bin/bash
set -e
source /etc/profile
base_dir=/usr/local/h5-node
function stop()
{
ps aux |grep pm2 | grep -v grep | awk '{print $2}' | xargs kill -9
}
function start()
{
cd $base_dir && gulp build --dev pro && npm run testEnv &
}
stop
sleep 5
start
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
不用惊讶,就是这么简单,因为上个脚本已经把所有工作都做完了,那么这个脚本就只负责一些启动工作就好啦。
另外要注意的一个点是,与 java 编译成 war 包不同的是,这个 node 项目并不需要删掉整个目录然后再 scp,更适合使用 rsync 这个命令进行两端同步。
现在我们去看下在 Jenkins 的框架中嵌入这些脚本会有什么效果:
首先构建一下 master 分支:
去看下结果如何:
容我趾高气昂的原地转三圈,事实上一个项目的构建就是这么简单。
如何验证自己刚才发布的是否成功了呢?
我们去项目所在服务器,执行一下查看服务状态的命令就知道了:
图中红框圈出的就是运行了 10s 的意思。
接下来在验证一下回滚
的效果如何。
此时要说明下我们看版本是否回滚的思路,因为经过我的测试,从 Jenkins 那里看是否回滚是没有效果的,因为我们的操作是在脚本层面进行的,其次这些操作也都是在服务器上进行的,因此查看是否回滚成功就不能通过 Jenkins 构建历史当中的构建信息来判断了,这句话很重要,关乎你对这里掌握是否熟练的问题,如果不明白,请阅读本段话三遍。
那么该如何来判断是否回滚到上一个版本了呢,依据脚本里的操作,我觉得可以从我们创建测 version.log 中记录的版本号,与$WORKSPACE 中代码的版本进行比对,如果回滚的构建让版本号变成了上一次构建的版本号,则说明回滚成功。
下面我将分别列出两种验证的方案的结果来帮助理解上边的两段话。
# 第一种是错误的示范。
思路:
我们去看三次构建信息,第一次是正常构建,第二次也是正常构建,第三次是把版本回滚到第一次的构建。
- 第一次构建:
- 第二次构建:
- 第三次回滚到第一次的构建:
什么鬼,明明要回滚到第一次构建的呢,怎么还是第二次构建的分支以及版本号呢,这就是这个地方的一个小问题,也是上边那两段话表达的意思,其实是发生了变化的,只不过从这里,看不出来的。
到底怎么看呢?
# 第二种正确的示范。
思路:
我们构建完第一次,来到服务器看 version.log 最底下的版本号,然后 git log 看看是否对照。
构建完第二次,同样是上边第一次的操作,然后验证是否对照。
第三次回滚完成,先看倒数第二次的(也就是第一次构建的)版本号是多少,然后与 git log 看看是否对照。
试验之前先将 version.log 清空。
- 第一次构建:
- 第二次构建:
- 第三次回滚:
由此可见回滚到上一个版本已经完成。
换句话说,回滚到上一个版本只是我们做的一个默认值,如果想往更靠前的回滚,那么只用将那个版本的 commit id 拷贝到构建选项中,举例如下图:
那么这个项目是可以回滚到之前任一版本的。
世上无难事,只要肯钻研琢磨,吃透了之后,Jenkins 会成为一个非常顺手的工具。
那么最后小结一下:
1,Jenkins 项目配置处拉取代码,然后通过选项参数定义出部署,回滚重启等变量。通过字符参数定义出要部署的分支,或者要回滚的 commit id。
2,根据用户选择的构建需求,将变量传递下去,通过中间脚本将想要部署的代码传到远程服务器,然后调用远程重启脚本。
3,远程服务器这里配置好准备环境,以后就只用重启就行啦。
# 补充一:后来的一些思考。
原本对于 node 项目以为足够了解了,然而在后来与朋友的一个偶然聊天当中,竟然聊出了另外的一些东东。他们公司发布 node 项目的方案大致的流程是在 Jenkins 本机拉代码,然后在本地进行 install 安装依赖,这样尽管使用的阿里的源,仍旧会非常麻烦且耗时间,等万事俱备之后,再将整个目录进行压缩,往远方服务器进行拷贝部署,思路有点与 java 项目部署的思路相像。
可是,我们公司从来都是这么弄得呀,这就激起了我的兴趣,事实上在这段聊天之前,在我的脑海当中,几乎认为 node 项目大概就是像本文所写的方式进行部署的罢,然而,事实似乎并没有我想象中的这么简单。
于是,我跑去前端小伙伴那里,问他:咱们都是第一次安装的时候进行 node install 解决了所有的依赖,那么以后发布当中如果有新的依赖之类的,是怎么处理的呢,他回应说,是由开发人员将新增的依赖加入到版本控制,一般都是跟新代码一起上线了,因此不会报出什么问题。
了解到这么多,感觉很多地方你省心了,也许就是别人已经费心了。总之,不断思考,不断最优,是我们不断的追随!
# 补充二:新遇到的一个坑。
公司开发小伙伴看到测试服务器一大堆,感觉到心累,因此想到优化方案,打算把前端项目重构,思路与 nginx 功能类似,大概就是部署一个独立的服务,比如用abc.com
进行访问,那么新建一个测试,打包成一个 test 目录,放到独立服务下,然后访问abc.com/test
就是对应的服务了,如此一来,无论添加几个服务,都是没有问题的。
思路蛮不错的,说干就干,但是他们开发完成之后,在打包的时候却出现问题了,总是报错。
最后才发现,当 Jenkins 把代码拉去到 Jenkins 本地之后,通过 rsync 的方式往远端服务器同步,结果项目里边的一些隐藏文件
没有传输过去,结果就导致了打包报错。
我在测试服务器本身进行git clone
发现里边确实有隐藏文件,然后mv a/* b
下,在去到 b 目录里打包,发现又报错,原来这种 mv 的方式也是无法移动隐藏文件的,要想连隐藏文件一起移动过去,则需要mv a/.* b
下,如此则可以保证所有文件都移动过去啦!!