来源:
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