yf_supervisord.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. # coding:utf-8
  2. import os
  3. import sys
  4. import fs
  5. import psutil
  6. import subprocess
  7. from crontab import CronTab
  8. local_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  9. if local_path not in sys.path:
  10. sys.path.append(local_path)
  11. from utils.comm_tools import shell_cmd, get_monitor_config
  12. class SupervisorTools:
  13. def __init__(self):
  14. supervisor_dir = "/data/supervisor"
  15. self.base_config_file = "/etc/supervisor/supervisord.conf"
  16. if supervisor_dir[-1] == '/':
  17. supervisor_dir = supervisor_dir[: -1]
  18. temp_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  19. utils_dir = os.path.join(temp_dir, 'utils')
  20. self.exception_warning_file = os.path.join(utils_dir, 'exception_warning.py')
  21. self.config_base = supervisor_dir
  22. self.python_bin = sys.executable
  23. self.bin_dir = os.path.dirname(self.python_bin)
  24. self.supervisord = os.path.join(self.bin_dir, 'supervisord')
  25. self.supervisorctl = os.path.join(self.bin_dir, 'supervisorctl')
  26. self.config_dir = os.path.join(self.config_base, 'config.d')
  27. self.log_dir = os.path.join(self.config_base, 'log')
  28. tmp_dir = os.path.dirname(self.base_config_file)
  29. if not os.path.isdir(tmp_dir):
  30. os.makedirs(tmp_dir)
  31. if not os.path.isdir(self.config_dir):
  32. os.makedirs(self.config_dir)
  33. if not os.path.isdir(self.log_dir):
  34. os.makedirs(self.log_dir)
  35. def _get_supervisor_app(self):
  36. '''获取supervisor所管理的程序'''
  37. status, app_name_list = False, []
  38. cmd = '{} status'.format(self.supervisorctl)
  39. code, stdout, stderr = shell_cmd(cmd)
  40. if code >= 0:
  41. for line in stdout.splitlines():
  42. tmp_line = line.split()
  43. if len(tmp_line) > 1:
  44. app_name = tmp_line[0].strip()
  45. app_name_list.append(app_name)
  46. status, msg = True, set(app_name_list)
  47. else:
  48. msg = '获取supervisor所管理的程序失败: {}'.format(stderr)
  49. return status, msg
  50. def _make_task_conf(self, app_name, dir_name, cmd, user):
  51. '''生成任务配置文件'''
  52. log_dir = os.path.join(self.log_dir, app_name)
  53. if not os.path.isdir(log_dir):
  54. os.makedirs(log_dir)
  55. conf_path = os.path.join(self.config_dir, '{}.conf'.format(app_name))
  56. log_file = os.path.join(log_dir, '{}.log'.format(app_name))
  57. conf_str = '''
  58. #程序唯一名称
  59. [program:{app_name}]
  60. #程序路径
  61. directory={dir_name}
  62. #运行程序的命令
  63. command={cmd}
  64. #标准日志输出位置
  65. stdout_logfile={log_file}
  66. autostart=true #supervisord启动后,同时启动此程序
  67. startsecs=5 #启动5秒后没有异常退出,就表示进程正常启动了,默认为1秒
  68. autorestart=true #程序退出后自动重启
  69. startretries=3 #启动失败自动重试次数,默认是3
  70. user={user} #用哪个用户启动进程,默认是root
  71. redirect_stderr=true #把stderr重定向到stdout标准输出,默认false
  72. stdout_logfile_maxbytes=50MB #stdout标准输出日志文件大小,日志文件备份数
  73. stdout_logfile_backups=10
  74. stopasgroup=true # 停止或终止进程时,同时结束进程包含的子进程
  75. killasgroup=true
  76. '''.format(app_name=app_name, dir_name=dir_name, cmd=cmd, log_file=log_file, user=user)
  77. with open(conf_path, 'w') as fw:
  78. fw.write(conf_str)
  79. def _make_supervisor_conf(self):
  80. cmd = '{} -u {}'.format(self.python_bin, self.exception_warning_file)
  81. conf = '''
  82. [unix_http_server]
  83. file={supervisor_dir}/supervisor.sock ; the path to the socket file
  84. ;chmod=0700 ; socket file mode (default 0700)
  85. ;chown=nobody:nogroup ; socket file uid:gid owner
  86. ;username=user ; default is no username (open server)
  87. ;password=123 ; default is no password (open server)
  88. [inet_http_server] ; inet (TCP) server disabled by default
  89. port=127.0.0.1:59001 ; ip_address:port specifier, *:port for all iface
  90. username=yunfei_yanfabu ; default is no username (open server)
  91. password=yanfabu6021 ; default is no password (open server)
  92. [supervisord]
  93. logfile={supervisor_dir}/supervisord.log ; main log file; default $CWD/supervisord.log
  94. logfile_maxbytes=50MB ; max main logfile bytes b4 rotation; default 50MB
  95. logfile_backups=10 ; # of main logfile backups; 0 means none, default 10
  96. loglevel=info ; log level; default info; others: debug,warn,trace
  97. pidfile={supervisor_dir}/supervisord.pid ; supervisord pidfile; default supervisord.pid
  98. nodaemon=false ; start in foreground if true; default false
  99. silent=false ; no logs to stdout if true; default false
  100. minfds=1024 ; min. avail startup file descriptors; default 1024
  101. minprocs=200 ; min. avail process descriptors;default 200
  102. ;umask=022 ; process file creation umask; default 022
  103. user=root ; setuid to this UNIX account at startup; recommended if root
  104. ;identifier=supervisor ; supervisord identifier, default is 'supervisor'
  105. ;directory=/tmp ; default is not to cd during start
  106. ;nocleanup=true ; don't clean up tempfiles at start; default false
  107. ;childlogdir=/tmp ; 'AUTO' child log dir, default $TEMP
  108. ;environment=KEY="value" ; key value pairs to add to environment
  109. ;strip_ansi=false ; strip ansi escape codes in logs; def. false
  110. [rpcinterface:supervisor]
  111. supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
  112. [supervisorctl]
  113. serverurl=unix://{supervisor_dir}/supervisor.sock
  114. ;username=chris ; should be same as in [*_http_server] if set
  115. ;password=123 ; should be same as in [*_http_server] if set
  116. ;prompt=mysupervisor ; cmd line prompt (default "supervisor")
  117. ;history_file=~/.sc_history ; use readline history if available
  118. [eventlistener:crash_warning]
  119. command={exception_warning_file}
  120. events=PROCESS_STATE
  121. redirect_stderr=false
  122. [include]
  123. files = {supervisor_dir}/config.d/*.conf
  124. '''.format(exception_warning_file=cmd, supervisor_dir=self.config_base)
  125. print(cmd)
  126. with open(self.base_config_file, 'w') as fw:
  127. fw.write(conf)
  128. def _update_task(self):
  129. '''启动添加的程序'''
  130. status, msg = True, 'supervisor管理器启动程序成功'
  131. cmd = '{} update all'.format(self.supervisorctl)
  132. code, stdout, stderr = shell_cmd(cmd)
  133. if code != 0:
  134. status, msg = False, 'supervisor管理器启动程序失败:{}\n{}'.format(stdout, stderr)
  135. return status, msg
  136. def make_task(self):
  137. '''创建supervisor任务并启动'''
  138. status, msg = self._get_supervisor_app()
  139. if not status:
  140. return status, msg
  141. app_name_set, app_name_list = msg, []
  142. monitor_config = get_monitor_config()
  143. with fs.open_fs('/') as root_fs:
  144. for script_dir, item in monitor_config.items():
  145. if not os.path.isdir(script_dir):
  146. continue
  147. file_filter, file_exclude = item['filter'], item.get('exclude', [])
  148. python_bin, env_name, cmd_user = item['executable'], item['name'], item['user']
  149. if not file_exclude:
  150. file_exclude = None
  151. if not file_filter:
  152. file_filter = None
  153. for path in root_fs.walk.files(script_dir, filter=file_filter, exclude=file_exclude, max_depth=1):
  154. dir_name, file_name = os.path.split(path)
  155. index = file_name.rfind('.')
  156. app_name = file_name[:index] if index != -1 else file_name
  157. app_name = "{}_{}".format(env_name, app_name)
  158. if app_name in app_name_set:
  159. continue
  160. cmd = '{} -u {}'.format(python_bin, path)
  161. self._make_task_conf(app_name=app_name, dir_name=dir_name, cmd=cmd, user=cmd_user)
  162. app_name_list.append(app_name)
  163. status, msg = True, 'supervisor管理器启动程序成功'
  164. if app_name_list:
  165. status, msg = self._update_task()
  166. return status, msg
  167. def _check_supervisord(self):
  168. ''' 检测supervisor管理器进程是否运行'''
  169. is_run = False
  170. for proc in psutil.process_iter():
  171. proc_name = proc.name().lower()
  172. if proc_name == 'supervisord':
  173. is_run = True
  174. break
  175. return is_run
  176. def start_supervisord(self):
  177. '''启动supervisor管理器'''
  178. msg = ''
  179. if not os.path.isfile(self.supervisord):
  180. msg = 'supervisor未安装,请先安装supervisor'
  181. else:
  182. is_run = self._check_supervisord()
  183. if not is_run:
  184. self._make_supervisor_conf()
  185. cmd = "{} -c {}".format(self.supervisord, self.base_config_file)
  186. result = subprocess.run(cmd, shell=True)
  187. if result.returncode == 0:
  188. is_run = True
  189. else:
  190. msg = '启动supervisor管理器失败, 请检查'
  191. if is_run:
  192. status, msg = self.make_task()
  193. return msg
  194. def _init_crond():
  195. cron = CronTab(user='root')
  196. python_bin = sys.executable
  197. main_file = os.path.abspath(__file__)
  198. cmd = "{} {}".format(python_bin, main_file)
  199. for job in cron:
  200. if job.command in cmd:
  201. break
  202. else:
  203. new_job = cron.new(cmd, comment="supervise监控脚本,禁止修改")
  204. new_job.minute.every(2)
  205. cron.write()
  206. if __name__ == '__main__':
  207. _init_crond()
  208. st = SupervisorTools()
  209. msg = st.start_supervisord()
  210. print(msg)