如何在 systemd 服务单元中启用 virtualenv?

How to enable a virtualenv in a systemd service unit?

我想在 systemd 服务文件中“激活”一个 virtualenv。

我想避免在 systemd 进程和 python 解释器之间有一个 shell 进程。

我目前的解决方案是这样的:

[Unit]
Description=fooservice
After=syslog.target network.target

[Service]
Type=simple
User=fooservice
WorkingDirectory={{ venv_home }}
ExecStart={{ venv_home }}/fooservice --serve-in-foreground
Restart=on-abort
EnvironmentFile=/etc/sysconfig/fooservice.env

[Install]
WantedBy=multi-user.target

/etc/sysconfig/fooservice.env

PATH={{ venv_home }}/bin:/usr/local/bin:/usr/bin:/bin
PYTHONIOENCODING=utf-8
PYTHONPATH={{ venv_home }}/...
VIRTUAL_ENV={{ venv_home }}

但是我遇到了麻烦。我收到 ImportErrors,因为 sys.path 中的一些条目丢失了。

virtualenv 是 "baked into the Python interpreter in the virtualenv"。这意味着您可以直接在该 virtualenv 中启动 pythonconsole_scripts,而无需先激活 virtualenv 或自行管理 PATH。:

ExecStart={{ venv_home }}/bin/fooservice --serve-in-foreground

ExecStart={{ venv_home }}/bin/python {{ venv_home }}/fooservice.py --serve-in-foreground

并删除 EnvironmentFile 条目。

要验证它确实正确,您可以通过 运行

检查 sys.path
{{ venv_home }}/bin/python -m site

并将输出与

进行比较
python -m site

我使用的不是 virtualenv,而是 pyenv:这里只是为了在 shebang 中使用真实的 .pyenv 路径,并确保它在 PATH 中

例如:pyenv 为用户 mortenb 激活 flask-prod,它是 运行 in prod

/home/mortenb/.pyenv/versions/flask-prod/bin/python --version
Python 3.6.2

然后在以 systemd *.service 开始的烧瓶脚本中添加以下 shebang:

#!/home/mortenb/.pyenv/versions/flask-prod/bin/python3

虽然库的路径确实嵌入到 virtualenv 的 python 解释器中,但我遇到了 python 使用安装在该 virtualenv 中的二进制文件的工具的问题。例如,我的 apache airflow 服务无法工作,因为它找不到 gunicorn 二进制文件。要解决此问题,请使用我的 ExecStart 指令和 Environment 指令(单独为服务设置环境变量)。

ExecStart={{ virtualenv }}/bin/python {{ virtualenv }}/bin/airflow webserver
Environment="PATH={{ virtualenv }}/bin:{{ ansible_env.PATH }}"

ExecStart 显式使用 virtualenv 的 python 解释器。我还添加了一个 PATH 变量,它在系统 PATH 之前添加了 virtualenv 的二进制文件夹。这样,我就得到了所需的 python 库和二进制文件。

请注意,我正在使用 ansible 来构建此服务,因此请注意 jinja2 的大括号。

在我的例子中,我只是尝试添加 Flask 所需的环境变量,例如

[Service]
Environment="PATH=/xx/yy/zz/venv/bin"
Environment="FLASK_ENV=development"
Environment="APP_SETTINGS=config.DevelopmentConfig"

我使用的是 virtualenv,所以 /xx/yy/zz/venv/bin 是 virtualenv 文件夹的路径。