• 项目自动化部署


    前言

    此教程,需要有node和shell语法基础

    客户端脚本

    • 在项目根目录下新建ssh.js(依赖请自行yarn add)
      在这里插入图片描述
    const path = require("path");
    const archiver = require("archiver");
    const fs = require("fs");
    const { NodeSSH } = require("node-ssh");
    const ssh = new NodeSSH();
    const shell = require("shelljs");
    const ora = require("ora");
    const dayjs = require("dayjs");
    const inquirer = require("inquirer");
    const configs = {
      host: "*****", // 服务器ip
      user: "root", // 服务器用户名
      password: "*******", // 服务器登陆密码
      path: "/usr/local/nginx/html/test", // 本地压缩文件上传到服务器的路径
      scriptName: "deploy.sh", // 服务器自动部署脚本名称
      scriptPath: "/usr/local/nginx/html/test", // 服务器自动部署脚本路径
    };
    
    /**
     *
     * fucn : nodejs项目 服务器单节点自动化部署
     * theory : 模拟手工ssh连接服务器把本地文件上传到服务器,再执行服务器自动运行脚本
     * extend  : git,svn部署只需要本功能运行服务器自动部署脚本即可。多节点部署简单的可以轮询服务器配置信息多次执行本功能,有性能要求到对本功能进行集群,把服务器信息推入mq
     *
     */
    
    // 项目打包发布目录
    const targetDir = path.join(__dirname, "/dist");
    // 输出到哪个文件目录,默认为当前目录下
    const outputDir = path.join(__dirname, "zips");
    // 压缩出来的文件名
    const fileName = `package.zip`;
    
    function compressFile(spinner) {
      return new Promise((resolve, reject) => {
        spinner.start("正在压缩⽂件...");
        // 如果文件夹不存在,则创建
        if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir);
        // 创建⽂件写⼊流
        let output = fs.createWriteStream(`${outputDir}/${fileName}`);
        // 设置压缩等级
        const archive = archiver("zip", { zlib: { level: 9 } });
    
        output
          .on("close", () => {
            resolve(spinner.succeed("压缩完成"));
          })
          .on("error", (err) => {
            reject(spinner.fail("压缩失败", err));
            process.exit(1);
          });
    
        archive.pipe(output);
        // 存储⽬标⽂件
        archive.directory(targetDir, false);
        // 完成归档
        archive.finalize();
      });
    }
    
    //将dist目录上传至正式环境
    function uploadFile(spinner) {
      return new Promise((resolve, reject) => {
        spinner.succeed("ssh连接服务器中...");
        ssh
          .connect({
            //configs存放的是连接远程机器的信息
            host: configs.host,
            username: configs.user,
            password: configs.password,
            port: 22, //SSH连接默认在22端口
          })
          .then(function (e) {
            spinner.succeed("连接成功");
            let lpath = `${outputDir}/${fileName}`; // 本地打包文件压缩后的地址
            let rpath = `${configs.path}/${fileName}`; // 存放项目的路径
            //上传网站的发布包至configs中配置的远程服务器的指定地址
            spinner.succeed("开始上传文件");
            ssh
              .putFile(lpath, rpath) // Local path, Remote path
              .then(function (status) {
                spinner.succeed("上传文件成功");
                resolve();
              })
              .catch((err) => {
                spinner.succeed("文件传输异常:" + err);
                reject();
                process.exit(1);
              });
          })
          .catch((err) => {
            console.log("ssh连接失败:", err);
            reject();
            process.exit(1);
          });
      });
    }
    
    //执行远端部署脚本
    function startRemoteShell(spinner) {
      return new Promise((resolve, reject) => {
        spinner.succeed("开始执行远端脚本");
        //在服务器上cwd配置的路径下执行sh deploy.sh脚本来实现发布
        ssh
          .execCommand(`sh ${configs.scriptName}`, { cwd: configs.scriptPath })
          .then(function (result) {
            spinner.succeed("脚本执行输出:");
            console.log(result.stdout);
            if (result.code === 0) {
              spinner.succeed("发布成功!");
              resolve();
              process.exit(1);
            } else {
              reject();
              process.exit(1);
            }
          })
          .catch((err) => {
            spinner.fail("执行远端部署脚本失败:" + err);
            reject();
            process.exit(1);
          });
      });
    }
    
    // 环境配置
    const envConfig = {
      test: {
        command: "yarn build:dev",
      },
      prod: {
        command: "yarn build",
      },
    };
    
    const choices = [
      {
        name: "测试服",
        value: "test",
      },
      {
        name: "正式服",
        value: "prod",
      },
    ];
    
    inquirer
      .prompt([
        {
          type: "list",
          message: "请选择你要发布的环境?",
          name: "type",
          choices: choices,
        },
      ])
      .then(async (answers) => {
        const env = answers.type;
        const { command, server_pwd } = envConfig[env];
        // 打包
        const spinner = ora("正在打包...").start();
    
        shell.exec(command, { silent: true }, async (code, stdout, stderr) => {
          if (code === 0) {
            spinner.succeed("打包成功");
            // 压缩
            await compressFile(spinner);
            // 上传文件
            await uploadFile(spinner);
            // 执行远程脚本
            await startRemoteShell(spinner);
          }
        });
      });
    
    
    • 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
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174

    服务器环境安装

    1. 安装nginx
    2. 安装unzip
    3. nginx配置服务
       server{
            listen  81;
            location / {
                root   html/test/dist;
                index  index.html index.htm;
            }
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    服务器脚本

    • 服务器/usr/local/nginx/html下新建test文件
    • test下新建deploy.sh(代码如下:)
      在这里插入图片描述
    #!/bin/bash
    #创建一个集合存放遍历出来的数据
    zip_list=()
    controller_tar(){
     for file in `ls  "/usr/local/nginx/html/test"`
     do
     #贪婪匹配文件后缀名是否为zip或者gz
        if [ "${file##*.}"x = "zip"x ]
        then
        #如果符合条件将文件放入集合中
            zip_list[${#zip_list[*]}]=${file}
        fi
     done
    }
    cd "/usr/local/nginx/html/test"   ----文件的目录位置
    #调用方法
    controller_tar
    #输出所有符合要求的文件名称
    echo ${zip_list[0]}
    if [ $(( $count )) -gt 0 ]; then  ----文件个数大于0开始进入条件
        ZIP_FILES=$(ls *.zip) #获取当前目录下所有.zip结尾的文件
        ZIP_TO="/usr/local/nginx/html/test/dist"  #解压的目标位置
        for zip_file in $ZIP_FILES; do
    	# 开始解压
    	#[注:  -j 参数仅提取文件;
    	#     -o 参数覆盖重名文件;
    	#     -d 指定解压至何处   ]
    	unzip -o $zip_file -d $ZIP_TO
    	# 解压后删除原有的zip压缩包
    	rm -rf $zip_file
          done
    else
    	echo "this direction is null";     -----如果没有文件,给出提示
    fi
    
    
    • 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

    运行

    node ssh // 发布命令
    ip:81  // 发布后访问路径
    
    • 1
    • 2
  • 相关阅读:
    防火墙基础
    c++ primer中文版第五版作业第二章
    C++ Reference: Standard C++ Library reference: Containers: array: array: begin
    93. 对 Promise 的理解?
    Git的使用教程
    国产处理器再获重大突破,走出国门挑战Intel和AMD
    Windows关闭zookeeper、rocketmq日志输出以及修改rocketmq的JVM内存占用大小
    社区分享丨东风康明斯基于JumpServer构建统一运维安全审计平台
    『Java』初试JavaAgent实现修改字节码和插桩
    分享一个基于python+爬虫的豆瓣电影数据可视化分析系统源码
  • 原文地址:https://blog.csdn.net/weixin_41217541/article/details/126318109