Προχωρημένη εγκατάσταση django με mod-wsgi και virtualenv

A cool python snake

Σε αυτό το άρθρο θα προσπαθήσουμε να δούμε έναν προχωρημένο τρόπο εγκατάσης του django και άλλων wsgi εφαρμογών, σε virtual hosts του apache. Θα χρησιμοποιήσουμε virtualenv για να απομονώσουμε το περιβάλλον μας από την εγκατάσταση της python του συστήματος και, ακόμα παραπέρα, να μπορούμε να στήσουμε πάνω από μία εφαρμογές με διαφορετικό η καθεμία σετ από dependencies.

Το υλικό του άρθρου προέρχεται από τις εξής πηγές. Οποιοσδήποτε αισθάνεται πως χρειάζεται διαφοροποιήσεις λόγω του συστήματος του, ας ανατρέξει σε αυτές:
http://code.google.com/p/modwsgi/wiki/QuickConfigurationGuide
http://code.google.com/p/modwsgi/wiki/VirtualEnvironments
https://docs.djangoproject.com/en/dev/howto/deployment/wsgi/modwsgi/
Για ακόμα μερικές ιδέες, όπως το να κρατάτε όλο το enviroment σε git, κοιτάξτε εδώ:
http://www.jeffknupp.com/blog/2012/02/09/starting-a-django-project-the-r...

Θα υποθέσουμε ότι δουλεύουμε πάνω σε μία διανομή βασισμένη σε apt/deb packages. Τα πάντα εδώ έχουν τεσταριστεί πάνω σε ένα Ubuntu Server 12.04.
Και ακριβώς επειδή βαριόμαστε να γράφουμε μόνιμα sudo αφού θέλουμε να κάνουμε αρκετές συνεχόμενες αλλαγές, ξεκινάμε δίνοντας

sudo -i

Εγκατάσταση του mod-wsgi

Από την στιγμή που έχουμε ήδη εγκατεστημένο τον apache, είναι ότι πιο απλό. Το μόνο που χρειάζεται είναι να δώσουμε:

apt-get install libapache2-mod-wsgi

Bonus: Αν αναρωτιέστε πιο από τα mpm του apache είναι πιο κατάλληλο για wsgi εφαρμογές, η ασφαλής απάντηση είναι το προεπιλεγμένο worker, εκτός αν κάτι άλλο στο σύστημα σας (πχ. php) σας αναγκάσει να χρησιμοποιήσετε το παλιό prefork. Το καινούργιο event mpm δεν συνίσταται ακόμα. http://serverfault.com/questions/15717/which-apache-package-is-best-for-...

Εγκατάσταση του virtualenv

To virtualenv είναι το λογισμικό που θα χρησιμοποιήσουμε ώστε να έχουμε απομονωμένα περιβάλλοντα για την κάθε εφαρμογή μας, και μάλιστα, απομωνομένα από το περιβάλλον της python της διανομής μας. Μέσα από αυτό θα χρησιμοποιήσουμε το pip για να εγκαθιστούμε τα πακέτα που θα χρειαζόμαστε κάθε φορά.

Μία καλή συνταγή εδώ είναι να εγκαταστήσουμε τα προγράμματα αυτά μέσω της διανομής, ώστε να είμαστε σίγουροι για dependencies και να έχουμε man pages εγκατεστημένα κεντρικά στο σύστημα, και μετά να εγκαταστήσουμε από πάνω τις τελευταίες εκδόσεις χειροκίνητα. Αυτό μπορεί να γίνει επειδή η διανομή εγκαθιστά πακέτα στους /usr/* φακέλους, ενώ το pip στο /usr/local/* που είναι σύνηθες να προηγείται στο path που το λειτουργικό ψάχνει για εντολές, όπως φαίνεται και εδώ:

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin

Ας κάνουμε ακριβώς αυτό:

apt-get install python-pip python-virtualenv

Για να δούμε τις εκδόσεις δίνουμε:

pip --version
virtualenv --version

Ας εγκαταστήσουμε τώρα τις τελευταίες εκδόσεις:

pip install -U pip
pip install -U virtualenv

Ελέγχοντας πάλι τις εκδόσεις θα πρέπει να δούμε ότι έχουμε τις τελευταίες.

Bonus: Επειδή το pip εγκαθιστά τα πακέτα του από source, κάποια στιγμή μπορεί να εγκαταστήσουμε κάποιο extension της python που να χρησιμοποιεί C.
Γι'αυτό ίσως έχει νόημα να έχουμε εγκατεστημένο τον πηγαίο κώδικα της python καθώς και τα βασικά εργαλεία για compile από τώρα:

apt-get install build-essential python-dev

Εγκαθιστώντας ένα παρθένο περιβάλλον βάσης για τις εφαρμογές μας

Το βήμα αυτό δεν είναι απαραίτητο αλλά προτείνεται ως καλή πρακτική στο documentation του mod-wsgi.
Ουσιαστικά φτιάχνουμε ένα καθαρό περιβάλλον python που είναι απομονωμένο από το κυρίως σύστημά μας.
Έστω ότι θα το φτιάξουμε στο /usr/local/virtualenvs/base

mkdir /usr/local/virtualenvs
cd usr/local/virtualenvs
virtualenv --no-site-packages base

Τώρα πρέπει να πούμε στον apache ότι η default εγκατάστασή μας για την python είναι αυτή που μόλις φτιάξαμε.
Γι'αυτό προσθέτουμε σε κάποιο από τα configuration του apache (πχ. /etc/apache2/apache2.conf) την εντολή

WSGIPythonHome /usr/local/virtualenvs/base

Η' μπορούμε να εκμεταλλευτούμε τον κατάλογο conf.d του apache και να γράψουμε σε μία εντολή

echo WSGIPythonHome /usr/local/virtualenvs/base > /etc/apache2/conf.d/wsgi

Bonus: Η επιλογή --no-site-packages είναι πλέον προεπιλεγμένη. Την βάζουμε εδώ μόνο για πληρότητα και για να είναι γνωστή σε περίπτωση που χρησιμοποιήσετε παλιότερη έκδοση.

Εγκαθιστώντας το django

Ας φτιάξουμε καταρχήν ένα φάκελο όπου θα ζουν οι διάφορες wsgi εφαρμογές μας.
Αντί για το /var/ww που θεωρείται ότι θα έχει αρχεία που αλλάζουν συχνά, ας επιλέξουμε το /usr/local/www/wsgiapps

mkdir /usr/local/www
mkdir /usr/local/www/wsgiapps
cd /usr/local/www/wsgiapps

Φτιάχνουμε το virtualenv που θα περιέχει την django εφαρμογή μας

virtualenv --no-site-packages djangotest

Και ας το ενεργοποιήσουμε για να κάνουμε τις εγκαταστάσεις που θέλουμε

cd djangotest
source bin/activate

Θα δείτε ότι πλέον το prompt έχει αλλάξει για να δείξει ότι δουλεύουμε με στο virtual περιβάλλον

(djangotest)root@ubuserver:/usr/local/www/wsgiapps/djangotest#

Είμαστε πλέον έτοιμοι να κάνουμε αυτό για το οποίο ήρθαμε, να εγκαταστήσουμε το django

pip install django

Και ας ξεκινήσουμε το πρώτο μας project:

django-admin.py startproject myproject .

Η τελεία στο τέλος λέει στο django ότι θέλουμε να ξεκινήσουμε το project στον τρέχον κατάλογο.
Μπορούμε πλέον να βγούμε από το virtualenv.

deactivate

Bonus: Κάθε φορά που θα θέλουμε να κάνουμε αλλαγή στο περιβάλλον μας, πχ. εντολές του django, εγκατάσταση πρόσθετων python libraries κλπ, θα πρέπει να ενεργοποιούμε το virtual περιβάλλον. Σε έναν server αυτό θα γίνει λίγες φορές οπότε είναι ανεκτό. Για το development περιβάλλον σας, ρίξτε μία ματιά στο virtualenvwrapper http://virtualenvwrapper.readthedocs.org που κάνει την χρήση πολλών virtualenvs παιχνιδάκι.

Ρύθμιση της εφαρμογής μας στον apache

Φτιάχνουμε ένα αρχείο στο /etc/apache2/sites-available με όνομα djangotest και τον εξής κώδικα:

<VirtualHost *:80>

    # Add virtualenv and project directories to python path
    WSGIDaemonProcess djangotest \
python-path=/usr/local/www/wsgiapps/djangotest/lib/python2.7/site-packages:\
/usr/local/www/wsgiapps/djangotest

    WSGIProcessGroup djangotest

    WSGIScriptAlias /djangotest /usr/local/www/wsgiapps/djangotest/myproject/wsgi.py

    <Directory /usr/local/www/wsgiapps/djangotest/myproject>
    <Files wsgi.py>
        Order allow,deny
        Allow from all
    </Files>
    </Directory>

</VirtualHost>

Ας εξηγήσουμε ένα ένα τα κομμάτια του virtualhost

    WSGIDaemonProcess djangotest \
python-path=/usr/local/www/wsgiapps/djangotest/lib/python2.7/site-packages:\
/usr/local/www/wsgiapps/djangotest

    WSGIProcessGroup djangotest

Εδώ ορίζουμε ότι το wsgi θα τρέχει ως daemon και ρυθμίζουμε το path στο οποίο θα ψάχνει η python για modules.
Τα ελάχιστα που χρειαζόμαστε είναι ο κατάλογος site-packages του virtual περιβάλλοντος της εφαρμογής,
καθώς και ο κατάλογος που βρίσκεται το project μας ώστε να μπορέσει να το κάνει import το django, χωρισμένα με άνω κάτω τελεία (:).
Στην τελευταία γραμμή δηλώνουμε ότι θα χρησιμοποιήσουμε τον daemon σε αυτό το virtual host.

Bonus: Η εντολή WSGIDaemonProcess παρέχει πλήθος ρυθμίσεων για τα πόσα processes και threads μπορεί να έχει ανοιχτά ταυτόχρονα, το timeout και πολλά άλλα. Εμείς εδώ ορίσαμε μόνο το pythonpath. Για τα υπόλοιπα κοιτάξτε εδώ http://code.google.com/p/modwsgi/wiki/QuickConfigurationGuide#Delegation...
Bonus: Αν κάποια modules της python τα χρησιμοποιούμε σε πάνω από ένα projects, έχει νόημα να τα έχουμε εγκατεστημένα κάπου κεντρικά (ακόμα και στο base περιβάλλον που φτιάξαμε στην αρχή). Σε αυτήν την περίπτωση απλά προσθέτουμε και τους υπόλοιπους site-packages καταλόγους στο path

    WSGIScriptAlias /djangotest /usr/local/www/wsgiapps/djangotest/myproject/wsgi.py

Εδώ δηλώνουμε το wsgi αρχείο απ'όπου θα ξεκινήσει η εφαρμογή μας.
Αυτό που έχουμε κερδίσει ορίζοντας το pythonpath εξωτερικά είναι ότι μπορούμε να χρησιμοποιήσουμε αυτούσιο το wsgi.py που μας δημιουργεί αυτόματα το django όταν ξεκινήσαμε το project.
Bonus: Υπάρχουν άλλοι τρόποι εγκατάστασης μίας εφαρμογής στον apache που στηρίζονται στο να αλλάξουν το path μέσα στο wsgi αρχείο, και ο καθένας έχει τις ιδιαιτερότητες και τα μείον του.
Μία τέτοια λύση μπορεί να είναι επιθυμητή αν σκοπεύετε να κάνετε το wsgi αρχείο σας να δουλεύει ανεξάρτητα του pythonpath με το οποίο καλέστηκε, πχ. για να διανέμετε το project σε άλλους που δεν θέλουν να ασχοληθούν με την πλευρά ρύθμισης του apache

    <Directory /usr/local/www/wsgiapps/djangotest/myproject>
    <Files wsgi.py>
        Order allow,deny
        Allow from all
    </Files>
    </Directory>

Εδώ ορίζουμε στον apache σε ποιά αρχεία θα έχει πρόσβαση.
Σημειώστε ότι τελικά, το μόνο αρχείο που χρειάζεται είναι το ίδιο το αρχείο wsgi, και σε αυτό χρειάζεται μόνο δικαιώματα ανάγνωσης. Κατά τα άλλα, το αρχείο και όλος ο κατάλογος μπορεί να ανήκει στον root.
Αυτός ήταν και ο λόγος που εγκαταστήσαμε την εφαρμογή μας στο /usr/local/www αντί για το /var/www και δεν μιλήσαμε καθόλου για χρήστες και δικαιώματα.
Bonus: Υπάρχουν άλλες δύο κατηγορίες φακέλων και αρχείων που θα μπορούσε να χρησιμοποιεί μία django εφαρμογή: Static αρχεία και αρχεία user uploaded/temporary.
Στην πρώτη περίπτωση χρειάζεται να πούμε στον apache ότι μπορεί να τα χρησιμοποιήσει με μία δεύτερη εντολή Directory αλλά μπορούν να ανήκουν στον root.
Στην δεύτερη πρέπει να είναι σε κατάλογο που ανήκει στον χρήστη του apache (www-data) και εκεί το /var/www είναι μία καλή επιλογή.

Houston we have liftoff!

Ώρα για το τελικό βήμα. Ενεργοποιούμε το virtual host που μόλις φτιάξαμε

a2ensite djangotest

Απενεργοποιούμε την default σελίδα του apache (για να αποφύγουμε την πιθανότητα να πάρει αυτή το αίτημα)

a2dissite default

Κάνουμε επανεκίνηση στον apache

service apache2 reload

Και πάμε με το πρόγραμμα περιήγησής μας (browser) να δούμε την αρχική μας σελίδα στο

www.myserver.com/djangotest

Θα μας χαιρετίσει η default σελίδα του django που μας λέει ότι η εγκατάστασή μας πέτυχε

Bonus: Και επειδή είμαστε πεσιμιστές, σημειώνουμε ότι μπορούμε να δούμε το αρχείο λαθών του apache δίνοντας

tail -f /var/log/apache2/error.log

Ενημερώστε μας μόνο για τυχόν λάθη που θα βρείτε ;)

Και μετά, ο κόσμος

Ομολογουμένως, αυτός ο τρόπος εγκατάστασης είναι αρκετά προχωρημένος, οπότε ας δούμε τι πλεονεκτήματα έχει:
- Μπορούμε πλέον χρησιμοποιώντας τις ίδιες αρχές να εγκαταστήσουμε πάνω από μία εφαρμογή django, ή οποιαδήποτε άλλη web εφαρμογή python με wsgi, όπως pylons, bottle.py, pyweb κλπ.
- Οι εφαρμογές μας μπορούν να έχουν διαφορετικές εκδόσεις και άλλα modules, είτε μεταξύ τους, είτε φυσικά με την εγκατάσταση python του server.
- Για έναν server που φιλοξενεί σελίδες πολλών χρηστών, ο κάθε χρήστης μπορεί να φτιάξει μία wsgi εφαρμογή μέσα σε ένα virtual περιβάλλον το οποίο θα ζει στον κατάλογο του, χωρίς να έχει ιδιαίτερα δικαιώματα. Το μόνο που απαιτείται είναι ο διαχειριστής να εγκαταστήσει τον vhost του χρήστη στον apache.
- Χρησιμοποιώντας το mod-wsgi σε μορφή daemon, έχουμε πληθώρα ρυθμίσεων που μπορούμε να κάνουμε για τον αριθμό των processes και threads για την κάθε εφαρμογή ξεχωριστά.

Τι δεν καλύψαμε και πράγματα να μάθουμε μετά

- Γενικές ρυθμίσεις του apache, τις οποίες δεν είναι ανάγκη να αναφερθούμε εδώ και γεμίζουν βιβλία από μόνες τους.
- Γενικά πως προχωράμε στο να φτιάξουμε μία εφαρμογή σε django πέραν του να δούμε την αρχική σελίδα
- Ρύθμιση βάσης δεδομένων και schema migrations μέσω του south.
- Deployment της εφαρμογής μας από το development περιβάλλον μας στο server μέσω git.
Όλα τα οποία θα μπορούσε να είναι επόμενα άρθρα.