Cron et virtualenv


227

J'essaie d'exécuter une commande de gestion Django à partir de cron. J'utilise virtualenv pour garder mon projet en sandbox.

J'ai vu des exemples ici et ailleurs qui montrent l'exécution de commandes de gestion à partir de virtualenv comme:

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

Cependant, même si syslog affiche une entrée au moment où la tâche aurait dû démarrer, cette tâche ne s'exécute jamais réellement (le fichier journal du script est vide). Si j'exécute la ligne manuellement depuis le shell, cela fonctionne comme prévu.

La seule façon dont je peux actuellement exécuter la commande via cron, est de décomposer les commandes et de les placer dans un script wrapper dumb bash:

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

ÉDITER:

ars a trouvé une combinaison fonctionnelle de commandes:

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

Au moins dans mon cas, l'invocation du script d'activation pour le virtualenv n'a rien fait. Cela fonctionne, ainsi de suite avec le spectacle.


Une différence que je vois est que le script exécutera manage.py avec / home / user / project comme répertoire de travail actuel. Votre commande cron serait exécutée avec votre répertoire personnel comme cwd. Peut-être que le fichier journal est là?
rettops

En fait, le chemin du journal est défini de manière absolue, il n'est tout simplement pas créé / ajouté car le script n'est pas en cours d'exécution.
John-Scott

Une solution rapide et sale aux problèmes de cron consiste à vider votre environnement (dans lequel votre commande fonctionne inexplicablement) avec envet exporttous dans un wrapper de script bash que vous appelez à partir de la crontab.
jberryman

Réponses:


250

Vous devriez pouvoir le faire en utilisant le pythondans votre environnement virtuel:

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

EDIT: Si votre projet django n'est pas dans le PYTHONPATH, alors vous devrez basculer vers le bon répertoire:

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

Vous pouvez également essayer de consigner l'échec depuis cron:

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

Une autre chose à essayer est de faire le même changement dans votre manage.pyscript tout en haut:

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

1
Cela ne fonctionne pas non plus. J'ai oublié de mettre cela dans ma liste de choses qui ne fonctionnent pas. Oui, je peux exécuter cette commande manuellement dans le shell mais cela ne fonctionne pas depuis cron.
John-Scott

Avez-vous remplacé ~le chemin complet? (Vous l'avez probablement fait, juste pour vous assurer ...)
ars

Ah, vous avez trouvé un exemple de travail! J'ai essayé toutes les combinaisons et l'activation de virtualenv semble n'avoir aucun effet. J'ai mis mon PYTHONPATH en .bashrc mais cela n'est apparemment pas utilisé par cron? Mettra à jour ma question pour mettre en évidence votre réponse.
John-Scott

Ouais, j'avais oublié que cron fonctionne dans un environnement très minimal. La recommandation générale est d'écrire des scripts bash pour configurer l'environnement dont votre travail aura besoin. Vous pouvez essayer de trouver le profil bash directement dans cron, mais cela peut conduire à des bugs subtils en fonction de ce qui se trouve dans votre profil (peut-être que si vous avez un profil séparé et minimal pour de tels besoins, ce serait bien).
ars

7
Un bon moyen de tester consiste à exécuter / bin / sh, puis à essayer d'exécuter votre commande à partir de là. Au moins, vous aurez la même configuration d'environnement que cron.
Dick

98

L'exécution à sourcepartir d'un fichier cron ne fonctionnera pas comme Cron l'utilise /bin/shcomme shell par défaut, qui ne prend pas en charge source. Vous devez définir la variable d'environnement SHELL sur /bin/bash:

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

Il est difficile de comprendre pourquoi cela échoue, car il /var/log/syslogn'enregistre pas les détails de l'erreur. Il est préférable de vous alias pour rooter afin de recevoir par e-mail toutes les erreurs cron. Ajoutez-vous simplement /etc/aliaseset courez sendmail -bi.

Plus d'informations ici: http://codeinthehole.com/archives/43-Running-django-cronjobs-within-a-virtualenv.html

le lien ci-dessus est changé en: https://codeinthehole.com/tips/running-django-cronjobs-within-a-virtualenv/


12
Ou '.' (commande point), qui est pris en charge par / bin / sh. /path/to/virtualenv/bin/activate
Reed Sandberg

5
DavidWinterbottom, si c'est votre vrai nom, vous êtes mon héros. Je n'ai jamais su cela à propos de sh vs bash et des fichiers source. Vous avez éclairé mon petit mec du monde du script bash. Merci.
joemurphy

Si vous avez un postactivatefichier, vous devriez le fairesource /path/to/virtualenv/bin/activate && source /path/to/virtualenv/bin/postactivate
dspacejs

1
Merci! Pour moi, cela fonctionne plutôt que la réponse acceptée par Gerald.
Martin Becker

1
à quoi sert «root»? quelqu'un peut-il expliquer
adnanmuttaleb

19

Ne cherchez plus:

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

Approche générique:

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

La beauté de ceci est que vous n'avez PAS besoin de changer la SHELLvariable pour crontab de shenbash


13

La seule façon correcte d'exécuter des tâches cron python lors de l'utilisation d'un virtualenv est d'activer l'environnement, puis d'exécuter le python de l'environnement pour exécuter votre code.

Une façon de faire est d'utiliser virtualenv activate_thisdans votre script python, voir: http://virtualenv.readthedocs.org/en/latest/userguide.html#using-virtualenv-without-bin-python

Une autre solution fait écho à la commande complète, y compris l'activation de l'environnement et son acheminement /bin/bash. Considérez ceci pour votre /etc/crontab:

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

1
Je suis très curieux de savoir s'il y a consensus sur le fait que c'est en fait la seule manière correcte.
Aaron Schumacher

1
C'est probablement la seule façon correcte. Mais il existe d'autres façons de fonctionner.
Will

4
Ce n'est pas "la seule façon correcte". J'ai réussi à exécuter un script dans un virtualenv simplement en pointant le cronjob vers le binaire python du virtualenv, tel que '/ home / user / folder / env / bin / python'. Pas besoin d'activer l'environnement.
Canucklesandwich

Si vous utilisez PYTHONPATH personnalisé dans un environnement virtuel, env / bin / python ne fonctionnera pas pour vous. C'est pourquoi il est préférable d'utiliser env / bin / activate
varela

1
cela dépend de la façon dont vous définissez le PYTHONPATH et si vous le définissez d'une manière qui nécessite "d'activer" le venv, vous vous trompez

10

Plutôt que de fouiner avec des shebangs spécifiques à virtualenv, ajoutez simplement PATHla crontab.

À partir d'un virtualenv activé, exécutez ces trois commandes et les scripts python devraient simplement fonctionner:

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

La première ligne du crontab devrait maintenant ressembler à ceci:

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

12
Pas une bonne solution. Toutes les tâches python dans la crontab s'exécuteraient alors avec le binaire de virtualenv. Faire de ce binaire un python pseudo-global va à l'encontre du but même de virtualenv.
Victor Schröder du

4

La meilleure solution pour moi était à la fois

  • utiliser le binaire python dans le répertoire venv bin /
  • définissez le chemin python pour inclure le répertoire des modules venv.

man pythonmentionne la modification du chemin dans shell à $PYTHONPATHou en python avecsys.path

D'autres réponses mentionnent des idées pour ce faire en utilisant le shell. Depuis python, l'ajout des lignes suivantes à mon script me permet de l'exécuter avec succès directement à partir de cron.

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

Voici à quoi cela ressemble dans une session interactive -

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
>>>

4

Je voudrais ajouter ceci parce que j'ai passé un certain temps à résoudre le problème et je n'ai pas trouvé de réponse ici pour la combinaison d'utilisation des variables dans cron et virtualenv. Alors peut-être que ça va aider quelqu'un.

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

Cela ne fonctionnait pas bien quand il était configuré comme

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

Merci @davidwinterbottom , @ reed-sandberg et @mkb pour avoir donné la bonne direction. La réponse acceptée fonctionne correctement jusqu'à ce que votre python ait besoin d'exécuter un script qui doit exécuter un autre binaire python à partir du répertoire venv / bin.


0

C'est une solution qui a bien fonctionné pour moi.

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

J'utilise miniconda avec la version 4.7.12 de Conda sur un Ubuntu 18.04.3 LTS.

Je suis capable de placer ce qui précède dans un script et de l'exécuter via crontab également sans aucun problème.


0

script 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())) 

Commande Cron

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

Dans la commande ci-dessus

  • * / 1 * * * * - Exécute chaque minute
  • cd / Workspace / testcron / - Chemin du script python
  • / Workspace / testcron / venvcron / bin / python3 - Chemin Virtualenv
  • Workspace / testcron / testcronwithparam.py - Chemin du fichier
  • param - paramètre
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.