CentOS7 配置OOM監控報警


由於程序設計不合理或者瞬間高並發訪問時,很有可能會觸發OOM(Out of memory),這里指的是操作系統級別的OOM。具體什么是OOM,以及怎樣發生這里不在贅述,因為筆者認為這是IT從業工作者的基本常識了。本篇主要記錄一下生產環境時對發生OOM的程序進行監控,便於我們及時發現以及事后問題的復盤。
在做這個監控時,筆者也做了很多考察搜索,幻想着會有那么一兩個成熟的開源軟件能實現這個監控,事與願違,筆者並未找到這樣的工具,無奈之下,只好自己動手實現了一個略顯粗糙的程序來達到我的目的。
實現思路:

    Apr 18 12:11:25 php001 kernel: Out of memory: Kill process 13546 (php-fpm) score 31 or sacrifice child

系統每次觸發OOM時會在/var/log/message文件下記下當時系統的運行狀況,以及被殺程序的pid和評分。因此筆者從此文件入手,寫下了這樣一個腳本。

1、目錄結構如下:
oom_monitor位於共享存儲NFS上,生產環境的機器都掛載了該NFS,因此所有機器上都會有這么一個目錄

[admin@prod.001:/mnt/alinas]$ tree -L 1 oom_monitor/
oom_monitor/
├── bin                #存放可執行的腳本文件
└── log                #存放日志文件

[admin@prod.001:/mnt/alinas]$ tree -L 1 oom_monitor/bin/
oom_monitor/bin/
├── oom_check.sh       #過濾"Out of memory"從/var/log/message,並生成對應文件保存在log目錄下,用於后面的發送警報
├── oom_dingding.py    #發送OOM對應信息到釘釘群
├── oom_mail.py        #發送OOM對應信息到郵箱
└── oom_send.sh        #用於觸發發送報警信息到釘釘和郵箱

2、具體內容如下:

[admin@prod.001:/mnt/nfs/oom_monitor/bin]$ cat oom_check.sh
#!/bin/sh
#獲取主機名
host_name=`hostname`

#定義獲取到的OOM日志存儲位置
oom_scrape_file=/mnt/nfs/oom_monitor/log/oom_scrape_$host_name

#定義上次發生OOM時的對應信息,用於去重,防止重復報警
old_oom_scrape_file=/mnt/nfs/oom_monitor/log/old_oom_scrape_$host_name

#獲取OOM報警信息
msg=$(sudo grep -i "out of memory" /var/log/messages|awk 'END {print}')

#獲取報警產生的時間
time=`echo $msg | awk '{print $3}'`

#獲取被殺的程序類型(java/php/mysql/...)
killed=`echo $msg | awk 'END {print $12}'`

#獲取上次OOM的信息
old_msg=`cat $old_oom_scrape_file`

#判斷兩次信息是否相同,相同則不再記錄此次信息,防止重復報警
if [ "$msg" == "$old_msg" ];then
 	exit 1
else

#如果兩次報警信息不相同則把這次獲取的信息覆蓋上次的信息
	[ ! -z "$msg" ] && echo "$msg" > $old_oom_scrape_file
fi

#記錄此次信息持久化到文件
if [ ! -z "$msg" ];then
	echo > $oom_scrape_file
	echo -e "發生時間: $time" >> $oom_scrape_file
	echo -e "日志信息: $msg" >> $oom_scrape_file
	echo -e "被殺程序: $killed" >> $oom_scrape_file
	echo -e "發生主機: $host_name" >> $oom_scrape_file
fi


[admin@prod.001:/mnt/nfs/oom_monitor/bin]$ cat oom_send.sh
#!/bin/sh

#定義獲取到的OOM日志存儲位置、OOM發送報警腳本位置
#這里用*的原因是:機器很多,每台機器一個文件,所以用*
oom_scrape_file=/mnt/nfs/oom_monitor/log/oom_scrape*                
oom_warn_script=/mnt/nfs/oom_monitor/bin/oom_mail.py
oom_dingding_script=/mnt/nfs/oom_monitor/bin/oom_dingding.py
History_file=/mnt/nfs/oom_monitor/log/history_oom_scrape
basedir=/mnt/nfs/oom_monitor/log/
cd $basedir
for file in `ls $oom_scrape_file`
do
	hostname=`grep "發生主機" $file|awk '{print $2}'`
	warn_time=`grep "發生時間" $file |awk '{print $2}'`
	killed=`grep "被殺程序" $file |awk '{print $2}'`

    #發送郵件報警,把日志信息全部發出去
	/usr/local/bin/python3 $oom_warn_script $file

    #發送釘釘報警
	/usr/local/bin/python3 $oom_dingding_script $hostname $killed $warn_time

    #報警信息發出后把日志信息追加到歷史信息文件中,然后刪除對應的oom信息文件,防止重復報警
	cat $file >> $History_file  && mv $file /tmp
done


[admin@prod.001:/mnt/nfs/oom_monitor/bin]$ cat oom_mail.py
#!/usr/bin/env python3
'''
當收到系統OOM時,觸發腳本發出郵件報警信息,信息格式如下:

您的主機「hostname」發生OOM,具體信息如下:
    /var/log/message中的信息

'''
import os
import smtplib
from email.mime.text import MIMEText
from email.header import Header
import time
import sys
def send_email(file_name):
    try:

        # 讀取測試報告中的內容作為郵件的內容
        with open(file_name, 'r', encoding='utf8') as f:
            mail_body = f.read()

        # 發件人地址
        send_addr = '此處替換為發件人的郵箱用戶名'

        # 收件人地址
        reciver_addr = ['接收人a的郵箱地址','接收人b的郵箱地址',]

        # 發送郵箱的服務器地址,這里用的是阿里雲的
        mail_server = 'smtp.mxhichina.com'
        now = time.strftime("%Y-%m-%d %H:%M:%S")

        # 郵件標題
        subject = '[OOM報警觸發]' + now

        # 發件人的郵箱及郵箱密碼
        username = '此處替換為發件人的郵箱用戶名'
        password = '此處替換為發件人的郵箱密碼'

        # 郵箱的內容和標題
        message = MIMEText(mail_body, 'html', 'utf8')
        message['From'] = send_addr
        message['To'] = ','.join(reciver_addr)
        message['Subject'] = Header(subject, charset='utf8')

        # 發送郵件,使用的使smtp協議
        smtp = smtplib.SMTP()

        #端口注意下,通常服務器的25端口是關閉的,所以我這里用了80、或者465也闊以
        smtp.connect(mail_server,80)
        smtp.login(username, password)
        smtp.sendmail(send_addr, message['To'].split(','), message.as_string())
        smtp.quit()
        print("郵件發送成功!")
    except:
        print("發送郵件失敗!")

send_email(file_name=sys.argv[-1])



[admin@prod.001:/mnt/nfs/oom_monitor/bin]$ cat oom_dingding.py
#!/usr/bin/env python3
import json
import requests
import datetime
import sys
def sendmessage(hostname,killed,warn_time):
    now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

#替換成你自己的釘釘群的webhook
    url = 'https://oapi.dingtalk.com/robot/send?access_token=917b00b5faee51bcb67e862c13b0a0ff605f0f74f4f692c9a70fe32351'

    HEADERS = {
        "Content-Type": "application/json;charset=utf-8"
    }
    message='''
【OOM報警觸發】:
    報警主機:%s
    被殺進程:%s
    報警時間:%s
    ''' %(hostname,killed,warn_time)
    String_textMsg = {
        "msgtype": "text",
        "text": {"content": message},
        "at": {
            "atMobiles": [
                "110120119"  # 如果需要@某人,這里寫他的手機號
            ],
            "isAtAll": 0  # 如果需要@所有人,這些寫1
        }
    }
    String_textMsg = json.dumps(String_textMsg)
    res = requests.post(url, data=String_textMsg, headers=HEADERS)
    print(res.text)
sendmessage(sys.argv[-3],sys.argv[-2],sys.argv[-1])

3、結果演示:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM