Pogodzenie Python-a z PHP na Apache2 (mod_wsgi + mod_php)

Podziel się z innymi!

    Wstęp

    Poradników i tutoriali opisujących konfigurację Apache z PHP i MySQL pod Linuxem (LAMP) jest cała masa. W Ubuntu sprowadza się to do wywołania w konsoli prostego polecenia

    sudo apt-get install php5 php5-mysql mysql-server apache2 libapache2-mod-auth-mysql

    … i mamy wszystko co potrzeba. Więcej na ten temat znajdziemy choćby w artykule Instalacja Apache + PHP5 + MySQL.

    Instalacja frameworka Django i preferowanego przez pythonowców serwera baz danych Postgres jest równie prosta – przynajmniej pod Ubuntu.

    sudo apt-get install python-django postgresql python-psycopg2 postfix python-imaging python-docutils

    Django ma jedną z najlepszych dokumentacji, a o Postgresie pisze niemal co drugi bloger, który miał coś z nim do czynienia tak więc zainteresowanych po prostu odeślę do wciąż aktualnego i w miarę kompletnego wpisu Instalacja PostgreSQL 8.3 w Ubuntu 8.04.

    O ile hostowanie skryptów PHP na Apache-u jest rzeczą naturalną zarówno w środowisku deweloperskim jak i produkcyjnym o tyle w przypadku Pythona a konkretnie aplikacji Django już nie. Django posiada świetny wbudowany serwer stworzony na podstawie klasy BaseHttpServer ze standardowej biblioteki Pythona, który świetnie sprawdza się do szybkiego testowania aplikacji o niepełnej funkcjonalności. Restartuje się automatycznie przy każdej zmianie w kodzie a ponadto ponieważ uruchamiany jest w terminalu wyświetla na wyjściu wszystkie instrukcje print.

    Użycie serwera wbudowanego w środowisku produkcyjnym jest zabronione licencją, a poza tym istnieje jeszcze kilka innych obiektywnych powodów aby tego nie robić, jak choćby bezpieczeństwo i stabilność, czy wydajność. Jak się okazuje także dla celów developerskich warto czasem podjąć trud konfiguracji serwera zewnętrznego.

    W przypadku Apache-a w kontekście hostowania skryptów napisanych w języku Python najczęściej stosuje się dwa rozwiązania tj. mod_python lub mod_wsgi. Oba z modułów nie powodują konfliktów z mod_php, tak więc chcąc na swoim serwerze obsługiwać zarówno aplikacje PHP i Python można zastosować dowolne z wymienionych rozwiązań.

    Ponoć konfiguracja Apache2 + mod_python jest częściej stosowana, lepiej przetestowana i udokumentowana, ja jednak zdecydowałem się na WSGI z uwagi na elastyczność jaką udało mi się uzyskać.

    Założenia

    W przypadku środowisk produkcyjnych kluczową kwestią jest bezpieczeństwo i stabilność serwera. W środowisku developerskim, w którym skrypty uruchamiane są jedynie na komputerze lokalnym osobiście stawiam na elastyczność kosztem nawet bezpieczeństwa i stabilności. Nie lubię grzebać w plikach konfiguracyjnych Apache-a i edytowanie pliku /etc/apach2/httpd.conf lub któregoś z plików w katalogu /etc/apache2/sites-available/ za każdym razem kiedy tworze nowy projekt lub zmieniam jego nazwę. W przypadku projektów realizowanych w języku PHP wystarczy, że w moim katalogu workspace stworzę nowy katalog np. „nowy_projekt_php”, wrzucę do niego plik index.php i wywołam w pasku adresu przeglądarki

    http://localhost/nowy_projekt_php

    Podobny efekt chciałem uzyskać w przypadku projektów realizowanych w Django.

    Instalacja mod_wsgi

    Moduł WSGI możesz zainstalować ze źródeł, co zostało przystępnie opisane w artykule Ubuntu Hardy – mod_wsgi Installation, ale w Ubuntu można skorzystać z repozytorium:

    sudo apt-get install libapache2-mod-wsgi

    Aby aktywować moduł (w Ubuntu 10.04 – można pominąć)

    sudo a2enmod mod-wsgi

    W przypadku instalacji z repozytorium wystarczy już tylko restart serwera.

    sudo /etc/init.d/apache2 restart

    Konfiguracja Apache

    Integracja mod_wsgi z Django jest świetnie opisana zarówno na stronach dokumentacji Django, jak też (może nawet lepiej) na stronie rozszerzenia specjalnie poświęconej temu zagadnieniu w artykule pod pt. Integration With Django.

    Ja jednak zaproponuję jeszcze inne rozwiązanie na które natknąłem się na forum w wątku Ubuntu + Apache2 + WSGI (mod_wsgi) with LAMP i lekko zmodyfikowałem na swoje potrzeby.

    Aby osiągnąć wyżej opisane założenie w najprostszy sposób należy:

    Stworzyć Virtualhosta w pliku httpd.conf

    sudo vim /etc/apache2/httpd.conf

    <VirtualHost *>
        ServerName testhost
        DocumentRoot /home/myuser/workspace/

        <Directory /home/myuser/workspace/>
            Options Indexes FollowSymLinks MultiViews ExecCGI

            AddType application/x-httpd-php .php .phtml
            AddType application/x-httpd-php-source .phps
            AddHandler application/x-httpd-php .php .phtml
            AddHandler cgi-script .cgi
            AddHandler wsgi-script .wsgi

            Order allow,deny
            allow from all
        </Directory>
    </Virtualhost>

    W przeciwieństwie do rozwiązania zaproponowanego na forum proponuję usunąć dyrektywę „AllowOverride None” dzięki czemu możliwe będzie skorzystanie z mod_rewrite.

    Trzeba jeszcze poinformować serwer, że domyślnym plikiem w aplikacji napisanej w Pythonie będzie index.wsgi

    sudo gedit /etc/apache2/mods-enabled/dir.conf

    <IfModule mod_dir.c>
        DirectoryIndex index.html index.cgi index.pl index.php index.xhtml index.htm index.wsgi
    </IfModule>

    W katalogu /home/myuser/workspace/ utwórzmy nowy podkatalog nowy_projekt_python a w nim plik index.wsgi o treści:

    def application(environ, start_response):
        status = '200 OK' 
        output = 'Hello World!'
     
        response_headers = [('Content-type', 'text/plain'),
                            ('Content-Length', str(len(output)))]
        start_response(status, response_headers)
     
        return [output]

    Jeśli po wywołaniu

    http://localhost/nowy_projekt_python
    

    widać „Hello World!” to znaczy, że wszystko jest ok, jeśli nie to może wystarczy zrestartować serwer 😉

    Twórcy Django dążą do tego aby ich framework był pythonowy jak to tylko możliwe dlatego np. konfigurację trzymają w plikach py a nie np. w xml albo yml-u. Dzięki takiemu podejściu możemy mieć pewność, że po wrzuceniu do katalogu nowy_projekt_python aplikacji Django i modyfikacji pliku index.wsgi, zobaczymy w przeglądarce to co byśmy widzieli po uruchomieniu serwera wbudowanego.

    Zmodyfikujmy index.wsgi

    import os, sys
    sys.path.append('/home/myuser/workspace')
    sys.path.append('/home/myuser/workspace/nowy_projekt_python')
     
    os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
     
    import django.core.handlers.wsgi
     
    _application = django.core.handlers.wsgi.WSGIHandler()
     
    def application(environ, start_response):
    	if environ['wsgi.url_scheme'] == 'https':
    		environ['HTTPS'] = 'on'
    	return _application(environ, start_response)

    Przydałby się jeszcze plik .htaccess

    <IfModule mod_rewrite.c>
        # Turn on URL rewriting
        RewriteEngine On

        # Installation directory
        RewriteBase /nowy_projekt_python/

        # Protect hidden files from being viewed
        <Files .*>
            Order Deny,Allow
            Deny From All
        </Files>

        # Allow any files or directories that exist to be displayed directly
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d

        # Rewrite all other URLs to index.wsgi/URL
        RewriteRule .* index.wsgi/$0 [PT]

    </IfModule>

    Zaproponowane tutaj rozwiązanie jest jedynie punktem wyjścia do stworzenia swojej własnej uniwersalnej konfiguracji środowiska developerskiego do pracy z projektami PHP i Python. Zapewne przydałoby się parę rzeczy inaczej skonfigurować lub też dodać kilka opcji jak np. dyrektywę zapobiegającą wyświetlaniu treści plików python w przeglądarce. Zachęcam do eksperymentowania i dzielenia się swoim doświadczeniem.

    Podziel się z innymi!