Cron i virtualenv

227

Próbuję uruchomić polecenie zarządzania Django z crona. Korzystam z virtualenv, aby utrzymać swój projekt w trybie piaskownicy.

Widziałem przykłady tu i gdzie indziej, które pokazują uruchamianie poleceń zarządzania z poziomu virtualenv, takich jak:

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

Jednak pomimo tego, że syslog pokazuje wpis, kiedy zadanie powinno się rozpocząć, zadanie to nigdy się nie uruchamia (plik dziennika skryptu jest pusty). Jeśli uruchomię linię ręcznie z powłoki, działa ona zgodnie z oczekiwaniami.

Jedynym sposobem, w jaki mogę obecnie uruchomić polecenie za pomocą crona, jest rozbicie poleceń i umieszczenie ich w skrypcie otępiającym bash:

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

EDYTOWAĆ:

ars wymyślił działającą kombinację poleceń:

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

Przynajmniej w moim przypadku wywołanie skryptu aktywacyjnego dla virtualenv nic nie zrobiło. To działa, podobnie jak w przypadku serialu.

John-Scott
źródło
Jedną z różnic, które widzę, jest to, że skrypt uruchomi manage.py z / home / user / project jako bieżącym katalogiem roboczym. Twoje polecenie cron zostanie uruchomione z twoim katalogiem domowym jako cwd. Może plik dziennika jest tam?
rettops
W rzeczywistości ścieżka dziennika jest zdefiniowana absolutnie, po prostu nie jest tworzona / dołączana, ponieważ skrypt nie działa.
John-Scott,
Szybkim i brudnym rozwiązaniem problemów z cronem jest zrzucenie środowiska (w którym niewytłumaczalnie działa twoje polecenie) envi exportwszystkie z nich w pakiecie skryptów bash, który wywołujesz z crontab.
jberryman

Odpowiedzi:

250

Powinieneś być w stanie to zrobić za pomocą pythonw swoim środowisku wirtualnym:

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

EDYCJA: Jeśli twojego projektu django nie ma w PYTHONPATH, musisz przejść do odpowiedniego katalogu:

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

Możesz także spróbować zarejestrować awarię z crona:

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

Kolejną rzeczą do wypróbowania jest wprowadzenie tej samej zmiany w manage.pyskrypcie na samej górze:

#!/home/my/virtual/bin/python
ars
źródło
1
To też nie działa. Zapomniałem umieścić na mojej liście rzeczy, które nie działają. Tak, mogę uruchomić to polecenie ręcznie w powłoce, ale nie działa z crona.
John-Scott,
Czy zastąpiłeś ~pełną ścieżką? (Prawdopodobnie zrobiłeś to, upewniając się, że ...)
ar
Ach, wymyśliłeś działający przykład! Próbowałem o każdej kombinacji i aktywacja virtualenv wydaje się nie mieć żadnego efektu. Ustawiam PYTHONPATH w .bashrc, ale najwyraźniej nie jest używany przez crona? Zaktualizuję moje pytanie, aby podświetlić Twoją odpowiedź.
John-Scott,
Tak, zapomniałem, że cron działa w bardzo minimalnym środowisku. Ogólna rekomendacja to pisanie skryptów bash, aby skonfigurować środowisko, którego potrzebujesz. Możesz spróbować pobrać profil bash bezpośrednio w cron, ale może to prowadzić do subtelnych błędów w zależności od tego, co jest w twoim profilu (być może, jeśli masz osobny i minimalny profil dla takich potrzeb, byłoby dobrze).
ars
7
Dobrym sposobem na przetestowanie jest wykonanie polecenia / bin / sh, a następnie próba wykonania polecenia z tego miejsca. Przynajmniej będziesz mieć taką samą konfigurację środowiska jak cron.
Dick,
98

Uruchamianie sourcez pliku cron nie działa, ponieważ cron używa go /bin/shjako domyślnej powłoki, która nie obsługuje source. Musisz ustawić zmienną środowiskową SHELL na /bin/bash:

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

Trudno jest ustalić, dlaczego to się nie udaje, ponieważ /var/log/syslognie rejestruje szczegółów błędu. Najlepiej aliasować się do rootowania, aby otrzymywać e-maile z wszelkimi błędami cron. Po prostu dodaj się /etc/aliasesi uruchom sendmail -bi.

Więcej informacji tutaj: http://codeinthehole.com/archives/43-Running-django-cronjobs-within-a-virtualenv.html

powyższy link został zmieniony na: https://codeinthehole.com/tips/running-django-cronjobs-within-a-virtualenv/

DavidWinterbottom
źródło
12
Lub „.” (polecenie kropki), które jest obsługiwane przez / bin / sh. /path/to/virtualenv/bin/activate
Reed Sandberg
5
DavidWinterbottom, jeśli to twoje prawdziwe imię, jesteś moim bohaterem. Nigdy nie wiedziałem tego o sh vs bash i plikach źródłowych. Zapaliłeś światło w moim małym kolesiu ze świata skryptów bashowych. Dzięki.
joemurphy
Jeśli masz postactivateplik, powinieneś to zrobićsource /path/to/virtualenv/bin/activate && source /path/to/virtualenv/bin/postactivate
dspacejs
1
Dzięki! Dla mnie to działa raczej niż zaakceptowana odpowiedź Geralda.
Martin Becker
1
do czego służy „root”? czy ktoś może wyjaśnić
adnanmuttaleb
19

Nie szukaj dalej:

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

Podejście ogólne:

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

Piękno tego polega na tym, że NIE musisz zmieniać SHELLzmiennej crontab z shnabash

Basil Musa
źródło
13

Jedynym poprawnym sposobem uruchamiania zadań cron Pythona podczas korzystania z virtualenv jest aktywacja środowiska, a następnie uruchomienie python środowiska w celu uruchomienia kodu.

Jednym ze sposobów na to jest użycie virtualenv's activate_thisw skrypcie python, patrz: http://virtualenv.readthedocs.org/en/latest/userguide.html#using-virtualenv-without-bin-python

Innym rozwiązaniem jest powtórzenie pełnej komendy, w tym aktywacja środowiska i przesłanie go do niego /bin/bash. Rozważ to dla /etc/crontab:

***** root echo 'source /env/bin/activate; python /your/script' | /bin/bash
Ivanhoe
źródło
1
Jestem bardzo ciekawy, czy istnieje konsensus, że tak naprawdę jest to jedyny właściwy sposób.
Aaron Schumacher
1
To chyba jedyny właściwy sposób. Ale są inne sposoby, które działają.
Czy
4
To nie jest „jedyny właściwy sposób”. Z powodzeniem wykonałem skrypt w virtualenv, po prostu wskazując cronjob na plik binarny python virtualenv, taki jak „/ home / user / folder / env / bin / python”. Nie trzeba w ogóle aktywować środowiska.
Canucklesandwich,
Jeśli użyjesz niestandardowej zmiennej PYTHONPATH w środowisku wirtualnym, env / bin / python nie będzie dla ciebie działać. Dlatego lepsze jest użycie env / bin /
Activ
1
to zależy od tego, jak ustawiłeś PYTHONPATH, a jeśli ustawisz go w sposób, który wymaga „aktywacji” venv, robisz to źle
10

Zamiast grzebać w shebangach specyficznych dla virtualenv, po prostu przejdź PATHna crontab.

Z aktywowanego virtualenv uruchom te trzy polecenia, a skrypty python powinny po prostu działać:

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

Pierwsza linia crontab powinna teraz wyglądać tak:

PATH=/home/me/virtualenv/bin:/usr/bin:/bin:  # [etc...]
joemaller
źródło
12
Niezbyt dobre rozwiązanie. Każde zadanie Pythona w crontabie uruchamiałoby się wtedy z plikiem binarnym z virtualenv. Uczynienie tego pliku binarnego pseudo-globalnym pytonem jest sprzeczne z samym celem virtualenv.
Victor Schröder,
4

Dla mnie najlepszym rozwiązaniem było jedno i drugie

  • użyj pliku binarnego python w katalogu venv bin /
  • ustaw ścieżkę Pythona, aby zawierała katalog modułów venv.

man pythonwspomina o modyfikacji ścieżki w powłoce w $PYTHONPATHlub w Pythonie przy pomocysys.path

Inne odpowiedzi wspominają pomysły na wykonanie tego przy użyciu powłoki. Z Pythona dodanie następujących wierszy do mojego skryptu pozwala mi pomyślnie uruchomić go bezpośrednio z crona.

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

Oto jak wygląda sesja interaktywna -

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
>>>
tutaj
źródło
4

Chciałbym to dodać, ponieważ poświęciłem trochę czasu na rozwiązanie problemu i nie znalazłem tutaj odpowiedzi na kombinację użycia zmiennych w cron i virtualenv. Więc może komuś pomoże.

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

Nie działał dobrze, gdy był skonfigurowany

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

Dzięki @davidwinterbottom , @ reed-sandberg i @mkb za podanie właściwego kierunku. Akceptowana odpowiedź faktycznie działa poprawnie, dopóki Twój python nie będzie musiał uruchomić skryptu, który musi uruchomić inny plik binarny Pythona z katalogu venv / bin.

Dmitriy
źródło
0

To rozwiązanie sprawdziło się dla mnie dobrze.

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

Używam miniconda z Condą w wersji 4.7.12 na Ubuntu 18.04.3 LTS.

Jestem w stanie umieścić powyższe w skrypcie i bez problemu uruchomić go za pomocą crontab.

Arun Thundyill Saseendran
źródło
0

skrypt Pythona

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

Polecenie Crona

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

W powyższym poleceniu

  • * / 1 * * * * - Wykonaj co mennicę
  • cd / Workspace / testcron / - Ścieżka skryptu python
  • / Workspace / testcron / venvcron / bin / python3 - Ścieżka Virtualenv
  • Workspace / testcron / testcronwithparam.py - Ścieżka do pliku
  • parametr - parametr
Ramesh Ponnusamy
źródło