【nodejs原理&源碼賞析(9)】用node-ssh實現輕量級自動化部署

【摘要】 node腳本實現輕量級自動化部署前端

示例代碼託管在:http://www.github.com/dashnowords/blogsvue

一. 需求描述

前端工程出包後實現簡易的自動化部署。node

二. 預備知識

網站的建設可使用任何本身熟悉的框架,三大框架都有本身的官方Cli工具,從代碼編寫到生成可用於生產環境部署的包基本都有自動化命令,各個打包工具也在零配置的追求上作了不少工做。本篇中從獲得一個生產環境的包之後開始,對站點部署的相關知識進行一些介紹。linux

首先你須要一個Web服務器,常見的有:git

  • Nginxgithub

  • Tomcatvue-cli

  • Apache或相關集成環境shell

    • XAMMPApache+MySQL+PHP+PERL 】express

    • LAMPLinux+Apache+MySQL+PHPnpm

  • nodejs或相關框架+守護進程

    • Express

    • Koa2

以上任何一種在服務器上運行起來後均可以擔任Web服務器的角色,只是具有的擴展功能和應用場景有區別,Nginx基本上是正式環境部署的首選方案。常見的基本部署方案以下:

IP+端口訪問

使用訪問,可直接訪問對應端口的服務,部署方式相對簡單:

域名訪問

使用域名訪問時,一般會使用A記錄進行解析,它只能映射到80端口(https時映射到443),這時就須要使用反向代理將80端口的請求分發到本地不一樣的內部端口來訪問對應服務:

本例中使用域名+IP的方式進行部署。

三. Nodejs應用的手動部署

Express爲例,步驟以下:

  1. 首先經過yarn global add express-generatornpm install express-generator -g全局安裝腳手架

  2. 完成後在工做目錄經過命令行express mydemo --ejs生成一個使用ejs做爲模板渲染引擎的express工程

  3. 命令行輸入cd mydemo && yarncd mydemo && npm install安裝依賴

  4. /bin/www文件中修改端口號爲指望的端口號(自動生成的是80端口),例如3001

  5. 將前端工程build出的包總體複製粘貼到/public目錄中

  6. 此時在本地工程根目錄下輸入npm start後,在瀏覽器中http://localhost:3001就能夠訪問到網站了

  7. 使用FTP工具(如FlashFxpFileZilla Client等)鏈接到部署機器,將mydemo目錄壓縮爲zip包後上傳到服務器指定目錄。

  8. 使用SSH工具(如XshellMobaXter)登陸遠程機器,假設爲linux系統,輸入unzip mydemo.zip解壓壓縮包,而後cd mydemo進入服務端工程,輸入npm start便可在服務器上開啓Web服務,經過ip地址:3001就能夠訪問到網站。

  9. 可是若是此時SSH工具斷開鏈接,就會發現express應用沒法繼續訪問了,因此還須要一個守護進程來維持應用的啓動狀態,在服務端經過npm install pm2 -g來安裝nodejs應用的部署管理模塊,它能夠實現多應用管理、Hook更新、自動重啓等等許多經常使用功能,詳細信息能夠訪問 【PM2官方網站】

  10. 最後,在工程根目錄輸入pm2 start ./bin/www便可之後臺模式運行應用。

四. 基於nodejs的自動部署

4.1 package.json中的scripts

瞭解了手動部署的過程後,就能夠經過自動化腳原本實現後續的更新和部署。nodejs工程的自動化是依賴於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 deployyarn 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的表現,感興趣的讀者能夠研究一下。

md原文.rar

來源:華爲雲社區  做者:大史不說話

相關文章
相關標籤/搜索