cron 和 virtualenv

Posted

技术标签:

【中文标题】cron 和 virtualenv【英文标题】:Cron and virtualenv 【发布时间】:2011-03-18 06:05:02 【问题描述】:

我正在尝试从 cron 运行 Django 管理命令。我正在使用 virtualenv 来保持我的项目沙盒化。

我在这里和其他地方看到过一些示例,这些示例显示了从 virtualenv 中运行的管理命令,例如:

0 3 * * * source /home/user/project/env/bin/activate && /home/user/project/manage.py command arg

但是,即使 syslog 在任务应该开始时显示条目,该任务实际上从未运行(脚本的日志文件为空)。如果我从 shell 手动运行该行,它会按预期工作。

我目前可以通过 cron 运行命令的唯一方法是将命令分解并将它们放入一个愚蠢的 bash 包装脚本中:

#!/bin/sh
source /home/user/project/env/bin/activate
cd /home/user/project/
./manage.py command arg

编辑:

ars 提出了一个有效的命令组合:

0 3 * * * cd /home/user/project && /home/user/project/env/bin/python /home/user/project/manage.py command arg

至少在我的情况下,为 virtualenv 调用激活脚本什么也没做。这行得通,在节目中以此类推。

【问题讨论】:

我看到的一个区别是脚本将运行 manage.py 并以 /home/user/project 作为当前工作目录。您的 cron 命令将以您的主目录作为 cwd 运行。也许日志文件在那里? 实际上日志路径是绝对定义的,它根本没有被创建/附加到,因为脚本没有运行。 对 cron 问题的快速而肮脏的解决方案是将您的环境(在其中您的命令莫名其妙地工作)与 envexport 一起转储到您从 crontab 调用的 bash 脚本包装器中. 【参考方案1】:

我很抱歉第 n 个答案,但我检查了答案,确实更简单、更整洁。

长话短说

在你的 cron 中使用你的 venv 的 python 二进制文件:

0 3 * * * /home/user/project/env/bin/python /home/user/project/manage.py 

长篇大论

当我们想要使用特定虚拟环境(即其二进制文件和模块)的 python 配置设置当前 shell 时,我们会激活虚拟环境。 与使用当前 shell 相关:在当前 shell 上执行多个 python 命令,而无需引用 venv 的完整 python 路径。 在 cron 甚至 bash 的框架中,激活环境的值是什么? 此外,我在一些答案中读到了一些对bash 的引用,而不是sh,或者仍然定义了一个包装器来调用Python 代码。但是我们为什么要操心这些呢?

我再说一遍,就这样做吧:

0 3 * * * /home/user/project/env/bin/python /home/user/project/manage.py 

documentation 确认:

您不需要特别激活环境;激活 只需将虚拟环境的二进制目录添加到您的路径中, 以便“python”调用虚拟环境的 Python 解释器 并且您可以运行已安装的脚本,而无需使用它们的完整 小路。但是,安装在虚拟环境中的所有脚本都应该 无需激活即可运行,并与虚拟机一起运行 环境的 Python 自动生成。

【讨论】:

【参考方案2】:

这是一种使 crontab 命令与常规命令非常相似的简单方法(在 Ubuntu 18.04 中测试)。需要牢记的一些关键注意事项:

您可以使用. 命令代替source。 (crontab 默认使用sh,而不是bash,所以它没有source。) ~$variables 在 crontab 命令中展开。 (只有 crontab 环境语句不进行变量扩展。)

如果您有文件~/myproject/main.py,以下是示例:

* * * * * cd ~/myproject && . .venv/bin/activate && python main.py > /tmp/out1 2>&1

也可以直接调用venv目录下python的具体路径,就不用调用activate了。

* * * * * ~/myproject/.venv/bin/python ~/myproject/main.py > /tmp/out2 2>&1

这样做的缺点是您需要指定两次项目路径,这使维护变得更加棘手。为避免这种情况,您可以使用 shell 变量,这样您只需指定一次项目路径:

* * * * * project_dir=~/myproject ; $project_dir/.venv/bin/python $project_dir/main.py > /tmp/out3 2>&1

【讨论】:

【参考方案3】:

我也有同样的问题:

我编写了一个自定义 django 命令来检查 geodjango 多边形内的 geodjango 位置坐标,并且在自动运行任务时遇到了麻烦,但是将此命令与 crontab 一起使用对我有用:

* * * * * ./home/project/locations/locations.sh >> /var/log/locations.log 2>&1

【讨论】:

【参考方案4】:

这也适用于 crontab -e

* */5 * * * cd /home/project && sudo /home/project/venv/bin/python scripte.py

【讨论】:

【参考方案5】:

由于 cron 在其自己的最小 sh 环境中执行,因此我在虚拟环境中运行 Python 脚本的方法如下:

* * * * * . ~/.bash_profile; . ~/path/to/venv/bin/activate; python ~/path/to/script.py

(注意:如果. ~/.bash_profile 不适合您,请尝试. ~/.bashrc. ~/.profile,具体取决于您的服务器设置。)

这会加载您的 bash shell 环境,然后激活您的 Python 虚拟环境,基本上让您使用与测试脚本相同的设置。

无需在 crontab 中定义环境变量,也无需修改现有脚本。

【讨论】:

【参考方案6】:

我在我的 Django 项目中添加了以下脚本作为 manage.sh,它获取 virtualenv,然后使用您传递给它的任何参数运行 manage.py 脚本。它使得在 virtualenv(cron、systemd 单元,基本上在任何地方)内运行命令变得非常容易:

#! /bin/bash

# this is a convenience script that first sources the venv (assumed to be in
# ../venv) and then executes manage.py with whatever arguments you supply the
# script with. this is useful if you need to execute the manage.py from
# somewhere where the venv isn't sourced (e.g. system scripts)

# get the script's location
DIR="$( cd "$( dirname "$BASH_SOURCE[0]" )" >/dev/null 2>&1 && pwd )"

# source venv <- UPDATE THE PATH HERE WITH YOUR VENV's PATH
source $DIR/../venv/bin/activate

# run manage.py script
$DIR/manage.py "$@"

然后在你的 cron 条目中你可以运行:

0 3 * * * /home/user/project/manage.sh command arg

请记住,您需要使 manage.sh 脚本可执行

【讨论】:

【参考方案7】:

如果您在 python 上并使用 Conda 虚拟环境,其中您的 python 脚本包含 shebang #!/usr/bin/env python,则以下工作:

* * * * * cd /home/user/project && /home/user/anaconda3/envs/envname/bin/python script.py 2>&1

此外,如果您想捕获脚本中的任何输出(例如打印、错误等),您可以使用以下内容:

* * * * * cd /home/user/project && /home/user/anaconda3/envs/envname/bin/python script.py >> /home/user/folder/script_name.log 2>&1

【讨论】:

【参考方案8】:

python 脚本

from datetime import datetime                                                                                                                                                                
import boto   # check wheather its taking the virtualenv or not                                                                                                                                                                        
import sys                                                                                                                                                                                   
param1=sys.argv[1]     #Param                                                                                                                                                                                                                                                                                                                                                                    
myFile = open('appendtxt.txt', 'a')                                                                                                                                                      
myFile.write('\nAccessed on ' + param1+str(datetime.now())) 

Cron 命令

 */1 * * * *  cd /Workspace/testcron/ && /Workspace/testcron/venvcron/bin/python3  /Workspace/testcron/testcronwithparam.py param  

在上面的命令中

*/1 * * * * - 每分钟执行一次 cd /Workspace/testcron/ - python 脚本的路径 /Workspace/testcron/venvcron/bin/python3 - Virtualenv 路径 Workspace/testcron/testcronwithparam.py - 文件路径 参数 - 参数

【讨论】:

【参考方案9】:

这是一个对我来说效果很好的解决方案。

source /root/miniconda3/etc/profile.d/conda.sh && \
conda activate <your_env> && \
python <your_application> &

我在 Ubuntu 18.04.3 LTS 上使用带有 Conda 4.7.12 版的 miniconda。

我可以将上面的内容放在一个脚本中,并通过 crontab 运行它,没有任何麻烦。

【讨论】:

【参考方案10】:

从 cronfile 运行 source 将不起作用,因为 cron 使用 /bin/sh 作为其默认 shell,它不支持 source。您需要将SHELL环境变量设置为/bin/bash

SHELL=/bin/bash
*/10 * * * * root source /path/to/virtualenv/bin/activate && /path/to/build/manage.py some_command > /dev/null

很难找出失败的原因,因为/var/log/syslog 没有记录错误详细信息。最好将自己别名为 root,这样您就会收到包含任何 cron 错误的电子邮件。只需将自己添加到 /etc/aliases 并运行 sendmail -bi

更多信息在这里: http://codeinthehole.com/archives/43-Running-django-cronjobs-within-a-virtualenv.html

上面的链接改为: https://codeinthehole.com/tips/running-django-cronjobs-within-a-virtualenv/

【讨论】:

或'.' (点命令),支持/bin/sh . /path/to/virtualenv/bin/activate DavidWinterbottom,如果这是你的真名,你就是我的英雄。我从来不知道 sh vs bash 和源文件。你给我的小 bash 脚本世界带来了一丝曙光。谢谢。 谢谢!对我来说,这是有效的,而不是 Gerald 接受的答案。 什么是'root'?谁能解释一下 @adnanmuttaleb,我假设您知道root 用户及其权限(万王之王)。 cronjob 计时后的第一列是执行 cronjob 的用户。【参考方案11】:

我想添加这个,因为我花了一些时间解决这个问题,但在这里没有找到关于 cron 和 virtualenv 中变量使用组合的答案。所以也许它会对某人有所帮助。

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DIR_SMTH="cd /smth"
VENV=". venv/bin/activate"
CMD="some_python_bin do_something"
# m h  dom mon dow   command
0 * * * * $DIR_SMTH && $VENV && $CMD -k2 some_target >> /tmp/crontest.log 2>&1

这样配置的时候效果不好

DIR_SMTH="cd /smth && .venv/bin/activate"

感谢 @davidwinterbottom、@reed-sandberg 和 @mkb 提供正确的方向。接受的答案实际上可以正常工作,直到您的 python 需要运行必须从 venv/bin 目录运行另一个 python 二进制文件的脚本。

【讨论】:

【参考方案12】:

别再看了:

0 3 * * * /usr/bin/env bash -c 'cd /home/user/project && source /home/user/project/env/bin/activate && ./manage.py command arg' > /dev/null 2>&1

通用方法:

* * * * * /usr/bin/env bash -c 'YOUR_COMMAND_HERE' > /dev/null 2>&1

这样做的好处是您不需要将 crontab 的 SHELL 变量从 sh 更改为 bash

【讨论】:

“不要再看了”是一个大胆的声明。特别是,当以下答案声称知道“唯一正确的方法”时:***.com/a/22724628/6919635 bash -c 是不必要的。只需使用. 命令而不是source。您无需更改 crontab 中的 SHELL 变量,或使用 bash -c 包装您的命令。见***.com/questions/3287038/cron-and-virtualenv/… bash -c 的全部意义在于避免在 cron 中设置 SHELL 环境。如果您确定您的脚本在 sh 中的行为不会有所不同,那么您可以继续,但如果您在 bash 上进行了测试,那么它们在 sh 中的行为可能会有所不同。为了安全起见,我宁愿坚持使用 bash -c 或任何您的默认 shell。 对我不起作用。 venv 被完全忽略 为了让它工作,你的命令应该包括以下cd /home/user/project &amp;&amp; source /home/user/project/env/bin/activate &amp;&amp; ./manage.py command arg【参考方案13】:

在使用 virtualenv 时运行 python cron 作业的唯一正确方法是激活环境,然后执行环境的 python 来运行你的代码。

一种方法是在您的 python 脚本中使用 virtualenv 的 activate_this,请参阅:http://virtualenv.readthedocs.org/en/latest/userguide.html#using-virtualenv-without-bin-python

另一个解决方案是回显完整的命令,包括激活环境并将其通过管道传输到/bin/bash。为您的/etc/crontab 考虑这个:

***** root echo 'source /env/bin/activate; python /your/script' | /bin/bash

【讨论】:

我很好奇是否有共识认为这实际上是唯一正确的方法。 这可能是唯一正确的方法。但还有其他可行的方法。 这不是“唯一正确的方法”。只需将 cronjob 指向 virtualenv 的 python 二进制文件,例如“/home/user/folder/env/bin/python”,我就成功地在 virtualenv 中执行了一个脚本。无需激活任何环境。 这取决于您如何设置 PYTHONPATH,如果您以需要“激活”venv 的方式设置它,那么您做错了 您不需要通过管道将命令传递给 bash。只需使用. 命令而不是source。见***.com/questions/3287038/cron-and-virtualenv/…【参考方案14】:

对我来说最好的解决方案是两个

使用 venv bin/ 目录下的 python 二进制文件 设置python路径 包括 venv 模块目录。

man python 提到在$PYTHONPATH 处修改shell 中的路径,或者在sys.path 处修改python 中的路径

其他答案提到了使用 shell 执行此操作的想法。在 python 中,将以下行添加到我的脚本中可以让我直接从 cron 成功运行它。

import sys
sys.path.insert(0,'/path/to/venv/lib/python3.3/site-packages');

这是它在交互式会话中的外观 --

Python 3.3.2+ (default, Feb 28 2014, 00:52:16) 
[GCC 4.8.1] on linux
Type "help", "copyright", "credits" or "license" for more information.

>>> import sys

>>> sys.path
['', '/usr/lib/python3.3', '/usr/lib/python3.3/plat-x86_64-linux-gnu', '/usr/lib/python3.3/lib-dynload']

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

>>> sys.path.insert(0,'/path/to/venv/modules/');

>>> import requests
>>>

【讨论】:

【参考方案15】:

不要乱用特定于 virtualenv 的 shebangs,只需将 PATH 添加到 crontab 上。

从激活的 virtualenv 中,运行这三个命令,python 脚本应该可以正常工作:

$ echo "PATH=$PATH" > myserver.cron
$ crontab -l >> myserver.cron
$ crontab myserver.cron

crontab 的第一行现在应该如下所示:

PATH=/home/me/virtualenv/bin:/usr/bin:/bin:  # [etc...]

【讨论】:

不是一个好的解决方案。然后,crontab 中的每个 python 任务都将使用 virtualenv 中的二进制文件运行。使该二进制文件成为 pseudo-global python 违背了 virtualenv 的目的。【参考方案16】:

您应该可以通过在虚拟环境中使用python 来做到这一点:

/home/my/virtual/bin/python /home/my/project/manage.py command arg

编辑:如果您的 django 项目不在 PYTHONPATH 中,那么您需要切换到正确的目录:

cd /home/my/project && /home/my/virtual/bin/python ...

您也可以尝试从 cron 记录失败:

cd /home/my/project && /home/my/virtual/bin/python /home/my/project/manage.py > /tmp/cronlog.txt 2>&1

要尝试的另一件事是在最顶部的 manage.py 脚本中进行相同的更改:

#!/home/my/virtual/bin/python

【讨论】:

那也行不通。忘了把它放在我的不起作用的事情清单中。是的,我可以在 shell 中手动运行该命令,但它在 cron 中不起作用。 您是否将~ 替换为完整路径? (你可能做到了,只是确保......) 啊,你想出了一个可行的例子!我已经尝试了每种组合,并且激活 virtualenv 似乎没有任何效果。我确实在 .bashrc 中设置了我的 PYTHONPATH 但这显然不被 cron 使用?将更新我的问题以突出显示您的答案。 是的,我忘记了 cron 在非常小的环境下运行。一般建议是编写 bash 脚本来设置您的工作需要的任何环境。您可以尝试直接在 cron 中获取 bash 配置文件,但这可能会导致细微的错误,具体取决于您的配置文件中的内容(也许如果您有一个单独且最小的配置文件来满足此类需求,那就没问题了)。 一个好的测试方法是执行/bin/sh,然后尝试从那里执行你的命令。至少您将拥有与 cron 相同的环境设置。

以上是关于cron 和 virtualenv的主要内容,如果未能解决你的问题,请参考以下文章

Cron(表达式)详解

使用弹性 beantalk 和 SQS 处理失败的 cron 作业

cron表达式0 * * * * ?啥意思

Elastic Beanstalk cron 和部署权限

cron 和 virtualenv

cron表达式是啥?