supervisor安装配置以及使用说明

摘要

什么是supervisor ?

supervisor是用python写的一个进程管理工具,用来启动,重启,关闭进程。

supervisor简单说明

简单

为啥简单呢?因为咱们通常管理linux进程的时候,一般来说都需要自己编写一个能够实现进程start/stop/restart/reload功能的脚本,然后丢到/etc/init.d/下面。这么做有很多不好的地方,第一我们要编写这个脚本,这就很耗时耗力了。第二,当这个进程挂掉的时候,linux不会自动重启它的,想要自动重启的话,我们还要自己写一个监控重启脚本。而,supervisor则可以完美的解决这些问题。好,怎么解决的呢,其实supervisor管理进程,就是通过fork/exec的方式把这些被管理的进程,当作supervisor的子进程来启动。这样的话,我们只要在supervisor的配置文件中,把要管理的进程的可执行文件的路径写进去就OK了。这样就省下了我们如同linux管理进程的时候,自己写控制脚本的麻烦了。第二,被管理进程作为supervisor的子进程,当子进程挂掉的时候,父进程可以准确获取子进程挂掉的信息的,所以当然也就可以对挂掉的子进程进行自动重启了,当然重启还是不重启,也要看你的配置文件里面有木有设置autostart=true了,这是后话。

精确

为啥说精确呢?因为linux对进程状态的反馈,有时候不太准确。为啥不准确?这个楼主也不知道啊,官方文档是这么说的,知道的告诉楼主一下吧,感激不尽。而supervisor监控子进程,得到的子进程状态无疑是准确的。

进程组

supervisor可以对进程组统一管理,也就是说咱们可以把需要管理的进程写到一个组里面,然后我们把这个组作为一个对象进行管理,如启动,停止,重启等等操作。而linux系统则是没有这种功能的,我们想要停止一个进程,只能一个一个的去停止,要么就自己写个脚本去批量停止。

集中式管理

supervisor管理的进程,进程组信息,全部都写在一个ini格式的文件里就OK了。而且,我们管理supervisor的时候的可以在本地进行管理,也可以远程管理,而且supervisor提供了一个web界面,我们可以在web界面上监控,管理进程。 当然了,本地,远程和web管理的时候,需要调用supervisor的xml_rpc接口,这个也是后话。

有效性

当supervisor的子进程挂掉的时候,操作系统会直接给supervisor发信号。而其他的一些类似supervisor的工具,则是通过进程的pid文件,来发送信号的,然后定期轮询来重启失败的进程。显然supervisor更加高效。。。至于是哪些类似supervisor工具,这个楼主就不太清楚了,楼主还听说过god,director,但是没用过。有兴趣的朋友可以玩玩

可扩展性

supervisor是个开源软件,牛逼点的,可以直接去改软件。不过咱们大多数人还是老老实实研究supervisot提供的接口吧,supervisor主要提供了两个可扩展的功能。一个是event机制,这个就是楼主这两天干的活要用到的东西。再一个是xml_rpc,supervisor的web管理端和远程调用的时候,就要用到它了。

权限

linux的进程,特别是监听在1024端口之下的进程,一般用户大多数情况下,是不能对其进行控制的。想要控制的话,必须要有root权限。而supervisor提供了一个功能,可以为supervisord或者每个子进程,设置一个非root的user,这个user就可以管理它对应的进程了。

组件构成

supervisord

supervisord是supervisor的服务端程序。

干的活:启动supervisor程序自身,启动supervisor管理的子进程,响应来自clients的请求,重启闪退或异常退出的子进程,把子进程的stderr或stdout记录到日志文件中,生成和处理Event

supervisorctl

这东西还是有点用的,如果说supervisord是supervisor的服务端程序,那么supervisorctl就是client端程序了。supervisorctl有一个类型shell的命令行界面,我们可以利用它来查看子进程状态,启动/停止/重启子进程,获取running子进程的列表等等。。。最牛逼的一点是,supervisorctl不仅可以连接到本机上的supervisord,还可以连接到远程的supervisord,当然在本机上面是通过UNIX socket连接的,远程是通过TCP socket连接的。supervisorctl和supervisord之间的通信,是通过xml_rpc完成的。 相应的配置在[supervisorctl]块里面

Web Server

Web Server主要可以在界面上管理进程,Web Server其实是通过XML_RPC来实现的,可以向supervisor请求数据,也可以控制supervisor及子进程。配置在[inet_http_server]块里面

XML_RPC接口

这个就是远程调用的,上面的supervisorctl和Web Server就是它弄的

supervisor的安装

1
pip install supervisor

生成配置文件

echo_supervisord_conf 命令用于生成默认的主配置文件

1
2
mkdir /etc/supervisor/conf.d/
echo_supervisord_conf > /etc/supervisor/supervisord.conf

创建启动文件

init

1
2
curl http://c.isme.pub/2017/04/20/supervisor/supervisord.init -o /etc/init.d/supervisord
chmod +x /etc/init.d/supervisord

systemd

配置文件详解

去掉了部分不需要的内容

socket监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[unix_http_server]
# UNIX socket文件的路径,supervisorctl用XML_RPC和supervisord通信就是通过它进行的。
# 不设置的话,默认为none。如果不设置的话,supervisorctl也就不能用了。非必须设置
file=/tmp/supervisor.sock ; (the path to the socket file)
# 上面socket 文件的 mode,默认是 0700
# 不设置的话,默认为0700。非必须设置
;chmod=0700 ; socket file mode (default 0700)
# 修改上面的socket文件的属组为user:group
# 不设置的话,默认为启动supervisord进程的用户及属组。非必须设置
;chown=nobody:nogroup ; socket file uid:gid owner
# 使用supervisorctl连接的时候,认证的用户
# 不设置的话,默认为不需要用户。非必须设置
;username=user ; (default is no username (open server))
和上面的用户名对应的密码,可以直接使用明码,也可以使用SHA加密
# 如:{SHA}82ab876d1387bfafe46cc1c8a2ef074eae50cb1d
# 默认不设置。。。非必须设置
;password=123 ; (default is no password (open server))

tcp监听

1
2
3
4
5
6
7
8
9
# 侦听在TCP上的socket,WebServer和远程的supervisorctl都要用到他
# 不设置的话,默认为不开启。非必须设置
;[inet_http_server] ; inet (TCP) server disabled by default
# 监听的IP和端口,侦听所有IP用:9001或*:9001。如果开放到公网,需要注意安全性
# 只要上面的[inet_http_server]开启了,就必须设置它
;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))

服务端配置

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
56
57
58
59
60
61
62
63
64
65
66
67
# 定义supervisord这个服务端进程的一些参数的
# 必须设置,不设置,supervisor就不能工作了
[supervisord]
# supervisord 主进程的日志路径
# 默认路径$CWD/supervisord.log,$CWD是当前目录。。非必须设置
logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log)
# 日志文件大小,超出会 rotate,默认 50MB
# 当设置为0时,表示不限制文件大小。默认值是50M,非必须设置。
logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB)
# 日志文件保留的数量,supervisor在启动程序时,会自动创建10个buckup文件,用于logrotate
# 当设置为0时,表示不限制文件的数量。默认情况下为10。非必须设置
logfile_backups=10 ; (num of main logfile rotation backups;default 10)
# 日志级别,默认 info,其它: critical,error,warn,info,debug,trace,orblather等
loglevel=info ; (log level;default info; others: debug,warn,trace)
# supervisord的pid文件路径。
# 默认为$CWD/supervisord.pid。非必须设置
pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
# 是否在前台启动,如果是true,supervisord进程将在前台运行
# 默认为false,也就是后台以daemon守护进程运行。非必须设置
nodaemon=false ; (start in foreground if true;default false)
# 可以打开的文件描述符的最小值,默认 1024。非必须设置
# 这个是最少系统空闲的文件描述符,低于这个值supervisor将不会启动。
# 系统的文件描述符在这里设置cat/proc/sys/fs/file-max
minfds=1024 ; (min. avail startup file descriptors;default 1024)
# 最小可用的进程描述符,低于这个值supervisor也将不会正常启动。
# ulimit -u这个命令,可以查看linux下面用户的最大进程数
# 默认为200。非必须设置
minprocs=200 ; (min. avail process descriptors;default 200)
# 进程创建文件的掩码
# 默认为022。非必须设置项
;umask=022 ; (process file creation umask;default 022)
# 这个参数可以设置一个非root用户,当我们以root用户启动supervisord之后。
# 我这里面设置的这个用户,也可以对supervisord进行管理
# 默认情况是不设置。非必须设置项
;user=chrism ; (default is current user, required if root)
# 这个参数是supervisord的标识符,主要是给XML_RPC用的。当你有多个
# supervisor的时候,而且想调用XML_RPC统一管理,就需要为每个supervisor设置不同的标识符了
# 默认是supervisord。非必需设置
;identifier=supervisor ; (supervisord identifier, default is 'supervisor')
# 这个参数是当supervisord作为守护进程运行的时候,设置这个参数的话
# 启动supervisord进程之前,会先切换到这个目录
# 默认不设置。非必须设置
;directory=/tmp ; (default is not to cd during start)
# 这个参数当为false的时候,会在supervisord进程启动的时候,把以前子进程
# 产生的日志文件(路径为AUTO的情况下)清除掉。有时候咱们想要看历史日志,当
# 然不想日志被清除了。所以可以设置为true
# 默认是false,有调试需求的同学可以设置为true。非必须设置
;nocleanup=true ; (don't clean up tempfiles at start;default false)
# 当子进程日志路径为AUTO的时候,子进程日志文件的存放路径。
# 默认路径是这个东西,执行下面的这个命令看看就OK了,处理的东西就默认路径
# python -c "import tempfile;print tempfile.gettempdir()"。非必须设置
;childlogdir=/tmp ; ('AUTO' child log dir, default $TEMP)
# 这个是用来设置环境变量的,supervisord在linux中启动默认继承了linux的
# 环境变量,在这里可以设置supervisord进程特有的其他环境变量。
# supervisord启动子进程时,子进程会拷贝父进程的内存空间内容。所以设置的
# 这些环境变量也会被子进程继承。
# 小例子:environment=name="haha",age="hehe"
# 默认为不设置。非必须设置
;environment=KEY="value" ; (key value pairs to add to environment)
# 这个选项如果设置为true,会清除子进程日志中的所有ANSI序列。
# 什么是ANSI序列呢?就是我们的\n,\t这些东西。
# 默认为false。非必须设置
;strip_ansi=false ; (strip ansi escape codes in logs; def. false)
[rpcinterface:supervisor]
# 这个选项是给XML_RPC用的,当然你如果想使用supervisord或者webserver这个选项必须要开启的
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

客户端配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 主要是针对supervisorctl的一些配置
[supervisorctl]
# supervisorctl本地连接supervisord的时候,本地UNIXsocket路径
# 注意这个是和前面的[unix_http_server]对应的
# 默认值就是unix:///tmp/supervisor.sock。非必须设置
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket
# supervisorctl远程连接supervisord的时候,用到的TCPsocket路径
# 注意这个和前面的[inet_http_server]对应
# 默认就是http://127.0.0.1:9001。非必须项
;serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket
# 同上
;username=chris ; should be same as http_username if set
;password=123 ; should be same as http_password if set
# 输入用户名密码时候的提示符,默认supervisor。非必须设置
;prompt=mysupervisor ; cmd line prompt (default "supervisor")
# 这个参数和shell中的history类似,我们可以用上下键来查找前面执行过的命令
# 默认是nofile的。。所以我们想要有这种功能,必须指定一个文件。非必须设置
;history_file=~/.sc_history ; use readline history if available

加载其他配置文件

1
2
3
4
;[include]
# 加载其他的配置文件,可以是 *.conf 或 *.ini
# 当我们要管理的进程很多的时候,可以把配置信息写到多个文件中,然后include进来
;files = relative/directory/*.ini

进程Program配置

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# 要管理的子进程程序名,":"后面的是名字,最好别乱写,和实际进程有点关联最好。
# 这样的program我们可以设置一个或多个,一个program就是要被管理的一个进程
;[program:theprogramname]
# 程序启动命令,需要在前台运行(非守护进程)可以带参数。必须设置的项
;command=/bin/cat ; the program (relative uses PATH, can take args)
# 定义进程名,如果我们下面的numprocs参数为1的话,就不用管这个参数了
# 默认值%(program_name)s也就是上面的那个program冒号后面的名字,
# 如果numprocs为多个的的时候显示每个子进程的名字,例子:%(program_name)s_%(process_num)02d
;process_name=%(program_name)s ; process_name expr (default %(program_name)s)
# 进程启动个数,当不为1时,就是进程池的概念,注意process_name的设置
# 默认为1。非必须设置
;numprocs=1 ; number of processes copies to start (def 1)
# 程序的启动目录(工作目录),进程运行前,会先切换到这个目录
# 有时候程序会根据相对路径加载配置文件。默认不设置。非必须设置
;directory=/tmp ; directory to cwd to before exec (def no cwd)
# 进程掩码,默认none,非必须
;umask=022 ; umask for process (default None)
# 子进程启动关闭优先级,优先级低的,最先启动,关闭的时候最后关闭
# 默认值为999。非必须设置
;priority=999 ; the relative start priority (default 999)
# 如果是true的话,子进程将在supervisord启动后被自动启动
# 默认就是true。非必须设置
;autostart=true ; start at supervisord start (default: true)
# 启动 1 秒后没有异常退出,就当作已经正常启动了
# 默认值为1。非必须设置
;startsecs=1 ; # of secs prog must stay up to be running (def. 1)
# 启动失败自动重试次数,默认是 3,非必须设置
# 当超过3次后,supervisor将把此进程的状态置为FAIL
;startretries=3 ; max # of serial start failures when starting (default 3)
# 这个是设置子进程挂掉后自动重启的情况,有三个选项,false,unexpected和true。
# 如果为false的时候,无论什么情况下,都不会被重新启动,
# 如果为unexpected,只有当进程的退出码不在下面的exitcodes里面定义的退出码的时候,才会被自动重启。
# 当为true的时候,只要子进程挂掉,将会被无条件的重启
;autorestart=unexpected ; when to restart if exited after running (def: unexpected)
# 注意和上面的的autorestart=unexpected对应。exitcodes里面的定义的
# 退出码是expected的。
;exitcodes=0,2 ; 'expected' exit codes used with autorestart (default 0,2)
# 进程停止信号,可以为TERM,HUP,INT,QUIT,KILL,USR1,orUSR2等信号
# 默认为TERM。当用设定的信号去干掉进程,退出码会被认为是expected
# 非必须设置
;stopsignal=QUIT ; signal used to kill process (default TERM)
# 当我们向子进程发送stopsignal信号后,到系统返回信息
# 给supervisord,所等待的最大时间。超过这个时间,supervisord会向该子进程发送一个强制kill的信号。
# 默认为10秒。非必须设置
;stopwaitsecs=10 ; max num secs to wait b4 SIGKILL (default 10)
# 主要用于,supervisord管理的子进程,这个子进程本身还有子进程。
# 那么我们如果仅仅干掉supervisord的子进程的话,子进程的子进程有可能会变成孤儿进程。
# 所以可以设置可个选项,把整个该子进程的整个进程组都干掉。
# 设置为true的话,一般killasgroup也会被设置为true。
# 需要注意的是,该选项发送的是stop信号
# 默认为false。非必须设置。。
;stopasgroup=false ; send stop signal to the UNIX process group (default false)
# 和上面的stopasgroup类似,不过发送的是kill信号
;killasgroup=false ; SIGKILL the UNIX process group (def false)
# 如果supervisord是root启动,我们在这里设置这个非root用户
# 可以用来管理该program
# 默认不设置。非必须设置项
;user=chrism ; setuid to this UNIX account to run the program
# 如果为true,则stderr的日志会被写入stdout日志文件中
# 默认为false,非必须设置
;redirect_stderr=true ; redirect proc stderr to stdout (default false)
# 子进程的stdout的日志路径,可以指定路径,AUTO,none等三个选项。
# 设置为none的话,将没有日志产生。设置为AUTO的话,将随机找一个地方生成日志文件
# 而且当supervisord重新启动的时候,以前的日志文件会被清空。
# 当redirect_stderr=true的时候,sterr也会写进这个日志文件
;stdout_logfile=/a/path ; stdout log path, NONE for none; default AUTO
# 日志文件最大大小,和[supervisord]中定义的一样。默认为50
;stdout_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB)
# 和[supervisord]定义的一样。默认10
;stdout_logfile_backups=10 ; # of stdout logfile backups (default 10)
# 设定capture管道的大小,当值不为0的时候,子进程可以从stdout
# 发送信息,而supervisor可以根据信息,发送相应的event。
# 默认为0,为0的时候表达关闭管道。非必须项
;stdout_capture_maxbytes=1MB ; number of bytes in 'capturemode' (default 0)
# 当设置为ture的时候,当子进程由stdout向文件描述符中写日志的时候
# 将触发supervisord发送PROCESS_LOG_STDOUT类型的event
# 默认为false。非必须设置
;stdout_events_enabled=false ; emit events on stdout writes (default false)
# 设置stderr写的日志路径,当redirect_stderr=true。这个就不用设置了,设置了也是白搭。
# 因为它会被写入stdout_logfile的同一个文件中
# 默认为AUTO,也就是随便找个地存,supervisord重启被清空。非必须设置
;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" ; process environment additions (def no adds)
;serverurl=AUTO ; override serverurl computation (childutils)

事件监听器配置

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
# event其实和program的地位是一样的,也是suopervisor启动的子进程
# 不过它干的活是监听supervisord发送的event。他的名字就叫listener了。
# 我们可以在listener里面做一系列处理,比如报警等等,与上面重复项不再单独解释
;[eventlistener:theeventlistenername]
;command=/bin/eventlistener ; 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)
# event事件的类型,也就是说,只有写在这个地方的事件类型。才会被发送
;events=EVENT ; event notif. types to subscribe to (req'd)
# event队列缓存大小,当buffer超过10的时候,最旧的event将会被清除,并把新的event放进去。
# 默认值为10。非必须选项
;buffer_size=10 ; event buffer queue size (default 10)
;directory=/tmp ; directory to cwd to before exec (def no cwd)
;umask=022 ; umask for process (default None)
;priority=-1 ; the relative start priority (default -1)
;autostart=true ; start at supervisord start (default: true)
;startsecs=1 ; # of secs prog must stay up to be running (def. 1)
;startretries=3 ; max # of serial start failures when starting (default 3)
;autorestart=unexpected ; autorestart if exited after running (def: unexpected)
;exitcodes=0,2 ; 'expected' exit codes used with autorestart (default 0,2)
;stopsignal=QUIT ; signal used to kill process (default TERM)
;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 ; setuid to this UNIX account to run the program
;redirect_stderr=false ; redirect_stderr=true is not allowed for eventlisteners
;stdout_logfile=/a/path ; 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_events_enabled=false ; emit events on stdout writes (default false)
;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_events_enabled=false ; emit events on stderr writes (default false)
;environment=A="1",B="2" ; process environment additions
;serverurl=AUTO ; override serverurl computation (childutils)

进程分组

1
2
3
4
5
6
7
8
9
10
11
# 给programs分组,划分到组里面的program。我们就不用一个一个去操作了
# 我们可以对组名进行统一的操作。注意:program被划分到组里面之后
# 就相当于原来的配置从supervisor的配置文件里消失了。
# supervisor只会对组进行管理,而不再对组里面的单个program进行管理了
;[group:thegroupname]
# 组成员,用逗号分开
# 这个是个必须的设置项
;programs=progname1,progname2 ; each refers to 'x' in [program:x] definitions
# 优先级,相对于组和组之间的
# 默认999。非必须选项
;priority=999 ; the relative start priority (default 999)

启动服务

1
2
#supervisord -c /etc/supervisord.conf
/etc/init.d/supervisord start

客户端管理

可以使用supervisord的命令行客户端管理工具supervisorctl,来管理进程。

1
2
3
4
5
6
7
8
supervisorctl status
supervisorctl stop usercenter
supervisorctl start usercenter
supervisorctl restart usercenter
supervisorctl [stop|start|restart] all
supervisorctl [stop|start|restart] pro:*
supervisorctl reread
supervisorctl update

个人配置

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
56
mkdir -p /etc/supervisor/conf.d/
cat /etc/supervisor/supervisord.conf
###############
[unix_http_server]
file=/var/run/supervisor.sock ; (the path to the socket file)
chmod=0700 ; sockef file mode (default 0700)
[supervisord]
logfile=/data/logs/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log)
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
childlogdir=/data/logs/supervisor ; ('AUTO' child log dir, default $TEMP)
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)
nodaemon=false ; (start in foreground if true;default false)
minfds=1024 ; (min. avail startup file descriptors;default 1024)
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL for a unix socket
[include]
files = /etc/supervisor/conf.d/*.conf
#++++++++++++++++++++++#
cat /etc/supervisor/conf.d/deva.conf
###############
[program:deva]
environment=LANG="en_US.utf8", LC_ALL="en_US.UTF-8", LC_LANG="en_US.UTF-8"
directory = /data/web_data/xxx/
command = python app.py
user = www
autostart = true
autorestart = true
stdout_logfile = /data/logs/supervisor/deva.log
stderr_logfile = /data/logs/supervisor/deva_err.log
#++++++++++++++++++++++#
cat /etc/supervisor/conf.d/userList.conf
###############
[program:userList]
environment=LANG="en_US.utf8", LC_ALL="en_US.UTF-8", LC_LANG="en_US.UTF-8"
directory = /data/web_data/xxx/handlers/tools/
command = python UserList.py
user = www
numprocs=3
numprocs_start=1
process_name=%(program_name)s_%(process_num)02d
autostart = true
autorestart = true
stdout_logfile = /data/logs/supervisor/userList.log
stderr_logfile = /data/logs/supervisor/userList_err.log