In meinem ersten Versuch mit diesem Zeug habe ich ein Beispiel gegeben, wie man Django-Projekte hinter lighttpd mit einfachen FCGI-Skripten, die in den Server integriert sind, ausführen kann. Ich werde ein wenig mehr über dieses Zeug erzählen, mit einer Möglichkeit, lighttpd und Django zu kombinieren, die viel mehr Flexibilität bei der Verteilung von Django-Anwendungen über Maschinen bietet. Dies ist besonders wichtig, wenn Sie mit hohen Lasten auf Ihren Servern rechnen. Natürlich sollten Sie den Django-Caching-Middleware verwenden, aber es gibt Zeiten, in denen selbst das nicht ausreicht und die einzige Lösung darin besteht, mehr Hardware an das Problem zu werfen.
Aktualisierung: Ich pflege meine Beschreibungen jetzt in meinem Trac-System. Siehe die lighty+FCGI-Beschreibung für Django.
Hinweis: Da Django sehr neue Software ist, habe ich keine Produktionserfahrungen damit. Daher handelt es sich hier eher um einen theoretischen Standpunkt, in den Wissen einfließt, das ich durch den Betrieb von Produktionssystemen für mehrere größere Portale gewonnen habe. Am Ende kommt es nicht so sehr darauf an, welche Software Sie verwenden - es kommt nur darauf an, wie Sie sie über Ihren Server-Farm verteilen können.
Um dieser Dokumentation zu folgen, benötigen Sie die folgenden Pakete und Dateien, die auf Ihrem System installiert sind:
- [Django][2] selbst - derzeit aus dem SVN geholt. Folgen Sie den Setup-Anweisungen oder verwenden Sie
python setup.py install.
- [Flup][3] - ein Paket mit verschiedenen Möglichkeiten, WSGI-Anwendungen auszuführen. In dieser Dokumentation verwende ich den threaded WSGIServer.
- [lighttpd][4] selbstverständlich. Sie benötigen mindestens die FastCGI-, die Rewrite- und die Accesslog-Module, diese werden in der Regel mit dem System kompiliert.
- [Eunuchs][5] - nur erforderlich, wenn Sie Python 2.3 verwenden, da Flup socketpair in den vorkompilierten Servern verwendet und dies erst ab Python 2.4 verfügbar ist.
- [django-fcgi.py][6] - mein FCGI-Server-Skript, das eines Tages Teil der Django-Distribution sein könnte, aber für den Moment einfach hier herunterladen. Legen Sie dieses Skript irgendwo in Ihren $PATH, z.B.
/usr/local/bin und machen Sie es ausführbar.
- Wenn das oben genannte aus irgendeinem Grund nicht funktioniert (vielleicht unterstützt Ihr System socketpair nicht und kann daher den vorkompilierten Server nicht verwenden), können Sie [django-fcgi-threaded.py][7] - eine Alternative, die den Threading-Server mit all seinen Problemen verwendet. Ich verwende es zum Beispiel auf Mac OS X für die Entwicklung.
Bevor wir beginnen, lassen Sie uns ein wenig über Serverarchitektur, Python und hohe Last sprechen. Die noch bevorzugte Installation von Django erfolgt hinter Apache2 mit mod python2. mod python2 ist eine recht leistungsfähige Erweiterung für Apache, die einen vollständigen Python-Interpreter (oder sogar viele Interpreter mit unterschiedlichen Namensräumen) in den Apache-Prozess integriert. Dies ermöglicht es Python, viele Aspekte des Servers zu steuern. Aber es hat einen Nachteil: Wenn der einzige Zweck darin besteht, Anfragen von Benutzern an die Anwendung weiterzuleiten, ist es eine ziemliche Übertreibung: Jeder Apache-Prozess oder Thread wird einen vollständigen Python-Interpreter mit Stack, Heap und allen geladenen Modulen enthalten. Apache-Prozesse werden auf diese Weise etwas fett.
Ein weiterer Nachteil: Apache ist einer der flexibelsten Server da draußen, aber im Vergleich zu kleinen Servern wie lighttpd ein Ressourcenfresser. Und - aufgrund der Architektur der Apache-Module - wird mod_python die vollständige Anwendung im Sicherheitskontext des Webservers ausführen. Zwei Dinge, die Sie in Produktionsumgebungen nicht oft mögen.
Ein natürlicher Ansatz ist also die Verwendung leichterer HTTP-Server und das Hinterlegen Ihrer Anwendung dahinter - unter Verwendung des HTTP-Servers selbst nur zum Servieren von Medien und unter Verwendung von FastCGI, um Anfragen vom Benutzer an Ihre Anwendung weiterzuleiten. Manchmal stellen Sie diesen kleinen HTTP-Server hinter einen Apache-Front, der nur mod_proxy (entweder direkt oder über mod_rewrite) verwendet, um Anfragen an den Webserver Ihrer Anwendung weiterzuleiten - und glauben Sie es oder nicht, dies ist tatsächlich viel schneller als das Servieren der Anwendung direkt mit Apache!
Die zweite Falle ist Python selbst. Python hat eine recht schöne Threading-Bibliothek. Es wäre also ideal, Ihre Anwendung als Thread-Server zu erstellen - weil Threads viel weniger Ressourcen als Prozesse verbrauchen. Aber das wird Sie beißen, wegen einer speziellen Funktion von Python: der GIL. Der gefürchtete globale Interpreter-Lock. Dies ist kein Problem, wenn Ihre Anwendung zu 100% Python ist - der GIL greift nur, wenn interne Funktionen verwendet werden oder wenn C-Erweiterungen verwendet werden. Schade, dass fast alle DBAPI-Bibliotheken mindestens einige Datenbank-Client-Code verwenden, der eine C-Erweiterung verwendet - Sie starten einen SQL-Befehl und das Threading wird deaktiviert, bis der Aufruf zurückkehrt. Keine mehreren Abfragen laufen ...
Die bessere Option ist also die Verwendung eines Fork-Servers, weil der GIL dann nicht greift. Dies ermöglicht einem Fork-Server, mehrere Prozessoren in Ihrer Maschine effizient zu nutzen - und so auf lange Sicht viel schneller zu sein, trotz des Overheads von Prozessen gegenüber Threads.
Für diese Dokumentation nehme ich einen dreischichtigen Ansatz zur Verteilung der Software: Die Front wird Ihr vertrauenswürdiger Apache sein, der einfach alles an Ihren projektspezifischen lighttpd weiterleitet. Der lighttpd hat Zugriff auf das Dokumentenstammverzeichnis Ihres Projekts und leitet spezielle Anfragen an Ihren FCGI-Server weiter. Der FCGI-Server selbst kann auf einem anderen Rechner laufen, wenn dies für die Lastverteilung erforderlich ist. Er wird einen vorkompilierten Server verwenden, weil es in Python ein Threading-Problem gibt, und er kann Multiprozessor-Maschinen nutzen.
Ich werde nicht viel über die erste Ebene sprechen, weil Sie dies leicht selbst einrichten können. Leiten Sie einfach Dinge an die Maschine weiter, auf der Ihr lighttpd läuft (in meinem Fall läuft der Apache normalerweise auf anderen Maschinen als die Anwendungen). Schauen Sie in der mod_proxy-Dokumentation nach, normalerweise ist es nur ProxyPass und ProxyPassReverse.
Die zweite Ebene ist interessanter. lighttpd ist ein bisschen seltsam in der Konfiguration von FCGI-Dingen - Sie benötigen FCGI-Skripte im Dateisystem und müssen diese mit Ihrem FCGI-Serverprozess verbinden. Die FCGI-Skripte müssen tatsächlich keinen Inhalt enthalten - sie müssen nur im Dateisystem vorhanden sein.
Wir beginnen also mit Ihrem Django-Projektverzeichnis. Legen Sie einfach ein Verzeichnis public_html dort hinein. Das ist der Ort, an dem Sie Ihre Mediendateien ablegen, z.B. das Admin-Media-Verzeichnis. Dieses Verzeichnis wird das Dokumentenstammverzeichnis für Ihren Projekt-Server sein. Stellen Sie sicher, dass Sie nur Dateien dort ablegen, die keine privaten Daten enthalten - private Daten wie Konfigurationen und Module sollten besser an Orten bleiben, die nicht vom Webserver zugänglich sind. Als Nächstes richten Sie eine lighttpd-Konfigurationsdatei ein. Sie werden nur die Rewrite- und die FastCGI-Module verwenden. Keine Notwendigkeit, ein Zugriffsprotokoll zu führen, dies wird von Ihrer ersten Ebene, Ihrem Apache-Server, geschrieben. In meinem Fall befindet sich das Projekt in /home/gb/work/myproject - Sie müssen dies an Ihre eigene Situation anpassen. Speichern Sie den folgenden Inhalt als /home/gb/work/myproject/lighttpd.conf
server.modules = ( "mod_rewrite", "mod_fastcgi" )
server.document-root = "/home/gb/work/myproject/public_html"
server.indexfiles = ( "index.html", "index.htm" )
server.port = 8000
server.bind = "127.0.0.1"
server.errorlog = "/home/gb/work/myproject/error.log"
fastcgi.server = (
"/main.fcgi" => (
"main" => (
"socket" => "/home/gb/work/myproject/main.socket"
)
),
"/admin.fcgi" => (
"admin" => (
"socket" => "/home/gb/work/myproject/admin.socket"
)
)
)
url.rewrite = (
"^(/admin/.*)$" => "/admin.fcgi$1",
"^(/polls/.*)$" => "/main.fcgi$1"
)
mimetype.assign = (
".pdf" => "application/pdf",
".sig" => "application/pgp-signature",
".spl" => "application/futuresplash",
".class" => "application/octet-stream",
".ps" => "application/postscript",
".torrent" => "application/x-bittorrent",
".dvi" => "application/x-dvi",
".gz" => "application/x-gzip",
".pac" => "application/x-ns-proxy-autoconfig",
".swf" => "application/x-shockwave-flash",
".tar.gz" => "application/x-tgz",
".tgz" => "application/x-tgz",
".tar" => "application/x-tar",
".zip" => "application/zip",
".mp3" => "audio/mpeg",
".m3u" => "audio/x-mpegurl",
".wma" => "audio/x-ms-wma",
".wax" => "audio/x-ms-wax",
".ogg" => "audio/x-wav",
".wav" => "audio/x-wav",
".gif" => "image/gif",
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".png" => "image/png",
".xbm" => "image/x-xbitmap",
".xpm" => "image/x-xpixmap",
".xwd" => "image/x-xwindowdump",
".css" => "text/css",
".html" => "text/html",
".htm" => "text/html",
".js" => "text/javascript",
".asc" => "text/plain",
".c" => "text/plain",
".conf" => "text/plain",
".text" => "text/plain",
".txt" => "text/plain",
".dtd" => "text/xml",
".xml" => "text/xml",
".mpeg" => "video/mpeg",
".mpg" => "video/mpeg",
".mov" => "video/quicktime",
".qt" => "video/quicktime",
".avi" => "video/x-msvideo",
".asf" => "video/x-ms-asf",
".asx" => "video/x-ms-asf",
".wmv" => "video/x-ms-wmv"
)
Ich binde den lighttpd nur an die localhost-Schnittstelle, weil in meiner Testumgebung der lighttpd auf demselben Host wie der Apache-Server läuft. In Multi-Server-Einstellungen werden Sie den lighttpd-Servern natürlich an die öffentliche Schnittstelle binden. Die FCGI-Skripte kommunizieren in dieser Einstellung über Sockets, weil in dieser Testumgebung ich nur einen Server für alles verwende. Wenn Ihre Maschinen verteilt wären, würden Sie die "host" und "port" Einstellungen anstelle der "socket" Einstellung verwenden, um mit FCGI-Servern auf verschiedenen Maschinen zu verbinden. Und Sie würden mehrere Einträge für die "main" Sache hinzufügen, um die Last der Anwendung auf mehrere Maschinen zu verteilen. Schauen Sie in der lighttpd-Dokumentation nach, welche Optionen Sie haben werden.
Ich richte zwei FCGI-Server für dies ein - einen für die Admin-Einstellungen und einen für die Haupt-Einstellungen. Alle Anwendungen werden durch die Haupt-Einstellungen FCGI weitergeleitet und alle Admin-Anfragen werden an den Admin-Server geleitet. Dies geschieht mit den beiden Rewrite-Regeln - Sie müssen eine Rewrite-Regel für jede Anwendung hinzufügen, die Sie verwenden.
Da lighttpd die FCGI-Skripte benötigt, um sie zu existieren, um den PATH_INFO an das FastCGI weiterzugeben, müssen Sie die folgenden Dateien berühren: /home/gb/work/myprojectg/public_html/admin.fcgi ``/home/gb/work/myprojectg/public_html/main.fcgi
Sie müssen keinen Code enthalten, sie müssen nur im Verzeichnis aufgeführt sein. Ab lighttpd 1.3.16 (zum Zeitpunkt der Abfassung dieser Zeilen nur in svn) können Sie ohne die Stubs für die .fcgi laufen - Sie fügen einfach "check-local" => "disable" zu den beiden FCGI-Einstellungen hinzu. Dann sind die lokalen Dateien nicht mehr erforderlich. Wenn Sie also diese Konfigurationsdatei erweitern möchten, müssen Sie nur einige sehr grundlegende Regeln beachten:
- jede Einstellungsdatei benötigt ihren eigenen .fcgi-Handler
- jeder .fcgi muss im Dateisystem berührt werden - dies könnte in einer zukünftigen Version von lighttpd verschwinden, aber für den Moment ist es erforderlich
- die Lastverteilung erfolgt auf Ebene der .fcgi - fügen Sie mehrere Server oder Sockets hinzu, um die Last auf mehrere FCGI-Server zu verteilen
- jede Anwendung benötigt eine Rewrite-Regel, die die Anwendung mit dem .fcgi-Handler verbindet
Jetzt müssen wir die FCGI-Server starten. Das ist eigentlich ganz einfach, verwenden Sie einfach das bereitgestellte django-fcgi.py-Skript wie folgt:
django-fcgi.py --settings=myproject.work.main
--socket=/home/gb/work/myproject/main.socket
--minspare=5 --maxspare=10 --maxchildren=100
--daemon
django-fcgi.py --settings=myproject.work.admin
--socket=/home/gb/work/myproject/admin.socket
--maxspare=2 --daemon
Diese beiden Befehle starten zwei FCGI-Serverprozesse, die die angegebenen Sockets zur Kommunikation verwenden. Der Admin-Server wird nur zwei Prozesse verwenden - dies liegt daran, dass der Admin-Server oft nicht der Server mit den vielen Hits ist, das ist der Hauptserver. Daher erhält der Hauptserver eine höhere als Standard-Einstellung für Reserveprozesse und maximale Kindprozesse. Natürlich ist dies nur ein Beispiel - passen Sie es an Ihre Bedürfnisse an.
Der letzte Schritt ist das Starten Ihres lighttpd mit Ihrer Konfigurationsdatei: lighttpd -f /home/gb/work/myproject/lighttpd.conf
Das war's. Wenn Sie jetzt entweder den lighttpd direkt unter http://localhost:8000/polls/ oder durch Ihren Front-Apache zugreifen, sollten Sie die Ausgabe Ihrer Anwendung sehen. Zumindest, wenn alles richtig gelaufen ist und ich nicht zu viele Fehler gemacht habe.