python-supervisor

来源:
http://blog.csdn.net/xyang81/article/details/51555473
http://blog.csdn.net/chenyulancn/article/details/52789565

Supervisor(http://supervisord.org/)是用Python开发的一个client/server服务,是Linux/Unix系统下的一个进程管理工具,不支持Windows系统。

它可以很方便的监听、启动、停止、重启一个或多个进程。用Supervisor管理的进程,当一个进程意外被杀死,supervisort监听到进程死后,会自动将它重新拉起,很方便的做到进程自动恢复的功能,不再需要自己写shell脚本来控制。

安装 pip install supervisor

配置: 生成默认配置文件/etc/supervisor/supervisord.conf

mkdir /etc/supervisor
echo_supervisord_conf > /etc/supervisor/supervisord.conf

组成:

supervisord 服务端
supervisorctl 客户端

配置参数
supervisor的配置参数较多,下面介绍一下常用的参数配置,详细的配置及说明,请参考官方文档介绍。
注:分号(;)开头的配置表示注释

[unix_http_server]
file=/tmp/supervisor.sock   ;UNIX socket 文件,supervisorctl 会使用
;chmod=0700                 ;socket文件的mode,默认是0700
;chown=nobody:nogroup       ;socket文件的owner,格式:uid:gid

;[inet_http_server]         ;HTTP服务器,提供web管理界面
;port=127.0.0.1:9001        ;Web管理后台运行的IP和端口,如果开放到公网,需要注意安全性
;username=user              ;登录管理后台的用户名
;password=123               ;登录管理后台的密码

[supervisord]
logfile=/tmp/supervisord.log ;日志文件,默认是 $CWD/supervisord.log
logfile_maxbytes=50MB        ;日志文件大小,超出会rotate,默认 50MB,如果设成0,表示不限制大小
logfile_backups=10           ;日志文件保留备份数量默认10,设为0表示不备份
loglevel=info                ;日志级别,默认info,其它: debug,warn,trace
pidfile=/tmp/supervisord.pid ;pid 文件
nodaemon=false               ;是否在前台启动,默认是false,即以 daemon 的方式启动
minfds=1024                  ;可以打开的文件描述符的最小值,默认 1024
minprocs=200                 ;可以打开的进程数的最小值,默认 200

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ;通过UNIX socket连接supervisord,路径与unix_http_server部分的file一致
;serverurl=http://127.0.0.1:9001 ; 通过HTTP的方式连接supervisord

; [program:xx]是被管理的进程配置参数,xx是进程的名称
[program:xx]
command=/opt/apache-tomcat-8.0.35/bin/catalina.sh run  ; 程序启动命令
autostart=true       ; 在supervisord启动的时候也自动启动
startsecs=10         ; 启动10秒后没有异常退出,就表示进程正常启动了,默认为1秒
autorestart=true     ; 程序退出后自动重启,可选值:[unexpected,true,false],默认为unexpected,表示进程意外杀死后才重启
startretries=3       ; 启动失败自动重试次数,默认是3
user=tomcat          ; 用哪个用户启动进程,默认是root
priority=999         ; 进程启动优先级,默认999,值小的优先启动
redirect_stderr=true ; 把stderr重定向到stdout,默认false
stdout_logfile_maxbytes=20MB  ; stdout 日志文件大小,默认50MB
stdout_logfile_backups = 20   ; stdout 日志文件备份数,默认是10
; stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件)
stdout_logfile=/opt/apache-tomcat-8.0.35/logs/catalina.out
;stdout_events_enabled=false   ; 当设置为ture的时候,当子进程由stdout向文件描述符中写日志的时候,将
                                 触发supervisord发送PROCESS_LOG_STDOUT类型的event
                                 默认为false。。。非必须设置
;stderr_logfile=/a/path        ; 这个东西是设置stderr写的日志路径,当redirect_stderr=true。这个就不用
                                 设置了,设置了也是白搭。因为它会被写入stdout_logfile的同一个文件中
                                 默认为AUTO,也就是随便找个地存,supervisord重启被清空。。非必须设置
;stderr_logfile_maxbytes=1MB   ; 这个出现好几次了,就不重复了
;stderr_logfile_backups=10     ; 这个也是
;stderr_capture_maxbytes=1MB   ; 这个一样,和stdout_capture一样。 默认为0,关闭状态
;stderr_events_enabled=false   ; 这个也是一样,默认为false
;environment=A="1",B="2"       ; 这个是该子进程的环境变量,和别的子进程是不共享的
stopasgroup=false     ;默认为false,进程被杀死时,是否向这个进程组发送stop信号,包括子进程
killasgroup=false     ;默认为false,向进程组发送kill信号,包括子进程

;[group:thegroupname]  ;这个东西就是给programs分组,划分到组里面的program。我们就不用一个一个去操作了
                         我们可以对组名进行统一的操作。 注意:program被划分到组里面之后,就相当于原来
                         的配置从supervisor的配置文件里消失了。。。supervisor只会对组进行管理,而不再
                         会对组里面的单个program进行管理了
;programs=progname1,progname2  ; 组成员,用逗号分开
                                 这个是个必须的设置项
;priority=999                  ; 优先级,相对于组和组之间说的
                                 默认999。。非必须选项

;包含其它配置文件
[include]
files = relative/directory/*.ini    ;可以指定一个或多个以.ini结束的配置文件

进程管理配置参数,不建议全都写在supervisord.conf文件中,应该每个进程写一个配置文件放在include指定的目录下包含进supervisord.conf文件中。
1> 创建/etc/supervisor/config.d目录,用于存放进程管理的配置文件
2> 修改/etc/supervisor/supervisord.conf中的include参数,将/etc/supervisor/conf.d目录添加到include中

[include]
files = /etc/supervisor/config.d/*.ini
[program:tomcat]
command=/opt/apache-tomcat-8.0.35/bin/catalina.sh run
stdout_logfile=/opt/apache-tomcat-8.0.35/logs/catalina.out
autostart=true
autorestart=true
startsecs=5
priority=1
stopasgroup=true
killasgroup=true

启动 (服务端):

supervisord -c /etc/supervisor/supervisord.conf # 指定配置文件目录

控制进程(客户端)

  • 交互终端: supervisorctl

  • bash 终端:

supervisorctl status   查看状态
supervisorctl update  更新配置
pervisorctl stop program_name  停止某个进程 
supervisorctl start program_name  启动某个进程, 不停supervisor 添加program
supervisorctl restart program_name  重启某个进程
supervisorctl stop all  关闭所有进程
supervisorctl reload    重新启动配置中的所有程序
supervisorctl update
  • web管理:
    出于安全考虑,默认配置是没有开启web管理界面,需要修改supervisord.conf配置文件打开http访权限,将下面的配置:
;[inet_http_server]         ; inet (TCP) server disabled by default
;port=127.0.0.1:9001        ; (ip_address:port specifier, *:port for all iface)
;username=user              ; (default is no username (open server))
;password=123               ; (default is no password (open server))

修改成:

[inet_http_server]         ; inet (TCP) server disabled by default
port=0.0.0.0:9001          ; (ip_address:port specifier, *:port for all iface)
username=user              ; (default is no username (open server))
password=123               ; (default is no password (open server))

port:绑定访问IP和端口,这里是绑定的是本地IP和9001端口
username:登录管理后台的用户名
password:登录管理后台的密码

开机启动:

http://blog.csdn.net/xyang81/article/details/51555473

注意:

修改ip后
supervisorctl要和supervisord配置文件一致才能正常启动
supervisord -c /etc/supervisor/supervisord.conf # 指定配置文件目录
supervisorctl -c /etc/supervisor/supervisord.conf # 指定配置文件目录

参考:https://stackoverflow.com/questions/18859063/supervisor-socket-error-issue

event

为监控程序添加通知提醒 或者 自定义一些别的操作
官网
参考博客:
http://talk.withme.me/?p=318
https://www.cnblogs.com/linxiyue/p/8121982.html

event listener本身也是作为supervisor的子程序运行的。事件通知协议的实现基于event listener子程序的stdin和stdout。supervisor发送特定格式的信息到event listener的stdin,然后从event listener的stdout获得特定格式的输出,从而形成一个请求/应答循环。

先来看下怎么配置event listener:
来自http://blog.51cto.com/lixcto/1540169

;[eventlistener:theeventlistenername] ;这个东西其实和program的地位是一样的,也是suopervisor启动的子进程,不过它干的活是订阅supervisord发送的event。
                                       
;command=/bin/eventlistener    ; 这个和上面的program一样,表示listener的可执行文件的路径
;process_name=%(program_name)s ; 这个也一样,进程名,当下面的numprocs为多个的时候,才需要。否则默认就
                                 OK了
;numprocs=1                    ; 相同的listener启动的个数
;events=EVENT                  ; event事件的类型,也就是说,只有写在这个地方的事件类型。才会被发送
                      
                                 
;buffer_size=10                ; 这个是event队列缓存大小,单位不太清楚,楼主猜测应该是个吧。当buffer
                                 超过10的时候,最旧的event将会被清除,并把新的event放进去。
                                 默认值为10。。非必须选项
;directory=/tmp                ; 进程执行前,会切换到这个目录下执行
                                 默认为不切换。。。非必须
;umask=022                     ; 淹没,默认为none,不说了
;priority=-1                   ; 启动优先级,默认-1,也不扯了
;autostart=true                ; 是否随supervisord启动一起启动,默认true
;autorestart=unexpected        ; 是否自动重启,和program一个样,分true,false,unexpected等,注意
                                  unexpected和exitcodes的关系
;startsecs=1                   ; 也是一样,进程启动后跑了几秒钟,才被认定为成功启动,默认1
;startretries=3                ; 失败最大尝试次数,默认3
;exitcodes=0,2                 ; 期望或者说预料中的进程退出码,
;stopsignal=QUIT               ; 干掉进程的信号,默认为TERM,比如设置为QUIT,那么如果QUIT来干这个进程 那么会被认为是正常维护,退出码也被认为是expected中的
;stopwaitsecs=10               ; max num secs to wait b4 SIGKILL (default 10)
;stopasgroup=false             ; send stop signal to the UNIX process group (default false)
;killasgroup=false             ; SIGKILL the UNIX process group (def false)
;user=chrism                   ;设置普通用户,可以用来管理该listener进程。
                                默认为空。。非必须设置
;redirect_stderr=true          ; 为true的话,stderr的log会并入stdout的log里面
                                默认为false。。。非必须设置
;stdout_logfile=/a/path        ; 这个不说了,好几遍了
;stdout_logfile_maxbytes=1MB   ; logfile的最大字节数
;stdout_logfile_backups=10     ; logfile文件数, 超过循环覆盖
;stdout_events_enabled=false   ; (listener本身发送stdout enent?  可以再起一个listenser作为监控的监控?) 
;stderr_logfile=/a/path        ; 错误输出的位置
;stderr_logfile_maxbytes=1MB   ; 
;stderr_logfile_backups=10        ; 
;stderr_events_enabled=false   ; 
;environment=A="1",B="2"       ; 这个是该子进程的环境变量  默认为空。。。非必须设置
;serverurl=AUTO                ; override serverurl computation (childutils)

Event Notification Protocol:

通信数据
Header Tokens

当出现异常时(配置里events= 决定发送哪些消息) supervisord 会给listener process发送消息, listener先会通过stdin接收一个Header Tokens, 如示例,以空格分隔的key:value 对, 以 '\n'结尾, 所以用 sys.stdin.readline()来获取一行

ver:3.0 server:supervisor serial:21 pool:listener poolserial:10 eventname:PROCESS_COMMUNICATION_STDOUT len:54
Key Description Example
ver The event system protocol version 协议版本 3.0
server The identifier of the supervisord sending the event (see config file [supervisord] section identifier value. supervisor的标识符,由[supervisord]块中的identifier选项设置。
serial An integer assigned to each event. No two events generated during the lifetime of a supervisord process will have the same serial number. The value is useful for functional testing and detecting event ordering anomalies. event的序列号 独一无二 30
pool The name of the event listener pool which generated this event. listener的pool的名字。当配置numprocs > 1时 myeventpool
poolserial An integer assigned to each event by the eventlistener pool which it is being sent from. No two events generated by the same eventlister pool during the lifetime of a supervisord process will have the same poolserial number. This value can be used to detect event ordering anomalies. event在pool中的的序列号, 每个listener进程单独计数的event 30
eventname The specific event type name (see Event Types) event类型名称 这个很重要,被配置里envent选项中包含 TICK_5
len An integer indicating the number of bytes in the event payload, aka the PAYLOAD_LENGTH header后面的body字节长度。 22
event payload

紧跟Header Tokens还发送了 the event payload, 即body, 用来描述envent具体对应了哪一个program进程, listener也是通过stdin来获取,但是这次是根据字节数来读sys.stdin.read(int(headers['len']))

enventname=PROCESS_COMMUNICATION_STDOUT时格式如下(格式由eventType来决定,):

processname:foo groupname:bar pid:123
This is the data that was sent between the tags
event type

Supervisord支持的Event有:

    PROCESS_STATE 进程状态发生改变
    PROCESS_STATE_STARTING 进程状态从其他状态转换为正在启动(Supervisord的配置项中有startsecs配置项,是指程序启动时需要程序至少稳定运行x秒才认为程序运行正常,在这x秒中程序状态为正在启动)
    PROCESS_STATE_RUNNING 进程状态由正在启动转换为正在运行
    PROCESS_STATE_BACKOFF 进程状态由正在启动转换为失败
    PROCESS_STATE_STOPPING 进程状态由正在运行转换为正在停止
    PROCESS_STATE_EXITED 进程状态由正在运行转换为退出
    PROCESS_STATE_STOPPED 进程状态由正在停止转换为已经停止(exited和stopped的区别是exited是程序自行退出,而stopped为人为控制其退出)
    PROCESS_STATE_FATAL 进程状态由正在运行转换为失败
    PROCESS_STATE_UNKNOWN 未知的进程状态
    REMOTE_COMMUNICATION 使用Supervisord的RPC接口与Supervisord进行通信
    PROCESS_LOG 进程产生日志输出,包括标准输出和标准错误输出
    PROCESS_LOG_STDOUT 进程产生标准输出
    PROCESS_LOG_STDERR 进程产生标准错误输出
    PROCESS_COMMUNICATION 进程的日志输出包含 和
    PROCESS_COMMUNICATION_STDOUT 进程的标准输出包含 和
    PROCESS_COMMUNICATION_STDERR 进程的标准错误输出包含 和
    SUPERVISOR_STATE_CHANGE_RUNNING Supervisord启动
    SUPERVISOR_STATE_CHANGE_STOPPING Supervisord停止
    TICK_5 每隔5秒触发
    TICK_60 每隔60秒触发
    TICK_3600 每隔3600触发
    PROCESS_GROUP Supervisord的进程组发生变化
    PROCESS_GROUP_ADDED 新增了Supervisord的进程组
    PROCESS_GROUP_REMOVED 删除了Supervisord的进程组
Event Listener States

Listener有三种状态, 由supervisor服务端来维护,Listener通过stdout来告诉 supervisor自己的状态, 以便supervisor决定下一条消息的发送时间

Name Description
ACKNOWLEDGED The event listener has acknowledged(accepted or rejected) an event send.
READY Event notificatons may be sent to this event listener
BUSY Event notifications may not be sent to this event listener.
  • supervisor调起listener, listener就处于ACKNOWLEDGED状态了;
  • listener通过sys.stdout.write('READY\n') 来通知supervisor自己处于READY状态;
  • supervisor 为listener发送消息, 发送完supervisor就把listener标记为BUSY状态;
  • listener处理完, 发送write_stdout('RESULT 2\nOK')表示自己处理成功了, 或者write_stdout('RESULT 2\nFAIL') 来表示自己处理失败了(这里前面一行RESULT 2, 是为了告诉supervisor这个是给你的, 不是普通的log)
  • supervisor接收到 RESULT 2\nOK 或 RESULT 2\nFAIL 把listener置为ACKNOWLEDGED, 然后决定是重新发送一次还是发送下一条
  • listener exit(autostart=true配置下 会被supervisor无限次调起 )或 加一个while 1, 发送 READY\n' 告诉supervisor开始新一轮

listener code:

import sys

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')

if __name__ == '__main__':
    main()

补充:

今天发现supervisorctl 报错: unix:///tmp/supervisor.sock no such file

找到了https://blog.csdn.net/qq_28885149/article/details/79364685

额, 想起来最近tmp临时文件满了, 清了一次, 导致supervisor文件丢失了
为了避免这个问题, 需要重启 supervisord, 我是用supervisord来监控yarn-cluster模式的spark-streaming的,得一个个手动kill了。。。。

然后修改下文件配置 supervisord.conf:

1,   /tmp/supervisor.sock  改为 ./tmp/supervisor.sock
2,  /tmp/supervisord.log  改为  ./tmp/supervisord.log
3,  /tmp/supervisord.pid  改为  ./tmp/supervisor.pid

创建supervisor.sock

touch ./tmp/supervisor.sock

重启 supervisord -c supervisord.conf

你可能感兴趣的:(python-supervisor)