Ubuntu系统中Python脚本的开机自启动以及持续检测运行状态
- 1. 实现原理
- 2. 创建service软链接
- 3. 修改service文件内容
- 4. 创建/etc/rc.local文件
- 5. starter.py文件对目标python脚本进行状态的检查、启动
需求:系统为ubuntu18.04,只要服务器是开机状态,python脚本aProgramThatNeedsToRunAllTheTime.py(名字为啥这么长和丑是有原因的)就必须是在运行的状态。
主要参考了【Ubuntu】Ubuntu18.04默认没有/etc/rc.local,需要手动配置:ubuntu18.04不再使用 inited 管理系统,改用 systemd。systemd中也有rc.local服务,需要手动开启。
systemd是linux系统第一个运行的程序,相当于以前的init进程,pid=1。也可以理解为systemd是初始化整个系统所需的资源,负责启用和管理系统的各种服务。
rc.local是ubuntu的开机自启动的配置文件,执行的时机:在系统所有服务启动后开始执行rc.local中的配置,在ubuntu18.04中,默认rc.local服务器并没有启动。
1. 实现原理
systemd默认会读取/etc/systemd/system下的配置文件。一般系统安装完后在/lib/systemd/system/下会有rc-local.service文件,将该文件软链接到/etc/systemd/system下,然后创建rc.local文件,将需要开机自启动的脚本(命名为start.py)运行命令写入,则开机后start.py脚本即可运行,start.py脚本用于检查aProgramThatNeedsToRunAllTheTime.py脚本是否在运行,未在运行则马上启动它。
2. 创建service软链接
将/lib/systemd/system/rc-local.service 链接到/etc/systemd/system/目录下面来
ln -fs /lib/systemd/system/rc-local.service /etc/systemd/system/rc-local.service
3. 修改service文件内容
一般正常的启动文件主要分为三部分
[Unit]段:启动顺序与依赖关系
[Service]段:启动行为如何启动,启动类型
[Install]段:定义如何安装这个配置文件,即怎样做到开机启动
由于ubuntu18.04默认rc.local服务器不启动,所以默认文件里面是缺少了Install段的
sudo vim /etc/systemd/system/rc-local.service
在文件末尾加上Install段
[Install]
WantedBy=multi-user.target
Alias=rc-local.service
4. 创建/etc/rc.local文件
sudo touch /etc/rc.local
给rc.local添加可执行权限
sudo chmod 777 /etc/rc.local
编辑/etc/rc.local文件,将start.py脚本的运行写入
sudo vim /etc/rc.local
nohup英文全称no hang up(不挂起),用于在系统后台不挂断地运行命令,退出终端不会影响程序的运行
&表示把该命令以后台的job的形式运行
2>&1表示将标准错误2重定向到标准输出&1,标准输出&1再被重定向输入到指定的log文件中
0-stdin(standard input,标准输入)
1-stdout(standard output,标准输出)
2-stderr(standard error,标准错误输出)
#!/bin/bash
nohup /opt/anaconda3/envs/py39/bin/python -u /home/zy/starter.py > /home/zy/starter_out.log 2>&1 &
注:此时的文件必须都使用绝对路径
5. starter.py文件对目标python脚本进行状态的检查、启动
脚本中都需要使用绝对路径
# -*- coding:utf-8 -*-
import os
from loguru import logger
import time, datetime
def execCmd(cmd):
r = os.popen(cmd)
text = r.read()
r.close()
return text
def doSomething():
# 避免输出日志被覆盖
now = datetime.datetime.now()
os.system(f'nohup /opt/anaconda3/envs/py39/bin/python -u /home/zy/aProgramThatNeedsToRunAllTheTime.py > /home/zy/out_{now.strftime("%Y_%m_%d_%H_%M_%S")}.log 2>&1 &')
def kill_program(texts, num):
for i in range(num - 2):
Id = texts[i]
os.system('kill -9 ' + Id)
if __name__ == '__main__':
while True:
# ps -ef是linux查看进程信息指令,|是管道符号导向到grep去查找特定的进程,最后一个|是导向grep过滤掉grep进程:因为grep查看程序名也是进程,会混到查询信息里
# python脚本的名字最好复杂点,这样能避免检测到同名程序
programIsRunningCmd="ps -ef|grep aProgramThatNeedsToRunAllTheTime.py|grep -v grep|awk '{print $2}'"
programIsRunningCmdAns = execCmd(programIsRunningCmd) #调用函数执行指令,并返回指令查询出的信息
ansLine = programIsRunningCmdAns.split('\n') #将查出的信息用换行符‘\n’分开
#判断如果返回行数>0则说明python脚本程序已经在运行,打印提示信息结束程序,否则运行脚本代码doSomething()
if len(ansLine) == 1:
# ansLine:['']表示程序未运行,执行doSomething启动它
doSomething()
elif len(ansLine) == 2:
# ansLine:['1420', '']表示程序正在运行,第一个元素为程序的PID
logger.info("The program is working.")
elif len(ansLine) > 2:
# 可能还会出现该脚本同时被运行了好几个进程
logger.info(f"There are {len(ansLine) - 1} programs running!")
logger.info(ansLine)
# kill_program(ansLine, len(ansLine))
else:
logger.info(f"Error:the length of programIsRunningCmdAns.split('\n') is {len(ansLine)}")
# 每过一分钟就检测一次程序是否在正常运行
time.sleep(60)