玩命加载中 . . .

前后端分离项目线上部署


前言

  1. 将项目部署至线上,使得任何用户在不同条件下也能够访问。那么首先需要一个服务器,网上有很多教程,能够教会大家如何拥有属于自己的服务器,这里不再赘述。本人购买的是阿里云的linux/CentOs7.7服务器(据说 21 年底,CentOs 官方将不再维护 CentOs8了,大家看看要配哪个系统好吧,博主之前不知情,选了 CentOs8 ,后来因为某种特殊原因决定重换系统,于是降低版本为 CentOs7)。
  2. 当大家拥有自己的服务器后,就需要配置各种环境以支持项目能够顺利运行,这也是为什么开发者会说“这个项目在我的机器上能跑”的原了,因为“我的机器上有相关配置,你的机器上怎晓得有没有”。网上虽然有相关的资料,但杂多繁乱,本人当初因为配置环境浪费了很多时间,这里贴下 @b站up主CodeSheep 的 pdf ,真是一个很好的配置说明,一条龙服务,很快就能配置大部分必备的环境

    链接:https://pan.baidu.com/s/1yKVkabO-N-MCx4UwRbbKcQ
    提取码:h464

下面将会使用基础和进阶的方法部署项目,先说比较麻烦的手动部署流程,毕竟事情要一步一步来,从易到难嘛。

前端的手动化部署

这里使用了 webpack ,由于本文重点不在于如何使用 webpack 配置项目,因此不详述,默认诸位看官已经学会相关知识点。

  1. 首先要将本地项目打包成 dist 包,这点通过 webpack 配置可以完成。
  2. 打开 Xshell 工具,在 nginx 已经配合好的前提下,修改 nginx 的配置文件:
    // 找到【 nginx 的安装目录下的 conf】,修改 conf 下的 nginx.conf 。
    // 举例:我的安装路径是 /usr/local/nginx ,所以 vim /usr/local/nginx/conf/nginx.conf
    // 进到文件里面后,按 i 可以进入编辑模式修改文件,修改好后按 esc 可以退出编辑模式,然后输入 :wq 保存并退出文件
    
    // 修改配置如下:
    // 1. 在第一行加上 user root;
    // 2. 找到 server{ location / {} },修改如下
    server {
      listen       80;
      server_name  localhost;
    
      #charset koi8-r;
    
      #access_log  logs/host.access.log  main;
    
      // 这里修改
      location / {
        root   { nginx 的安装目录/html/xxx};
        // eg. root   /usr/local/nginx/html/dist,dist 为打包后的前端项目;
        index  index.html index.htm;
      }
    }
    
    // 配置好 nginx.conf 后,保存退出,重启 nginx
    // 重启命令为 { nginx 的安装路径/sbin/nginx -s reload },举例:/usr/local/nginx/sbin/nginx -s reload
  3. 打开 Xftp 工具,找到 { nginx 的安装路径/html/ },例如 /usr/local/nginx/html/ ,将 dist 包上传至 html 目录下。访问 ip 地址就可以看到前端项目的静态界面了

    前端项目的部署相对来说简单一些,只要将打包后的文件夹上传到服务器即可(配置好 nginx 的前提下)。下面看下后端项目的部署

    后端的手动化部署

  4. 配置好 Tomcat ,相关教程看 上文3.2 的 pdf
  5. 在 Tomcat 的安装目录下新建一个文件夹,用来存放后端项目,这里命名为 myApp 。打开 Xftp 工具,选中要上传的后端文件,拖拽实现上传
  6. 打开 Xshell 工具,用命令行进入 myApp ,安装项目所需的依赖 npm install 。用 pm2 监控项目,先全局安装 pm2 npm install pm2 -g ,然后用 pm2 启动项目入口文件 pm2 start {入口文件} 。到这里,后端项目的部署也完成啦,咕咕咕~

    到这里,其实本应该能成功启动前后端项目,并能够跑通服务,但绝大多数情况下总是事与愿违的,似乎老天爷不会让事情这么顺利,总要让我们经历一番磨练,所谓“天将降大任于斯人也,必先苦其心志,劳其筋骨是也”,于是总会出现各种奇奇怪怪的 bug 。博主也遇到了,一度很伤脑筋,所以后来终于解决了,博主的上一篇博客:ERROR 之部署服务器篇 记录了博主遇到的一些困难,说不定与读者相同,可以借鉴参考其解决方法,如果能顺利帮助到大家就再好不过了!

如果读者已经顺利完成了项目的非自动化部署,相信能够感受到诸多的不便之处,下面博主会为大家介绍自动化部署前后端分离项目的流程,最终的效果便是在本地编辑器的终端运行两次命令:npm run deploy 服务器的主机 服务器的密码 ,便能够分别实现前后端分离这两个项目从打包到上传服务器再到启动项目的一条龙服务,希望对大家有所帮助。

前端的自动化部署

首先我们需要搞清楚前端项目的一系列部署流程,那便是 打包–登录服务器–上传打包文件 ,那么按照这个思路,我们来具体捋清楚实现过程:

  1. 逻辑思考
  • ①运行脚本命令;
  • ②然后脚本读取配置文件(包含服务器host、port、web目录及本地目录等信息);
  • 打包生成 dist 包npm run build
  • 使用 scp2 连接服务器
  • 将本地打包的 dist 上传/usr/local/nginx/html/
  • 获取脚本命令的自定义参数,这里的参数为服务器主机和密码,并赋值给服务器配置文件(第④点的目的是防止将服务器的主机和密码泄露在项目中)。
  1. 逻辑实现
    // 在 package.json 文件的 scripts 字段定义脚本命令 "deploy"
    "scripts": {
      "build": "webpack --config webpack/webpack.prod.js", //博主这里的配置是这样的,可能跟大家有点出入
      "deploy": "npm run build && node ./deploy/index.js"
    }
    
    // 看到上面的 deploy 中的路径,我们可以知道需要在项目根目录下新建文件夹 deploy ,并在 deploy 下新建文件 index.js ,这个文件是为了使用 scp2 连接服务器,因此里面还放有服务器的一些相关配置。现在开始编写 index.js :
    // 注:scp2 的官网有相关 API 的使用方法,需要提醒的是文件上传的路径需要特别注意,因为不同的工具,写法不同,最终的效果也是有所差别的,例如有些工具上传文件时如果发现服务器该路径下没有对应文件,会自动生成文件夹,但有些工具却不会;同时,上传的文件路径后面带不带 “/” ,也是会有不同的效果的,读者需要稍微留意一下。
    // 1.引入 scp2 ,用于连接服务器
    const client = require('scp2');
    // 2.服务器的配置选项
    const server = {
      host: '', // ip 地址
      port: 22, // 端口号
      username: 'root', // 用户名
      password: '', // 密码
      path: '/usr/local/nginx/html/dist'  // 存放项目的路径
    }
    // 4.用解构赋值获取脚本命令后面的两个参数:主机和密码
    const [ , , host, password] = process.argv;
    server.host = host;
    server.password = password;
    // 3.连接服务器并上传 dist 包到服务器的指定目录 path
    client.scp('dist/', {
      port: server.port,
      host: server.host,
      username: server.username,
      password: server.password,
      path: server.path
    }, function(err) {
      if (err) {
        console.log('文件上传失败', err)
      } else {
        console.log('文件上传成功');
      }
    })
    
    // 之后执行【 npm run deploy 服务器主机 服务器密码 】便可实现前端项目的自动化部署了

    后端的自动化部署

    与前端项目的自动化部署一样,我们也需要搞清楚后端项目的一系列部署流程,也就是 用一个文件夹单独存放需要上传的文件–登录服务器–上传文件–启动项目 ,我们同样来看看具体实现:
  2. 逻辑思考
  • 手动将需要上传的文件复制到一个新建文件夹里,这个文件夹博主命名为 oppService
  • ②运行脚本命令;
  • ③然后脚本读取配置文件–包含服务器host、port 和 oppService 目录以及 deploy.sh 脚本等信息;
  • 调用 node-ssh API 连接服务器
  • ⑤将 oppService 文件夹上传至服务器指定目录
  • ⑥启动项目的方式博主是在服务器上运行自己编写的 .sh 脚本,里面编写的是项目上传之后服务器要做的后续命令 – 进入服务器上的 oppService 文件夹里以执行 deploy.sh 脚本
  • 获取 npm scripts 命令的自定义参数
  1. 逻辑实现
    // 同样在 package.json 文件的 scripts 字段定义脚本命令 "deploy"
    "scripts": {
      "deploy": "node ./deploy/index.js"
    }
    
    
    // 在项目根目录下新建文件夹 deploy ,并在 deploy 下新建文件 index.js 。开始编写 index.js :
    // 1.引入 node-ssh 模块 ,准备调用内置 API
    // 这里有个小问题,貌似官方有个 bug 没解决,不能直接 const node_ssh = require('node-ssh'); ,而是得改成下面的形式去导入
    const node_ssh = require('node-ssh').NodeSSH;
    const ssh = new node_ssh();
    // 6.用解构赋值获取脚本命令后面的两个参数:主机和密码,并存入 config 中
    const [ , , host, password] = process.argv;
    // 2.服务器的配置选项
    const config = {
      path: {
        localPath: 'oppService/',
        romotePath: '/usr/local/apache-tomcat-8.5.71/Online-programming-platform_service',
      },
      romote: {
        host: '',
        port: 22,
        username: 'root',
        password: ''
      }
    }
    function uploadFile() {
      // 3.连接服务器
      ssh.connect(config.romote)
      .then(() => {
        // 4.上传 dist 包到服务器的指定目录 path
        ssh.putDirectory(config.path.localPath, config.path.romotePath)
        .then(() => {
          // 5.执行脚本,完成文件上传后服务器的后备工作
          ssh.execCommand('sh deploy.sh', { cwd: config.path.romotePath })
          .then((res) => {
            if (!res.stderr) {
              process.exit(0);
            }
          })
        }).catch(err => {
          console.log(err)
        })
      }).catch(err => {
        console.log('服务器连接失败!!')
      })
    }
    uploadFile();
    
    
    // 编写 deploy.sh 脚本
    #!/bin/bash
    
    # ①先关闭之前的服务,安装依赖之后再重启
    pm2 stop src/app.ts
    
    # ②查看文件中是否已经包含 node_modules ,如果有则先删除
    file='node_modules'
    if [ -e $file ]; then
      rm -rf $file
      npm cache clean --force
    fi
    # ③重新安装依赖
    cnpm install
    
    # ④查看8080端口是否被占用,有则 kill ,防止项目无法开启
    port=8080
    pid=$(netstat -nlp | grep :$port | awk '{print $7}' | awk -F"/" '{ print $1 }')
    if [ -n "$pid" ]; then
      kill -9 $pid;
    fi
    
    # ⑤重新启动 pm2
    pm2 start ./src/app.ts
    到这里,前后端分离项目的自动化部署就已经结束了,如果有遇到什么 bug ,也可以看看我的上一篇博客 ERROR 之部署服务器篇,或许能够带来一点点帮助。

    拓展

    这样一来,项目部署的效率瞬间就提上来了,一条命令就搞定一切,但博主觉得,其实还有优化空间,例如后端项目需要事先将要上传的文件抽离出来,那我们每次编写完代码之后都得把文件更新一遍,不是很方便。这个问题就等后续再完善吧,毕竟在部署这里耽误的时间也差不多了(= ̄ω ̄=)。如果读者觉得有什么好的解决方案或者是其他可以优化的地方,也可以在评论区提出来哩,先谢过诸位啦~

    后续等有空了会回头来完善的哈

    写在最后

    从对服务器一窍不通,到掌握线上项目的部署,再到命令行的熟练操作,以及对解决问题能力的锻炼,这些都是博主在完成这个前后端分离项目线上部署带来的好处,毫无疑问收获是颇丰的。但实现的过程却并不轻松,有时候一个 bug 一卡就是两三天(终究还是能力不够吧 T^T),浪费了很多时间,但坚持总会有作用的,至少排查问题的能力或多或少能有所提高,真心希望读者能顺利完成项目的部署,直接通关【妖魔鬼怪(bug)快离开,妖魔鬼怪(bug)快离开】。

    以上 ^_^


文章作者: hcyety
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hcyety !
评论
  目录