Cron e virtualenv


227

Sto cercando di eseguire un comando di gestione Django da cron. Sto usando virtualenv per mantenere sandbox il mio progetto.

Ho visto esempi qui e altrove che mostrano l'esecuzione di comandi di gestione da virtualenv come:

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

Tuttavia, anche se syslog mostra una voce quando l'attività dovrebbe essere avviata, questa attività non viene mai effettivamente eseguita (il file di registro per lo script è vuoto). Se eseguo la linea manualmente dalla shell, funziona come previsto.

L'unico modo in cui riesco attualmente a far eseguire il comando tramite cron, è quello di rompere i comandi e metterli in uno stupido script wrapper bash:

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

MODIFICARE:

ars ha creato una combinazione funzionante di comandi:

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

Almeno nel mio caso, invocare lo script activ per virtualenv non ha fatto nulla. Questo funziona, così via con lo spettacolo.


Una differenza che vedo è che lo script eseguirà manage.py con / home / user / project come directory di lavoro corrente. Il tuo comando cron verrebbe eseguito con la tua home directory come cwd. Forse il file di registro è lì?
Rettops

In realtà il percorso del registro è definito in modo assoluto, semplicemente non è stato creato / aggiunto perché lo script non è in esecuzione.
John-Scott,

Una soluzione rapida e sporca ai problemi di cron è scaricare il tuo ambiente (in cui il tuo comando funziona inspiegabilmente) con enve exporttutti in un wrapper di script bash che chiami dal crontab.
jberryman,

Risposte:


250

Dovresti essere in grado di farlo utilizzando l' pythonambiente virtuale:

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

EDIT: Se il tuo progetto django non è nel PYTHONPATH, dovrai passare alla directory giusta:

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

Puoi anche provare a registrare l'errore da cron:

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

Un'altra cosa da provare è apportare la stessa modifica nel tuo manage.pyscript in alto:

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

1
Anche quello non funziona. Ho dimenticato di inserirlo nella mia lista di cose che non funzionano. Sì, posso eseguire quel comando manualmente nella shell ma non funziona da cron.
John-Scott,

Hai sostituito ~con il percorso completo? (Probabilmente l'hai fatto, assicurandoti solo ...)
ars

Ah, hai trovato un esempio funzionante! Ho provato su ogni combinazione e l'attivazione di virtualenv sembra non avere alcun effetto. Ho impostato il mio PYTHONPATH in .bashrc ma questo apparentemente non è usato da cron? Aggiornerò la mia domanda per evidenziare la tua risposta.
John-Scott,

Sì, avevo dimenticato che cron funziona in un ambiente molto minimale. La raccomandazione generale è quella di scrivere script bash per impostare l'ambiente di cui il tuo lavoro avrà bisogno. Potresti provare a trovare il profilo bash direttamente in cron, ma questo può portare a bug sottili a seconda di cosa c'è nel tuo profilo (forse se hai un profilo separato e minimo per tali esigenze, andrebbe bene).
Ars

7
Un buon modo per testare è eseguire / bin / sh, quindi provare a eseguire il comando da lì. Almeno avrai la stessa configurazione dell'ambiente di cron.
Dick,

98

L'esecuzione sourceda un cronfile non funzionerà come cron usa /bin/shcome shell predefinita, che non supporta source. È necessario impostare la variabile di ambiente SHELL su /bin/bash:

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

È difficile individuare il motivo per cui ciò non riesce poiché /var/log/syslognon registra i dettagli dell'errore. Meglio aliasarsi su root in modo da ricevere e-mail con eventuali errori cron. Basta aggiungersi /etc/aliasesed eseguire sendmail -bi.

Maggiori informazioni qui: http://codeinthehole.com/archives/43-Running-django-cronjobs-within-a-virtualenv.html

il link sopra è cambiato in: https://codeinthehole.com/tips/running-django-cronjobs-within-a-virtualenv/


12
O '.' (comando punto), che è supportato da / bin / sh. /path/to/virtualenv/bin/activate
Reed Sandberg

5
DavidWinterbottom, se questo è il tuo vero nome, sei il mio eroe. Non l'ho mai saputo di sh vs bash e dei file sorgente. Hai acceso una luce nel mio piccolo mondo di sceneggiatura di bash. Grazie.
Joemurphy,

Se hai un postactivatefile, dovresti farlosource /path/to/virtualenv/bin/activate && source /path/to/virtualenv/bin/postactivate
dspacejs il

1
Grazie! Per me, questo funziona piuttosto che la risposta accettata da Gerald.
Martin Becker,

1
a cosa serve 'root'? qualcuno può spiegare
adnanmuttaleb

19

Non cercare oltre:

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

Approccio generico:

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

Il bello di questo è che NON è necessario cambiare la SHELLvariabile per crontab da shabash


13

L'unico modo corretto per eseguire i lavori cron di Python quando si utilizza un virtualenv è attivare l'ambiente e quindi eseguire il pitone dell'ambiente per eseguire il codice.

Un modo per farlo è usare virtualenv activate_thisnel tuo script Python, vedi: http://virtualenv.readthedocs.org/en/latest/userguide.html#using-virtualenv-without-bin-python

Un'altra soluzione è l'eco del comando completo che include l'attivazione dell'ambiente e il piping in /bin/bash. Considera questo per il tuo /etc/crontab:

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

1
Sono molto curioso di sapere se ci sia consenso sul fatto che questo sia effettivamente l'unico modo corretto.
Aaron Schumacher,

1
Questo è probabilmente l'unico modo corretto. Ma ci sono altri modi che funzionano.
Sarà il

4
Questo non è "l'unico modo corretto". Ho eseguito con successo uno script in un virtualenv semplicemente puntando il cronjob al binario python di virtualenv, come '/ home / user / cartella / env / bin / python'. Non è necessario attivare l'ambiente di sorta.
Canucklesandwich,

Se usi PYTHONPATH personalizzato in ambiente virtuale env / bin / python non funzionerà per te. Ecco perché è meglio usare env / bin /
activ

1
dipende da come si imposta PYTHONPATH e se lo si imposta in un modo che richiede "l'attivazione" del venv, lo si fa in modo errato

10

Invece di andare in giro con shebang specifici per virtualenv, basta anteporre PATHal crontab.

Da un virtualenv attivato, eseguire questi tre comandi e gli script python dovrebbero funzionare:

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

La prima riga del crontab ora dovrebbe apparire così:

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

12
Non è una buona soluzione. Ogni attività python nel crontab verrebbe quindi eseguita con il binario da virtualenv. Rendere quel binario un pitone pseudo-globale va contro lo scopo stesso di virtualenv.
Victor Schröder,

4

La soluzione migliore per me era per entrambi

  • usa il binario python nella directory bin / venv
  • imposta il percorso python per includere la directory dei moduli venv.

man pythonmenziona la modifica del percorso in shell at $PYTHONPATHo in python consys.path

Altre risposte menzionano idee per farlo usando la shell. Da Python, l'aggiunta delle seguenti righe al mio script mi ​​permette di eseguirlo con successo direttamente da cron.

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

Ecco come appare in una sessione interattiva -

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

Vorrei aggiungere questo perché ho trascorso un po 'di tempo a risolvere il problema e non ho trovato una risposta qui per la combinazione di utilizzo delle variabili in cron e virtualenv. Quindi forse aiuterà qualcuno.

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

Non ha funzionato bene quando è stato configurato come

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

Grazie @davidwinterbottom , @ reed-sandberg e @mkb per aver dato la giusta direzione. La risposta accettata funziona davvero bene fino a quando il tuo pitone non deve eseguire uno script che deve eseguire un altro binario python dalla directory venv / bin.


0

Questa è una soluzione che ha funzionato bene per me.

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

Sto usando la miniconda con Conda versione 4.7.12 su Ubuntu 18.04.3 LTS.

Sono in grado di inserire quanto sopra all'interno di uno script ed eseguirlo anche tramite crontab senza problemi.


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

Comando Cron

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

Nel comando sopra

  • * / 1 * * * * - Esegui ogni minuto
  • cd / Workspace / testcron / - Percorso dello script python
  • / Workspace / testcron / venvcron / bin / python3 - Percorso Virtualenv
  • Area di lavoro / testcron / testcronwithparam.py - Percorso del file
  • param - parametro
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.