Python3下通过XML_RPC协议连接supervisor达到supervisorctl的效果(By UNIX Socket)
在使用supervisor的时候查阅师傅们的博客时,发现大多文章都比较老了。。。加上天下文章一大抄的现状(有的文章居然还写着supervisor仅支持python2),找些有价值的方案真的很费劲。。。。
这里记录下在使用过程中我的需求、遇到的坑以及解决方案。
标题提到的解决方案在这里:
使用场景
我使用supervisor主要用于两个场景:需要后台运行的服务、需要对启动的进程进行强管理时。相比更加僵硬的nohup &
的方式,使用supervisor会显得更加优雅、方便。不需要使用kill PID
或restart
的方式来杀死或是重启我们自己的进程。
安装/配置文件
这一部分各位师傅的文章写的都已近蛮详细了,不做赘述,百度搜索下supervisor可以找到大量教程。这里放出我遇到的一些坑点:
- 我习惯将program直接写在conf文件内,这样保证我的supervisor仅使用一个文件进行配置。而不将各个program拆分成ini文件再在conf文件内进行include。
- 作为安全人员,使用一个组件的时候第一反应时有没有1days…果然:
CVE-2017-11610
,漏洞影响范围为:Supervisor 3.1.2 <= Version <= 3.3.2 ,记得检查自己的版本。
实现通过Py代码控制supervisor
描述下我的需求场景:实现一个web按钮,可以重启/关闭/打开supervisor启动的进程。
其实关于这个需求,解决的办法已经有很多了,但是看下来很多都不是很和我的心意,我将搜到的几个方案在这里列一下。
- 使用supervisorctl:这个是最简单的方式,通过命令行的方式就可以达到控制supervisor的效果。但是对我的需求来说,我不想在我的代码里写下这样丑陋的部分:
os.system("supervisorctl start xxxxxxx")
简直尴尬癌犯了。 - 使用inet_http_server配置:通过inet_http_server配置,supervisor会开一http服务,可以通过web界面友好的控制。对于不需要集成化的朋友用这个方案就可以了,记得设置好密码;对于我的需求,我不想开两个端口、两个服务来实现这样一个小按钮的需求。我更期望的是能够将supervisor提供的http服务中的按钮搬到我的web界面内来。emmmm…
- 使用xml_rpc:supervisor支持RPC协议,所以可以通过构建xml_rpc client来与supervisor进行通信。同时supervisor提供的一些方法也很友好。
针对上面几种方式,最符合我的需求的就是使用xml_rpc,于是踩坑之路就开始了。
坑0x01
python想要搭建xml_rpc client端连接supervisor,只要你搜一个博客基本都是这么写的:
import xmlrpclib
client = xmlrpclib.Server('http://localhost:9001/RPC2')
然后xmlrpclib这玩意在python3里面早就变成了xmlrpc。。。连接的方式也变成了这样:
import xmlrpc.client
client = xmlrpc.client.ServerProxy('http://127.0.0.1:9001/RPC2')
后面的使用就没有什么区别了。
坑0x02
代码继续:突然发现,当开启了inet_http_server,并且设置了密码,使用这种方式连接会蜜汁会报错:
import xmlrpc.client
client = xmlrpc.client.ServerProxy('http://username@password:127.0.0.1:9001/RPC2')
socket.gaierror: [Errno 8] nodename nor servname provided, or not known
咱也不知道咋回事,咱也不敢问,反正通过这种http方式连接我是没有成功连接上。
然后看到了一个应该是翻译国外的issue论坛的文章:https://www.icode9.com/content-1-312817.html
里面的师傅也遇到了同样的写法问题,然后有人给出的解决方案是:使用自带的unix socket直接通信。
这可真是个好东西
坑0x03
然后开始改用unix 的连接方式:
s = xmlrpc.client.ServerProxy("unix://tmp/supervisor.sock")
然后又gg了
OSError: unsupported XML-RPC protocol
继续寻找师傅的大腿:http://www.voidcn.com/article/p-titxqxik-bvb.html
里面写了新的写法,哦,原来写的不对。
import supervisor.xmlrpc
import xmlrpc.client
s = xmlrpc.client.ServerProxy('http://127.0.0.1',
transport=supervisor.xmlrpc.SupervisorTransport
(None,None,serverurl='unix:///tmp/supervisor.sock'))
这里还有一个小坑:serverurl写的内容,要和你的supervisor.conf 里面这个配置写的相同就可以了:
[unix_http_server]
file=/tmp/supervisor.sock
[rpcinterface:supervisor]
supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock
然后就可以正常使用了:
>>> s.system.listMethods()
['supervisor.addProcessGroup', 'supervisor.clearAllProcessLogs', 'supervisor.clearLog', 'supervisor.clearProcessLog', 'supervisor.clearProcessLogs', 'supervisor.getAPIVersion', 'supervisor.getAllConfigInfo', 'supervisor.getAllProcessInfo', 'supervisor.getIdentification', 'supervisor.getPID', 'supervisor.getProcessInfo', 'supervisor.getState', 'supervisor.getSupervisorVersion', 'supervisor.getVersion', 'supervisor.readLog', 'supervisor.readMainLog', 'supervisor.readProcessLog', 'supervisor.readProcessStderrLog', 'supervisor.readProcessStdoutLog', 'supervisor.reloadConfig', 'supervisor.removeProcessGroup', 'supervisor.restart', 'supervisor.sendProcessStdin', 'supervisor.sendRemoteCommEvent', 'supervisor.shutdown', 'supervisor.signalAllProcesses', 'supervisor.signalProcess', 'supervisor.signalProcessGroup', 'supervisor.startAllProcesses', 'supervisor.startProcess', 'supervisor.startProcessGroup', 'supervisor.stopAllProcesses', 'supervisor.stopProcess', 'supervisor.stopProcessGroup', 'supervisor.tailProcessLog', 'supervisor.tailProcessStderrLog', 'supervisor.tailProcessStdoutLog', 'system.listMethods', 'system.methodHelp', 'system.methodSignature', 'system.multicall']
参考
https://www.icode9.com/content-1-312817.html
http://supervisord.org/configuration.html#unix-http-server-section-example
http://www.voidcn.com/article/p-titxqxik-bvb.html