Supervisor进程管理
在后台应用中,有时候程序进程会异常中止退出,如果没有一个守护进程去守护这个应用进程我们就需要及时发现并重启进程。如果每一个应用进程都写一个自己的守护进程难免会比较麻烦,而Supervisor可以解决这种情况。Supervisor是一个python开发的类unix系统的进程管理系统。
简介
supervisor管理进程,是通过fork/exec的方式将这些被管理的进程当作supervisor的子进程来启动,所以我们只需要将要管理进程的可执行文件的路径添加到supervisor的配置文件中就好了。此时被管理进程被视为supervisor的子进程,若该子进程异常中断,则父进程可以准确的获取子进程异常中断的信息,通过在配置文件中设置autostart=ture,可以实现对异常中断的子进程的自动重启。
安装
Supervisor可以使用pip安装:
pip install supervisor
通过系统包管理工具安装
CentOS:
yum install epel-release
yum install supervisor
Ubuntu:
sudo apt-get install supervisor
配置
Supervisor 是一个 C/S 模型的程序,supervisord
是 server 端,supervisorctl
是 client 端。
使用echo_supervisord_conf
可以获得配置内容,默认配置文件在/etc/supervisord.conf
,你可以将配置文件放到自己指定的目录下,最后启动用-c指定配置文件路径。
配置有如下几个模块:
[unix_http_server]: 这部分设置HTTP服务器监听的UNIX domain socket
file=/var/run/supervisor/supervisor.sock 指定socket文件的路径,supervisorctl用XML_RPC和 supervisord通信就是通过它进行
;chmod=0700 ; 设置上面socket文件权限为0700
;chown=nobody:nogroup ; 设置上面socket文件属主和属组
;username=user ; 使用supervisorctl连接的时候,认证的用户,不设置默认为不需要用户
;password=123 ; 和上面用户对应的密码,可使用SHA加密
;[inet_http_server] ; 侦听在TCP上的scoket,Web服务和远程supervisorctl都有用到,默认不开启
;port=127.0.0.1:9001 ; 监听端口
;username=user ; 同上的unix_http_server
;password=123 ;
[supervisord] 这个主要是定义supervisord这个服务端进程的一些参数的,必须设置
logfile=/var/log/supervisor/supervisord.log ; supervisor主进程的日志文件路径
logfile_maxbytes=50MB ; supervisor日志文件最大值
logfile_backups=10 ; 日志文件保存的数量,用于日志轮转;设置为0表示不限制文件数量
loglevel=info ; 日志级别,有critical, error, warn, info, debug, trace, or blather等
pidfile=/var/run/supervisord.pid ; supervisord的pid文件路径
nodaemon=false ; 如果是true,supervisord进程将在前台运行,默认为false,也就是后台以守护进程运行
minfds=1024 ; 这个是最少系统空闲的文件描述符,低于这个值supervisor将不会启动
minprocs=200 ; 最小可用的进程描述符数目,默认200
;umask=022 ; 进程创建文件的掩码
;user=chrism ; (default is current user, required if root)
;identifier=supervisor ; 这个参数是supervisord的标识符,主要是给XML_RPC用的。当你有多个
supervisor的时候,而且想调用XML_RPC统一管理,就需要为每个
supervisor设置不同的标识符了
;directory=/tmp ; 这个参数是当supervisord作为守护进程运行的时候,设置这个参数的话,启动
supervisord进程之前,会切换到这个目录执行
;nocleanup=true ; 这个参数当为false的时候,会在supervisord进程启动的时候,把以前子进程
产生的日志文件(路径为AUTO的情况下)清除掉。
;childlogdir=/tmp ; 当子进程日志路径为AUTO的时候,子进程日志文件的存放路径。
;environment=KEY=value ; 这个是用来设置环境变量的,supervisord在linux中启动默认继承了linux的
环境变量,在这里可以设置supervisord进程特有的其他环境变量。
supervisord启动子进程时,子进程会拷贝父进程的内存空间内容。 所以设置的
这些环境变量也会被子进程继承。
;strip_ansi=false ; (strip ansi escape codes in logs; def. false)
[rpcinterface:supervisor] ; 这个选项是给XML_RPC用的,当然你如果想使用supervisord或者web server 这
个选项必须要开启
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl] ; 这个主要是针对supervisorctl的一些配置
serverurl=unix:///var/run/supervisor/supervisor.sock ; 这个是supervisorctl本地连接supervisord的时候, 本地UNIX socket路径,注意这个是和前面的[unix_http_server]对应。
;serverurl=http://127.0.0.1:9001 ; supervisorctl远程连接supervisord的时候,用到的TCP socket路径,这个 和前面的[inet_http_server]对应
;username=chris ; should be same as http_username if set
;password=123 ; should be same as http_password if set
;prompt=mysupervisor ; cmd line prompt (default "supervisor")
;history_file=~/.sc_history ; 这个参数和shell中的history类似,我们可以用上下键来查找前面执行过的命令
;[program:theprogramname] ; 要管理的进程配置,theprogramname是进程名,可以配置多个program
;command=/bin/cat ; 启动进程的命令,可带参数,有一点需要注意的是,我们的command只能是那种在 终端运行的进程,不能是守护进程。这个想想也知道了,比如说command=service httpd start。httpd这个进程被linux的service管理了,我们的supervisor再 去启动这个命令,这已经不是严格意义的子进程了。
;process_name=%(program_name)s ; 这个是进程名,如果我们下面的numprocs参数为1的话,就不用管这个参数,默认 就是上面theprogramname,如果numprocs是多个,建议给每个进程命名
;numprocs=1 ; 启动进程的数目。当不为1时,就是进程池的概念
;directory=/tmp ; 进程运行前,会前切换到这个目录
;umask=022 ; umask for process (default None)
;priority=999 ; 子进程启动关闭优先级,优先级低的,最先启动,关闭的时候最后关闭
;autostart=true ; 如果是true的话,子进程将在supervisord启动后被自动启动
;autorestart=true ; 这个是设置子进程挂掉后自动重启的情况,有true、false、unexpected三个选 项,false表示无论什么情况都不会被重新启动,如果为unexpected,只有当进 程的退出码不在下面的exitcodes里面定义的退出码的时候,才会被自动重启。当 为true的时候,只要子进程挂掉,将无条件的重启
;startsecs=10 ; number of secs prog must stay running (def. 1)
;startretries=3 ; 当进程启动失败后,最大尝试启动的次数,当超过3次后,supervisor将把此进程 的状态置为FAIL
;exitcodes=0,2 ; 和上面的的autorestart=unexpected对应
;stopsignal=QUIT ; signal used to kill process (default TERM)进程停止信号,可以为TERM, HUP, INT, QUIT, KILL, USR1, or USR2等信号,默认为TERM。当用设定的信 号去干掉进程,退出码会被认为是expected
;stopwaitsecs=10 ; 当我们向子进程发送stopsignal信号后,到系统返回信息给supervisord,所等 待的最大时间。 超过这个时间supervisord会向该子进程发送一个强制kill的信 号。
;user=chrism ; setuid to this UNIX account to run the program
;redirect_stderr=true ; 如果为true,则stderr的日志会被写入stdout日志文件中
;stdout_logfile=/a/path ; 子进程的stdout的日志路径,可以指定路径,AUTO,none等三个选项,设置为 none的话,将没有日志产生。设置为AUTO的话,将随机找一个地方生成日志文 件,而且当supervisord重新启动的时候,以前的日志文件会被清空。当 redirect_stderr=true的时候,sterr也会写进这个日志文件
;stdout_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB)
;stdout_logfile_backups=10 ; # of stdout logfile backups (default 10)
;stdout_capture_maxbytes=1MB ; number of bytes in 'capturemode' (default 0)
;stdout_events_enabled=false ; 设置为ture的时候,当子进程由stdout向文件描述符中写日志的时候,将触发 supervisord发送PROCESS_LOG_STDOUT类型的event
;stderr_logfile=/a/path ; stderr log path, NONE for none; default AUTO
;stderr_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB)
;stderr_logfile_backups=10 ; # of stderr logfile backups (default 10)
;stderr_capture_maxbytes=1MB ; number of bytes in 'capturemode' (default 0)
;stderr_events_enabled=false ; emit events on stderr writes (default false)
;environment=A=1,B=2 ; 这个是该子进程的环境变量,和别的子进程是不共享的
;serverurl=AUTO ; override serverurl computation (childutils)
运行
后面第二个开始的命令可以省略“-c supervisor.conf”
supervisord -c supervisor.conf 通过配置文件启动supervisor
supervisorctl -c supervisor.conf status 查看状态
supervisorctl -c supervisor.conf reload 重新载入配置文件
supervisorctl -c supervisor.conf start [all]|[x] 启动所有/指定的程序进程
supervisorctl -c supervisor.conf stop [all]|[x] 关闭所有/指定的程序进程
supervisorctl update 更新新的配置到supervisord
supervisorctl reload 重新启动配置中的所有程序
supervisorctl start program_name 启动某个进程(program_name=你配置中写的程序名称)
supervisorctl stop program_name 停止某一进程 (program_name=你配置中写的程序名称)
supervisorctl restart program_name 重启某一进程 (program_name=你配置中写的程序名称)
supervisorctl 查看正在守护的进程
supervisorctl stop all 停止全部进程
测试demo
# 目录结构
supervisor_demo
├── logs
├── run.py
└── supervisord.conf
# cat run.py
# -*- coding:utf-8 -*-
import logging
# 创建记录器Logger #reate logger
logger_name = "example" #设置logger名字
logger = logging.getLogger(logger_name) #创建logger实例
logger.setLevel(logging.DEBUG) #设置logger默认级别
# 创建处理器Handler #create file handler
log_path = "./log.txt" #定义日志文件路径
fh = logging.FileHandler(log_path, mode='a') #创建文件处理器(存储到文件中,模式为a追加)
fh.setLevel(logging.WARN) #设置文件日志存储的级别
# 这里再测试创建一个handler用于输出到控制台
ch = logging.StreamHandler()
# 创建格式化器Formatter #create formatter
fmt = "%(asctime)-15s %(levelname)s %(name)s %(filename)s %(lineno)d %(process)d %(message)s"
datefmt = "%a %d %b %Y %H:%M:%S" #不使用datefmt也没问题
formatter = logging.Formatter(fmt, datefmt) #不使用datefmt设置datefmt=None
# logger对象可以添加多个Handler对象 #add handler and formatter to logger
fh.setFormatter(formatter)
logger.addHandler(fh)
ch.setFormatter(formatter)
logger.addHandler(ch)
if __name__ == "__main__":
import time
while True:
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
logger.critical('critical end !!!')
time.sleep(20)
print('one time run over')
print('continue')
# cat supervisor.conf
[unix_http_server]
file=/var/run/supervisor/supervisor.sock ; (the path to the socket file)
[inet_http_server] ; inet (TCP) server disabled by default
port=0.0.0.0:9001 ; (ip_address:port specifier, *:port for all iface)
[supervisord]
logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10 ; (num of main logfile rotation backups;default 10)
loglevel=info ; (log level;default info; others: debug,warn,trace)
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=false ; (start in foreground if true;default false)
minfds=1024 ; (min. avail startup file descriptors;default 1024)
minprocs=200 ; (min. avail process descriptors;default 200)
user=root ; (default is current user, required if root)
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
;serverurl=unix:///var/run/supervisor/supervisor.sock ; use a unix:// URL for a unix socket
serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket
[program:my_supervisor_test]
command=/usr/bin/python run.py ; the program (relative uses PATH, can take args)
;process_name=%(program_name)s ; process_name expr (default %(program_name)s)
;numprocs=1 ; number of processes copies to start (def 1)
directory=/soft/supervisor_demo ; directory to cwd to before exec (def no cwd)
;umask=022 ; umask for process (default None)
;priority=999 ; the relative start priority (default 999)
;autostart=true ; start at supervisord start (default: true)
autorestart=true ; retstart at unexpected quit (default: true)
startsecs=10 ; number of secs prog must stay running (def. 1)
startretries=3 ; max # of serial start failures (default 3)
;exitcodes=0,2 ; 'expected' exit codes for process (default 0,2)
;stopsignal=QUIT ; signal used to kill process (default TERM)
;stopwaitsecs=10 ; max num secs to wait b4 SIGKILL (default 10)
;user=chrism ; setuid to this UNIX account to run the program
redirect_stderr=true ; redirect proc stderr to stdout (default false)
stdout_logfile=/soft/supervisor_demo/logs/outlog.txt ; stdout log path, NONE for none; default AUTO
;stdout_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB)
;stdout_logfile_backups=10 ; # of stdout logfile backups (default 10)
;stdout_capture_maxbytes=1MB ; number of bytes in 'capturemode' (default 0)
;stdout_events_enabled=false ; emit events on stdout writes (default false)
;stderr_logfile=/soft/supervisor_demo/logs/error.txt ; stderr log path, NONE for none; default AUTO
;stderr_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB)
;stderr_logfile_backups=10 ; # of stderr logfile backups (default 10)
;stderr_capture_maxbytes=1MB ; number of bytes in 'capturemode' (default 0)
;stderr_events_enabled=false ; emit events on stderr writes (default false)
;environment=A=1,B=2 ; process environment additions (def no adds)
;serverurl=AUTO ; override serverurl computation (childutils)
[include]
files = supervisord.d/*.ini
# 启动
# supervisord -c supervisor.conf