深圳幻海软件技术有限公司 欢迎您!

Supervisor这个监控告警功能你用过吗?

2023-02-28

Supervisor简介Supervisor是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启。它是通过fork/exec的方式把这些被管理的进程当作supervisor的子进程来启动,这样只要在supervisor的配置

Supervisor简介

Supervisor是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启。它是通过fork/exec的方式把这些被管理的进程当作supervisor的子进程来启动,这样只要在supervisor的配置文件中,把要管理的进程的可执行文件的路径写进去即可。也实现当子进程挂掉的时候,父进程可以准确获取子进程挂掉的信息的,可以选择是否自己启动和报警。supervisor还提供了一个功能,可以为supervisord或者每个子进程,设置一个非root的user,这个user就可以管理它对应的进程。

今天要说的不是supervisor,关于supervisor的使用,百度一大堆,这里不多赘述,今天重点要说的是supervisor事件监控。

Event & Listener

Event 是在 Supervisor 3.0 引入的一个高级特性,如果只简单使用 Supervisor 管理进程,则不需要了解 Event。

但如果希望监控 Supervisor 管理的进程的各种状态(如: 启动、退出、失败、退出状态码 …)并支持告警,才需要了解Event。

利用 Supervisor 的 Event & Listener 功能进行订阅异常退出事件,并进行报警处理。

Supervisor 官方对其 Event 机制的描述是:一个进程的监控/通知框架。

该机制主要通过一个 event listener 订阅 event 通知实现。当被 Supervisor 管理的进程有特定行为的时候,supervisor 就会自动发出对应类型的 event。即使没有配置 listener,这些 event 也是会发的;如果配置了 listener 并监听该类型的 event,那么这个 listener 就会接收到该 event。event listener 需要自己实现,并像 program 一样,作为 superviosr 的子进程运行。

具体配置实现

配置事件监听器

主管事件侦听器是通过 配置文件中的[eventlistener:x]指定的。关于Supervisor [eventlistener:x] 在配置中允许的键方面,几乎与supervisor [program:x]完全一样,只是Supervisor不遵循事件侦听器进程的“捕获模式”输出(即事件侦听器不能是 PROCESS_COMMUNICATIONS_EVENT事件生成器)。因此,在事件侦听器的配置中指定stdout_capture_maxbytes或 stderr_capture_maxbytes是错误的。可以放入配置文件的事件侦听器部分的数量没有人为限制。

vim /etc/supervisord.d/eventlistener.ini
[eventlistener:mylistener]
command=/opt/my_custom_listener.py  ; 自定义的监控程序
events=PROCESS_STATE_EXITED,PROCESS_STATE_FATAL,TICK_60  ; 监控事件
; 下面的配置和`[program:x]`完全一样
autostart=true
autorestart=true
log_stdout=true
log_stderr=true
stdout_logfile=/opt/supervisor_event_exited-stdout.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=3
buffer_size=10
stderr_logfile=/opt/supervisor_event_exited-stderr.log
stderr_logfile_maxbytes=50MB
stderr_logfile_backups=3
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

配置监听器脚本。

vim /opt/my_custom_listener.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from supervisor import childutils


def write_stdout(s):
    # only eventlistener protocol messages may be sent to stdout
    sys.stdout.write(s)
    sys.stdout.flush()

def write_stderr(s):
    sys.stderr.write(s)
    sys.stderr.flush()

def main():
    while 1:
        # transition from ACKNOWLEDGED to READY
        write_stdout('READY\n')

        # read header line and print it to stderr
        line = sys.stdin.readline()
        write_stderr(line)

        # read event payload and print it to stderr
        headers = dict([ x.split(':') for x in line.split() ])
        data = sys.stdin.read(int(headers['len']))
        write_stderr(data)

        # transition from READY to ACKNOWLEDGED
        write_stdout('RESULT 2\nOK')


        # 使用supervisor的childutils解析
        headers, payload = childutils.listener.wait(sys.stdin, sys.stdout)
        pheaders, pdata = childutils.eventdata(payload + '\n')
        # 当 program 的退出码为对应配置中的 exitcodes 值时, expected=1; 否则为0
        if int(pheaders.get('expected', 1)):
            childutils.listener.ok(sys.stdout)
            continue
        else:  # 0, 异常退出,根据 pheaders 的值发送报警处理
            ############################
            pass  # 你的自定制发送报警逻辑 #
            with open('/opt/sup.log', 'a') as f:  # 这里写入文件作为报警简单模拟
                f.write(str(pheaders))
                f.write(str(pdata))
                f.write('\n')
            ############################

            # 向 stdout 写入"RESULT\nOK",并进入下一次循环
            childutils.listener.ok(sys.stdout)

if __name__ == '__main__':
    main()
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.

这里告警的方式可以自己定义,可以邮件,也可以通过webhook调用im接口告警。

实际上supervisor自带有监控利器——superlance。

superlance

superlance是基于supervisor的事件机制实现的一系列命令行的工具集,它实现了许多supervisor本身没有实现的实用的进程监控和管理的特性,包括内存监控,http接口监控,邮件和短信通知机制等。同样的,superlance本身也是使用python编写的。

安装

由于superlance是一个python包,安装起来十分简单,通过easy_install或者pip就可以简单的安装:

easy_install superlance
pip install superlance
  • 1.
  • 2.

当然也可以到github上获得最新的源码(https://github.com/Supervisor/superlance)并安装。

python setup.py install
  • 1.

安装后执行以下httpok命令,如果该命令存在,则说明superlance已经正常安装了。

superlance组件

superlance是一系列命令行工具的集合,其包括以下这些命令:

  • httpok 通过定时对一个HTTP接口进行GET请求,根据请求是否成功来判定一个进程是否处于正常状态,如果不正常则对进程进行重启。
  • crashmail 当一个进程意外退出时,发送邮件告警。
  • memmon 当一个进程的内存占用超过了设定阈值时,发送邮件告警。
  • crashmailbatch 类似于crashmail的告警,但是一段时间内的邮件将会被合成起来发送,以避免邮件轰炸。
  • fatalmailbatch 当一个进程没有成功启动多次后会进入FATAL状态,此时发送邮件告警。crashmailbatch一样会进行合成报警。
  • crashsms 当一个进程意外退出时发送短信告警,这个短信也是通过email网关来发送的

邮件告警

superlance是使用sendmail来发送邮件的,所以需要安装sendmail。

apt install sendmail
  • 1.

安装完成后配置superlance。

cat /etc/supervisor/conf.d/redis.conf
[program:redis]
command=/usr/local/redis/bin/redis-server /home/redis/etc/redis.conf
directory=/usr/local/redis/bin/
user=redis
autostart = true
autorestart = true
#startsecs = 30
stopwaitsecs = 1
stopsignal = TERM
redirect_stderr = true
[eventlistener:redis_monitor]
command=crashmail -p redis -m xxx@qq.com
events=PROCESS_STATE_EXITED
redirect_stderr=false
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

首先配置了redis进程启动项目,接着配置了一个名为redis_monitor的事件监听器,它接受来自supervisor的PROCESS_STATE_EXITED事件,并且会触发crashmail的命令行调用。

PROCESS_STATE_EXITED是在一个supervisor的监控项对应的进程意外退出时会触发的事件,这就使得一个进程出现意外退出的情况下会通知到crashmail。

command参数中-p参数配置了crashmail只会对名为redis的监控项作出响应,而-m参数中则配置了崩溃邮件会被发送到的地址。

灵活利用superlance,可以完成各种告警需求,实现各种进程监控。