【Ubuntu系统中Python脚本的开机自启动以及持续检测运行状态】

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)

你可能感兴趣的:(ubuntu,python,linux)