Howto: bottle + Python3.1 + uWSGI + Cherokee? - Probleme

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
uKev
User
Beiträge: 15
Registriert: Mittwoch 9. Dezember 2009, 13:43

Problemstellung

Bei meiner Suche nach einer brauchbaren und effizienten Lösung um mit Python 3 Webapplikationen zu entwickeln, bin ich bei der Kombination aus Bottle, uWSGI und Cherokee hängen geblieben.
Nach einiger Recherche bin ich auch relativ weit gekommen und habe einige Fallstricke lösen können, die ich anderen gerne hiermit ersparen möchte. Allerdings scheitert es im Moment sozusagen auf der Zielgerade und da hoffe ich nun auf eure Hilfe. Ich vermute das Problem in der Art und Weise, wie in bottle WSGI implementiert ist.

Voraussetzungen

Meine Ausgangsbasis ist eine Minimalinstallation von Ubuntu Karmic Server in VirtualBox.

Vorgehensweise

1.
Die Cherokee Version, die bei Ubuntu 9.10 (Karmic) standardmäßig dabei ist, hat leider noch keine Unterstützung für uWSGI, deswegen empfiehlt es sich die Version aus den PPA-Quellen zu nehmen. Für die spätere Kompillierung von uwsgi wird das python3-dev Paket benötigt:

Code: Alles auswählen

sudo apt-get install python-software-properties
sudo add-apt-repository ppa:cherokee-webserver/ppa
sudo apt-get update
sudo apt-get install python3-dev cherokee
2.
uWSGI ist noch nicht als Paket für Ubuntu verfügbar, deswegen muss der Quellcode heruntergeladen und manuell kompilliert werden.

Code: Alles auswählen

mkdir /usr/src/uwsgi
cd /usr/src/uwsgi
wget http://projects.unbit.it/downloads/uwsgi-0.9.3.tar.gz
tar -xzvf uwsgi-0.9.3.tar.gz 
cd uwsgi-0.9.3
3.
Leider ist im Quellarchiv der Version 0.9.3 noch kein Makefile für Python 3.1 unter Linux enthalten (Nur für Python 2.X, die Python3.X Makefiles mit UNBIT im Namen sind für normale Linux-Systeme nicht brauchbar).
Der uWSGI Entwickler Roberto De Ioris war so nett und hat mir aber eine funktionierendes Makefile zur Verfügung gestellt.

Die Datei /usr/src/uwsgi/uwsgi-0.9.3/Makefile.Linux.Py31 muss mit folgendem Inhalt erstellt werden:

Code: Alles auswählen

CC=gcc

PYTHON_CFLAGS=`python3.1-config --cflags`
PYTHON_LIBS=`python3.1-config --libs`
XML_CFLAGS=`xml2-config --cflags`
XML_LIBS=`xml2-config --libs`

CFLAGS=$(PYTHON_CFLAGS) $(XML_CFLAGS) -DPYTHREE
LD_FLAGS=$(PYTHON_LIBS) $(XML_LIBS) -DPYTHREE

PROGRAM=uwsgi31

all:	clean uwsgi

uwsgi:	utils.o socket.o pymodule.o main.o
	$(CC) $(LD_FLAGS) utils.o socket.o pymodule.o main.o -o $(PROGRAM)

utils.o: utils.c
	$(CC) -c $(CFLAGS) utils.c

socket.o: socket.c
	$(CC) -c $(CFLAGS) socket.c

pymodule.o: uwsgi_pymodule.c
	$(CC) -c $(CFLAGS) -o pymodule.o uwsgi_pymodule.c

main.o:	uwsgi.c
	$(CC) -c $(CFLAGS) -o main.o uwsgi.c
	
clean:
	rm -f utils.o socket.o pymodule.o main.o
4.
Danach geht es mit der eigentlichen Kompillierung weiter.

Code: Alles auswählen

make -f Makefile.Linux.Py31
5.
Außerdem muss sich die erzeugte uwsgi Datei im PATH befinden, damit sie von Cherokee gefunden werden kann. Dazu kann man sie einfach in /usr/bin/ linken:

Code: Alles auswählen

ln -s /usr/src/uwsgi/uwsgi-0.9.3/uwsgi31 /usr/bin/uwsgi
6.
Damit Bottle und uWSGI zusammen spielen, müssen diese erst konfiguriert werden. Dazu erzeugt man eine Datei /home/kev/bottle_site/bottle_uwsgi.conf nach folgendem Format:

Code: Alles auswählen

<uwsgi>

    <pythonpath>/home/kev/bottle_site/</pythonpath>

    <app mountpoint="/">

        <script>app</script>

    </app>
</uwsgi>
/home/kev/bottle_site/ muss durch den Hauptpfad der eigenen Bottle-Anwendung ersetzt werden. app muss durch den Namen der Hauptdatei der Anwendung - ohne die Dateiendung .py - ersetzt werden.
In meinem Fall gibt es ein Verzeichnis /home/kev/bottle_site/ in der die Original bottle.py Datei liegt und meine eigene Anwendung app.py.
In app.py wird z.B. import bottle aufgerufen.

7.
Nun geht es an die Konfiguration von Cherokee, die am einfachsten über das eingebaute Webinterface erfolgt. Dazu startet man cherokee-admin:

Code: Alles auswählen

cherokee-admin
Für die Konfiguration startet man einen Webbrowser und öffnet die von cherokee-admin ausgegebene Adresse. Im Standardfall ist das die eigene IP auf Port 9090. Benutzer ist "admin" und das Passwort wird ebenfalls von cherokee-admin ausgegeben und ist bei jedem Aufruf von cherokee-admin neu generiert.

8.
In der Konfiguration hangelt man sich nun durch folgende Dialoge durch:
Viertueller Server -> default -> Verhalten -> Wizard -> uWSGI Run Wizard
Als Konfigurationsdatei gibt man hier nun die vorhin erstellte wsgi conf Datei an. In meinem Fall ist das: /home/kev/bottle_site/bottle_uwsgi.conf.

Soweit, so gut.

9.
Damit bottle richtig auf die Anfragen von uWSGI reagieren kann, muss man sein Hauptmodul (bei mir die app.py) anpassen.

Laut http://projects.unbit.it/uwsgi/wiki/Example muss dazu eine Referenz namens "application" auf einen WSGIHandler erzeugt werden.

Das habe ich wie folgt gemacht und mich dabei an http://bottle.paws.de/page/docs#apache-mod_wsgi orientiert:

Code: Alles auswählen

import os
os.chdir(os.path.dirname(__file__))
#ganz am Anfang der Datei einfügen.

import bottle
# sollte sowieso schon in der bottle-Anwendung enthalten sein

# hier sind die ganzen @route Dekoratoren und alle eigenen Anweisungen.

# ganz am Ende der Datei darf run() NICHT aufgerufen werden. Stattdessen muss dort stehen:

# falls man eine neuere bottle-Version benutzt, Ich benutze direkt die von github:
application = bottle.app() 

#falls man eine ältere bottle-Version benutzt und app() noch default_app() hieß:
application = bottle.default_app()
Damit sollte es eigentlich funktionieren. Aber.

Problem

Cherokee scheint die Anfragen korrekt an uWSGI weiter zu reichen.
uWSGI kann auch die Applikation aufrufen und initialisieren.
Allerdings kommt nichts brauchbares an den Browser zurück, es wird schlicht eine leere Seite ohne Inhalt angezeigt. Auch der Quellcode ist leer.

Zu Testzwecken kann man wsgi manuell starten. Die korrekten Parameter entnimmt man am Besten aus cherokee-admin -> information sources -> Nick: uWSGI X -> Interpreter.
Das X steht für irgendeine Ganzzahl.

Danach stellt man sicher, dass keine uwsgi-Prozesse laufen (ps -A|grep wsgi). Falls doch, killt man diese.
Danach startet man den Prozess von Hand im Terminal. In Meinem Fall ist das:

Code: Alles auswählen

$ /usr/bin/uwsgi -s 127.0.0.1:43700 -t 10 -M -p 1 -C  -x /home/kev/bottle_site/bottle_uwsgi.conf
*** Starting uWSGI on [Wed Dec 30 13:57:07 2009] ***
your process address space limit is -1 bytes (0 MB)
binding on TCP port: 43700
parsing config file /home/kev/bottle_site/bottle_uwsgi.conf
added /home/kev/bottle_site/ to pythonpath.
interpreter for app 0 initialized.
application 0 (/) ready
setting default application to 0
config file parsed.
request/response buffer (4096 bytes) allocated.
spawned uWSGI master process (pid: 1467)
spawned uWSGI worker 1 (pid: 1468)
[pid: 1468|app: 0|req: 1/1] MEINE_IP () {58 vars in 1005 bytes} [Wed Dec 30 13:57:11 2009] GET / => 
generated 0 bytes in 144 msecs (HTTP/1.1 200) 0 headers in 19 bytes
Wie man an der letzten Zeite sieht (generated 0 bytes in 144 msecs), ist die Seite wirklich leer.

Ab hier komme ich nicht mehr weiter. Die Applikation selbst funktioniert (über run() ohne uwsgi).

Für uWSGI und Python3 gibt es noch die Seite http://projects.unbit.it/uwsgi/wiki/RunOnPython3k mit Sachen, die man beachten soll. Allerdings betrifft das nur den Charset. Ich nehme doch an, dass ich zumindest kaputten Inhalt bekommen sollte, wenn ich das nicht beachte. In meinem Fall ist es aber gar keiner und ich weiß nicht woran das liegt, da ich mich mit WSGI usw. nicht benügend auskenne.

Ich vermute, dass die Methode __call__ in der Klasse Bottle im Modul bottle.py für die ganzen WSGI Requests letzten endes zuständig ist. Die Methodensignatur deckt sich auch mit der RunOnPython3k Seite von uwsgi.
Aber es kommt anscheinend nichts zurück.

Bin für jeden Tipp sehr dankbar und hoffe mit meiner Beschreibung jedem über die ersten Hürden helfen zu können, der Ähnliches vor hat.

Hier noch einmal zusammenfassend eine Liste mit Quellen/Seiten für weiterführende Informationen mit deren Hilfe ich diese Informationen zusammen getragen habe:
http://projects.unbit.it/uwsgi/wiki
http://projects.unbit.it/uwsgi/wiki/Install
http://projects.unbit.it/uwsgi/wiki/Doc
http://projects.unbit.it/uwsgi/wiki/Example
http://projects.unbit.it/uwsgi/wiki/RunOnPython3k
http://projects.unbit.it/uwsgi/wiki/RunOnCherokee
http://www.cherokee-project.com/doc/cookbook_uwsgi.html
http://bottle.paws.de/page/2009-12-02_release_notes_0.7
http://bottle.paws.de/page/docs#apache-mod_wsgi
http://bottle.paws.de/page/docs#how-default_app-works
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

Woah das ist mal schön ausformuliert; dennoch wird die Antwort kurz ausfallen. WSGI in Python3.x ist derzeit ein Witz und soweit ich weiß gibt es in Bottle auch keine Unterstützung dafür, aber defnull hier im Forum weiß sicherlich mehr.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Der Autor von modwsgi hat sich auch schon darüber aufgeregt, dass die WSGI-Community es nicht in den letzten Jahren geschafft hat, den Standard an Python 3.x anzupassen und einfach was eigenes gemacht. Vielleicht ist das kompatibel mit uwsgi oder nicht, keine Ahnung. Beides jedoch sind Indizien, dass es da ziemlich übel aussieht und ich allgemein nicht empfehlen würde, diesen Weg zu gehen.

Vor wenigen Wochen hätte ich zudem gesagt, die Zukunft liegt eher in asynchronen Webservern wie ihn Tornadoweb z.B. hat, aber da ist seit dem nix mehr passiert, sodass ich mir nicht sicher bin, in wie weit Python und speziell Python 3.x da überhaupt eine Rolle spielt.

Zur Zeit beobachte ich daher lieber nodejs.org, ein Server für JavaScript. Und vielleicht kommt auch wieder die Zeit von Java, das mit Servlet 3.0 (als Teil von Java EE 6) auch asynchrone Requests erlaubt.

Für Python würde ich lieber 2.x empfehlen, die Unterschiede sind auch nicht so groß, dass sich Python 3.x wirklich lohnen würde. Ich glaube inzwischen, es war ein Fehler, dass Python 3.x versucht hat, so nah wie möglich an Python 2.x zu bleiben. Größere Änderungen hätten vielleicht "Killer Features" erlaubt, wegen denen man schließlich schneller gewechselt hätte als nun, wo es nahezu egal ist.

Ruby hat es da vielleicht einfacher: Die Version 1.9 ist mehr als doppelt so schnell wie 1.8 und das ist schon ein überzeugendes Argument :) Man hätte CPython 2.x einfach langsamer machen müssen...

Stefan
uKev
User
Beiträge: 15
Registriert: Mittwoch 9. Dezember 2009, 13:43

Ja, ich weiß, dass es mit wsgi in Python 3 sehr übel aussieht und einem jeder davon abrät und zu Python 2 rät. Aber meine Hauptintention liegt ja genau darin, das zu ändern und praktikable Wege aufzuzeigen, mit Python 3 entwickeln zu können. Und uwsgi verspricht ja, dass es mit Python 3 funktioniert und falls wsgi endlich für Python 3 standardisiert wird, den Umstieg auf die standardkonforme Art so schmerzfrei, wie möglich, zu machen.
Siehe dazu auch http://projects.unbit.it/uwsgi/wiki/RunOnPython3k.
sma hat geschrieben: Vor wenigen Wochen hätte ich zudem gesagt, die Zukunft liegt eher in asynchronen Webservern wie ihn Tornadoweb z.B. hat.
Tornado hatte ich mir auch angesehen und befand sich ebenfalls auf meiner Liste der möglichen Wege. Allerdings landet vor Tornado am Ende laut Empfehlung (http://www.tornadoweb.org/documentation ... production) wieder ein anderer Webserver als Loadbalancer. Und genau das kann auch Cherokee. In meinem Setup würde Tornade also eher bottle ersetzen oder als zusätzliche Schicht zwischen Bottle und Cherokee kommen und man würde z.B. per Reverse HTTP Proxy von Cherokee auf Tornado zugreifen. Davon abgesehen kann Tornado noch kein Python 3.
Die Reverse HTTP Proxy geschichte geht mit dem Standard-Bottle-Webserver übrigens auch. So sieht nämlich im Augenblick mein Setup aus, so lange uWSGI noch nicht läuft und es funktioniert bisher sehr gut. Wie das performance-technisch aussieht, weiß ich noch nicht.
sma hat geschrieben: Für Python würde ich lieber 2.x empfehlen, die Unterschiede sind auch nicht so groß, dass sich Python 3.x wirklich lohnen würde.
Klar, würde ich auch, wenn ich jetzt ein produktiv funktionierendes System benötigen würde.
Aber es ist nun mal so, dass Python 3 der Nachfolger von Python 2 und somit die Zukunft darstellt. Ich sehe das ganze hier auch eher als ein "Forschungsprojekt" um herauszufinden, was möglich ist, was funktioniert und was vielleicht am besten funktioniert.
Und hey, mittlerweile bin ich durch die Reverse-Proxy Inspiration von Tornado immerhin soweit, dass ich ein funktionierendes Setup habe und es jetzt um (Performance)-Optimierung geht.
uWSGI wäre die eine Möglichkeit, Fapws3 als Bottle-Webserver die Andere. (http://bottle.paws.de/page/2009-12-19_C ... erformance)
Allerdings ist Fapws3 nicht mit Python3 getestet und wird wahrscheinlich auch nicht funktionieren.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Dein Ziel ehrt dich, doch den beschriebenen Weg inklusive Software selbst zu kompilieren nachdem man ein eigenes Makefile erstellt hat finde ich NICHT einfach.

Einfach Tornado zu benutzen, ist da deutlich einfacher. Ich betrachte dies dabei weniger als ein Webrahmenwerk (mit Django-ähnlichem Template-System und einer simplen mysql-Anbindung) sondern als stand-alone-Server. Diesen sollte man für Produktivzwecke hinter nginx betreiben, doch für die Entwicklung (und auch für inhouse-Lösungen, so wie sie in der Regel mein Ziel sind) ist das unnötig.

Nachteil an Tornado ist, dass das Python 2.x und nicht 3.x ist. Das könnte man aber vielleicht ja portieren. Und dann kann (und muss) man sich selbst Gedanken machen, was wohl bytes und was wohl str-Objekte sein sollen. Die Änderungen, die 2to3 vorschlägt, sehen alle harmlos aus. Ob es danach läuft, weiß ich allerdings nicht. Es sind allerdings nur 5855 Zeilen, das überblickt man noch.

Ob Python 3.x wirklich der Nachfolger von Python 2.6 ist, stelle ich mal provokant in Frage. Ich glaube eher, das wird Python 2.7 sein. Aber nun haben wir ja 2010 und ich werde selbst mal versuchen, möglichst immer die 3.x-Version einzusetzen und vielleicht ändere ich dann ja meine Meinung ;)

Stefan
Antworten