Cron và virtualenv


227

Tôi đang cố gắng để chạy một lệnh quản lý Django từ cron. Tôi đang sử dụng virtualenv để giữ cho dự án của tôi được sandbox.

Tôi đã thấy các ví dụ ở đây và các nơi khác hiển thị các lệnh quản lý đang chạy trong virtualenv như:

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

Tuy nhiên, mặc dù syslog hiển thị một mục khi tác vụ nên bắt đầu, tác vụ này không bao giờ thực sự chạy (tệp nhật ký cho tập lệnh trống). Nếu tôi chạy dòng thủ công từ shell, nó hoạt động như mong đợi.

Cách duy nhất hiện tại tôi có thể nhận lệnh để chạy qua cron, là phá vỡ các lệnh và đưa chúng vào một tập lệnh bao bọc bash câm:

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

BIÊN TẬP:

ars đã đưa ra một sự kết hợp làm việc của các lệnh:

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

Ít nhất trong trường hợp của tôi, việc gọi kịch bản kích hoạt cho virtualenv không làm gì cả. Điều này làm việc, như vậy với chương trình.


Một điểm khác biệt mà tôi thấy là tập lệnh sẽ chạy Manage.txt với / home / user / project làm thư mục làm việc hiện tại. Lệnh cron của bạn sẽ được chạy với thư mục chính của bạn là cwd. Có lẽ các tập tin nhật ký là ở đó?
sắp xếp lại

Trên thực tế, đường dẫn nhật ký được xác định hoàn toàn, đơn giản là nó không được tạo / nối vào vì tập lệnh không chạy.
John-Scott

Một giải pháp nhanh chóng và bẩn thỉu cho các vấn đề cron là kết xuất môi trường của bạn (trong đó lệnh của bạn đang hoạt động không thể giải thích được) envexporttất cả chúng trong một trình bao bọc tập lệnh bash mà bạn gọi từ crontab.
jberryman

Câu trả lời:


250

Bạn sẽ có thể làm điều này bằng cách sử dụng pythontrong môi trường ảo của bạn:

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

EDIT: Nếu dự án django của bạn không nằm trong PYTHONPATH, thì bạn sẽ cần chuyển sang thư mục bên phải:

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

Bạn cũng có thể thử đăng nhập thất bại từ cron:

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

Một điều khác để thử là tạo ra sự thay đổi tương tự trong manage.pykịch bản của bạn ở trên cùng:

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

1
Điều đó cũng không hoạt động. Quên để đặt nó trong danh sách của tôi những thứ không hoạt động. Có, tôi có thể chạy lệnh đó bằng tay trong shell nhưng nó không hoạt động từ cron.
John-Scott

Bạn đã thay thế ~với đường dẫn đầy đủ? (Bạn có thể đã làm, chỉ cần đảm bảo ...)
ars

Ah, bạn đã đưa ra một ví dụ làm việc! Tôi đã thử mọi kết hợp và kích hoạt virtualenv dường như không có tác dụng gì. Tôi có đặt PYTHONPATH của mình trong .bashrc nhưng điều này dường như không được sử dụng bởi cron? Sẽ cập nhật câu hỏi của tôi để làm nổi bật câu trả lời của bạn.
John-Scott

Vâng, tôi đã quên rằng cron chạy trong một môi trường rất tối thiểu. Đề xuất chung là viết các tập lệnh bash để thiết lập bất kỳ môi trường nào mà công việc của bạn sẽ cần. Bạn có thể thử tìm nguồn cung cấp hồ sơ bash trực tiếp bằng cron, nhưng điều này có thể dẫn đến các lỗi tinh vi tùy thuộc vào những gì trong hồ sơ của bạn (có lẽ nếu bạn có một hồ sơ riêng biệt và tối thiểu cho các nhu cầu như vậy, sẽ ổn thôi).
ars

7
Một cách tốt để kiểm tra là thực thi / bin / sh, và sau đó thử thực hiện lệnh của bạn từ đó. Ít nhất bạn sẽ có thiết lập môi trường giống như cron.
Dick

98

Chạy sourcetừ một cronfile sẽ không hoạt động như cron sử dụng /bin/shlàm vỏ mặc định của nó, không hỗ trợ source. Bạn cần đặt biến môi trường SHELL thành /bin/bash:

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

Thật khó để phát hiện tại sao điều này thất bại vì /var/log/syslogkhông ghi lại các chi tiết lỗi. Tốt nhất để bí danh chính mình để root để bạn nhận được email với bất kỳ lỗi cron. Đơn giản chỉ cần thêm bản thân /etc/aliasesvà chạy sendmail -bi.

Thêm thông tin ở đây: http://codeinthehole.com/archives/43-Rasty-django-cronjobs-within-a-virtualenv.html

liên kết ở trên được thay đổi thành: https://codeinthehole.com/tips/rasty-django-cronjobs-within-a-virtualenv/


12
Hoặc là '.' (lệnh chấm), được hỗ trợ bởi / bin / sh. /path/to/virtualenv/bin/activate
Reed Sandberg

5
DavidWinterbottom, nếu đó là tên thật của bạn, bạn là người hùng của tôi. Tôi không bao giờ biết rằng về sh vs bash và các tập tin nguồn. Bạn đã chiếu ánh sáng vào anh chàng bash-scripting nhỏ của tôi. Cảm ơn.
joemurphy

Nếu bạn có một postactivatetập tin, bạn nên thực hiệnsource /path/to/virtualenv/bin/activate && source /path/to/virtualenv/bin/postactivate
dspacejs

1
Cảm ơn! Đối với tôi, điều này hoạt động chứ không phải là câu trả lời được chấp nhận bởi Gerald.
Martin Becker

1
'gốc' để làm gì? bất cứ ai có thể giải thích
adnanmuttaleb

19

Đừng tìm đâu xa:

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

Cách tiếp cận chung:

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

Cái hay của việc này là bạn KHÔNG cần thay đổi SHELLbiến cho crontab từ shsangbash


13

Cách chính xác duy nhất để chạy các công việc cron python khi sử dụng virtualenv là kích hoạt môi trường và sau đó thực thi python của môi trường để chạy mã của bạn.

Một cách để làm điều này là sử dụng virtualenv activate_thistrong tập lệnh python của bạn, xem: http://virtualenv.readthedocs.org/en/latest/userguide.html#USE-virtualenv-without-bin-python

Một giải pháp khác là lặp lại lệnh hoàn chỉnh bao gồm kích hoạt môi trường và dẫn nó vào /bin/bash. Hãy xem xét điều này cho bạn /etc/crontab:

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

1
Tôi rất tò mò liệu có sự đồng thuận rằng trên thực tế đây là cách duy nhất đúng không.
Aaron Schumacher

1
Đây có lẽ là cách chính xác duy nhất. Nhưng có những cách khác mà làm việc.
Sẽ

4
Đây không phải là "cách duy nhất đúng." Tôi đã thực hiện thành công một tập lệnh trong virtualenv chỉ bằng cách trỏ cronjob vào nhị phân python của virtualenv, chẳng hạn như '/ home / user / thư mục / env / bin / python'. Không cần kích hoạt môi trường nào.
Canucklesandwich

Nếu bạn sử dụng PYTHONPATH tùy chỉnh trong môi trường ảo, env / bin / python sẽ không hoạt động cho bạn. Đó là lý do tại sao sử dụng env / bin / kích hoạt tốt hơn
varela

1
nó phụ thuộc vào cách bạn đặt PYTHONPATH và nếu bạn đặt nó theo cách yêu cầu "kích hoạt" venv, bạn đã làm sai

10

Thay vì mấp máy xung quanh với các shebang dành riêng cho virtualenv, chỉ cần trả trước PATHvào crontab.

Từ một virtualenv được kích hoạt, hãy chạy ba lệnh này và các kịch bản python sẽ hoạt động:

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

Dòng đầu tiên của crontab sẽ trông như thế này:

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

12
Không phải là một giải pháp tốt. Mỗi tác vụ python trong crontab sau đó sẽ chạy với tệp nhị phân từ virtualenv. Biến nhị phân đó thành một con trăn toàn cầu giả đi ngược lại mục đích của virtualenv.
Victor Schröder

4

Giải pháp tốt nhất cho tôi là cả hai

  • sử dụng nhị phân python trong thư mục venv bin /
  • đặt đường dẫn python để bao gồm thư mục mô-đun venv.

man pythonđề cập đến việc sửa đổi đường dẫn trong shell tại $PYTHONPATHhoặc trong python vớisys.path

Các câu trả lời khác đề cập đến ý tưởng để làm điều này bằng cách sử dụng vỏ. Từ python, việc thêm các dòng sau vào tập lệnh của tôi cho phép tôi chạy thành công trực tiếp từ cron.

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

Đây là giao diện của nó trong phiên tương tác -

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

Tôi muốn thêm điều này vì tôi đã dành thời gian giải quyết vấn đề và không tìm thấy câu trả lời ở đây cho sự kết hợp sử dụng biến trong cron và virtualenv. Vì vậy, có lẽ nó sẽ giúp được ai đó.

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

Nó không hoạt động tốt khi được cấu hình như

DIR_SMTH = "cd / smth &&. Venv / bin / kích hoạt"

Cảm ơn @davidwinterbottom , @ reed-sandberg@mkb đã đưa ra hướng đi đúng đắn. Câu trả lời được chấp nhận thực sự hoạt động tốt cho đến khi python của bạn cần chạy một kịch bản phải chạy một nhị phân python khác từ thư mục venv / bin.


0

Đây là một giải pháp đã làm việc tốt cho tôi.

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

Tôi đang sử dụng miniconda với phiên bản Conda 4.7.12 trên Ubuntu 18.04.3 LTS.

Tôi có thể đặt ở trên trong một kịch bản và chạy nó thông qua crontab mà không gặp rắc rối.


0

kịch bản trăn

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

Lệnh cron

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

Trong lệnh trên

  • * / 1 * * * * - Thực hiện mỗi một minte
  • cd / Workspace / testcron / - Đường dẫn của tập lệnh python
  • / Workspace / testcron / venvcron / bin / python3 - Đường dẫn Virtualenv
  • Không gian làm việc / testcron / testcronwithparam.py - Đường dẫn tệp
  • param - Thông số
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.