目录
一. 需求描述
二. 预备知识
IP + 端口访问
域名访问
三. Node.JS 应用的手动部署
四. 基于 Node.JS 的自动部署
4.1 package.JSON 中的 scripts
4.2 自动化发布脚本 deploy.JS
4.3 远端脚本 deploy.sh
五. 小结
示例代码托管在: http://www.github.com/dashnowords/blogs
博客园地址:《大史住在大前端》原创博文目录
华为云社区地址:[你要的前端打怪升级指南]
一. 需求描述
前端工程出包后实现简易的自动化部署.
二. 预备知识
网站的建设可以使用任何自己熟悉的框架, 三大框架都有自己的官方 Cli 工具, 从代码编写到生成可用于生产环境部署的包基本都有自动化命令, 各个打包工具也在零配置的追求上做了很多工作. 本篇中从得到一个生产环境的包以后开始, 对站点部署的相关知识进行一些介绍.
首先你需要一个 web 服务器, 常见的有:
Nginx
Tomcat
Apache 或相关集成环境
- XAMMP[Apache+MySQL+PHP+PERL ]
- LAMP[Linux+Apache+MySQL+PHP]
Node.JS 或相关框架 + 守护进程
Express
Koa2
以上任何一种在服务器上运行起来后都可以担任 Web 服务器的角色, 只是具备的扩展功能和应用场景有区别, Nginx 基本上是正式环境部署的首选方案. 常见的基本部署方案如下:
IP + 端口访问
使用访问, 可直接访问对应端口的服务, 部署方式相对简单:
域名访问
使用域名访问时, 通常会使用 A 记录进行解析, 它只能映射到 80 端口(https 时映射到 443), 这时就需要使用反向代理将 80 端口的请求分发到本地不同的内部端口来访问对应服务:
本例中使用域名 + IP 的方式进行部署.
三. Node.JS 应用的手动部署
以 Express 为例, 步骤如下:
首先通过
yarn global add express-generator
或
NPM install express-generator -g
全局安装脚手架
完成后在工作目录通过命令行
express mydemo --ejs
生成一个使用 ejs 作为模板渲染引擎的 express 工程
命令行输入 cd mydemo && yarn 或
cd mydemo && NPM install
安装依赖
在 / bin/www 文件中修改端口号为期望的端口号(自动生成的是 80 端口), 例如
3001
将前端工程 build 出的包整体复制粘贴到 / public 目录中
此时在本地工程根目录下输入 NPM start 后, 在浏览器中
http://localhost:3001
就可以访问到网站了
使用 FTP 工具 (如 FlashFxp 或 FileZilla Client 等) 连接到部署机器, 将 mydemo 目录压缩为 zip 包后上传到服务器指定目录.
使用 SSH 工具 (如 Xshell 或 MobaXter) 登录远程机器, 假设为 Linux 系统, 输入 unzip mydemo.zip 解压压缩包, 然后 cd mydemo 进入服务端工程, 输入 NPM start 即可在服务器上开启 Web 服务, 通过 ip 地址: 3001 就可以访问到网站.
但是如果此时 SSH 工具断开连接, 就会发现 express 应用无法继续访问了, 所以还需要一个守护进程来维持应用的启动状态, 在服务端通过 NPM install pm2 -g 来安装 Node.JS 应用的部署管理模块, 它可以实现多应用管理, Hook 更新, 自动重启等等许多常用功能, 详细信息可以访问 [PM2 官方网站] http://pm2.keymetrics.io/ .
最后, 在工程根目录输入
pm2 start ./bin/www
即可以后台模式运行应用.
四. 基于 Node.JS 的自动部署
4.1 package.JSON 中的 scripts
了解了手动部署的过程后, 就可以通过自动化脚本来实现后续的更新和部署. Node.JS 工程的自动化是依赖于 package.JSON 文件中的 scripts 配置项来实现的, 例如使用 vue-cli 搭建的工程中就会带有:
- {
- ...
- "scripts": {
- "serve": "vue-cli-service serve",
- "build": "vue-cli-service build",
- "lint": "vue-cli-service lint"
- },
- ...
- }
在项目根目录下打开命令行, 输入 NPM run [script-key]或者 yarn [script-key]([script-key]指上面示例中的 serve,build,lint 这些键名), 就会执行对应的 scripts[key]对应的命令. 我们先添加一条用于自动部署的脚本指令:
- {
- ...
- "scripts": {
- "build": "vue-cli-service build",
- "deploy" "node ./scripts/deploy/deploy.js"
- },
- ...
- }
当输入 NPM run deploy 或 yarn deploy 时, 实际上就相当于用 node 去执行./scripts/deploy/deploy.JS 这个脚本, 其中就编写了自动化发布的指令. scripts 还提供了生命周期钩子, 比如你对接的是一个测试环境, 希望每次 build 后自动发布, 就可以使用 post 钩子来实现:
- {
- ...
- "scripts": {
- "build": "vue-cli-service build",
- "postbuild":"npm run deploy",
- "deploy" "node ./scripts/deploy/deploy.js"
- },
- ...
- }
这样每次 build 执行完毕后, 就会自动执行 NPM run deploy, 也就是运行发布的脚本.
4.2 自动化发布脚本 deploy.JS
自动化发布脚本需要完成这样几个任务:
将打包出的 dist 压缩为 zip 包
使用 SSH 连接部署服务器, 将 zip 包发上去
上传完毕后, 启动事先写好后续任务并放在服务器上的 shell 脚本来完成剩余的工作
涉及的几个模块包括实现 SSH 连接的 node-SSH 模块(底层是 ssh2 模块, 这个模块是一个 Promise 封装), 用于制作 zip 压缩包的 archiver 模块. node-SSH 提供了上传本地目录的方法, 但实际使用过程中发现并不稳定, 从告警信息来看是 node-stream 模块在传送时将不同格式的文件转换为流时可能会出现异常, 实测大约有一半概率触发, 尝试修改了一些配置参数并未解决, 所以采用 archiver 模块先压缩为单个文件后再进行上传.
参考代码如下:
- const path = require('path');
- const archiver =require('archiver');
- const fs = require('fs');
- const node_ssh = require('node-ssh');
- const SSH = new node_ssh();
- const srcPath = path.resolve(__dirname,'../../dist');
- const configs = require('./config');
- console.log('开始压缩 dist 目录...');
- startZip();
- // 压缩 dist 目录为 public.zip
- function startZip() {
- var archive = archiver('zip', {
- zlib: { level: 5 } // 递归扫描最多 5 层
- }).on('error', function(err) {
- throw err;// 压缩过程中如果有错误则抛出
- });
- var output = fs.createWriteStream(__dirname + '/public.zip')
- .on('close', function(err) {
- /* 压缩结束时会触发 close 事件, 然后才能开始上传,
- 否则会上传一个内容不全且无法使用的 zip 包 */
- if (err) {
- console.log('关闭 archiver 异常:',err);
- return;
- }
- console.log('已生成 zip 包');
- console.log('开始上传 public.zip 至远程机器...');
- uploadFile();
- });
- archive.pipe(output);// 典型的 node 流用法
- archive.directory(srcPath,'/public');// 将 srcPach 路径对应的内容添加到 zip 包中 / public 路径
- archive.finalize();
- }
- // 将 dist 目录上传至正式环境
- function uploadFile() {
- SSH.connect({ //configs 存放的是连接远程机器的信息
- host: configs.host,
- username: configs.user,
- password: configs.password,
- port:22 //SSH 连接默认在 22 端口
- }).then(function () {
- // 上传网站的发布包至 configs 中配置的远程服务器的指定地址
- SSH.putFile(__dirname + '/public.zip', configs.path).then(function(status) {
- console.log('上传文件成功');
- console.log('开始执行远端脚本');
- startRemoteShell();// 上传成功后触发远端脚本
- }).catch(err=>{
- console.log('文件传输异常:',err);
- process.exit(0);
- });
- }).catch(err=>{
- console.log('ssh 连接失败:',err);
- process.exit(0);
- });
- }
- // 执行远端部署脚本
- function startRemoteShell() {
- // 在服务器上 cwd 配置的路径下执行 sh deploy.sh 脚本来实现发布
- SSH.execCommand('sh deploy.sh', { cwd:'/usr/bin/XXXXX' }).then(function(result) {
- console.log('远程 STDOUT 输出:' + result.stdout)
- console.log('远程 STDERR 输出:' + result.stderr)
- if (!result.stderr){
- console.log('发布成功!');
- process.exit(0);
- }
- });
- }
4.3 远端脚本 deploy.sh
当发布包上传至远程服务器后, 剩余的工作在远端来完成就可以了, 你只需要将后续的工作写进 shell 脚本并放在对应的目录里就可以了, 本例中 deploy.sh 放在了服务端项目目录 / mydemo 中. 示例如下(由于是自用系统, 不考虑灰度发布等, 直接暴力删除静态目录 public, 然后替换为新的包):
- #!/bin/bash
- cd /usr1/AAA/mydemo
- # 删除原静态资源目录
- rm -rf public
- cd /usr1/AAA
- # 解压新的包
- unzip public.zip
- # 将解压出的 public 目录移动到服务端程序目录 BBB 中
- mv public ./mydemo
提示:
如果脚本文件是在 Windows 下编写的, 请注意将编辑器中的回车换行改为 LF,Windows 下通常默认是 CRLF, 这可能会导致脚本在 Linux 机器上无法正常执行.
至此, 一个简易的自动化部署就做完了. 你只需要在本地输入 NPM run deploy, 后续的工作就会自动执行.
五. 小结
本篇只是一个简易的自动化部署流程, 由于部署环境没有外网所以暂时无法借助通用的自动化流水线实现全自动的 DevOps 流程. PM2 实际上还有非常多实用的功能, 可以管理多个不同的应用实例, 以集群模式运行实例, 或者预设发布流程, 可以直接响应 Web Hook 并对接指定的代码仓, 在根目录下建立 ecosystem.config.JS 配置文件就可以添加更多配置来指定 pm2 的表现, 感兴趣的读者可以研究一下.
来源: https://www.cnblogs.com/dashnowords/p/11293667.html