这是【Node.js实战】专栏内的第6篇文章,专栏是分享使用Node.js技术编写实用脚本技巧。
专栏现有文章:
欢迎读者关注【Node.js实战】专栏。
进入了新的一年,团队被分配了新的工作内容——每周巡检。
巡检工作简单,但需要人工重复性地登陆远程服务器、输入重复的命令,然后将命令的结果记录下来。每做一次估计花40
分钟,但要每周做,一年52周,一年下来就要花40*52=2080
分钟,这仅仅是团队一个人一年要花的时间。
不能这么玩呀,纯纯工具人,所以我一直在思考如何用程序帮我自动巡检掉。这篇文章的出现,说明我的想法方向是正确的,收益可观一年要花2080分钟,被我减到52 分钟。
如果再扩展程序帮助到团队,这个公式将从40*52*团队人数
变成1*52*团队人数
,时间等于金钱。
未自动巡检:
手动连接登陆远程服务器,再输入相应的命令获取结果,然后人工依据结果判断是否异常,相当麻烦,而且我要执行的命令不止一条。
自动巡检:
运行macOS笔记本创建好的快捷指令,它会自动巡检服务器,并且巡检完成后直接打开巡检结果表格。当然没有macOS依然可以,但就是没有快捷指令这步,需要自己执行程序。
完整源码:blog/ssh
自动化巡检思路简单,思路如下:
本地程序连接登陆远程服务器→本地shell命令远程执行→本地程序获取命令结果→结果数据整理成表格
实现过程中主要有以下两个难点:
sudo
命令时,如何自动输入密码解决Node.js本地运行程序如何连接登陆远程服务器:
社区已有的方案ssh2,它是用纯JavaScript为Node.js编写的SSH2客户端和服务器模块。可以使用它连接到远程服务器,并且ssh2提供了方法可以执行shell命令。
ssh2官方案例:
//... const { Client } = require('ssh2'); const conn = new Client(); conn.on('ready', () => { console.log('Client :: ready'); //执行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) => { //监听数据 console.log('STDOUT: ' + data); }).stderr.on('data', (data) => { console.log('STDERR: ' + data); }); }); }) //...
官方案例仅执行一条shell命令,当按照顺序依次执行一条以上的命令,官方的这个写法会非常麻烦。例如:首先执行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命令执行完成 再执行docker logs -f --tail 200 id */ conn.exec(`docker logs --tail 200 ${id}`,(err,stream)=>{ if (err) throw err; stream.on('close', () => { //如果命令再复杂点,还需要继续这样写下去 }).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数据是Buffer类型,需要转化成字符串 result += data }) }) }) } }
包一层后,再执行命令:
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}`) }) //... }
这样代码会显得更整洁点,使用也更方便。
解决登陆远程服务器帐号权限不足,在使用sudo
命令时,如何自动输入密码,可行方案有两种:
root
帐号密码进行登陆,这样即可不用考虑如何跳过密码输入的交互echo '密码' | sudo -S 命令
root
帐号密码团队不能给到我,所以我采用了后者来解决。
shell实现自动输入密码方法不只有使用管道命令echo '密码' | sudo -S 命令
,还有其他的方法,但它在自动巡检的场景中是最合适的,它不需要额外要求服务器下载其他工具包,像expect
指令它就需要安装expect包。巡检不只巡检一台服务器,如果每台都安装expect包,这工作量也烦人。
未自动输入密码:
自动输入密码:
至此,自动化巡检难点之处已解决,下面的工作就是以执行shell命令返回的结果判断服务器状态是否正常,如:团队巡检文档规定当执行 docker info |grep -A 5 "WARNING"
时,如果有返回结果则为异常。
//... const before = `echo "${config.password}" | sudo -S ` exec(before + 'docker info |grep -A 5 "WARNING"').then((content) => { if (content) { rol[2] = '异常' } }) //...
该部分逻辑以团队巡检文档内容为准,不过多赘述,该部分代码在sshServer.js文件。
为了做到巡检多台服务器的目的,巡检相关的逻辑代码使用函数进行包裹并从sshServer.js
文件中导出。
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 }); }) }
所有的服务器帐号密码均放置在config.json
文件中:
[ { "host": "xx", "port": "xx", "username": "xx", "password": "xx" } //... ]
在config.json
文件涉及到服务器信息需要保密,config.json
文件不会被提交至仓库。
目录结构如下:
最后,将巡检的结果数据整理成表格,如何将数据导出表格已有对应的文章实现说明【Node.js】写一个数据自动整理成表格的脚本
思路是一样的。
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 = [] //表格数据 二维数组 const tables = [ ['服务器ip', 'docker是否正常运行', 'docker远程访问', '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') })
巡检结果统一暂存于tables
数组中,以便导出。
使用命令行巡检还是太累了。 最好是鼠标点下自动触发自动巡检。
我们可以借助Mac快捷指令自定义再简化下。
快捷指令可以运行Shell。这样只需要编写一个名字叫做【巡检服务器】的快捷指令。
运行Shell后,以WPS打开server.xlsx
文件。
快捷指令添加至访达。
这样就可以轻松实现自动巡检服务器功能了。
文章灵感来源于工作,通过使用Node.js+Shell+ssh2做到自动连接登陆远程服务器,运行相关Shell命令,检查服务器程序运行是否正常等情况。
对于程序员来说,懒,才是第一生产力!!!
如果我的文章对你有帮助,你的👍就是对我的最大支持^_^。
本文作者:凌览
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!