dikamilo.net

Kolejny blog w sieci...

Django na serwerze produkcyjnym vipserv.org

Przez ostatni tydzień zajmowałem się dwoma projektami w Django Framework co pozwoliło mi oswoić się z nową wersją (1.4) tegoż frameworka która ukazała się nie tak dawno bo pod koniec marca bieżącego roku. Jedną ze zmian jest całkowicie nowa hierarchia plików tworzona przez django-admin. Plik manage.py jest teraz wyrzucony po za folder projektu gdzie możemy również tworzyć poszczególne aplikacje. Oczywiście nie ma problemów ze stosowaniem starej hierarchii plików. Inną ciekawą zmianą jest sposób obsługiwania statycznych plików. Zmieniło się to znacznie (według mnie na lepsze).

Zawsze staram się aktualizować swoje projekty pod nową wersje bibliotek, frameworków z jakich korzystają. Tak samo było i teraz - postanowiłem zaktualizować bloga pod Django 1.4. Zmiany jakie wprowadziłem po części wymusiły na mnie całkowicie nową konfigurację projektu na serwerze produkcyjnym. Mimo tego że administracja vipserv.org - serwera na którym się hostuje, udostępnia poradnik instalowania django na serwerze oraz obsługi samego pythona, to i tak musiałem spędzić kilka dobrych godzin aby doprowadzić całość do ładu i składu.

Dlatego też chciałbym się podzielić garścią porad i snippetów aby przeprowadzić to bez bólu.

Kiedyś w panelu administracyjnym vipserv.org była opcja automatycznego instalowania django i bazowej konfiguracji projektu. Od jakiegoś czasu mechanizm ten został całkowicie uproszczony i musimy robić to ręcznie.

Tworzenie projektu na serwerze

Pierwsze co musimy zrobić to utworzyć nową aplikację w panelu Passenger oraz przypisać jej adres/domenę. Następnie logując się przez ssh na nasze konto musimy utworzyć katalog site-packages w naszym katalogu domowym oraz pobrać do niego django. Całość mniej więcej wygląda tak:

mkdir ~/site-packages
cd ~/site-packages
cp /usr/share/python-default/site.py site.py
wget 'http://www.djangoproject.com/download/1.4/tarball/'
tar xzvf Django-1.4.tar.gz
rm Django-1.4.tar.gz

Dodatkowo musimy stworzyć plik django.pth ze ścieżką do folderu z django aby był on widoczny dla projektu. Dzięki temu możemy łatwo zmieniać wersję django dla poszczególnych projektów.

nano ~/site-packages/django.pth
/home/nasz_login/site-packages/Django-1.4

Następnie musimy przekopiować sobie plik django-admin.py do naszego katalogu bin aby móc z niego korzystać bez podawania pełnej ścieżki. Jeżeli katalog bin nie istnieje to oczywiście musimy go utworzyć.

mkdir ~/bin
cd ~/bin
cp ~/site-packages/Django-1.4/django/bin/django-admin.py django-admin.py

Teraz w katalogu rails możemy utworzyć projekt django lub pobrać go z svn. Projekt musi się tak samo nazywać jak w panelu Passenger.

cd ~/rails
django-admin.py startproject my_project

Dodatkowo dla poprawnego działania Passengera musimy w folderze projektu utworzyć foldery tmp log public oraz site-packages. W site-packages również możemy stworzyć sobie plik django.pth aby np. zmienić wersje Django dla projektu.

cd ~/rails/my_project/
mkdir tmp log public site-packages

Na koniec musimy jeszcze stworzyć plik passenger_wsgi.py który będzie odpowiedzialny za uruchamianie naszego projektu na serwerze.

nano ~/rails/my_project/passenger_wsgi.py

Zawartość pliku:

import sys, os
INTERP = "/usr/local/bin/python2.7"

if sys.executable != INTERP: os.execl(INTERP, INTERP, *sys.argv)

import os, sys, site
site.addsitedir('/home/login/rails/my_project/site-packages')
site.addsitedir('/home/login/rails/my_project')
site.addsitedir('/home/login/rails')
site.addsitedir('/home/login/site-packages')
os.environ['LD_LIBRARY_PATH'] = '/usr/local/lib'

os.environ['DJANGO_SETTINGS_MODULE'] = 'my_project.settings'
import django.core.handlers.wsgi

from paste.exceptions.errormiddleware import ErrorMiddleware
application = django.core.handlers.wsgi.WSGIHandler()
application = ErrorMiddleware(application, debug=True)

Od teraz aplikacja powinna się uruchomić bez problemów. Przy każdej zmianie jakiegokolwiek pliku py w projekcie musimy zresetować aplikację co można uczynić w panelu passengera.

Baza danych - development vs production

Tworząc projekt na lokalnym komputerze bardzo często testujemy go na wbudowanym w Django serwerze developerskim. Jeżeli korzystamy z bazy danych - a zdarza się to często to istnieje potrzeba istnienia dwóch konfiguracji baz dla serwera developerskiego oraz dla serwera produkcyjnego. Oczywiście mówię tutaj o przypadku gdy całość projektu razem z jego konfiguracją (settings.py) trzymamy w systemie kontroli wersji a na serwerze produkcyjnym tylko aktualizujemy całość (np. svn update w folderze z projektem). Osobiście na serwerze produkcyjnym używam bazy mysql a testując i tworząc projekt na swoim laptopie wykorzystuję do tego sqllite.

Początkowo miałem to rozwiązane tak że w pliku settings.py tworzyłem sobie dodatkową zmienną PRODUCTION do której przypisywałem True lub False w zależności czy projekt był uruchamiany na serwerze czy na laptopie oraz miałem dwie wersje DATABASES którymi mogłem sterować dzięki zmiennej PRODUCTION. Rozwiązanie proste ale nie praktyczne szczególnie jeżeli projekt trzyma się w systemie kontroli wersji. Dość często zwyczajnie zapominałem zmienić stan zmiennej PRODUCTION przed wykonaniem commita.

Dużo lepszym rozwiązaniem jest w pliku settings.py ustawić DATABASES na bazę używaną do testowania oraz dodatkowo na końcu pliku dodać:

try:
    from production_settings import *
except ImportError:
    pass

Dzięki temu na serwerze produkcyjnym możemy stworzyć sobie plik production_settings.py w którym nadpiszemy standardowe ustawienia.

from settings import *

DATABASES = {
# ustawienia bazy
}

Rozwiązanie to stosuję od dość niedawna i sprawuje się doskonale.

Pliki statyczne

Uruchamiając projekt na wbudowanym w django serwerze nie musimy się specjalnie martwić o pliki statyczne. Serwer automatycznie serwuje takie pliki jeżeli mamy odpowiednio ustawione ścieżki w pliku settings.py. Tak samo nie musimy się martwić o pliki statycznie wykorzystywane przed panel administratora.

W Django 1.4 mechanizm plików statycznych został znacznie poprawiony i uproszczony. W zasadzie trzema ustawieniami jesteśmy to skonfigurować pod swoje potrzeby.

Konfigurując apache na serwerze musieli byśmy ustawiać aliasy oraz ścieżki do folderów ręcznie, na serwerze vipserv.org jest mechanizm który robi to automatycznie. Standardowo tworząc projekt w panelu Passenger ustawiana jest ścieżka /home/login/rails/my_project/public jako katalog który będzie obsługiwany przez apache.

W Django 1.4 mamy od tego trzy zmienne w pliku settings.py:

  • STATIC_ROOT - ścieżka do katalogu w którym będą przechowywane wszystkie pliki statyczne. Nie powinniśmy umieszczać tutaj żadnych plików.
  • STATICFILES_DIRS - lista katalogów w naszymi plikami statycznymi
  • STATIC_URL - ścieżka w adresie po której będziemy się odwoływać do plików statycznych (standardowo /static/)

Ustawienie STATICFILES_DIRS oraz STATIC_URL wystarcza do obsługi plików statycznych na serwerze developerskim. W szablonach możemy się odwoływać do plików statycznych w następujący sposób:

{{ STATIC_URL }}css/style.css
{{ STATIC_URL }}js/jquery.js

Co spowoduje wygenerowanie adresów:

/static/css/style.css
/static/js/jquery.js

W STATICFILES_DIRS możemy podać wiele folderów w których trzymamy pliki statyczne, dodatkowo możemy jest odpowiednio otagować. Dokładny opis jak tego używać znajduje się w dokumentacji Django.

Ja u siebie w projekcie mam jeden folder static_files w którym trzymam wszystkie pliki wykorzystywane w szablonach (css, js, obrazki).

Ustawienie STATIC_ROOT przyda się do serwowania plików statycznych na serwerze produkcyjnym. Powinniśmy tutaj ustawić ścieżkę na folder public ustawiony w panelu Passenger oraz dodatkowo dodać na końcu /static. Dzięki temu z łatwością będziemy mogli serwować pliki statyczne.

import os.path
from os.path import join

STATIC_URL = '/static/'
SETTINGS_PATH = os.path.abspath(os.path.dirname(__file__))

# ścieżka w panelu Passenger na katalog aplikacji: 
# /home/login/rails/my_project/public/
STATIC_ROOT = join(join(SETTINGS_PATH, '../public'), 'static')

STATICFILES_DIRS = (join(SETTINGS_PATH, 'static_files'),)

Dla jasności drzewo katalogów w fodlerze ~/rails wygląda następująco:

~/rails/
    my_project/
        log
        tmp
        site-packages
        manage.py
        passenger_wsgi.py
        my_project
            settings.py
            static_files
                css
                    pliki_css
                js
                    pliki_js
                images
                    pliki png

        public
            static

Gdy mamy już tak skonfigurowany projekt to możemy uruchomić polecenie które zbierze wszystkie pliki statyczne:

python manage.py collectstatic

Wszystkie pliki w ścieżkach umieszczonych w STATICFILES_DIRS jak i również wykorzystywane przez dodatkowe moduły django np. panel admina zostaną przekopiowane do folderu ~/rails/my_project/public/static/.

Dzięki temu wszystkie pliki statyczne będą osągalne przez adres domena/static/

domena/static/css/style.css
domena/static/js/jquery.js

Na serwerze developerskim nie ma potrzeby uruchamiać collectstatic bo serwowanie plików statycznych załatwia sam serwer. Rozwiązanie jest o tyle wygodne że nie musimy się martwić o adresy do plików statycznych użycie {{ STATIC_URL }} w szablonach załatwia sprawę.

Wcześniej wspominałem że w STATIC_ROOT nie powinniśmy umieszczać żadnych plików ponieważ są one nadpisywane za każdym razem gdy uruchomimy collectstatic. Jednak ja w STATIC_ROOT mam stworzony dodatkowy folder który na pewno się nie nadpisze a w którym trzymam wszystkie pliki wykorzystywane na stronie przesłane przez FTP (np. obrazki do wpisów na blogu).

Tagi

Komentarze (3)

  • #1 // Hubert napisał:

    W ich dokumentacji średnio to jest opisane, dzięki za wpis :)

  • #2 // Lucas napisał:

    Bardzo ciekawy artykul Mam jednak pytanie z innej beczki. Jak chodzi Django na hostingu od vipserv.org? Szukam jakiegos hostingu dla siebie (pod django i php) i zastanwiam sie jeszcze nad appfog (+ darmowy hosting, rozne technologie, - brak mozliwosci trzymania plikow statycznych, po restarcie aplikacji wszystko co zostalo uploadowane za pomoca aplikacji przepada)

  • #3 // Kamil Łuszczki napisał:

    Według mnie Django na vipserv działa rewelacyjnie a z nowym passenger'em (aktualizacja) wydaje mi się że działa jeszcze szybciej. Nie ma z tym żadnych problemów, jedynym minusem (ale niekoniecznie) może być fakt że aplikacje Pythona oraz Rubiego są po pewnym czasie wyłączane przez serwer (jeżeli nie są używane) co może powodować dłuższe ładowanie strony za pierwszym razem.

    Nie wiem jak inne serwery sobie z tym radzą bo nie używałem nic po za vipserver. Z darmowymi serwerami dał bym sobie jednak spokój.