最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Node.js+pm2+ssh2模塊實現(xiàn)簡單的自動化部署腳本

 更新時間:2023年10月16日 08:52:07   作者:泯瀧  
本文將介紹如何使用Node.js和ssh2模塊實現(xiàn)一個簡單的部署腳本,將本地的項目文件上傳到遠程服務器上,我們將使用dotenv模塊來管理環(huán)境變量,以及child_process模塊來執(zhí)行命令行操作

安裝ssh2和dotenv模塊

首先,我們需要安裝ssh2和dotenv模塊:

npm install ssh2 dotenv --save

然后,我們需要在項目根目錄下創(chuàng)建一個.env文件,用來存放一些敏感的配置信息,例如服務器的IP地址、端口號、用戶名、私鑰等。這樣,我們就可以避免將這些信息暴露在代碼中,也方便我們根據(jù)不同的環(huán)境進行切換。.env文件的內(nèi)容如下:

HOST=192.168.1.100
SSHPORT=22
USER=root
KEYFILE=~/.ssh/id_rsa
SSHKEY="
-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----
"
const fs = require('fs');
const Client = require('ssh2').Client;
require('dotenv').config();

其中,fs模塊是Node.js內(nèi)置的文件系統(tǒng)模塊,用來讀寫文件;Client是ssh2模塊提供的一個類,用來創(chuàng)建SSH連接;dotenv模塊是用來加載.env文件中的配置信息到process.env對象中。

定義一些常量存放SSH連接配置

然后,我們需要定義一些常量,用來存放SSH連接配置和本地目錄路徑和遠程目錄路徑:

// SSH連接配置
const sshConfig = {
    host: process.env.HOST || '127.0.0.1',
    port: process.env.SSHPORT || 22,
    username: process.env.USER || 'root',
    privateKey: process.env.SSHKEY || fs.readFileSync(process.env.KEYFILE || '/.ssh/id_rsa').toString(),
    // 這里使用的是通過密鑰登入,使用密碼登入也是可以的,兩種配置項可以并存,其中一個失敗了ssh2會則嘗試另一個方法
};

// 本地目錄路徑和遠程目錄路徑
const localDir = __dirname;
const remoteDir = '/www/wwwroot/img-service';

其中,我們使用了process.env對象中的屬性來獲取環(huán)境變量的值,如果沒有定義,則使用默認值。注意,私鑰需要轉(zhuǎn)換為字符串格式。

創(chuàng)建Client實例調(diào)用connect方法建立SSH連接

接著,我們需要創(chuàng)建一個Client實例,并調(diào)用connect方法來建立SSH連接:

// 創(chuàng)建SSH連接
const conn = new Client();
conn.on('ready', () => {
    console.log('SSH連接成功');
    // ...
}).connect(sshConfig);

// 監(jiān)聽error事件  
conn.on('error', (err) => {  
    console.error('SSH連接失敗', err);  
});  
  
// 結(jié)束SSH連接  
conn.on('end', () => {  
    console.log('SSH連接已斷開');  
});

在ready事件的回調(diào)函數(shù)中,我們需要進行部署操作。

具體來說,我們需要做兩件事:

一是執(zhí)行npm run build命令來構(gòu)建項目;

二是將構(gòu)建后的文件上傳到遠程服務器上。(當然,構(gòu)建指令也可以在連接之前進行)

// 項目構(gòu)建
const { execSync } = require('child_process');
execSync('npm run build', { stdio: 'inherit' })

execSync 是 Node.js 的一個內(nèi)置模塊,它可以同步地執(zhí)行一個子進程,并返回子進程的輸出。這樣可以避免異步的回調(diào)地獄,也可以保證構(gòu)建的順序和正確性。stdio 參數(shù)是用來控制子進程的輸入輸出的,它可以是一個數(shù)組或一個字符串。如果是一個數(shù)組,那么它表示子進程的標準輸入、標準輸出和標準錯誤的流。如果是一個字符串,那么它表示子進程的所有流的模式。inherit 表示子進程的流和父進程的流相同,也就是說,子進程的輸出會顯示在父進程的控制臺中。

使用sftp進行文件上傳

歐克,現(xiàn)在我們寫一下將本地目錄下的所有文件上傳至服務器上指定目錄的代碼,使用sftp進行文件上傳:

// 將本地目錄下的所有文件上傳至服務器上指定目錄
    const uploadPromise = [];
    conn.sftp((err, sftp) => {
        if (err) throw err;
        // 待上傳文件or目錄
        const files = ['dist', 'package.json', '.env'];

        const uploadFile = (file) => {
            return new Promise((resolve, reject) => {
                try {
                    const localFilePath = localDir + '/' + file;
                    const remoteFilePath = remoteDir + '/' + file;
                    const readStream = fs.createReadStream(localFilePath);
                    const writeStream = sftp.createWriteStream(remoteFilePath);
                    writeStream.on('close', () => {
                        console.log(`文件 ${file} 上傳成功`);
                        resolve();
                    });
                    writeStream.on('error', (err) => {
                        console.log(`文件 ${file} 上傳失敗:${err}`);
                        reject(err);
                    });
                    readStream.pipe(writeStream);
                } catch (error) {
                    reject(error);
                }
            });
        }

同時我們需要有人解析文件目錄,并執(zhí)行我們的上傳指令:

const uploadDir = (files) => {
            files.forEach((file) => {
                // 檢查是否存在文件
                const isExist = fs.existsSync(file);
                const stat = fs.lstatSync(file);
                if (!isExist) {
                    console.log(`文件 ${file} 不存在`);
                }else if (stat.isDirectory(file)){
                    const dirFiles = fs.readdirSync(file);
                    uploadDir(dirFiles.map((dirFile) => file + '/' + dirFile));
                }else if (stat.isFile(file)){
                    uploadPromise.push(uploadFile(file));
                }
            });
        }
        uploadDir(files);

最后,還記得我們收集的Promise數(shù)組嗎?直接用Promise.all幫我們處理等待全部文件上傳后的回調(diào):

Promise.all(uploadPromise).then(() => {
            console.log('所有文件上傳成功');
            // 執(zhí)行SSH命令
            conn.shell((err, stream) => {
                if (err) throw err;
                stream.on('close', () => {
                    console.log('遠程命令執(zhí)行完畢');
                    conn.end();
                }).on('data', (data) => {
                    console.log('遠程命令輸出:\n' + data);
                }).stderr.on('data', (data) => {
                    console.log('遠程命令錯誤:\n' + data);
                });
                stream.end('ls -l /www/wwwroot/img-service\npm2 restart img-service\nexit\n');
            });
        }).catch((err) => {
            console.log('上傳失?。? + err);
        });

歐克,最后附上完整代碼

const fs = require('fs');
const Client = require('ssh2').Client;
require('dotenv').config();
// 項目構(gòu)建
const { execSync } = require('child_process');
execSync('npm run build', { stdio: 'inherit' })
// SSH連接配置
const sshConfig = {
    host: process.env.HOST || '127.0.0.1',
    port: process.env.SSHPORT || 22,
    username: process.env.USER || 'root',
    privateKey: process.env.SSHKEY || fs.readFileSync(process.env.KEYFILE || '/.ssh/id_rsa').toString(),
};
// 本地目錄路徑和遠程目錄路徑
const localDir = __dirname;
const remoteDir = '/www/wwwroot/img-service';
// 創(chuàng)建SSH連接
const conn = new Client();
// 監(jiān)聽ready事件
conn.on('ready', () => {
    console.log('SSH連接成功');
    // 將本地目錄下的所有文件上傳至服務器上指定目錄
    const uploadPromise = [];
    conn.sftp((err, sftp) => {
        if (err) throw err;
        const files = ['dist', 'package.json', '.env'];
        const uploadFile = (file) => {
            return new Promise((resolve, reject) => {
                try {
                    const localFilePath = localDir + '/' + file;
                    const remoteFilePath = remoteDir + '/' + file;
                    const readStream = fs.createReadStream(localFilePath);
                    const writeStream = sftp.createWriteStream(remoteFilePath);
                    writeStream.on('close', () => {
                        console.log(`文件 ${file} 上傳成功`);
                        resolve();
                    });
                    writeStream.on('error', (err) => {
                        console.log(`文件 ${file} 上傳失?。?{err}`);
                        reject(err);
                    });
                    readStream.pipe(writeStream);
                } catch (error) {
                    reject(error);
                }
            });
        }
        const uploadDir = (files) => {
            files.forEach((file) => {
                // 檢查是否存在文件
                const isExist = fs.existsSync(file);
                const stat = fs.lstatSync(file);
                if (!isExist) {
                    console.log(`文件 ${file} 不存在`);
                }else if (stat.isDirectory(file)){
                    const dirFiles = fs.readdirSync(file);
                    uploadDir(dirFiles.map((dirFile) => file + '/' + dirFile));
                }else if (stat.isFile(file)){
                    uploadPromise.push(uploadFile(file));
                }
            });
        }
        uploadDir(files);
        Promise.all(uploadPromise).then(() => {
            console.log('所有文件上傳成功');
            // 執(zhí)行SSH命令
            conn.shell((err, stream) => {
                if (err) throw err;
                stream.on('close', () => {
                    console.log('遠程命令執(zhí)行完畢');
                    conn.end();
                }).on('data', (data) => {
                    console.log('遠程命令輸出:\n' + data);
                }).stderr.on('data', (data) => {
                    console.log('遠程命令錯誤:\n' + data);
                });
                stream.end('ls -l /www/wwwroot/img-service\npm2 restart img-service\nexit\n');
            });
        }).catch((err) => {
            console.log('上傳失?。? + err);
        });
    });
}).connect(sshConfig);
// 監(jiān)聽error事件
conn.on('error', (err) => {
    console.error('SSH連接失敗', err);
});
// 結(jié)束SSH連接
conn.on('end', () => {
    console.log('SSH連接已斷開');
});

以上就是Node.js+pm2+ssh2模塊實現(xiàn)簡單的自動化部署腳本的詳細內(nèi)容,更多關于Node.js pm2 ssh2自動化部署的資料請關注腳本之家其它相關文章!

相關文章

  • 手把手教你使用TypeScript開發(fā)Node.js應用

    手把手教你使用TypeScript開發(fā)Node.js應用

    為了減少代碼編寫過程中出現(xiàn)的錯誤,以及更好的維護你的項目,本文將手把手教你配置一個簡單的開發(fā)環(huán)境來編寫Node.js的應用程序,感興趣的小伙伴們可以參考一下
    2019-05-05
  • 詳解Node.js中間件是怎樣工作的

    詳解Node.js中間件是怎樣工作的

    這篇文章主要介紹了詳解Node.js中間件是怎樣工作的,對中間件感興趣的同學,可以參考下
    2021-04-04
  • Node.JS文件系統(tǒng)解析實例詳解

    Node.JS文件系統(tǒng)解析實例詳解

    這篇文章主要介紹了Node.JS文件系統(tǒng)解析實例詳解的相關資料,需要的朋友可以參考下
    2017-05-05
  • 詳解nodejs實現(xiàn)本地上傳圖片并預覽功能(express4.0+)

    詳解nodejs實現(xiàn)本地上傳圖片并預覽功能(express4.0+)

    本篇文章主要介紹了nodejs實現(xiàn)本地上傳圖片并預覽功能(express4.0+) ,具有一定的參考價值,有興趣的可以了解一下
    2017-06-06
  • npm 更改默認全局路徑以及國內(nèi)鏡像的方法

    npm 更改默認全局路徑以及國內(nèi)鏡像的方法

    今天小編就為大家分享一篇npm 更改默認全局路徑以及國內(nèi)鏡像的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05
  • Nodejs處理Json文件并將處理后的數(shù)據(jù)寫入新文件中

    Nodejs處理Json文件并將處理后的數(shù)據(jù)寫入新文件中

    這篇文章主要介紹了Nodejs處理Json文件并將處理后的數(shù)據(jù)寫入新文件中,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • node操作mysql數(shù)據(jù)庫實例詳解

    node操作mysql數(shù)據(jù)庫實例詳解

    這篇文章主要介紹了node操作mysql數(shù)據(jù)庫,結(jié)合實例形式較為詳細的分析了node操作數(shù)據(jù)庫的連接、增刪改查、事務處理及錯誤處理相關操作技巧,需要的朋友可以參考下
    2017-03-03
  • 利用forever和pm2部署node.js項目過程

    利用forever和pm2部署node.js項目過程

    這篇文章主要給大家介紹了如何利用forever和pm2部署node.js項目的相關資料,文中給出了詳細的介紹和示例代碼供大家參考學習,相信對大家的學習或者工作具有一定的學習價值,需要的朋友們下面隨著小編一起來看看吧。
    2017-05-05
  • Node.js進程退出的深入理解

    Node.js進程退出的深入理解

    NodeJS可以感知和控制自身進程的運行環(huán)境和狀態(tài),也可以創(chuàng)建子進程并與其協(xié)同工作,這使得NodeJS可以把多個程序組合在一起共同完成某項工作,下面這篇文章主要給大家介紹了關于Node.js進程退出的相關資料,需要的朋友可以參考下
    2022-04-04
  • node中短信api實現(xiàn)驗證碼登錄的示例代碼

    node中短信api實現(xiàn)驗證碼登錄的示例代碼

    這篇文章主要介紹了node中短信api實現(xiàn)驗證碼登錄的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-01-01

最新評論

靖宇县| 府谷县| 聊城市| 禹城市| 准格尔旗| 山东省| 郁南县| 庄河市| 南宫市| 娄烦县| 丰宁| 庆元县| 临汾市| 加查县| 寻乌县| 襄垣县| 黎平县| 闸北区| 东方市| 河津市| 曲靖市| 东海县| 赤城县| 孝感市| 洞头县| 布拖县| 德安县| 娄底市| 达尔| 公主岭市| 安图县| 登封市| 丹东市| 日照市| 收藏| 绥滨县| 汝南县| 蒙自县| 交城县| 龙岩市| 濮阳市|