ubuntu python虚拟环境venv搭配systemd服务实战

文章目录

    • 参考文章
    • 目录结构
    • 步骤
      • 安装venv
      • 查看python版本
      • 创建虚拟环境
      • 激活虚拟环境
      • 运行我们程序看缺少哪些依赖库,依次安装它们
      • 接下来我们配置python程序启动脚本,脚本中启动python程序前需先激活虚拟环境
      • 配置.service文件
      • 然后执行部署脚本,成功了
      • 但是又莫名其妙搞出来一个问题,后面一直不能复现,后来又好了,一直无法复现(考虑问题复现时,使用备用方案)
      • 反正现在是正常的

参考文章

Python虚拟环境使用教程(以虚拟环境管理工具venv为例)(virtualenv、venv、pyenv、virtualenvwrapper、conda不同管理工具对比)

目录结构

在这里插入图片描述

步骤

安装venv

它会基于当前版本的python3来安装python3-venv

在这里插入图片描述

apt update && apt install python3.8-venv

查看python版本

执行python

在这里插入图片描述

创建虚拟环境

进入项目目录,执行:

python3 -m venv .venv-python3.8

(我把每个项目虚拟环境固定取名为.venv-python3.8,后续方便脚本操作)

在这里插入图片描述

可以看到,生成了目录.venv-python3.8

我这刚生成的虚拟环境占空间也不大,才7兆多:

在这里插入图片描述

激活虚拟环境

source .venv-python3.8/bin/activate

在这里插入图片描述

运行我们程序看缺少哪些依赖库,依次安装它们

执行:

python3 ip_change

在这里插入图片描述

发现少了ping3,装上:

pip install ping3

在这里插入图片描述

反复执行:

python3 ip_change

缺少啥库就装啥库:

在这里插入图片描述

下载太慢,我换成清华源,怎么还给我卡住了?

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple gevent

在这里插入图片描述

第二天看,好了:

在这里插入图片描述

继续:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

可以了,跑起来了,接口也能调通:

在这里插入图片描述
在这里插入图片描述

接下来我们配置python程序启动脚本,脚本中启动python程序前需先激活虚拟环境

注意journalctl -u <servicename>没有及时打印python日志的原因是因为在systemd unit文件中执行shell脚本,脚本中再执行python命令,命令没加-u参数导致的。估计是systemd自动把shell脚本输出重定向到journalctl日志,跟我们之前遇到的问题一样

ky_ai_ip_change.sh

#!/bin/bash

# 打印所有,包括注释
# set -v
# 打印执行命令
# set -x
# 命令出错退出
set -e
# 使用未初始化变量退出
set -u

USER="root"

# --------------------------------------------------------------------------

# 检查是否是root
WHO=$(whoami | grep "${USER}$")
if [ -z "${WHO}" ]; then
    echo
    echo "Please change to \"${USER}\" user mode first!"
    echo
    exit 1
fi

# --------------------------------------------------------------------------

# 获取脚本所在路径
SCRIPT_LOCATION=$(
    cd "$(dirname "$0")" || {
        echo "cd Failure"
        exit 1
    }
    pwd
)
echo "SCRIPT_LOCATION = $SCRIPT_LOCATION"

# --------------------------------------------------------------------------

VENV_NAME=".venv-python3.8"
PYTHON_ENTRY_FILE="ip_change"

# --------------------------------------------------------------------------

# 激活虚拟环境
# 加指令注释消除shellcheck警告,source后有变量就会警告
# shellcheck source=/ky/tml/ky_ai_ip_change/.venv-python3.8/bin/activate
source $SCRIPT_LOCATION/$VENV_NAME/bin/activate
if [ $? -ne 0 ]; then
    echo "Execute [source $SCRIPT_LOCATION/$VENV_NAME/bin/activate] failed"
    exit 1
fi

# 运行项目
# python -u $SCRIPT_LOCATION/$PYTHON_ENTRY_FILE
# if [ $? -ne 0 ]; then
#     echo "Execute [python $SCRIPT_LOCATION/$PYTHON_ENTRY_FILE] failed"
#     exit 1
# fi

# 改一改,避免命令报错直接退出脚本,没有机会退出虚拟环境
if python -u $SCRIPT_LOCATION/$PYTHON_ENTRY_FILE; then
    echo "命令 [python -u $SCRIPT_LOCATION/$PYTHON_ENTRY_FILE] 执行成功"
else
    echo "命令 [python -u $SCRIPT_LOCATION/$PYTHON_ENTRY_FILE] 执行失败"
fi

deactivate
if [ $? -ne 0 ]; then
    echo "Execute [deactivate] failed,退出虚拟环境失败"
    exit 1
fi
echo "Execute [deactivate] successfully,退出虚拟环境成功"

配置.service文件

ky_ai_ip_change.service

[Unit]
Description=ky_ai_ip_change
After=network.target

[Service]
ExecStart=/ky/tml/ky_ai_ip_change/ky_ai_ip_change.sh
WorkingDirectory=/ky/tml/ky_ai_ip_change
Restart=always
RestartSec=3

[Install]
WantedBy=default.target

然后执行部署脚本,成功了

install.sh

#!/bin/bash

# 打印所有,包括注释
# set -v
# 打印执行命令
# set -x
# 命令出错退出
set -e
# 使用未初始化变量退出
set -u

USER=root
# USER_HOME=/root

# --------------------------------------------------------------------------

# 检查是否是root
WHO=$(whoami | grep "${USER}$")
if [ -z "${WHO}" ]; then
    echo
    echo "Please change to \"${USER}\" user mode first!"
    echo
    exit 1
fi

# --------------------------------------------------------------------------

# 获取脚本所在路径
SCRIPT_LOCATION=$(
    cd "$(dirname "$0")" || {
        echo "cd Failure"
        exit 1
    }
    pwd
)
# echo "SCRIPT_LOCATION = $SCRIPT_LOCATION"
chmod 777 ${SCRIPT_LOCATION} -R

# --------------------------------------------------------------------------

# 不同服务只用改 SERVICE_NAME 变量即可
SERVICE_NAME="ky_ai_ip_change"
SERVICE_FILE_NAME="$SERVICE_NAME.service"

SERVICE_SOURCE_FILE_PATH="$SCRIPT_LOCATION/$SERVICE_FILE_NAME"

# 不能在 /etc/systemd/system 中创建子目录吗?(貌似是的,测试很多次都不行)
# SERVICE_TARGET_DIR_NAME="ky_ai_service"
# SERVICE_TARGET_DIR_PATH="/etc/systemd/system/$SERVICE_TARGET_DIR_NAME"
SERVICE_TARGET_DIR_PATH="/etc/systemd/system"

# --------------------------------------------------------------------------

# 创建目标目录
# if [ ! -d "$SERVICE_TARGET_DIR_PATH" ]; then
#     mkdir -p "$SERVICE_TARGET_DIR_PATH"
#     echo "创建目录:[$SERVICE_TARGET_DIR_PATH]"
# else
#     echo "目录已存在,不重新创建:[$SERVICE_TARGET_DIR_PATH]"
# fi
# chmod 777 "$SERVICE_TARGET_DIR_PATH" -R

# --------------------------------------------------------------------------

# 判断服务是否存在
if systemctl list-unit-files --type=service | grep -q "$SERVICE_NAME"; then
    echo "$SERVICE_NAME.service exists"
    # 打印服务状态
    # systemctl status $SERVICE_NAME
    echo
    # 这句明明是打印居然会触发 set -e 报错退出,加上 || true
    # systemctl status $SERVICE_NAME || true
    # 貌似信息比较长时,会有分页等待用户输入阻塞程序,加上 --no-pager 选项
    systemctl status $SERVICE_NAME --no-pager || true
    echo
    # 询问用户是否删除
    read -p "Do you want to delete $SERVICE_NAME.service? (y/n): " choice
    if [[ $choice == "y" || $choice == "Y" ]]; then
        # 删除服务
        systemctl stop $SERVICE_NAME
        echo "已 stop [$SERVICE_NAME] 服务 "
        systemctl disable $SERVICE_NAME
        echo "已 disable [$SERVICE_NAME] 服务"
        # rm /etc/systemd/system/$SERVICE_NAME.service  # 不用删,会自动删的
        systemctl daemon-reload
        echo "已 daemon-reload"
        echo "$SERVICE_NAME.service has been deleted"
    else
        echo "Exiting script"
        exit 0
    fi
else
    echo "$SERVICE_NAME.service not exists"
fi
echo

# --------------------------------------------------------------------------

# 这句明明是打印居然会触发 set -e 报错退出
# systemctl list-unit-files | grep "$SERVICE_NAME"
# systemctl list-unit-files | grep "$SERVICE_NAME" || true

# --------------------------------------------------------------------------

# Function: create_symlink
# Description: Check if a symlink exists and is valid. If it is valid, prompt the user to delete and relink it.
#              If it is invalid, display an error message. If it does not exist, create a new symlink.
# Parameters:
#   $1 - The target path of the symlink
#   $2 - The path of the symlink
# Returns:
#   None

function create_symlink() {
    SRC=$1
    LINK=$2

    if [ -e $LINK ]; then
        if [ -L $LINK ]; then
            echo "The symlink $LINK is valid."
            read -p "Do you want to delete and relink it? (y/n) " choice
            case "$choice" in
            y | Y)
                rm $LINK
                echo "The symlink $LINK has been deleted."
                ;;
            *)
                return 0
                ;;
            esac
        else
            echo "The symlink $LINK is invalid."
        fi
    fi
    echo "Force create soft link: [$LINK -> $SRC]"
    ln -sf $SRC $LINK
    if [ $? -ne 0 ]; then
        echo "Force create soft link: [$LINK -> $SRC] failed"
        exit 1
    fi
    ls -l --color=auto $LINK
}

# --------------------------------------------------------------------------

# 判断软链接/usr/local/bin/node是否存在,如果存在,判断软链接是否有效,如果有效,询问用户是否删除,如果用户选择是,则删除此软链接,并重新创建
SERVICE_TARGET_FILE_PATH="$SERVICE_TARGET_DIR_PATH/$SERVICE_FILE_NAME"
# ls -l --color=auto $NODE_LINK
create_symlink $SERVICE_SOURCE_FILE_PATH $SERVICE_TARGET_FILE_PATH
echo

# --------------------------------------------------------------------------

# 通知systemd重新加载配置文件
systemctl daemon-reload
if [ $? -ne 0 ]; then
    echo "Systemctl daemon-reload failed"
    exit 1
fi
echo "Daemon-reload successfully"

# 启用服务
systemctl enable $SERVICE_NAME
if [ $? -ne 0 ]; then
    echo "Enable service [$SERVICE_NAME] failed"
    exit 1
fi
echo "Enable service [$SERVICE_NAME] successfully"

# 启动服务
systemctl start $SERVICE_NAME
if [ $? -ne 0 ]; then
    echo "Start service [$SERVICE_NAME] failed"
    exit 1
fi
echo "Start service [$SERVICE_NAME] successfully"

# --------------------------------------------------------------------------

echo
# systemctl status $SERVICE_NAME || true
# 貌似信息比较长时,会有分页等待用户输入阻塞程序,加上 --no-pager 选项
systemctl status $SERVICE_NAME --no-pager || true

# --------------------------------------------------------------------------

echo
echo "Service [$SERVICE_FILE_NAME] install successfully"
echo

但是又莫名其妙搞出来一个问题,后面一直不能复现,后来又好了,一直无法复现(考虑问题复现时,使用备用方案)

参考文章:Current command vanished from the unit file, execution of the command list won‘t be resumed.

后面如果又碰到,可以考虑直接systemd unit文件中直接执行python指令而不是脚本,同时定义ExecStartPreExecStopPost执行指令前的激活虚拟环境和退出虚拟环境的操作:


如果使用systemd来执行Python服务,可以在service配置文件中设置ExecStartPreExecStopPost来在执行前进入虚拟环境,执行后退出虚拟环境。

以下是一个示例的service配置文件,展示了如何在执行前进入虚拟环境,执行后退出虚拟环境:

[Unit]
Description=My Python Service

[Service]
ExecStartPre=/bin/bash -c 'source /path/to/venv/bin/activate'
ExecStart=/path/to/python /path/to/script.py
ExecStopPost=/bin/bash -c 'deactivate'

[Install]
WantedBy=multi-user.target

在这个示例中,ExecStartPre指定了在执行前要执行的命令,即进入虚拟环境的命令source /path/to/venv/bin/activateExecStart指定了要执行的Python脚本的路径。ExecStopPost指定了在执行后要执行的命令,即退出虚拟环境的命令deactivate

请将/path/to/venv替换为你的虚拟环境的路径,将/path/to/python替换为你的Python解释器的路径,将/path/to/script.py替换为你的Python脚本的路径。

通过这样的配置,当你启动或停止该service时,会自动进入和退出虚拟环境。

反正现在是正常的

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/66658.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

考研算法第40天:众数 【模拟,简单题】

题目 本题收获 又是一道比较简单的模拟题&#xff0c;就不说解题思路了&#xff0c;说一下中间遇到的问题吧&#xff0c;就是说cin输入它是碰到空格就停止输入的&#xff0c;详细的看下面这篇博客对于cin提取输入流遇到空格的问题_while(cin) 空格_就是那个党伟的博客-CSDN博…

JVM 调优实例

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ JVM提供了多种垃圾回收器&#xff0c;可以根据应用程序的需求选择最适合的垃圾回收器。例如&#xff0c;如果应用程序需要更快的响应时间&#xff0c;可以选择并行垃圾回收…

Hello,SpringBoot!

一、回顾什么是Spring Spring是一个开源框架&#xff0c;2003 年兴起的一个轻量级的Java 开发框架&#xff0c;作者&#xff1a;Rod Johnson Spring是为了解决企业级应用开发的复杂性而创建的&#xff0c;简化开发。 Spring是如何简化Java开发的 为了降低Java开发的复杂性…

模仿火星科技 基于cesium+ 贴地测量+可编辑

当您进入Cesium的编辑贴地测量世界&#xff0c;下面是一个详细的操作过程&#xff0c;帮助您顺利使用这些功能&#xff1a; 1. 创建提示窗&#xff1a; 启动Cesium应用&#xff0c;地图场景将打开&#xff0c;欢迎您进入编辑模式。在屏幕的一角&#xff0c;一个友好的提示窗将…

【在一个升序数组中插入一个数仍升序输出】

在一个升序数组中插入一个数仍升序输出 题目举例&#xff1a; 有一个升序数组nums&#xff0c;给一个数字data&#xff0c;将data插入数组nums中仍旧保证nums升序&#xff0c;返回数组中有效元素个数。 比如&#xff1a;nums[100] {1, 2, 3, 5, 6, 7, 8, 9} size 8 data 4 …

LabVIEW开发高压配电设备振动信号特征提取与模式识别

LabVIEW开发高压配电设备振动信号特征提取与模式识别 矿用高压配电设备是井下供电系统中的关键设备之一&#xff0c;肩负着井下供配电和供电安全的双重任务&#xff0c;其工作状态直接影响着井下供电系统的安全性和可靠性。机械故障占配电总故障的70%。因此&#xff0c;机械故…

报错Uncaught (in promise) Error: Manifest request to...

在使用nuxt框架时&#xff0c;出现如下报错&#xff1a; 解决方案&#xff1a; 不要打开两个以上的开发者工具更换nuxt的端口号 参考资料&#xff1a;https://github.com/nuxt/nuxt.js/issues/6202

DP(状态机模型)

状态机模型和01背包问题的区别就在于&#xff0c;01背包中每个物品选或不选都是独立的&#xff0c; 不受前者约束不对后者产生影响&#xff0c;而状态机不一样。换成01那种状态之间的转化图来看的话,01背包中0和1的转化不受任何约束&#xff0c;可以说是有向完全图&#xff1b;…

浅析 C 语言的共用体、枚举和位域

前言 最近在尝试阅读一些系统库的源码&#xff0c;但是其中存在很多让我感到既熟悉又陌生的语法。经过资料查阅&#xff0c;发现是 C 语言中的共用体和位域。于是&#xff0c;趁着课本还没有扔掉&#xff0c;将一些相关的知识点记录在本文。 文章目录 前言共用体 (union)枚举…

理解 Python 的 for 循环

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 在本篇博客中&#xff0c;我们将讨论 Python 中 for 循环的原理。 我们将从一组基本例子和它的语法开始&#xff0c;还将讨论与 for 循环关联的 else 代码块的用处。 然后我们将介绍迭代对象、迭代器和迭代器协议&…

Android 14重要更新预览

Android 14重要更新预览 国际化 Android 14 在 Android 13 的基础上进一步扩展了按应用设定语言功能&#xff0c;提供了一些额外的功能&#xff1a; 自动生成应用的 localeConfig&#xff1a;从 Android Studio Giraffe Canary 7 和 AGP 8.1.0-alpha07 开始&#xff0c;您可以…

Java版企业电子招标采购系统源码Spring Cloud + Spring Boot +二次开发+ MybatisPlus + Redis tbms

​ 功能描述 1、门户管理&#xff1a;所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含&#xff1a;招标公告、非招标公告、系统通知、政策法规。 2、立项管理&#xff1a;企业用户可对需要采购的项目进行立项申请&#xff0c;并提交审批&#xff0c;查…

【人工智能前沿弄潮】—— SAM自动生成物体mask

SAM自动生成物体mask 由于SAM可以高效处理提示&#xff0c;可以通过在图像上抽样大量的提示来生成整个图像的mask。这种方法被用来生成数据集SA-1B。 类SamAutomaticMaskGenerator实现了这个功能。它通过在图像上的网格中对单点输入提示进行抽样&#xff0c;从每个提示中SAM可…

【动态规划刷题 6】 删除并获得点数 粉刷房子

740. 删除并获得点数 给你一个整数数组 nums &#xff0c;你可以对它进行一些操作。 每次操作中&#xff0c;选择任意一个 nums[i] &#xff0c;删除它并获得 nums[i] 的点数。之后&#xff0c;你必须删除 所有 等于 nums[i] - 1 和 nums[i] 1 的元素。 开始你拥有 0 个点数。…

【数据库】Redis可以替代Mysql吗

Redis和Mysql的搭配 Redis可以替代Mysql吗什么是RedisRedis适用的场景以及优点Redis的缺点 什么是MysqlMysql的优点Mysql缺点 总结 Redis可以替代Mysql吗 Redis不能代替MySQL&#xff0c; Redis和MySQL只能是一种互补。 什么是Redis Redis是一种非关系型数据库&#xff0c;也…

【iPhone】手机还有容量,拍视频却提示 iPhone 储存空间已满

文章目录 前言解决方案 结语 前言 今天在用 iPhone 录像的时候突然提醒我 iPhone储存空间已满 你没有足够的储存空间来录制视频” 可我明明还有 20G 的容量 我非常疑惑&#xff0c;因为我之前还剩1个G都能录像&#xff0c;现在20G反而不行了&#xff0c;于是重启了手机&#…

Kali中AWD靶机环境搭建

Kali中AWD靶机环境搭建 1、kali安装docker2、克隆项目&#xff08;400多M&#xff0c;下载会有点久&#xff09;3、进入项目4、下载镜像5、改镜像名6、比赛环境搭建6.1 启动靶机6.2 连接裁判机&#xff0c;启动check脚本6.3 关闭环境命令 7、 靶机访问方式7.1 web界面访问7.2 s…

代理模式:静态代理+JDK/CGLIB 动态代理

文章目录 1. 代理模式2. 静态代理3. 动态代理3.1. JDK 动态代理机制3.1.1. 介绍 3.1.2. JDK 动态代理类使用步骤3.1.3. 代码示例3.2. CGLIB 动态代理机制3.2.1. 介绍3.2.2. CGLIB 动态代理类使用步骤3.2.3. 代码示例 3.3. JDK 动态代理和 CGLIB 动态代理对比 4. 静态代理和动态…

HTML|计算机网络相关

1.三次握手 第一次握手&#xff1a;客户端首先向服务端发送请求。 第二次握手&#xff1a;服务端在接收到客户端发送的请求之后&#xff0c;需要告诉客户端已收到请求。 第三次握手&#xff1a;客户端在接收到服务端发送的请求和确认信息之后&#xff0c;同样需要告诉服务端已…

【数模】主成分分析PCA

主成分分析(Principal Component Analysis,PCA)&#xff0c;是一种降维算法&#xff0c;它能将多个指标转换为少数几个主成分&#xff0c;这些主成分是原始变量的线性组合&#xff0c;且彼此之间互不相关&#xff0c;其能反映出原始数据的大部分信息。使用场景&#xff1a;一般…
最新文章