1、批量巡檢流程
通常腳本由三部分組成,包括:
(1)巡檢腳本:實作對性能資訊或名額的采集與回傳;
(2)巡檢腳本下發腳本:由主伺服器通過Except工具分别根據待巡檢伺服器資訊清單中的賬号密碼連接配接對應主機,将腳本下發至對應主機,再目标主機上執行巡檢腳本;
(3)待巡檢伺服器資訊清單:包括待巡檢伺服器的管理IP和登陸密碼。
由于需要擷取伺服器軟硬體和性能資訊,則需要獲得各伺服器的root權限;
由于巡檢資訊需要回傳至主伺服器,則需要支援SCP指令;
由于實作需要主機間互動,則需要各主機之間支援Expect工具。
2、環境參數準備
2.1相關伺服器資訊
(1)日志上傳目标伺服器IP位址:作為參數輸入或在腳本中預先設定,例如192.168.52.3;
(2)日志上傳目标伺服器root密碼:作為參數輸入或在腳本中預先設定,例如123456;
(3)待巡檢伺服器資訊清單:檔案格式,作為參數輸入或在腳本中預先設定,每一行為一台伺服器的IP位址和密碼,例如:

(4)伺服器支援Expect工具:
2.2日志存儲路徑
日志上傳存儲路徑:/tmp/ checklog_directory
本地日志存儲路徑:/tmp/ local_serverlog_directory
巡檢腳本檔案存儲路徑:/tmp/check_script
3、Expect工具
Expect是建立在tcl基礎上的用來進行自動化控制和測試的工具。
主要解決shell腳本中不可互動的問題。通常用于需要手動輸入資料的場景,可在腳本中使用expect來實作自動化。
通常遠端登入Linux伺服器時,運作指令、腳本或程式時,這些指令、腳本或程式都需要在伺服器回顯某些提示符後,再次從終端輸入某些繼續運作的指令,由于回顯提示符要求不固定,則這些輸入都需要人為的手工進行。
例如正常遠端登入Linux伺服器和首次遠端登入時伺服器的響應步驟并不相同。此時則需要使用expect工具根據程式的提示,模拟标準輸入提供給程式,進而實作自動化互動執行。
3.1、指令
Expect工具工作原理即為根據回顯内容進行判斷,進而執行下一步指令。其主要指令包括:
指令 | 說明 |
---|---|
spawn | 啟動新的互動程序, 後面跟指令或者指定程式 |
expect | 從程序中接收資訊, 如果比對成功, 就執行expect後的動作 |
send | 向程序發送字元串 |
send exp_send | 用于發送指定的字元串資訊 |
exp_continue | 在expect中多次比對就需要用到 |
send_user | 用來列印輸出 相當于shell中的echo |
interact | 允許使用者互動 |
exit | 退出expect腳本 |
eof | expect執行結束, 退出 |
set | 定義變量 |
puts | 輸出變量 |
set timeout | 設定逾時時間 |
3.2、安裝
(1)安裝tcl:yum install -y tcl tclx tcl-devel
(2)安裝expect:yum –y install expec
3.3、測試
(1)測試腳本
(2)測試結果
4、巡檢腳本實作
4.1、腳本實作
檔案名為:check_command.sh
#!/bin/bash
#
#檢查是否為root權限,否則退出
[ $(id -u) -gt 0 ] && echo "please used the root or sudo " && exit 1
#設定參數
main_ipaddr=$1 #日志上傳伺服器IP
main_passwd=$2 #日志上傳伺服器密碼
main_log_path="/tmp/checklog_directory" #日志上傳路徑
local_log_path="/tmp/local_serverlog_directory" #本地日志存儲路徑
LogName=`ifconfig | grep "inet"|sed '2,4d' |awk -F" " '{print $2}' ``echo "-"``date +%y%m%d%H%M%S` #構造日志名稱
#擷取系統資訊
function get_status(){
echo -e "#系統基礎資訊:"
OS=` head -n 1 /etc/centos-release ` #系統版本
Kernel=` uname -r ` #系統核心
Hostname=` hostname ` #主機名稱
Default_lang=` echo $LANG ` #預設語言
Time=$(date +'%Y-%m-%d %H:%M:%S') #目前時間
Logintime=$(who -b |awk '{print $3,$4}') #此使用者登入時間
Uptime=$(uptime|awk '{print $3}'|awk -F "," '{print $1}') #系統已運作時間
Ipaddr=$(ifconfig |grep 'inet ' |awk {'print$2'}|grep -v '127.0.0.1') #IP位址
Netmask=$(ifconfig|grep "inet"|sed '2,4d'|awk -F " " '{print $4}') #掩碼
cpu_info=$(cat /proc/cpuinfo|grep "name"|cut -d: -f2|awk '{print "*"$1,$2,$3,$4}'|uniq -c|sed 's/^[ \t]*//g') #CPU資訊
cpu_type=$(grep 'model name' /proc/cpuinfo | uniq | awk -F":" '{print $2}') #CPU型号
cpu_arch=$(uname -m) #CPU架構
cpu_number_physical=$(grep 'physical id' /proc/cpuinfo | sort | uniq | wc -l) #實體CPU數
cpu_number_vir=$(grep 'processor' /proc/cpuinfo | wc -l) #邏輯CPU數
cpu_cores=$(grep 'cores' /proc/cpuinfo | uniq | awk -F":" '{print $NF}' ) #每CPU核心數
Sigle_memory_capacity=$(dmidecode|grep -P -A5 "Memory\s+Device"|grep "Size"|grep -v "Range"|grep '[0-9]'|awk -F ":" '{print $2}'|sed 's/^[ \t]*//g') #單記憶體容量
Maximum_memory_capacity=$(dmidecode -t 16|grep "Maximum Capacity"|awk -F ":" '{print $2}'|sed 's/^[ \t]*//g') #最大存儲容量
Number_of_memory_slots=$(dmidecode -t 16|grep "Number Of Devices"|awk -F ":" '{print $2}'|sed 's/^[ \t]*//g') #記憶體卡槽數
Memory_total=$(cat /proc/meminfo|grep "MemTotal"|awk '{printf("MemTotal:%1.0fGB\n",$2/1024/1024)}'|awk -F ":" '{print $2}') #理論總記憶體
Product_name=$(dmidecode|grep -A10 "System Information"|grep "Product Name"|awk -F ":" '{print $2}'|sed 's/^[ \t]*//g') #記憶體品牌
Process_numbers=$(top -d 2 -n 1 -b|grep "Tasks" |awk -F':' '{print $2}'|awk -F" " '{print $1}') #總程序數
Process_running=$(top -d 2 -n 1 -b|grep "Tasks"|awk -F "[: ,]" '{print $8}') #活躍程序數
Process_sleeping=$(top -d 2 -n 1 -b|grep "Tasks"|awk -F "[: ,]" '{print $11}') #睡眠程序數
Process_stoping=$(top -d 2 -n 1 -b|grep "Tasks"|awk -F "[: ,]" '{print $16}') #停止程序數
Process_zombie=$(top -d 2 -n 1 -b|grep "Tasks"|awk -F "[: ,]" '{print $21}') #僵屍程序數
cpu_load=$(uptime | awk '{for(i=6;i<=NF;i++) printf $i""FS;print ""}') #系統負載
cpu_load_number=$(uptime | awk -F"load average:" '{print $2}' | awk -F"," '{print $1}' | awk -F"." '{print $1}' |sed 's/^[ \t]*//g') #擷取近1分鐘占用CPU核心數
cpu_user_space=$(top -d 2 -n 1 -b|grep 'C[Pp][Uu]'|head -1|awk -F "[:,]" '{print $2}'|awk -F" " '{print $1}') #使用者空間占用CPU的百分比
cpu_system_space=$(top -d 2 -n 1 -b|grep 'C[Pp][Uu]'|head -1|awk -F "[:,]" '{print $3}'|awk -F" " '{print $1}') #核心空間占用CPU的百分比
cpu_idle=$(top -d 2 -n 1 -b|grep C[Pp][Uu]|grep id|awk -F"," '{print $4}'|awk -F" " '{print $1}') #空閑CPU
cpu_wait=$(top -d 2 -n 1 -b|grep 'C[Pp][Uu]'|head -1|awk -F "[:,]" '{print $6}'|awk -F" " '{print $1}') #等待占用CPU的百分比
Pem_totle_m=$(free -m |grep "Mem" |awk -F" " '{print $2"M"}') #實際總實體記憶體M
Mem_free_m=$(free -m |grep "Mem" |awk -F" " '{print $4"M"}') #實際可用記憶體M
Mem_used=$(top -d 2 -n 1 -b|grep "Mem"|head -1|sed 's/[:,]/ /g'|awk -F" " '{print $7}'|awk '{printf("%dM\n",$1/1024)}') #使用者緩存容量
Mem_free=$( top -d 2 -n 1 -b|grep "Mem"|head -1|sed 's/[:,]/ /g'|awk -F" " '{print $5}'|awk '{printf("%dM\n",$1/1024)}') #空閑緩存容量
Mem_buffer=$(top -d 2 -n 1 -b|grep "Mem"|head -1|sed 's/[:,]/ /g'|awk -F" " '{print $9}'|awk '{printf("%dM\n",$1/1024)}') #緩沖緩存容量
Swap_total=$(top -d 2 -n 1 -b|grep "Swap"|awk -F" " '{print $3}'|awk '{printf("%dM\n",$1/1024)}') #交換分區總容量
Swap_free=$(top -d 2 -n 1 -b|grep "Swap"|awk -F" " '{print $5}'|awk '{printf("%dM\n",$1/1024)}') #交換分區空閑容量
Swap_used=$(top -d 2 -n 1 -b|grep "Swap"|awk -F" " '{print $7}'|awk '{printf("%dM\n",$1/1024)}') #交換分區占用容量
Swap_avail=$(top -d 2 -n 1 -b|grep "Swap"|awk -F" " '{print $9}'|awk '{printf("%dM\n",$1/1024)}') #交換分區可用容量
rx_pck=$(sar -n DEV 1 1 |sed '1,4d'|sed '2,$d'|awk -F" " '{print $3}') #網卡1每秒接收資料包數
tx_pck=$(sar -n DEV 1 1 |sed '1,4d'|sed '2,$d'|awk -F" " '{print $4}') #網卡1每秒發送資料包數
rx_kB=$(sar -n DEV 1 1 |sed '1,4d'|sed '2,$d'|awk -F" " '{print $5}') #網卡1每秒接收位元組數
tx_kB=$(sar -n DEV 1 1 |sed '1,4d'|sed '2,$d'|awk -F" " '{print $6}') #網卡1每秒發送位元組數
echo -e "系統版本|$OS
系統核心|$Kernel
主機名稱|$Hostname
預設語言|$Default_lang
目前時間|$Time
此使用者登入時間|$Logintime
系統已運作時間|$Uptime
IP位址|$Ipaddr
掩碼|$Netmask
CPU資訊|$cpu_info
CPU型号|$cpu_type
CPU架構|$cpu_arch
實體CPU數|$cpu_number_physical
邏輯CPU數|$cpu_number_vir
每CPU核心數|$cpu_cores
單記憶體容量|$Sigle_memory_capacity
最大存儲容量|$Maximum_memory_capacity
記憶體卡槽數|$Number_of_memory_slots
理論總記憶體|$Memory_total
記憶體品牌|$Product_name
總程序數|$Process_numbers
活躍程序數|$Process_running
睡眠程序數|$Process_sleeping
停止程序數|$Process_stoping
僵屍程序數|$Process_zombie
系統負載|$cpu_load
擷取近1分鐘占用CPU核心數|$cpu_load_number
使用者空間占用CPU的百分比|$cpu_user_space
核心空間占用CPU的百分比|$cpu_system_space
空閑CPU|$cpu_idle
等待占用CPU的百分比|$cpu_wait
實際總實體記憶體M|$Pem_totle_m
實際可用記憶體M|$Mem_free_m
使用者緩存容量|$Mem_used
空閑緩存容量|$Mem_free
緩沖緩存容量|$Mem_buffer
交換分區總容量|$Swap_total
交換分區空閑容量|$Swap_free
交換分區占用容量|$Swap_used
交換分區可用容量|$Swap_avail
網卡1每秒接收資料包數|$rx_pck
網卡1每秒發送資料包數|$tx_pck
網卡1每秒接收位元組數|$rx_kB
網卡1每秒發送位元組數|$tx_kB"
}
#擷取磁盤狀态資訊
function get_disk_status(){
echo -e "#磁盤使用情況:"
IFS="
"
for i in ` df -iP | sed 1d | awk '{print $(NF-1)"\t"$NF"\t"$(NF-2)}'`;do
DISK_UTILIZ=$( echo $i |awk '{print $1}') #使用率
MOUNT_DISK=$(echo $i |awk '{print $2}') #挂載點
DISK_FREE=$(echo $i |awk '{print $3}') #分區大小
if [[ $(echo $MOUNT_DISK | sed s/%//g) -gt 70 ]];then
echo "異常""("$MOUNT_DISK"使用率為"$DISK_UTILIZ",超過70%)"
else
continue
fi
done
df -hP |sed 1d |awk '{print $NF" ""分區"" ""剩餘空間"" "$(NF-2),"使用率"" "$(NF-1)}'
}
#擷取CPU狀态
function get_cup_status(){
echo -e "#CPU狀态"
cpu_number=$(cat /proc/cpuinfo |grep name |cut -d: -f2 |uniq -c |awk '{print $1}') #提取CPU核數
cpu_load_number=$(uptime | awk -F"load average:" '{print $2}' | awk -F"," '{print $1}' | awk -F"." '{print $1}' |sed 's/^[ \t]*//g') #擷取近1分鐘占用CPU核心數
if [[ $cpu_load_number -lt $cpu_number ]];then
CPU_STATUS=正常
else
CPU_STATUS=異常
fi
}
#擷取記憶體狀态
function get_memory_status(){
echo -e "#記憶體狀态:"
MEM_TOTLE=$(free -m |grep "Mem" |awk -F" " '{print $2}') #總實體記憶體
MEM_FREE=$(free -m |grep "Mem" |awk -F" " '{print $4}') #可用記憶體
MEM_TOTLE_M=$(free -m |grep "Mem" |awk -F" " '{print $2"M"}') #總實體記憶體M
MEM_FREE_M=$(free -m |grep "Mem" |awk -F" " '{print $4"M"}') #可用記憶體M
MEM_USED=$(echo $(($MEM_TOTLE-$MEM_FREE))) #總占用記憶體
PERCENT=$(printf "%d%%" $(($MEM_USED*100/$MEM_TOTLE))) #記憶體占用率
PERCENT_N=$(echo $PERCENT|sed s/%//g) #記憶體占用率數值
if [[ $PERCENT_N -lt 80 ]];then
MEM_STATUS=正常
else
MEM_STATUS=異常
fi
echo -e "$MEM_STATUS\n"總記憶體大小:"$MEM_TOTLE_M,"剩餘記憶體大小:"$MEM_FREE_M,"記憶體使用率"$PERCENT"
}
#檢測是否存在暴力破解密碼
function get_ssh_deny(){
echo -e "#是否存在暴力破解密碼:"
SYSTEM_TYPE=$(head -n 1 /etc/os-release |awk -F"=" '{print $2}'|awk -F" " '{print $1}'|sed 's/"//g') #擷取系統類型
if [[ $SYSTEM_TYPE = 'CentOS' ]];then
SSH_SUM=$(cat /var/log/secure |grep "authentication failure" |wc -l)
SSH_DIY=10
if [[ $SSH_SUM -gt $SSH_DIY ]];then
echo ""請注意,密碼嘗試次數為"$SSH_SUM"
else
echo "正常"
fi
else
echo "系統類型不支援"
fi
}
#檢查開機自啟任務
function get_auto_start_status() {
echo -e "#開機自啟任務"
conf=$(grep -v "^#" /etc/rc.d/rc.local | sed '/^$/d')
echo "$conf"
}
#檢查可登陸使用者與無密碼的使用者
function get_user(){
echo -e "#可登陸使用者與無密碼的使用者:"
/usr/bin/w #目前線上使用者資訊
user=$(cat /etc/passwd|awk -F":" '$7 ~"/bin/bash"{print $1}') #可登陸使用者
echo -e "可登陸使用者:\n$user"
echo "未設定密碼使用者:"
for i in $user ;do
cat /etc/shadow|grep $i|awk -F":" '$2 ~"!!"{print $1,$2}' #未設定密碼的使用者
done
}
#檢查計劃任務
function get_corn(){
echo -e "#計劃任務:"
user=` cat /etc/passwd |awk -F":" '$7 ~"/bin/bash"{print $1}' ` #擷取使用者清單
for cronuser in $user;do
crontab -l -u $cronuser > /dev/null 2>&1
if [ $? -eq 0 ];then
echo "$cronuser"
echo "######"
crontab -l -u $cronuser | grep -vE "^#|^$"
echo "######"
else
echo "使用者$cronuser不存在計劃任務"
fi
done
}
#檢查程序狀态
function get_process(){
echo -e "#是否存在僵屍程序:"
if [[ $(ps -aux | grep Zs |grep -v grep | wc -l ) -ge 1 ]];then #檢查僵屍程序是否存在
echo "存在僵屍程序"
ps -aux |grep Zs |grep -v grep #輸出僵屍程序資訊
else
echo "不存在僵屍程序"
fi
echo -e "記憶體占用率TOP10進行清單:"
ps aux | awk '{print $2, $4, $6, $11}' | sort -k3rn | head -n 10 #擷取記憶體占用率TOP10進行清單
echo -e "CPU使用率TOP10進行清單:"
top b -n1 | head -17 | tail -11 #擷取CPU使用率TOP10進行清單
}
#擷取系統巡檢資訊并寫入本地巡檢日志
function check_gather_log(){
if [ ! -x "$local_log_path" ];then #判斷本地日志存儲路徑是否存在
mkdir "$local_log_path" #建立本地日志存儲路徑
fi
if [ -f "$local_log_path/$LogName.log" ];then #判斷日志檔案是否存在
rm -rf $local_log_path/$LogName.log #删除已存在日志檔案
fi
touch $local_log_path/$LogName.log #建立日志檔案
get_status>>$local_log_path/$LogName.log #調用對應方法并将巡檢結果寫入日志檔案
get_disk_status>>$local_log_path/$LogName.log
get_cup_status>>$local_log_path/$LogName.log
get_memory_status>>$local_log_path/$LogName.log
get_ssh_deny>>$local_log_path/$LogName.log
get_auto_start_status>>$local_log_path/$LogName.log
get_user>>$local_log_path/$LogName.log
get_corn>>$local_log_path/$LogName.log
get_process>>$local_log_path/$LogName.log
}
#執行巡檢
check_gather_log
#執行日志回傳
scp_log_to_main_server="scp $local_log_path/$LogName.log root@$main_ipaddr:$main_log_path"
#調用Expect工具輔助日志回傳的密碼驗證過程
/usr/bin/expect<<EOF
set timeout 20
spawn $scp_log_to_main_server
expect {
"*yes/no)?"
{
send "yes\n"
"*password:*" {send "main_passwd\n"}
}
"*password:"
{
send "$main_passwd\n"
}
}
expect "*password:" { send "$main_passwd\n" }
expect "100%"
expect eof
EOF
4.2、腳本準備
設定檔案可執行權限:chmod +x check_command.sh
複制檔案至目标路徑:cp check_command.sh /tmp/check_script/
5、下發腳本實作
5.1、在目标伺服器上建立腳本存儲路徑
檔案名:check_expect_mkdir.sh
#!/usr/bin/expect
set timeout 30
set server_ip [lindex $argv 0]
set server_passwd [lindex $argv 1]
set CheckScriptPath [lindex $argv 2]
spawn ssh -o StrictHostKeyChecking=no root@$server_ip
expect {
"*yes/no)?"
{
send "yes\n"
"*password:*" {send "server_passwd\n"}
}
"*password:*"
{
send "$server_passwd\n"
}
}
expect "*password:*" { send "$server_passwd\n" }
expect "*#" { send "mkdir $CheckScriptPath\n"}
expect eof
exit
5.2、将巡檢腳本下發至目标伺服器
檔案名:check_expect_upload.sh
#!/usr/bin/expect
set timeout 30
set server_ip [lindex $argv 0]
set server_passwd [lindex $argv 1]
set check_script_path [lindex $argv 2]
spawn scp $check_script_path/check_command.sh root@$server_ip:$check_script_path
expect {
"*yes/no)?"
{
send "yes\n"
"*password:*" {send "server_passwd\n"}
}
"*password:*"
{
send "$server_passwd\n"
}
}
expect "*password:*" { send "$server_passwd\n" }
expect "100%"
expect eof
exit
5.3、在目标伺服器上執行巡檢腳本
檔案名:check_expect_do.sh
#!/usr/bin/expect
set timeout 20
set server_ip [lindex $argv 0]
set server_passwd [lindex $argv 1]
set check_script_path [lindex $argv 2]
set main_ipaddr [lindex $argv 3]
set main_passwd [lindex $argv 4]
spawn ssh -o StrictHostKeyChecking=no root@$server_ip
expect {
"*yes/no)?"
{
send "yes\n"
"*password:*" {send "server_passwd\n"}
}
"*password:"
{
send "$server_passwd\n"
}
}
expect "*password:" { send "$server_passwd\n" }
expect "*#" { send "cd $check_script_path;./check_command.sh $main_ipaddr $main_passwd\n" }
expect eof
exit
5.4、控制腳本下發與執行
檔案名:check_ctrl2.sh
#!/bin/bash
#
#加載參數
login_info=$1 #參數1,待巡檢伺服器檔案
main_ipaddr=$2 #參數2,日志上傳目标伺服器IP位址
main_passwd=$3 #參數3,日志上傳目标伺服器登陸密碼
main_log_path="/tmp/checklog_directory" #日志上傳目标伺服器日志存儲路徑
check_script_path="/tmp/check_script" #巡檢腳本檔案存儲路徑
#檢測參數數量,如果參數數量異常則輸出提示
if [ $# -ne 3 ];then
echo -e "parameters if fault!\n"
exit;
fi
#解析登陸清單
cat $login_info |while read line
do
server_ip=` echo $line |awk '{print $1}' ` #讀取目标伺服器IP
server_passwd=` echo $line |awk '{print $2}' ` #讀取目标伺服器密碼
echo -e "\n######目前伺服器:$server_ip######\n"
./check_expect_mkdir.sh $server_ip $server_passwd $check_script_path #建立腳本存儲路徑
./check_expect_upload.sh $server_ip $server_passwd $check_script_path #下發測試腳本
./check_expect_do.sh $server_ip $server_passwd $check_script_path $main_ipaddr $main_passwd #執行巡檢腳本
done
6、測試執行
腳本執行:./check_ctrl2.sh server_info_file.txt 192.168.52.3 123456
執行結果:
日志内容: