自动化服务器巡检的实现过程

由于上级的工作安排,今年的5月份开始每天都需要做一些服务器信息的巡检;

对方交接时,完全是通过手敲指令、手动记录来实现的;

熟悉了一段时间,把流程和记录方式优化了一下(依旧是手动);

后来听说Y哥那边在部署总行提供的新的监控系统,基本可以替代巡检;

就打算坚持一下,不研究自动化巡检了,等监控上线;

等来等去就等了4个月。。。。。

新的监控一直不能正式上线,告警很多很杂,项目组一直在做优化和过滤;

还好这4个月一直不太忙,每天花点时间巡检好像也没什么;

进入三季度最后一个月突然忙了起来;

决定不等了,研究一下脚本;

这篇随笔就用来记录和整理自动化服务器巡检的实现过程吧。


自动化巡检系统大致分为几个部分:

1. 常规服务器巡检脚本;

2. 定制服务器巡检脚本;

3. 脚本的下发;

4. 巡检信息的统一收集;

5. 巡检信息每日自动发送到邮箱。


 先上一张结构图:

第一次做这种多服务器互相发送文件的系统真的是没有经验;

最一开始应该先考虑网络结构,因为图中的Windows管理机并不能和特殊网段服务器通信,导致后面加入特殊网段后,不得不用堡垒机下发脚本和收集结果,导致结构很混乱,不过还好,特殊网段服务器不算多;

如果一开始就用堡垒机作为下发和收集的核心,整体结构会清晰很多。


1. 常规服务器巡检脚本

常规服务器巡检脚本是在一台Linux服务器上编写、测试,再上传到Windows管理机的。需要巡检的服务器每天定时去提供SFTP服务的Windows管理机上下载脚本,方便后续脚本更新,实现此功能的脚本(getshell.sh)如下:

#!/bin/bash
# ----- 从SFTP下载巡检脚本 -----

sftpip=XX.XXX.XX.XXX
user=XXXXXXX
downDir=/patrol/shell

# ----- 随机延时约0-30s后下载 -----

sleeptime=`expr $RANDOM / 1000`
sleep $sleeptime

sftp $user@$sftpip << EOF
cd $downDir
lcd /root/patrol
get patrol.sh
bye
EOF

SFTP脚本无法像FTP一样直接在脚本中写入密码,需要先将访问者的公钥提供给SFTP服务端,生成公钥的方法我参考的是http://bbs.chinaunix.net/thread-508290-1-1.html

在Windows管理机上我使用的是FreeSSHD提供的SFTP服务,把公钥放在软件配置中指定的位置就可以了。

因为涉及到的服务器比较多,好几十条公钥,所以合成公钥是用bat脚本实现的:

@echo off
dir
copy *.pub result.pub
echo DONE
pause

脚本中添加了一个随机延时功能,原因是一开始同步操作多个服务器进行SFTP访问时,出现了超过SFTP最大连接数的现象。因为每个服务器访问SFTP的时间很短,因此随机延时0-30s就够了。其中$RANDOM是系统自带的随机数变量。

写好脚本后在服务器的crontab中添加了一行定时任务,每天7点执行getshell.sh:

0 7 * * * /bin/bash /root/patrol/getshell.sh > /dev/null 2>&1

后面的 > /dev/null 2>&1 是不写定时任务日志的意思,因为功能比较简单,感觉没必要写日志。crontab的写法参考的是菜鸟教程:https://www.runoob.com/w3cnote/linux-crontab-tasks.html

下面就是巡检脚本(patrol.sh)的主体了:

#!/bin/bash
PATH=/sbin:/usr/sbin:/usr/local/sbin:/root/bin:/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games
# ----- 巡检脚本 V2.0 -----

# ----- 定义变量:上传SFTP地址、用户名、密码、路径 -----

cd /root/patrol
sftpip=XX.XXX.XX.XXX
user=XXXXXXX
remoteDir=/patrol/upload

# ----- 定义变量:本机信息、巡检参数、时间参数 -----

localip=`ifconfig | grep 'XX.XXX.XX' | grep Bcast | awk '{print $2}' |awk -F: '{print $2}'`
loadavg=`top -b -n 1 | grep 'load average' | awk '{print $11 $12 $13 $14 $15}'`
cpuid=`top -b -n 1 | grep %Cpu | awk '{print $8 $9}'`
diskfree=`df -h | awk '{print $5}'`
memtotal=`free -l | grep Mem | awk '{print $2}'`
memocp=`free -l | grep buffers/cache | awk '{print $3}'`
memratio=`expr $memocp \* 100 / $memtotal`

if [ $memratio -ge 80 ]
then
    memwarn=内存过高!!!
else
    memwarn=内存正常
fi

date=$(date "+%Y%m%d")
filename=$date"_"$localip.txt

# ----- 主程序 -----

echo -e $date-----$localip-----$loadavg-----$cpuid-----$diskfree-----$memratio-----$memwarn >$filename

# ----- 随机延时约0-30s后上传SFTP -----

sleeptime=`expr $RANDOM / 1000`
sleep $sleeptime

sftp $user@$sftpip << EOF
cd $remoteDir
put $filename
bye
EOF

rm $filename

整个脚本中,比较难的就是各个巡检项变量的定义,从上到下依次是本机IP、平均负载、CPU空闲、逻辑卷占用、内存总量、内存占用、内存占比,一共七项。shell中复杂变量的定义需要用··框起来,就是键盘上1左边的点;

其中grep 'XXX'是为了抓取需要的行,awk '{print $X}'是为了抓取这一行内需要的位置,memratio这一项中的expr 'XXX'是为了用上面的memtotalmemocp两个变量计算内存占比,然后加了一个内存是否过高的判断;

主程序只有一行,就是把各个变量echo到一个文件中,文件以当天的日期_服务器IP.txt命名。echo -e的目的是激活转义字符。

后面还是添加了延时和SFTP上传,上传后删除掉了服务器上的文件,防止时间长了占用太多服务器磁盘。

写完上述内容后,手动运行patrol.sh就可以实现功能了,但是添加了crontab定时任务后发现不生效:

30 7 * * * /bin/bash /root/patrol/patrol.sh > /dev/null 2>&1

一开始认为是crontab写得有问题,后来发现是没有在脚本中添加环境变量,即开头的PATH这一行,参考的是:https://www.cnblogs.com/huskiesir/p/9970291.html

因为脚本中用到的变量比较多,所以干脆把系统提示的所有环境变量都扔进去了。。。

目前巡检脚本的输出结果是这样的:

20211014-----XX.XXX.XX.XX-----average:0.18,0.11,0.09-----63.6id,-----Use% 1% 1% 10% 0% 17% 51% 59% 1% 9% 1% 33% 38% 27% 10% 5% 1% 0%-----24-----内存正常

只对内存的状态进行了一个简单的判断,后续优化的过程中打算把每一项的状态都判断出来,这样看巡检结果就比较方便了,尤其是逻辑卷占用这块,现在看着简直头疼,不过这也比之前每天点开一大堆服务器手动巡检强太多了。


2. 定制服务器巡检脚本

除了每台服务器都需要的常规项巡检,某些服务器还需要针对某个服务或者某些定时生成的文件进行巡检,实现起来其实就是一些特殊变量的定义,最后还是全部echo到一个txt中统一收集,下面一一进行列举:

查看进程

custom_patrol=`ps -ef | grep XXX | grep XXX`

没什么好说的,就是看一些进程在不在,输出结果就是ps -ef的某一行。

② 查看文件

custom_patrol=`ls -l /某路径 | grep 文件关键字`

也没啥好说的,就是看一些文件在不在,grep后面可以跟一些日期变量啥的,可以抓取文件名中有日期的文件。一开始我还不知道脚本中不能用ll这个命令,查了一下才知道ll就是ls -l的缩写。。。

③ 查看consul服务数

custom_patrol=`/某路径/consul catalog services | wc -l`

查看consul微服务的个数,后面的 | wc -l 是计数的意思,不加的话会列出服务的列表。

④ telnet端口

telnet本来是一个很简单的指令:

telnet XX.XXX.XX.XXX 某端口

但是用脚本实现起来就要稍微复杂一点了,因为telnet本身是类似于FTP、SSH、SFTP这类让系统进入某个客户端的指令。在脚本中,进去容易,出来就得靠EOF了,所以实现起来是这样的:

custom_patrol=`telnet XX.XXX.XX.XXX 某端口 << EOF
exit
EOF`

没错EOF就是为了多加一个exit让系统退出来。。。这样的话就把telnet的情况定义成了变量,实现了端口检测。

⑤ Redis数据库状态检测

custom_patrol=`redis-cli -c -p 某端口 -h XX.XXX.XX.XXX -a '密码'` << EOF
cluster nodes
ping
exit
EOF`

同样用到了EOF格式,cluster nodes是为了检查主从节点,ping是为了pong,exit是为了退出。

⑥ Oracle数据库表空间占用查询 

custom_patrol=`su - oracle -c "sqlplus 用户名/密码 << EOF
select round(sum(bytes)/1024/1024/1024,2)||'G' from user_segments where TABLESPACE_NAME = '表名';
exit
EOF
exit" | grep -E 'G|M' | grep -v Mining`

公司内的要求是Oracle数据库只能用oracle用户来启动,因此这个变量的声明首先要切换到oracle用户,在su - oracle后加-c "指令"添加切换用户后的指令,sqlplus的指令同样用EOF格式。

因为进入sqlplus后会显示一大堆东西,因此加了两个grep来缩小一下范围,一个是-E显示含有G或者M的行,一个-v是不显示含有Mining的行

⑦ 文件下载测试

这个功能一开始是想把下载信息定义成变量,然后echo这个变量来显示下载是否成功,后来感觉这样操作太麻烦了,直接下载这个文件,然后用ls -l检测文件是否存在(或是否为当天下载)即可。

curl -o download_test.zip http://XX.XXX.XX.XXX:某端口/路径/文件名\&name=n.zip
custom_patrol=`ls -lh | grep download_test.zip`

curl下载这行我一直没跑出来,后来是让开发小伙伴RCM帮忙改的,现在也没太搞懂,有空再研究下。。。


目前用到的一些需要定制的巡检基本就是这些。在echo某个多行变量到txt的时候,打开txt发现写入的时候忽略了换行,参考https://blog.csdn.net/jxfgh/article/details/6757488在echo后加了一个while循环,实现了换行:

echo -e "$XXX" > result.txt | while read i
do
    echo $i
done

3. 脚本的下发4. 巡检信息的统一收集都已经在1. 常规服务器巡检脚本的脚本中实现了,不再赘述。


5. 巡检信息每日自动发送到邮箱

每天每台服务器的巡检信息上传到Windows管理机后,需要进行汇总、上传FTP、归档这几个动作,通过bat脚本实现:

@echo off
copy nul all.tx
for %%a in (*.txt) do type %%a >>all.tx && echo. >> all.tx
ren all.tx *.txt
ren all.txt %date:~0,4%%date:~5,2%%date:~8,2%.tmp
del *.txt
ren %date:~0,4%%date:~5,2%%date:~8,2%.tmp %date:~0,4%%date:~5,2%%date:~8,2%.txt
ftp -i -s:"upload"
move %date:~0,4%%date:~5,2%%date:~8,2%.txt D:\FTPhome\patrol\history_data
del %date:~0,4%%date:~5,2%%date:~8,2%.txt

其中ftp命令文件upload:

open XX.XX.XX.XX
用户名
密码
lcd 本地路径
cd FTP路径
binary
put *.txt
bye

上传到FTP后,下一步就是下载到办公机然后通过邮件发送了,这个功能是通过Windows任务计划程序和Python实现的,首先通过定时任务启动bat去调用两个.py:

@echo off
cd 本地路径
python ftp_download.py
python auto_email.py
move %date:~0,4%%date:~5,2%%date:~8,2%.txt 本地路径\history_data

ftp_download.py(参考:https://www.cnblogs.com/Dahlia/p/10551929.html)

#coding=utf-8
import os
import time
from ftplib import FTP  # 引入ftp模块

class MyFtp:

    ftp = FTP()

    def __init__(self,host,port=21):
        self.ftp.connect(host,port)

    def login(self,username,pwd):
        self.ftp.set_debuglevel(2)  # 打开调试级别2,显示详细信息
        self.ftp.login(username,pwd)
        print(self.ftp.welcome)

    def downloadFile(self,localpath,remotepath,filename):
        os.chdir(localpath)   # 切换工作路径到下载目录
        self.ftp.cwd(remotepath)   # 要登录的ftp目录
        self.ftp.nlst()  # 获取目录下的文件
        file_handle = open(filename,"wb").write   # 以写模式在本地打开文件
        self.ftp.retrbinary('RETR %s' % os.path.basename(filename),file_handle,blocksize=1024)  # 下载ftp文件
        # ftp.delete(filename)  # 删除ftp服务器上的文件

    def close(self):
        self.ftp.set_debuglevel(0)  # 关闭调试
        self.ftp.quit()

if __name__ == '__main__':
    ftp = MyFtp('FTP地址')
    ftp.login('用户名','密码')
    date = time.strftime("%Y%m%d", time.localtime())
    ftp.downloadFile('本地路径','FTP路径','%s.txt' % date)
    ftp.close()

auto_email.py(参考:https://www.cnblogs.com/yufeihlf/p/5726619.html)

#coding: utf-8
  
import time
import smtplib    
from email.mime.multipart import MIMEMultipart    
from email.mime.text import MIMEText    
from email.mime.image import MIMEImage 
from email.header import Header   
    
#设置smtplib所需的参数
#下面的发件人,收件人是用于邮件传输的。
smtpserver = 'SMTP服务器'
username = '用户名'
password = '密码'
#成功开启IMAP/SMTP服务,在第三方客户端登录时,登录密码输入以下授权密码:
#HLSNDVBIOUQEUURU
sender = '发送者邮箱'
receiver = '接收者邮箱'
#若收件人为多个收件人
#receiver=['XXX@126.com','XXX@126.com']
date = time.strftime("%Y%m%d", time.localtime())

#subject = 'Automatic server patrol'
#通过Header对象编码的文本,包含utf-8编码信息和Base64编码信息。以下中文名测试ok
subject = '服务器巡检结果'
subject = Header(subject, 'utf-8').encode()
    
#构造邮件对象MIMEMultipart对象
#下面的主题,发件人,收件人,日期是显示在邮件页面上的。
msg = MIMEMultipart('mixed') 
msg['Subject'] = subject
msg['From'] = '发件人'
msg['To'] = receiver
#收件人为多个收件人,通过join将列表转换为以;为间隔的字符串
#msg['To'] = ";".join(receiver) 
#若无时间,就默认一般为当前时间,该值一般不设置
#msg['Date']='2012-3-16'

#构造文字内容   
text = "今日巡检结果"    
text_plain = MIMEText(text, 'plain', 'utf-8')    
msg.attach(text_plain)    

#构造图片链接
# sendimagefile=open(r'D:\pythontest\testimage.png','rb').read()
# image = MIMEImage(sendimagefile)
# image.add_header('Content-ID','<image1>')
# image["Content-Disposition"] = 'attachment; filename="testimage.png"'
# msg.attach(image)

#构造html
#发送正文中的图片:由于包含未被许可的信息,网易邮箱定义为垃圾邮件,报554 DT:SPM :<p><img src="cid:image1"></p>
# html = """
# <html>  
#   <head></head>  
#   <body>  
#     <p>Hi!<br>  
#        How are you?<br>  
#        Here is the <a href="http://www.baidu.com">link</a> you wanted.<br> 
#     </p> 
#   </body>  
# </html>  
# """    
# text_html = MIMEText(html,'html', 'utf-8')
# text_html["Content-Disposition"] = 'attachment; filename="texthtml.html"'   
# msg.attach(text_html)    


# 构造附件
sendfile=open('本地路径\%s.txt' % date,'rb').read()
text_att = MIMEText(sendfile, 'base64', 'utf-8') 
text_att["Content-Type"] = 'application/octet-stream'  
#以下附件可以重命名成aaa.txt(测试不成功) 
# text_att["Content-Disposition"] = 'attachment; filename="Python自动邮件附件测试.txt"'
#另一种实现方式
text_att.add_header('Content-Disposition', 'attachment', filename = "%s.txt" % date)
#以下中文测试不ok
#text_att["Content-Disposition"] = u'attachment; filename="中文附件.txt"'.decode('utf-8')
msg.attach(text_att)    
       
#发送邮件
smtp = smtplib.SMTP()    
smtp.connect(smtpserver)
#我们用set_debuglevel(1)就可以打印出和SMTP服务器交互的所有信息。
#smtp.set_debuglevel(1)  
smtp.login(username, password)    
smtp.sendmail(sender, receiver, msg.as_string())    
smtp.quit()

对两个.py的理解比较少,基本上调试了一下能够运行以后就没有再改动了,其中还遇到了ftplib使用报错计算机名为中文导致邮件发送报错两个问题,分别参考这两篇解决的:

https://blog.csdn.net/BobYuan888/article/details/82980817

https://blog.csdn.net/weixin_41278305/article/details/110943401

内网环境安装VSCode的离线插件参考了这两篇:

https://blog.csdn.net/l508742729/article/details/103543755

https://blog.csdn.net/sinat_36188088/article/details/105203338


至此自动化服务器巡检并且通过邮件发送巡检结果就基本实现了,后续考虑继续优化巡检脚本,增加判断语句,直接给出巡检项是否正常的结果,减少人工查看的部分。

posted @ 2021-09-18 16:40  BTday  阅读(1546)  评论(0编辑  收藏  举报