<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ssh2.js+Shell 一套組合拳下來,一年要花 2080 分鐘做的工作竟然節(jié)省...

          共 6848字,需瀏覽 14分鐘

           ·

          2023-07-02 00:24


          推薦關(guān)注↓

          作者:凌覽

          https://juejin.cn/post/7194340178939347000


          前言

          團(tuán)隊被分配了新的工作內(nèi)容——每周巡檢。

          巡檢工作簡單,但需要人工重復(fù)性地登陸遠(yuǎn)程服務(wù)器、輸入重復(fù)的命令,然后將命令的結(jié)果記錄下來。每做一次估計花40分鐘,但要每周做,一年52周,一年下來就要花40*52=2080分鐘,這僅僅是團(tuán)隊一個人一年要花的時間。

          不能這么玩呀,純純工具人,所以我一直在思考如何用程序幫我自動巡檢掉。這篇文章的出現(xiàn),說明我的想法方向是正確的,收益可觀一年要花2080分鐘,被我減到52 分鐘

          如果再擴(kuò)展程序幫助到團(tuán)隊,這個公式將從40*52*團(tuán)隊人數(shù)變成1*52*團(tuán)隊人數(shù),時間等于金錢。

          未自動巡檢:

          917055c7c1a2e340d8117c7459e619e3.webp

          手動連接登陸遠(yuǎn)程服務(wù)器,再輸入相應(yīng)的命令獲取結(jié)果,然后人工依據(jù)結(jié)果判斷是否異常,相當(dāng)麻煩,而且我要執(zhí)行的命令不止一條。

          自動巡檢:

          b7d5be857628547ae01d65ee546fdb49.webp

          運行macOS筆記本創(chuàng)建好的快捷指令,它會自動巡檢服務(wù)器,并且巡檢完成后直接打開巡檢結(jié)果表格。當(dāng)然沒有macOS依然可以,但就是沒有快捷指令這步,需要自己執(zhí)行程序。

          完整源碼:https://github.com/CatsAndMice/blog/tree/main/ssh

          實現(xiàn)

          實現(xiàn)難點

          自動化巡檢思路簡單,思路如下:

          本地程序連接登陸遠(yuǎn)程服務(wù)器→本地shell命令遠(yuǎn)程執(zhí)行→本地程序獲取命令結(jié)果→結(jié)果數(shù)據(jù)整理成表格

          實現(xiàn)過程中主要有以下兩個難點:

          • Node.js本地運行程序如何連接登陸遠(yuǎn)程服務(wù)器
          • 登陸遠(yuǎn)程服務(wù)器帳號權(quán)限不足,在使用sudo命令時,如何自動輸入密碼

          實現(xiàn)細(xì)節(jié)

          解決Node.js本地運行程序如何連接登陸遠(yuǎn)程服務(wù)器:

          社區(qū)已有的方案ssh2,它是用純JavaScript為Node.js編寫的SSH2客戶端和服務(wù)器模塊。可以使用它連接到遠(yuǎn)程服務(wù)器,并且ssh2提供了方法可以執(zhí)行shell命令。

          ssh2地址:https://github.com/mscdex/ssh2

          ssh2官方案例:

                
                //...
          const?{?Client?}?=?require('ssh2');
          const?conn?=?new?Client();
          conn.on('ready',?()?=>?{
          ??console.log('Client?::?ready');
          ??//執(zhí)行uptime
          ??conn.exec('uptime',?(err,?stream)?=>?{
          ????if?(err)?throw?err;
          ????stream.on('close',?(code,?signal)?=>?{
          ??????console.log('Stream?::?close?::?code:?'?+?code?+?',?signal:?'?+?signal);
          ??????conn.end();
          ????}).on('data',?(data)?=>?{
          ??????//監(jiān)聽數(shù)據(jù)
          ??????console.log('STDOUT:?'?+?data);
          ????}).stderr.on('data',?(data)?=>?{
          ??????console.log('STDERR:?'?+?data);
          ????});
          ??});
          })
          //...

          官方案例僅執(zhí)行一條shell命令,當(dāng)按照順序依次執(zhí)行一條以上的命令,官方的這個寫法會非常麻煩。例如:首先執(zhí)行docker ps \-a \-q獲取所有docker容器id,然后再docker logs \--tail 200 id

                
                ?//...
          ?//?獲取docker所有容器ID
          ?conn.exec('docker?ps?-a?-q',?(err,?stream)?=>?{
          ????if?(err)?throw?err;
          ????stream.on('close',?(code,?signal)?=>?{
          ??????/**
          ????????docker?ps?-a?-q命令執(zhí)行完成
          ????????再執(zhí)行docker?logs?-f?--tail?200?id
          ??????*/

          ??????conn.exec(`docker?logs??--tail?200?${id}`,(err,stream)=>{
          ?????????if?(err)?throw?err;
          ??????????stream.on('close',?()?=>?{
          ??????????//如果命令再復(fù)雜點,還需要繼續(xù)這樣寫下去
          ??????????
          ??????????}).on('data',?(data)?=>?{
          ????????????console.log(?data);
          ??????????}).stderr.on('data',?(data)?=>?{
          ????????????console.log(data);
          ??????????});
          ??????})
          ??????
          ????}).on('data',?(data)?=>?{
          ??????console.log('STDOUT:?'?+?data);
          ????}).stderr.on('data',?(data)?=>?{
          ??????console.log('STDERR:?'?+?data);
          ????});
          ??});
          ?//...

          要想寫法整潔點,我們需要再給 exec方法用Promise包一層。

          execFn.js:

                
                module.exports?=?(c?=?conn)?=>?{
          ????return?(command)?=>?{
          ????????return?new?Promise((resolve,?reject)?=>?{
          ????????????c.exec(command,?(err,?stream)?=>?{
          ????????????????if?(err)?{
          ????????????????????reject(err)
          ????????????????????return
          ????????????????}
          ????????????????let?result?=?''
          ????????????????stream.on('close',?()?=>?{
          ????????????????????resolve(String(result))
          ????????????????}).on('data',?(data)?=>?{
          ????????????????????//data數(shù)據(jù)是Buffer類型,需要轉(zhuǎn)化成字符串
          ????????????????????result?+=?data
          ????????????????})
          ????????????})
          ????????})
          ????}
          }

          包一層后,再執(zhí)行命令:

                
                const?execFn?=?require('./execFn.js')

          module.exports?=?(config,?conn)?=>?{
          ????conn.on('ready',async?()=>{
          ??????const?exec?=?execFn(conn)
          ??????const?result?=?await?exec('docker?ps?-a?-q')
          ??????//...
          ??????exec(`docker?logs?--tail?200?${id}`)
          ????})
          ????//...
          }

          這樣代碼會顯得更整潔點,使用也更方便。

          解決登陸遠(yuǎn)程服務(wù)器帳號權(quán)限不足,在使用sudo命令時,如何自動輸入密碼,可行方案有兩種:

          • 簡單粗暴,直接使用root帳號密碼進(jìn)行登陸,這樣即可不用考慮如何跳過密碼輸入的交互
          • 使用shell管道命令echo '密碼' | sudo -S 命令

          root帳號密碼團(tuán)隊不能給到我,所以我采用了后者來解決。

          shell實現(xiàn)自動輸入密碼方法不只有使用管道命令echo '密碼' | sudo \-S 命令,還有其他的方法,但它在自動巡檢的場景中是最合適的,它不需要額外要求服務(wù)器下載其他工具包,像expect指令它就需要安裝expect包。巡檢不只巡檢一臺服務(wù)器,如果每臺都安裝expect包,這工作量也煩人。

          未自動輸入密碼:

          1efaa04c45dd3c46fc8c9705782e184c.webp

          自動輸入密碼:

          5deab5ce320d1ecba7aa083e1f38cfed.webp

          至此,自動化巡檢難點之處已解決,下面的工作就是以執(zhí)行shell命令返回的結(jié)果判斷服務(wù)器狀態(tài)是否正常,如:團(tuán)隊巡檢文檔規(guī)定當(dāng)執(zhí)行 docker info |grep \-A 5 "WARNING"時,如果有返回結(jié)果則為異常。

                
                //...
          const?before?=?`echo?"${config.password}"?|?sudo?-S?`
          exec(before?+?'docker?info?|grep?-A?5?"WARNING"').then((content)?=>?{
          ????if?(content)?{
          ????????rol[2]?=?'異常'
          ????}
          })
          //...

          該部分邏輯以團(tuán)隊巡檢文檔內(nèi)容為準(zhǔn),不過多贅述,該部分代碼在sshServer.js文件。

          sshServer.js地址:

          https://github.com/CatsAndMice/blog/blob/main/ssh/sshServer.js

          為了做到巡檢多臺服務(wù)器的目的,巡檢相關(guān)的邏輯代碼使用函數(shù)進(jìn)行包裹并從sshServer.js文件中導(dǎo)出。

          sshServer.js:

                
                const?execFn?=?require('./execFn.js')
          //...
          module.exports?=?(config,?conn)?=>?{
          ????return?new?Promise((resolve,?reject)?=>?{
          ????????const?exec?=?execFn(conn)
          ????????conn.on('ready',?async?(err)?=>?{
          ????????????if?(err)?reject(err)
          ????????????console.log('連接成功');
          ????????????//省略
          ????????????
          ????????}).connect({
          ????????????...config,
          ????????????readyTimeout:?5000
          ????????});
          ????})

          }

          所有的服務(wù)器帳號密碼均放置在config.json文件中:

                
                [
          ????{
          ????????"host":?"xx",
          ????????"port":?"xx",
          ????????"username":?"xx",
          ????????"password":?"xx"
          ????}
          ????//...
          ]

          config.json文件涉及到服務(wù)器信息需要保密,config.json文件不會被提交至倉庫。

          目錄結(jié)構(gòu)如下:

          988423d0a8fe8a3e1ca4bff8265d9ff3.webp

          最后,將巡檢的結(jié)果數(shù)據(jù)整理成表格

          思路是一樣的。

          index.js

                
                const?{?Client?}?=?require('ssh2');
          const?configs?=?require('./config.json')
          const?sshServer?=?require('./sshServer.js');
          const?fs?=?require('fs');
          const?path?=?require('path');
          const?nodeXlsx?=?require('node-xlsx')

          const?promises?=?[]

          //表格數(shù)據(jù)?二維數(shù)組
          const?tables?=?[
          ????['服務(wù)器ip',?'docker是否正常運行',?'docker遠(yuǎn)程訪問',?'Docker日志是否有報錯信息']
          ]

          configs.forEach((config)?=>?{
          ????const?conn?=?new?Client();
          ????promises.push(sshServer(config,?conn))
          })

          Promise.all(promises).then((data)?=>?{
          ????data.forEach((d)?=>?{
          ????????if?(Array.isArray(d))?{
          ????????????tables.push(d)
          ????????}
          ????})
          ????//生成xlsx表格
          ????const?buffer?=?nodeXlsx.build([{?name:?'巡檢',?data:?tables?}])
          ????const?file?=?path.join(__dirname,?'/server.xlsx')
          ????fs.writeFileSync(file,?buffer,?'utf-8')
          })

          巡檢結(jié)果統(tǒng)一暫存于tables數(shù)組中,以便導(dǎo)出。

          實現(xiàn)快捷指令巡檢

          使用命令行巡檢還是太累了。最好是鼠標(biāo)點下自動觸發(fā)自動巡檢。

          我們可以借助Mac快捷指令自定義再簡化下。

          880d8e074011434cccab65b5f99b9e30.webp

          快捷指令可以運行Shell。這樣只需要編寫一個名字叫做【巡檢服務(wù)器】的快捷指令。

          6264445ae5b51a0c769ca1cd83339b11.webp

          運行Shell后,以WPS打開server.xlsx文件。

          e1c79863cf7330e5f2f7ed178e4ee46d.webp6bfda5ce5315dc4fa517dbf6007ae302.webp

          快捷指令添加至訪達(dá)。

          5b3b23ac76c3cd18c18878ea64d9ee80.webp

          這樣就可以輕松實現(xiàn)自動巡檢服務(wù)器功能了。

          總結(jié)

          文章靈感來源于工作,通過使用Node.js+Shell+ssh2做到自動連接登陸遠(yuǎn)程服務(wù)器,運行相關(guān)Shell命令,檢查服務(wù)器程序運行是否正常等情況。

          對于程序員來說,懶,才是第一生產(chǎn)力!!!

          - EOF -


          26a3ee98d6b27e53e74242aa388c4a91.webp

          加主頁君微信,不僅前端技能+1

          fd86c3506d992d311612bc018326f2df.webp29770489f54ac26d1a9a6ff60a3f6753.webp

          主頁君日常還會在個人微信分享前端開發(fā)學(xué)習(xí)資源技術(shù)文章精選,不定期分享一些有意思的活動崗位內(nèi)推以及如何用技術(shù)做業(yè)余項目

          加個微信,打開一扇窗



          推薦閱讀??點擊標(biāo)題可跳轉(zhuǎn)

          1、圖文并茂:瀏覽器工作原理

          2、前端正確處理“文字溢出”的思路

          3、前端加載超大圖片(100M以上)實現(xiàn)秒開解決方案


          覺得本文對你有幫助?請分享給更多人

          推薦關(guān)注「前端大全」,提升前端技能

          點贊和在看就是最大的支持 ??

          瀏覽 38
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲手机在线 | 影音先锋男人网 | 被操视频网站 | 婷婷五月五A片 | 无码内射网 |