對於磁盤的監控屬於最基礎的監控,但是很多時候往往因爲運維的疏忽而忽略監控磁盤,最終導致事故發生。希望讀到這篇文章的朋友,一定要把監控磁盤這件事重視起來。
本案例需求如下:
1)每分鐘檢測一次磁盤狀況
2)當磁盤空間使用率或者iNode使用率高於90%,需要發郵件告警,假設收件郵箱爲[email protected]
3)統計使用率超過90%的分區所有子目錄的大小,並把排名前三的子目錄寫到郵件內容中發給上面的郵箱
4)第一次告警後,如果我們沒有及時處理,則需要每隔30分鐘告警一次
5)每分鐘腳本執行時,需檢測該腳本是否已執行完,如果沒有完成則本次不執行
知識點一:查看磁盤使用
命令:df
df查看已掛載磁盤的總容量、使用容量、剩餘容量等,可以不加任何參數,默認是以k爲單位顯示的,常用選項有-i,-h,-k,-m。-i選項查看iNode使用狀況,-h選項會使用合適的單位顯示,例如G或M,-k、-m選項分別以K、M爲單位顯示。
# df -h 文件系統 容量 已用 可用 已用% 掛載點 /dev/sda3 16G 8.0G 7.9G 51% / devtmpfs 903M 0 903M 0% /dev tmpfs 912M 0 912M 0% /dev/shm tmpfs 912M 8.7M 903M 1% /run tmpfs 912M 0 912M 0% /sys/fs/cgroup /dev/sda1 197M 113M 85M 58% /boot tmpfs 183M 0 183M 0% /run/user/0
第一列是分區名字,第二列爲該分區總共的容量,第三列爲已經使用了多少,第四列爲還剩下多少,第五列爲已經使用的百分比,最後一列爲掛載點。
知識點二:查看目錄或文件大小
命令:du
du命令用來查看某個目錄或文件所佔空間的大小。
語法:du [-abckmsh] [文件名或目錄名]
常用參數:
-a:表示全部文件和目錄的大小都列出來。若後面不加任何選項和參數。則只會列出目錄(包含子目錄)的大小。若du命令不指定單位的話,默認顯示單位爲KB。
示例命令:
# du /tmp/test 0 /tmp/test # du -a /tmp/test 0 /tmp/test/1.txt 0 /tmp/test
-b:表示列出的值以B爲單位輸出。
-k:表示以KB爲單位輸出,這和默認不加任何選項的輸出值是一樣的。
-m:表示以MB爲單位輸出。
-h:表示系統自動調節單位。
-c:表示最後加總。不常用,示例命令:
# du -c /tmp/test 0 /tmp/test 0 總用量
-s:表示只列出總和。常用選項,示例命令:
# du -s /tmp/test 0 /tmp/test
常用的用法:du -sh filename
結合find查找某個目錄下所有子目錄,並統計大小,命令爲:
# find /dir/ -type d |sed '1d' |xargs du -sm
說明:用sed '1d'刪除第一行,原因是第一行是目錄本身,而我們要統計的是子目錄。
知識點三:查看進程
在Windows下可以進入任務管理器查看進程,在Linux下有一些命令也可以查看進程。
1)top
這個命令用於動態監控進程所佔系統資源,每隔3秒變一次。該命令的特點是把佔用系統資源(CPU、內存、磁盤IO等)最高的進程放到最前面。
top命令打印出了很多信息,包括系統負載(load average)、進程數(Tasks)、CPU使用情況、內存使用情況以及交換分區使用情況。
top重點查看的還是下面的進程使用系統資源詳細情況。這部分東西反映的還是比較多,不過需要關注的也就幾項:RES,%CPU,%MEN,COMMAND。RES爲進程所佔內存大小,%CPU爲進程使用CPU百分比。COMMAND爲具體進程名字。
在top狀態下,按M可按內存使用大小排序,按P切回CPU排序,按數字1可列出每顆CPU的使用狀態。常用的一個命令爲:top -bn1,表示靜態打印系統資源使用情況,適合用在shell腳本中。top還有一個-c選項,可以顯示具體的命令,也就是說在COMMAND這一列出更加詳細。
2)ps
ps命令用來彙報當前系統進程的狀況,常見用法有ps aux和ps -elf。
在本例中,我們檢查某個進程是否存在,可以用:
# ps aux |grep '進程名'
假如,本腳本的名字爲mon_disk.sh,則需要這樣統計:
# ps aux |grep 'mon_disk.sh' |grep -vE "$$|grep"
說明:這裏的$$爲本進程PID ,之所以要排除掉它,是因爲我們需要檢查之前的舊進程而不是本次的進程,並且需要把grep這個進程也排除掉。
知識點四:告警收斂思路分析
本案例中有要求,如果發生告警後,下次再告警應該是30分鐘後。腳本本應該是一分鐘執行一次,告警郵件也會一分鐘發一次,告警郵件也會一分鐘發一次,如果我們不能在短時間內修復完問題,則會造成郵件騷擾。
這裏的思路是引入一個計數器,而且需要考慮以下幾個場景:
1)腳本從來沒有告警過,第一次告警
這種情況,不用考慮太多東西,直接發郵件,但需要做兩件事,第一需要記錄此時的時間戳到一個臨時文件中,第二需要建立一個臨時文件記錄告警次數,告警發生但不一定發郵件,要區分差異。
2)腳本之前告警過,距離上一次告警超過30分鐘
判斷距離上一次告警多久需要藉助記錄時間戳的臨時文件,求本次告警時間戳和上一次告警時間戳的差值,是否大於1800秒。根據需求,只要大於30分鐘就可以立即發郵件,同時記錄時間戳和告警次數到兩個不同的臨時文件中。
3)腳本之前告警過,距離上一次告警不超過30分鐘
不超過30分鐘則需要查看記錄告警次數的臨時文件,只有次數大於等於30纔會再一次發郵件。
本案例參考腳本
#!/bin/bash ##監控磁盤使用情況,並做告警收斂(30分鐘發一次郵件) ##作者: ##日期: ##版本:v0.1 #把腳本名字存入變量s_name s_name=`echo $0 |awk -F '/' '{print $NF}'` #定義收件人郵箱 [email protected] #定義檢查磁盤空間使用率函數 chk_sp() { df -m |sed '1d' |awk -F '%| +' '$5>90 {print $7,$5}'>/tmp/chk_sp.log n=`wc -l /tmp/chk_sp.log|awk '{print $1}'` if [ $n -gt 0 ] then tag=1 for d in `awk '{print $1}' /tmp/chk_sp.log` do find $d -type d |sed '1d' |xargs du -sm |sort -nr|head -3 done > /tmp/most_sp.txt fi } #定義檢查iNode使用率的函數 chk_in() { df -i |sed '1d'|awk -F '%| +' '$5>90 {print $7,$5}'>/tmp/chk_in.log n=`wc -l /tmp/chk_in.log|awk '{print $1}'` if [ $n -gt 0 ] then tag=2 fi } #定義告警函數(這裏的mail.py是案例二中的那個腳本) m_mail(){ log=$1 #此處的$1表示第一個函數chk_sp t_s=`date +%s` t_s2=`date -d "1 hours ago" +%s` if [ ! -f /tmp/$log ] then #創建$log文件 touch /tmp/$log #增加a權限,只允許追加內容,不允許更改或刪除 chattr +a /tmp/$log #第一次告警,可以直接寫入1小時以前的時間戳 echo $t_s2 >> /tmp/$log fi #無論$log文件是否是剛剛創建,都需要查看最後一行的時間戳 t_s2=`tail -1 /tmp/$log|awk '{print $1}'` #取出最後一行即上次告警的時間戳後,立即寫入當前的時間戳 echo $t_s>>/tmp/$log #取兩次時間戳差值 v=$[$t_s-$t_s2] #如果差值超過1800,立即發郵件 if [ $v -gt 1800 ] then #發郵件,其中$2爲mail函數的第二個參數,這裏爲一個文件 python mail.py $mail_user "磁盤使用率超過90%" "`cat $2`" 2>/dev/null #定義計數器臨時文件,並寫入0 echo "0" > /tmp/$log.count else #如果計數器臨時文件不存在,需要創建並寫入0 if [ ! -f /tmp/$log.count ] then echo "0" > /tmp/$log.count fi nu=`cat /tmp/$log.count` #30分鐘內每發生一次告警,計數器加1 nu2=$[$nu+1] echo $nu2>/tmp/$log.count #當告警次數超過30次,需要再次發郵件 if [ $nu2 -gt 30 ] then python mail.py $mail_user "磁盤使用率超過90%持續30分鐘了" "`cat $2`" 2>/dev/null #第二次告警後,將計數器再次從0開始 echo "0" > /tmp/$log.count fi fi } #把進程情況存入臨時文件,如果加管道求行數會有問題 ps aux |grep "$s_name" |grep -vE "$$|grep">/tmp/ps.tmp p_n=`wc -l /tmp/ps.tmp|awk '{print $1}'` #當進程數大於0,則說明上次的腳本還未執行完 if [ $p_n -gt 0 ] then exit fi chk_sp chk_in if [ $tag == 1 ] then m_mail chk_sp /tmp/most_sp.txt elif [ $tag == 2 ] then m_mail chk_in /tmp/chk_in.log fi