You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
constexpress=require('express');constapp=express();// 拦截所以 get 请求,如果系统正在升级中,则返回提示页面app.get('*',function(req,res,next){if(upgrading){res.sendFile('./upgrading.html',{root: __dirname});}else{next();}});
要说明的一点是:app.get('*', ...) 必须写在前面,你可以在这里 的 Description 中找到解释。如此一来,当用户恰好在应用重新构建时访问网站,就会出现一个友好的提示页,而当构建完成后,用户再次访问网站,就是一个升级后的应用,整个过程,服务器始终是保持在线的状态,http server 并没有停止或者重启。
前言
一个前端项目简单的开发部署流程通常是这样的:先本地开发,当完成某个功能后构建打包,然后将生成的静态文件通过
ftp
或者其他方式上传到服务器中。并将代码push
到GitHub
或者 码云 等远端仓库中进行托管(为了突出本文的重点,暂不考虑测试的环节)。这种工作流不免有些劳神费力,而且每天频繁的打包上传也会占用很多时间。一种理想的方式是:你只需要在服务器上创建一个“脚本”,执行这个脚本,他就会自动从
git
服务器拉取你的项目代码,并启动你的项目,而当你每次向git
服务器push
代码时,它又会自动拉取最新的代码并重新编译,更新服务。为了实现上述的“理想方式”,本文将详细介绍如何使用
Nuxt
+Webhooks
+Docker
来实现一个Vue SSR
项目的自动化部署。但我们首先需要解决这么几个问题:production
)启动你的项目?要解决上面的问题,你需要了解以下基础知识:
SSH Key
。Nuxt
+Docker
知识。Webhooks
。Node
+express
知识。如果你对上述知识不是很了解或者不知道如何将他们结合在一起来以达到所谓的“理想方式”,那么接下的内容将从项目创建到实际部署,一步步的带你完成这项工作。
一、使用 create-nuxt-app 脚手架创建项目
创建时的各种选项如下图所示,你可以根据自己项目的实际情况进行选择,但
server framework
请选择express
,本文也将以express
作为服务端框架展开介绍。二、修改 package.json 中的 npm scripts
Nuxt
脚手架生成的项目,默认在生产环境下需要先执行npm run build
构建代码,然后再执行npm start
启动服务,这略显繁琐,也不利于自动部署、重新构建等工作的展开,这里将两者的功能合二为一,执行npm start
,即可在编码中使用构建并启动服务。得益于Nuxt
配置中的 dev 参数, 在不同的环境下(NODE_ENV
),即使使用的都是new Builder(nuxt).build()
来进行构建,但由于dev
参数的不同,Nuxt
的构建行为也会相应的不同并进行针对性的优化。这里生产环境(production
)下启动服务也不再是通过node
命令而是使用 nodemon,它用于监听server/index.js
文件的变化,在server/index.js
更新时可以自动重启服务。调整前后的npm scripts
如下:同时,删除
server/index.js
中原本的条件判断:调整之后,执行
npm run dev
,就会在 3000 端口启动一个有代码热替换(HMR
)等功能的一个开发(development
)服务,而执行npm start
就会构建出压缩后的代码,并启动一个带gzip
压缩等功能的生产(production
)服务。三、添加 Webhooks 接口
Webhooks
是什么?简单来说:假如你向一个仓库添加了Webhook
,那么当你push
代码时,git
服务器就会自动向你指定的地址,发送一个带有更新信息(payload
)的post
请求。了解更多,请阅读 GitHub 关于 Webhooks 的介绍文档 或者 码云的文档。由于我们使用了express
来创建http
服务,所以我们可以像这样方便的添加一个接口,用于接收来自git
服务器的post
请求:这里的
app
是一个express
应用,我们通过了Node
的crypto
模块计算签名并和Webhooks
请求中的签名比对来进行鉴权,以保证接口调用的安全性(这里的能够获取到Webhooks
请求的请求体 ——req.body
是由于使用了 body-parser 中间件)。如果鉴权通过则返回成功状态,并执行upgrade
函数来更新服务,如果鉴权失败,则返回无权限提示。同时,你需要向仓库添加Webhook
,如下图:四、如何无缝更新服务
如果你的项目已经在
http://www.example.com/
下启动成功,那么当你每次向GitHub
仓库push
代码时,你的接口都会收到一个来自GitHub
的post
请求,并在鉴权通过后执行upgrade
函数来更新服务。关于如何在服务器上启动项目我们按下不表,先介绍upgrade
函数都做了什么。execCommand
函数如下,这里我们使用了Node
的 child_process 模块,用以创建子进程,来执行拉取代码, 更新npm
依赖等命令:build
函数,会根据配置文件,重新构建项目,这里的upgrading
是一个标记应用是否正在升级的flag
。createServer
函数如下,这里有两个全局变量,render
和server
,其中render
变量保存了最新构建后的nuxt.render
中间件,而server
变量是应用的http server
实例。访问这里,查看完整的
server/index.js
文件。但这里存在一个问题☝️,就是每次执行build
函数,重新构建时,由于Nuxt
会删除上一次构建生成的文件(清空.nuxt/dist/client
和.nuxt/dist/server
文件夹),而构建完成之后才会生成新的文件,那么如果用户恰好在这个空档期访问网站怎么办?一种解决方案是干预webpack
的这种行为,不去清空这两个文件夹,不过我目前没有找到Nuxt
中可以修改这个配置的地方(欢迎评论),另一种解决方案就是在项目重新构建的时候,给用户返回一个友好的提示页,告诉他系统正在升级中。这也是我设置upgrading
变量来标记应用是否正在升级中的意义所在,下面这段代码将展示,如果实现这种效果:要说明的一点是:
app.get('*', ...)
必须写在前面,你可以在这里 的Description
中找到解释。如此一来,当用户恰好在应用重新构建时访问网站,就会出现一个友好的提示页,而当构建完成后,用户再次访问网站,就是一个升级后的应用,整个过程,服务器始终是保持在线的状态,http server
并没有停止或者重启。至此,你已经可以把项目代码上传到
GitHub
或者 码云了(不同的服务商对Webhooks
的鉴权方式可能会有所不同,你需要参考他们的文档对接口的鉴权方式进行一点调整)。五、部署公钥管理
为私有项目添加部署公钥,使得项目在服务器上或者在
Docker
中可以安全的进行代码克隆和后续的拉取更新,参考链接1、参考链接2。这里以GitHub
为例进行介绍:生成一个
GitHub
用的SSH key
一般情况下,是不需要使用
-f ~/.ssh/github_id_rsa
来指定生成SSH Key
的文件名的,默认生成的是id_rsa
。但考虑到一台机器同时使用不同的git
服务器的可能性,所以这里对生成的SSH key
名称进行了自定义。这里的邮箱是你的git
服务器 (GitHub
)登录邮箱。在
~/.ssh
目录下新建一个 config 文件,添加如下内容,参考文档。其中
Host
和HostName
填写git
服务器的域名,IdentityFile
指定私钥的路径,StrictHostKeyChecking
设置为no
可以跳过下图中(yes/no)
的询问,这一点对于Docker
流畅的创建镜像很有必要(否则可能要写expect
脚本),当然你也可以通过执行ssh-keyscan github.com > ~/.ssh/known_hosts
将host keys
提前添加到known_hosts
文件中。在项目仓库添加部署公钥
测试公钥是否可用
如果出现下图所示内容则表明大功告成,可以执行下一步了。👏👏👏🎉🎉🎉
至此,如果你不需要使用
Docker
部署,而是使用传统的部署方式,那么你只需要在服务器上安装Node
和git
,并把仓库代码克隆到服务器上,然后执行npm start
在 80 端口启动服务就可以了。你可以使用nohup
命令或者 forever 等使服务常驻后台。六、Docker 部署
1. 安装 Docker CE (阿里云 Ubuntu 18.04 已亲试)
2. 创建 Dockerfile
3. 创建 Docker Image
通过
cat
命令读取之前创建的SSH
公钥和私钥的内容并作为变量传递给Docker
。由于build
镜像的过程需要执行git clone
和npm install
,取决于机器性能和带宽,可能需要花费一定的时间。一个正常的build
过程如下图:4. 启动容器
在后台启动容器,并把容器内的 3000 端口 发布到主机的 80 端口。
5. 进入执行中的容器
必要的时候可以进入容器中执行一些操作:
七、留个后门
有时候我们可能需要执行一些命令,来对项目进行更佳灵活的操作,比如切换项目的分支、进行版本回滚等。但如果只是为了执行一行命令就需要连接服务器,再进入容器内,难免有些繁琐,启发于
Webhooks
,我们不妨留个后门👻:八、总结
如果你按照上述步骤成功了部署了你的
Vue SSR
项目,那么当你每次push
代码到git
服务器,它都会自动拉取并更新。👏👏👏🎉🎉🎉虽然我试图全面详细的介绍如何撸一套自动化部署的前端项目,但这对于一个真实的项目来说,可能远远不够。
例如,对于测试而言,可能我们需要创建两个的
Docker
镜像(或者使用两台服务器),一个启动在 80 端口,一个启动在 3000 端口,分别拉取master
分支和dev
分支的代码,通过对Webhooks
的payload
进行判断,来决定这次的push
行为应该更新哪个服务,通常我们在dev
上进行频繁的提交,由测试人员测试通过之后,我们将dev
分支的代码阶段性地合并到master
分支,来进行正式版的更新。又比如日志监控的完善等等,所以我的这篇博客权当抛砖迎玉,欢迎各位大佬指正不足之处,评论交流,或者给我的这个项目提交
PR
,大家一起来完善这个事情。The text was updated successfully, but these errors were encountered: