当作为systemd/systemctl服务运行时,Python导入本地模块失败

50

我有一个Python应用程序,想要将其作为系统服务运行。当我手动运行该应用程序时,它可以正常运行。但是当我将其作为服务运行时,它无法找到使用pip install -e my_module安装的本地模块。

该应用程序的主函数包含以下代码:

print(sys.argv)
import pip
installed_packages = pip.get_installed_distributions()
installed_packages_list = sorted(["%s==%s" % (i.key, i.version) for i in installed_packages])
print(installed_packages_list)
print('doing tox')
import tox
print('doing my_mod')
import my_mod
print(my_mod.__file__)
from my_mod.auth.http_auth_provider import HTTPAuthProvider
当我手动运行它时,我得到以下结果(请注意,在“已安装的软件包”中第二行包括my-mod):
['/usr/bin/pv_api']
['aiohttp==0.19.0', 'chardet==2.3.0', 'jsonschema==2.5.1', 'pip==7.0.0', 'pluggy==0.3.1', 'pv-api==0.0.0', 'py==1.4.31', 'pycrypto==2.6.1', 'pymongo==3.1.1', 'pyyaml==3.11', 'setuptools==19.6.2', 'six==1.10.0', 'tox==2.3.1', 'virtualenv==14.0.6', 'my-mod==0.1.0', 'webauthsession==1.1.1']
doing tox
doing my_mod
/root/my_module/my_mod/__init__.py

当通过该服务运行时,日志看起来像这样(请注意,'已安装的软件包'中第二行不包括'my-mod'):

When run through the service the logs look like this (note that my-mod is NOT included on second line in 'installed packages'):

2016-02-26_00:39:01.90403 ['/usr/bin/pv_api']
2016-02-26_00:39:01.90406 ['aiohttp==0.19.0', 'chardet==2.3.0', 'jsonschema==2.5.1', 'pip==7.0.0', 'pluggy==0.3.1', 'pv-api==0.0.0', 'py==1.4.31', 'pycrypto==2.6.1', 'pymongo==3.1.1', 'pyyaml==3.11', 'setuptools==19.6.2', 'six==1.10.0', 'tox==2.3.1', 'virtualenv==14.0.6', 'webauthsession==1.1.1']
2016-02-26_00:39:01.90407 doing tox
2016-02-26_00:39:01.90407 doing my_mod
2016-02-26_00:39:01.90642 Traceback (most recent call last):
2016-02-26_00:39:01.90642   File "/usr/bin/pv_api", line 9, in <module>
2016-02-26_00:39:01.90642     load_entry_point('pv-api==0.0.0', 'console_scripts', 'pv_api')()
2016-02-26_00:39:01.90643   File "/usr/lib/python3.4/site-packages/pkg_resources/__init__.py", line 547, in load_entry_point
2016-02-26_00:39:01.90643     return get_distribution(dist).load_entry_point(group, name)
2016-02-26_00:39:01.90643   File "/usr/lib/python3.4/site-packages/pkg_resources/__init__.py", line 2719, in load_entry_point
2016-02-26_00:39:01.90643     return ep.load()
2016-02-26_00:39:01.90643   File "/usr/lib/python3.4/site-packages/pkg_resources/__init__.py", line 2379, in load
2016-02-26_00:39:01.90643     return self.resolve()
2016-02-26_00:39:01.90643   File "/usr/lib/python3.4/site-packages/pkg_resources/__init__.py", line 2385, in resolve
2016-02-26_00:39:01.90644     module = __import__(self.module_name, fromlist=['__name__'], level=0)
2016-02-26_00:39:01.90644   File "/usr/lib/python3.4/site-packages/pv/api/main.py", line 33, in <module>
2016-02-26_00:39:01.90644     import my_mod
2016-02-26_00:39:01.90644 ImportError: No module named 'my_mod'

这也许是有用的信息:

[root@7bb8a6866a85 etc]# ls -la /usr/lib/python3.4/site-packages/my-mod.egg-link 
-rw-r--r-- 1 root root 37 Feb 26 00:20 /usr/lib/python3.4/site-packages/my-mod.egg-link
[root@7bb8a6866a85 etc]# cat /usr/lib/python3.4/site-packages/my-mod.egg-link 
/root/my_module

编辑:

从“installed_packages”的输出中可以看出,通过requirements.txt安装的所有其他软件包都能够被正确地找到。只有这个我本地具有源代码的库在我以服务运行时找不到。(当我从命令行或从python3解释器中运行import my_mod时可以找到它。

8个回答

67

我曾经遇到一个类似的问题,需要将一个upstart的heartbeat.conf转换为systemd的heartbeat.service,但是遇到了requests模块。解决方法是在新的.service文件中指定运行该服务的用户:

[Unit]
Description=web server monitor

[Service]
WorkingDirectory=/home/<user>/
User=<user>
ExecStart=/home/<user>/heartbeat.py
Restart=always

[Install]
WantedBy=multi-user.target

没有User=<user>,我在journalctl中看到了以下内容:

systemd[1]: Started web server monitor.
heartbeat.py[26298]: Traceback (most recent call last):
heartbeat.py[26298]:   File "/home/<user>/heartbeat.py", line 2, in <
heartbeat.py[26298]:     import requests
heartbeat.py[26298]: ImportError: No module named requests
systemd[1]: heartbeat.service: Main process exited, code=exited, status=1/FAILURE
systemd[1]: heartbeat.service: Unit entered failed state.

8
请注意,它是 User=<user>。值取决于用户名! - kuga
@kuga 很好的观点。我已经更新了答案以反映这一点。 - TemporalWolf

7
将Python的站点包环境变量添加到systemctl *.service文件中。
[Unit]
Description=web server monitor

[Service]
WorkingDirectory=/home/user/
User=user
ExecStart=/home/user/heartbeat.py
Restart=always
Environment="PYTHONPATH=$PYTHONPATH:/home/nvidia/.local/lib/python3.6/site-packages"

[Install]
WantedBy=multi-user.target


那对我起作用了,一旦我设置好环境路径,一切都运行得很好。 - Kfir Ram

6
如果您确实想以 root 用户身份运行该服务,您需要使用 sudo 安装模块:sudo pip install my_module

3

首先在Python提示符下尝试以下内容。

$ python
>>> import my_mod
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named my_mod
>>>

修复方法1

如果您遇到上述输出,则可能是由于权限问题引起的。请使用以下方法授予 site-packages 权限。

sudo chmod -R go+rX /usr/local/lib/python2.7/dist-packages

修复方法2

尝试按照以下方式导出PYTHONPATH:

export PYTHONPATH="/usr/.local/lib/python2.7/site-packages"

修复方法 3

检查您的计算机是否同时运行了多个版本的 Python。

如果是这样,请检查代码的开头是否包含适当的解释器,例如 #!/usr/bin/python


1
这几乎揭示了真正的问题。问题在于该服务是以一个没有对我安装模块的文件夹具有读取权限的用户身份运行的。 - user1753106

1
由于您的python脚本是使用python3.4执行的,您的问题很可能是使用了pip而不是pip3(或者可能是sudo -H pip3)。但是,既然您还特别询问如何从systemd服务中使用python模块,我建议尽可能使用aptsudo,而不是搞乱pip(因为它以引入文件权限问题而臭名昭著)。
sudo apt install python3-my_module

1
我遇到了同样的问题。我认为pip install必须是用户特定的。
所以我切换到root用户,然后安装包。之后它就可以工作了。

但是,我认为在服务文件中指定User=myUser是一种更合适的方式,但是我希望它能以root权限运行,我不确定当我指定用户时它是否会以root权限运行。

希望对某些人有所帮助。


1

1)安装supervisor软件包(更详细的说明在此处):

sudo apt-get install supervisor

2)在/etc/supervisor/conf.d/my_mod.conf中为您的守护进程创建配置文件:

[program:my_mod]
directory=/path/to/project/root
environment=ENV_VARIABLE=example,OTHER_ENV_VARIABLE=example2
command=python my_mod.py
autostart=true
autorestart=true

3) 重新启动 supervisor 来加载您的新的 .conf 文件

supervisorctl update
supervisorctl restart my_mod

该服务已经安装。问题在于,Python环境无法找到这个库。通过要求文件安装的所有其他库都能被正确地找到。 - user1753106
1
在导入my_mod之前添加这行代码sys.path.append("/root/my_module/my_mod"),怎么样? - Hexoul

1

我也遇到了导入错误,但我的不是本地导入。我的包没有被正确导入。请查看服务故障日志*

Feb 11 06:41:52 pl-dev-demo-1 python3[1675804]:     from foo import test
Feb 11 06:41:52 pl-dev-demo-1 python3[1675804]: ModuleNotFoundError: No module named 'foo'
Feb 11 06:41:52 pl-dev-demo-1 systemd[1]: xyzxyz.service: Main process exited, code=exited, status=1/FAILURE

我花了很多时间才明白,然后我尝试了这种方法,效果非常完美。

在模块中,我设置了我的代码路径:

import sys
sys.path.append('path_of_my_code_parent_package')

from foo import test 

阅读此文档:

更持久地设置PYTHONPATH


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接