以前端项目为例,编译构建的目的主要是将代码变成一些静态资源,部署的目的是可以使用域名访问到这些静态资源。本次实验的前端项目同时使用node起了一个简单的服务端,目的是做请求转发。
编译构建阶段
- 先上脚本
#!/bin/bash
set -o errexit
NPM_RUN_COMMAND="npm run build:${ENV} --prefix"
function clean
{
echo "Clean up..."
# 部署用的全套代码以及node层的依赖
rm -rf /output
# 前端编译代码
rm -rf dist
# 代码检查结果
rm -f eslint-output.xml
echo "Clean up successfully!"
}
function output
{
mkdir -p /output
# 复制server代码(不包含node_modules)、bin至output
cp server.js /output/pigeon.js
cp api_route.js /output/api_route.js
cp package.json /output
# 复制bin至output
cp -r bin /output
# 复制静态资源
cp -r dist/static/css /output/css
cp -r dist/static/img /output/img
cp -r dist/static/js /output/js
cp -r dist/static/fonts /output/fonts
# 复制静态html
cp dist/index.html /output
echo "Downloading server node_modules"
npm i --production --prefix /output
# 执行权限
chmod +x /output/bin/control
}
function build
{
echo "Downloading basic & client node_modules"
# url里的xx是我司简称hhhh
npm config set registry http://registry.m.xx.com
npm i && npm i --prefix client
#echo "code quality check"
#npm run lint-report
echo "client production build"
$NPM_RUN_COMMAND
}
function main
{
clean
build $@
output
}
main $@
- 简要解释脚本 从main()中可以看出这段简单的脚本由三部分构成,clean(),清除上一次编译构建产生的内容,build(),执行构建,output(),将build阶段产生的内容放置到指定位置。
(1).clean(),rm -rf disk移除项目路径下编译构建之后的内容,通常情况下在项目路径下执行npm build,静态资源都会放到/disk中,这个默认路径可以在webpack中设置。rm -rf /output,实验中把build之后的内容放到了根目录下的output文件夹中,所以每次build之前都要清除这个目录下的内容, echo为了看一下build过程,方便调错。
(2).build(),build过程对于前端项目来说主要就是npm build,如果你的前端项目有服务端,那么将服务端和客户端的依赖分开放置是个不错的方式,--prefix的作用就是将node依赖安装到指定目录。
(3).output(),output的作用就是将编译构建的内容放置到指定位置。执行权限那一步完全可以省略,我司预发环境部署的时候容器启动会自动去寻找/bin/control作为启动进程,所以要给control增加执行权限。
- 执行后结果
部署阶段
- 先上脚本
#!/bin/bash
cd "$(dirname $0)"/.. || exit 1
PROC_NAME='pigeon'
help(){
echo "${0} <start|stop|restart|status>"
exit 1
}
status(){
ps -eo 'pid,command' | grep "$PROC_NAME" | grep -v grep > /dev/null 2>&1
ret=$?
if [ 0 -eq $ret ]; then
echo "running"
return 0
else
echo "not running"
return 1
fi
}
stop_server() {
echo ${DATE} "stop " ${PROC_NAME} " server"
SERVER_PIDS=`ps aux | grep "$PROC_NAME" | grep -v "grep"| awk '{print $2}'`
for id in $SERVER_PIDS;do
kill -TERM ${id}
echo "kill process,pid:${id}"
done
for i in 1 2 3 4 5; do
sleep 1
status
if [ $? -eq 1 ]; then
ok "stop server"
return 0
fi
done
die "stop server"
}
start(){
npm start
}
stop(){
stop_server
}
restart(){
stop
start
}
case "${1}" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
status|health|checkhealth|st)
status
;;
*)
help
;;
esac
- 简要解释脚本 部署脚本主要有start(),restart(),stop(),status()组成
(1). start()启动服务,我司容器启动时会默认调用start函数,对于前端项目来说,start()一般是封装的了npm的script,比如我的scripts
"scripts": {
"dev": "vue-cli-service serve",
"build:prod": "vue-cli-service build",
"build:pre": "vue-cli-service build",
"build:stage": "vue-cli-service build --mode staging",
"preview": "node build/index.js --preview",
"lint": "eslint --ext .js,.vue src",
"test:unit": "jest --clearCache && vue-cli-service test:unit",
"test:ci": "npm run lint && npm run test:unit",
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
"start": "nohup node pigeon.js &"
},
所以start()实际上执行的是nohup node pigeon.js,nohup可以使node像后台服务一样执行。
(2). stop(),停止服务,主要是通过ps aux | grep "$PROC_NAME"找到关于当前项目启动的全部pid,然后循环调用kill pid。
(3). restart(),重新启动服务,先调用stop()停止服务,然后调用start()启动服务。
(4). status(),可以通过 bash /bin/control status查看服务状态。
(5). help(), 通过bash /bin/control help查看该脚本中支持的命令。
- 启动服务后简单的验证 对于带有服务端的前端项目,启动后我习惯执行两步简单的验证
(1).判断是否可以准发请求,执行curl -d "username=xxx&password=xxx" -H "Accept: application/json" -X POST "http://localhost:8088/dev-api/xx/xx/user/login" -v ,如果服务转发没问题,通常情况下可以获得服务端的返回值,比如{request-id: 'xxxxx'}等等。
(2).判断是否可以访问静态资源,执行curl http://localhost:8088/static/index.html 通常情况下会返回一个<html>阿巴阿巴...</html>
如果以上两步都得到了预期内的返回值,那么就可以去做申请域名,域名解析等等后续步骤了
附属说明
对于一个合格的部署脚本来说,直接启动的应该是守护进程,用于检测服务进程的状态并对于不同的状态自动执行不同的动作,下一篇简单写一下关于守护进程的事。文中如有问题,请多多指教,感恩。