jenkins+gitlab+pm2自动化部署react项目

服务器

环境:centos、jenkins(汉化版)、gitlab、node、pm2都已经安装好里,这里也不再描述安装过程。
cnpm安装环境(npm网络不是很稳定,下载慢):

1
2
3
npm install -g cnpm --registry=https://registry.npm.taobao.org
//然后建软链接,要不然输入cnpm -v会出错,未找到命令的错误
sudo ln -s /usr/node/node-v8.12.0-linux-x64/node-v8.12.0-linux-x64/bin/cnpm /usr/local/bin/cnpm

jenkins设置(Gitlab Plugin、NodeJs Plugin安装)

进入jenkins的页面

点击左上角的新建任务,输入你的任务名字比如(text-web),建议名字由项目名字-前端部署的端口号,记住名字后面会用到,选择构建一个自由风格的软件任务

会进入一个页面有General、源码管理、构建触发器、构建环境、构建、构建后操作的tab页面。
1:先是General中描述,输入你想要对项目的描述。

2:源码管理,选择Git,在Repository URL输入,你要部署项目的gitlab的地址,我这里引用的方式是http不是ssh的,Credentials中选择就是你自己的gitlab的账号,还没有添加过的添加add

会出现下面页面UserName和Password添加你自己的gitlab账号,点击添加回到之前的页面在Credentials中就可以选择你添加的账号。

Branch Specifier (blank for ‘any’)输入dev,因为我只是设置dev的时候才去采用自动化构建。
3:构建触发器
选择Build when a change is pushed to GitLab. GitLab webhook ,出现下图,记住GitLab webhook Url地址:

点击高级按钮出现下图,看到Secret token,点击Generate出现一串字符,记录下来。

4:构建环境
选择Provide Node & npm bin/ folder to PATH
在NodeJS Installation选择NodeJs(之前安装过)

5:构建
点击添加构建步骤,选择执行Shell输入(deploy.sh就是要执行的脚本文件名)

1
2
3
cnpm install
cnpm run build
sh deploy.sh


记得保存,在jenkins设置中记录两个,第一构建触发器中的GitLab webhook Url地址和Secret token后续会用到。

gitlab(汉化)设置

进入要部署的gitlab项目地址,点击设置中的导入所有仓库

,
在链接(URL)填入上面记录的GitLab webhook Url地址(就是jenkins你的任务地址,记得修改jenkins部署的是8000端口,要是之前直接复制的地址是8080的,一定记得改成8000端口的,一定记得改成8000端口的,一定记得改成8000端口的,重要的事情说三遍,我之前也是入坑了),在安全令牌输入Secret token后面复制的一串字符,在推送事件时间下输入dev,因为只想在dev推送的时候触发自动构建和jenkins中设置的dev相对应,点击添加Web钩子, 记得端口对应jenkins的端口。

react项目

最主要的是已经四个文件部署要用到的,config.prod.js(定义了端口和代理ip地址)、deploy.sh(运行的脚本)、pm2.json、server.js

1:config.prod.js

1
2
3
4
5
6
7
module.exports = {
//端口号
port: 4005,
//api代理地址
api: 'http://localhost:8080/',
}

2:pm2.json

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "text-web",
"script": "server.js",
"env": {
"NODE_ENV": "production"
},
"error_file": "log/app-err.log",
"out_file": "log/app-out.log",
"exec_mode": "cluster",
"instances": 1,
"log_date_format": "YYYY-MM-DD HH:mm Z"
}

3:server.js

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
'use strict';
// 依赖
const path = require('path');
const express = require('express');
const proxy = require('express-http-proxy');
const helmet = require('helmet');
const compression = require('compression');
const config = require('./config.prod');
// express 实例
const app = express();
// 设置 HTTP 头
// reference: http://expressjs.com/zh-cn/advanced/best-practice-security.html
app.use(helmet());
// 开启 gzip 压缩
// reference: http://expressjs.com/zh-cn/advanced/best-practice-performance.html
app.use(compression());
// 静态资源服务
app.use(express.static(path.join(__dirname, 'dist')));
// api proxy
app.use('/api', proxy(config.api, {
forwardPath: function (req, res) {
// return require('url').parse("/smartShoes-wechat" + req.url).path;
return require('url').parse(req.url).path;
}
}));
app.get('*', function (req, res) {
res.sendFile(__dirname + '/dist/index.html');
});
const port = config.port || process.env.PORT
console.log("prot:" + config.port)
app.listen(port, function () {
console.log('🌎 => App is running on port %s', port)

4:deploy.sh文件,要修改两个地址project_home/最后面的text-web-4005修改为你的项目-端口号,project_name就是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
echo '---------------------------------- 部署Begin ----------------------------------'
#定义基础路径及目录
#项目部署的地址/usr/server固定,后面由项目名字加上config.prod中的端口号,根据项目
project_home=/usr/server/text-web-4005
#jenkins服务器路径
jenkins_home=/root/.jenkins/workspace
#jenkins的任务名字
project_name=text-web
#server.js运行的依赖
mode_modules=/usr/server/node_modules/node_modules.tar.gz
tar -czf dist.tar.gz dist
rm -rf dist
if [ -d "$project_home/" ]; then
{
echo '----------------------------------文件存在----------------------------------'
rm -rf $project_home
}
fi
#建立新文件夹
mkdir $project_home
echo '---------------------------------- 文件拷贝Begin ----------------------------------'
cp $jenkins_home/$project_name/dist.tar.gz $project_home
cp $jenkins_home/$project_name/config.prod.js $project_home
cp $jenkins_home/$project_name/server.js $project_home
cp $jenkins_home/$project_name/pm2.json $project_home
cp $mode_modules $project_home
echo '---------------------------------- 文件拷贝End ----------------------------------'
echo '---------------------------------- 解压Begin ----------------------------------'
cd $project_home
tar -xzvf dist.tar.gz
tar -xzvf node_modules.tar.gz
mkdir log
rm -rf $project_home/dist.tar.gz
rm -rf $project_home/node_modules.tar.gz
echo '---------------------------------- 解压End ----------------------------------'
echo '---------------------------------- Pm2Begin ----------------------------------'
#到/usr文件夹目的是因为确保pm2第一次在安全的目录运行
cd /usr
pm2 list
cd $project_home
pm2 startOrRestart pm2.json
echo '---------------------------------- Pm2End ----------------------------------'
echo '---------------------------------- 部署End ----------------------------------'

pm2遇到的坑

当我在给一个项目配置的时候pm2出现了以下错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PM2 |
PM2 | Error: ENOENT: no such file or directory, uv_cwd
PM2 | at Object.resolve (path.js:1167:25)
PM2 | at Function.Module._resolveLookupPaths (module.js:424:17)
PM2 | at Function.Module._resolveFilename (module.js:542:20)
PM2 | at Function.Module._load (module.js:475:25)
PM2 | at Module.require (module.js:597:17)
PM2 | at require (internal/module.js:11:18)
PM2 | at Object.<anonymous> (/usr/node/node-v8.12.0-linux-x64/node-v8.12.0-linux-x64/lib/node_modules/pm2/lib/ProcessContainer.js:13:15)
PM2 | at Module._compile (module.js:653:30)
PM2 | at Object.Module._extensions..js (module.js:664:10)
PM2 | at Module.load (module.js:566:32)
PM2 | [2019-04-25T18:47:51.855Z] PM2 log: App name:arms-console id:0 disconnected
PM2 | [2019-04-25T18:47:51.856Z] PM2 log: App [arms-console] with id [0] and pid [27634], exited with code [1] via signal [SIGINT]
PM2 | [2019-04-25T18:47:51.857Z] PM2 log: Script /usr/server/arms-console-4004/server.js had too many unstable restarts (16). Stopped. "errored"

其它项目都是行的就是这个项目不行,后来在网上查了一通,也有人遇到这个问题,大概的解释就是在你的命令行中第一次运行pm2的那个文件夹是不能被删除的一删除就会出现这个错误,然后我运行pm2 kill然后在一个安全的目录先运行pm2 -v,问题解决了。因为我的脚本是每次部署都会把之前的文件夹删除,然后建立文件夹把必要的文件复制进去,然后解压然后删除不必要的问题,刚刚有个项目的目录是第一次运行pm2的文件夹被自动化部署的时候删除,就出现这个错误所以我在脚本里防止出现这个错误, cd /usr pm2 list,会先前去安全目录运行pm2 list,然后进入项目目录,运行pm2命令。请大家也能注意一下,在pm kill之后要先到安全目录运行以下pm2命令。