Zope Hosting und Performance

Der Schockwellenreiter hat Probleme mit seinem Zope-Server. Da ich nun schon seit einigen Jahren professionell (in der Firma) Zope-Hosting mache und dabei auch ein paar ziemlich heftige Portale laufen habe (zwischen 2000 und 3000 Hits pro Minute sind da nicht selten - allerdings verteilt auf viele Systeme), hier mal ein paar Tipps von mir zur Skalierung von Zope.

  • Der wichtigste Schritt, den ich jedem empfehlen würde, ist entschlacken. Werft aus dem Zope all das raus was nicht rein muss - was statisch erstellt werden kann, was sich nur selten ändert, wo kein Content-Management nötig ist: raus damit. Packt es in normale Apache-Verzeichnisse. Nehmt Apaches mod_rewrite und sorgt dafür das die alten Adressen weiter funktionieren, aber aus dem Apache kommen. Das betrifft vor allem all die kleinen Scheisserlein wie Layoutgrafiken und so - die brauchen nicht aus dem Zope zu kommen, die werden besser aus dem Apache geliefert.
  • Benutzt das Zope-Caching, wenn irgend möglich. Irgend möglich heisst: genug Speicher auf dem Server, damit auch sich fettfressende Prozesse mal etwas Luft haben. Generell führt das Zope-eigene Caching dazu, das Prozesse immer fetter werden - das Aufräumen im eigenen Cache ist recht unbrauchbar. Also setzt eine Prozessüberwachung ein, die einen Zope-Prozess abschiesst und neu startet wenn er zu viel Speicher braucht. Ja, das ist wirklich sinnvoll und notwendig.
  • Gute Cachingmöglichkeiten gibt es im Zope zwei: den RAMCacheManager und den HTTPCacheManager. Ersterer speichert Ergebnisse von Zope-Objekten im Hauptspeicher und kann daher einzelne Seitenkomponenten cachen - packt das da rein, was komplex zu ermitteln ist.Der zweite (HTTPCache) arbeitet mit einem Squid zusammen. Packt einen Squid als HTTP Accelerator vor euren Zope und konfiguriert den HTTP Cache Manager entsprechend, so das Zope die passenden Expire-Header erzeugt. Dann wird ein Grossteil eures Traffics aus dem Squid betrieben. Der ist schneller als euer Zope. Alternativ zum Squid kann man auch einen Apache als HTTP Accelerator mit lokalem Cache konfigurieren - ideal für die, die keinen Squid installieren können oder wollen, aber durchaus Möglichkeiten zur weitergehenden Konfiguration eines Apache haben.
  • Grosse Zope-Objekte (und das meine ich wirklich im Sinne von KB) machen Zope tot. Beim Caching zerfetzen sie einem die schönste Cache-Strategie und Zope selber wird arschlahm wenn die Objekte zu gross werden. Der Grund liegt in der Zope-Architektur: alle Objekte werden erst mühsam in mehreren Schichten durch mehrere Softwarelayer zusammengedengelt. Im Speicher - und belegen daher im Speicher auch entsprechend Platz. Raus mit den komplexen Objekten mit riesigen KB-Zahlen. Macht sie kleiner. Erstellt sie statisch per Cronjob. Liefert sie aus dem Apache aus - es gibt nix blöderes als seine grossen PDFs alle im Zope in der ZODB abzulegen, oder gar dynamisch dort zu generieren.
  • Installiert ZEO. Das Teil rockt. Im Prinzip ist es nur die ZODB mit einem primitiven Serverprotokoll. Wichtig daran: euer Zope kann auf mehrere Prozessgruppen aufgeteilt werden. Das wollt ihr wenn ihr per Prozessüberwachung einen amoklaufenden Zopeprozess abschiessen wollt, aber das Portal von aussen möglichst unbeschädigt aussehen soll - in dem Fall packt ihr auf den Apache einfach mod_backhand drauf, oder eine andere Balancer-Technik zwischen Apache und Zope. Zusätzlich macht der ZEO auch das Packen der ZODB (sollte täglich laufen) einfacher, da der Pack im Hintergrund auf dem ZEO läuft und die Zope-Server selber nicht gross beeinträchtigt werden.
  • Wenn ihr habt, nehmt einen SMP-Server. Oder kauft euch einen. Wirklich - das bringt ne Menge. Voraussetzung ist allerdings die oben angesprochene Technik mit den mehreren Prozessgruppen - Python hat ein globales Interpreterlock, welches dazu führt das auch auf einer Mehrprozessor-Maschine eh nie mehr als ein Python-Thread gleichzeitig läuft. Von daher wollt ihr mehrere Prozessgruppen.
  • Performance gewinnt man auch durch Ausschaltung von Schichten. Leider ist das oft nur mit Softwareänderungen realisierbar, daher eher für Selberstricker interessant. Packt komplexe Abläufe raus aus dem Zope-Server und packt sie in Zope Products. Zope Products laufen native ohne Beschränkungen im Python Interpreter. Zope Python Scripte und DTML Dokumente werden hingegen durch viele Layer geschleppt die dafür sorgen das ihr euch an die Zugriffsrechte von Zope haltet, nichts böses tut und auch sonst ganz lieb seid. Und sorgen dafür das ihr langsamer werdet. Products lohnen sich - kosten aber Arbeit und sind entgegen der anderen mehr technischen Tipps nicht immer umsetzbar.
  • Zusätzlich hat es sich bewährt nicht zu viel Daten in die ZODB zu packen, vor allem nichts was diese erweitert - die ZODB wird nämlich nur grösser, kleiner wird sie nur beim Packen. Nach einiger Zeit hat man dann locker eine ZODB im GB-Bereich und braucht sich über langsame Serverstarts nicht wundern ...
  • Wenn komplexere Abläufe im System vorkommen kann es Sinn machen die ganz auszulagern. Bei mir kommt dann immer das |TooFPy| zum Einsatz. Einfach die ganzen komplexeren Sachen in ein Tool umwandeln und dort reinhängen - der Code läuft ungebremst schnell ab. Vom Zope dann einfach mit einem SOAP Client oder XMLRPC Client auf den Toolserver zugreifen und die Funktionen dort ausführen. Ja, die mehrfache XML Wandlung ist tatsächlich weniger kritisch als der Ablauf von komplexerem Code im Zope - vor allem wenn dieser Code einiges an Laufzeit beansprucht. Zope blockiert dann nämlich einen seiner Listener - die Anzahl ist statisch. Und einfach hochdrücken bringt nix - dank des globalen Interpreterlocks würden dann nur mehr Prozesse aufeinander warten, das dieses Lock freigegeben ist (z.B. bei jeder C-Erweiterung die zum Einsatz kommt). Für die XMLRPC Kommunikation gibt es eine gute und schnelle C-Implementierung die in Python integriert werden kann, damit ist das XML-Overhead-Problem irrelevant.
  • Wenn ihr PostgreSQL als Datenbank benutzt: nehmt PsycoPG als Datenbanktreiber. Das Session-Pooling bringt Zope erst richtig auf Touren. Generell solltet ihr schauen ob der entsprechende Datenbanktreiber irgendeine Form von Session Pooling unterstützt - notfalls über einen externen SQL Proxy. Denn sonst hängt in Zope bei SQL-Queries unter Umständen das ganze System, weil eine fette Query auf das Ergebnis wartet. Da ist schon mancher drauf reingefallen und hat lernen müssen das 16 Zope-Threads nicht gleichbedeutend mit 16 parallel bearbeiteten Zope-Zugriffen bedeuten muss, wenn SQL-Datenbanken im Spiel sind.

Es gibt natürlich noch eine ganze Menge mehr Sachen die man machen kann, aber die obigen sind weitestgehend aus dem Stand zu bewältigen und hauptsächlich davon abhängig das ihr genug Speicher im Server habt (und evtl. eine Mehrprozessormaschine - geht aber auch ohne). Wichtig ist der Speicher - je mehr desto besser. Wenn geht, steckt halt noch mehr Speicher rein. Man kann nicht zu viel Speicher haben ...

Was machen wenn das alles auch noch nicht reicht (ja, ich hatte das schon - manchmal hilft nur die ganz grobe Axt). Nun, in dem Fall gibt es Abwandungen der obigen Techniken. Meine Lieblingstechnik in dem Bereich ist aktives Caching. Damit meine ich, das im Zope an einer Stelle hinterlegt wird welche Dokumente aktiv gecached werden sollen. Dazu gehört dann auf der Kiste ein Script, das die Seiten vom Zope abruft und in ein Verzeichnis packt. Per Apache Rewrite Rules wird dann dafür gesorgt das von aussen die statischen Inhalte ausgeliefert wird. Im Prinzip sorgt man also dafür das die Seiten die am häufigsten besucht werden und die für diese Technik geeignet sind (also z.B. keine Personalisierungsdaten enthalten) einfach eine statische Seite rausgeht, egal was sonst passiert - die normalen Cachetechniken gehen da einfach nicht brutal genug vor, da geht noch zu viel Traffic durch auf den Server.

Ein weiterer Schritt ist natürlich der Einsatz weiterer Maschinen - einfach weitere Kisten daneben stellen und mit der ZEO-Technik miteinander verbinden.

Zope ist eine fantastische Software - gerade die hohe Integration von Entwicklungsumgebung, CMS und Server ist oft ungemein praktisch und die leichte Integrierbarkeit in externe Datenquellen ist ebenfalls sehr nett. Aber Zope ist ein Ressourcenschwein, das muss man so einfach mal sagen.

tags: Sysadmin, Texte

Lutz Jan. 19, 2005, 10:13 a.m.

Hi,
interessante Tipps, allerdings läuft doch das meiste darauf raus, dass man Zope nach möglichkeit vom reinen Visitortraffic abschottet und lediglich zur Contentverwaltung einsetzt.
Wir setzen auch PsycoPG ein. Wie bringt man den zum Sessionpooling ? Macht er das automatisch ?
Gruß
Lutz





hugo Jan. 19, 2005, 10:46 a.m.

PsycoPG macht automatisch Session Pooling. Das ist der wesentliche Vorteil von PsycoPG gegenüber anderen (Popy und PygreSQL) PostgreSQL Treibern für Python.

Und ja, der Visitortraffic ist das Problem bei Websites - wenn man kann, kann man natürlich das Zope nur noch für CMS benutzen. Ich kenne durchaus Systeme in denen der Zope-Server komplett intern läuft und der gesamte Content dann per Mirrorscript davon abgezogen wird und nur noch statisch gehostet ist. wget -m ist da manchmal schon ein brauchbarer Anfang um sowas zu machen.

Ich selber halte nicht soooo viel von rein statisch ausgelieferten Systemen mit festen Mirrorzeitpunkten - ich finde das man durchaus die Dynamik erhalten kann. Deshalb auch ganz oben die Geschichten mit den Proxies. Wenn man seine Seiten so halten kann das möglichst jeder Besucher die gleiche Seite gleich sieht - also sich unnötige Personalisierungen verkneift und zum Beispiel auch so unnütze Sachen wie die aktuelle Uhrzeit auf den Webseiten anzuzeigen (Uhren haben die User selber, die brauchen die nicht von der Website und das Veröffentlichungsdatum und die Zeit steht eh an Beiträgen mit dran) - dann kann man mit einem HTTP Accelerator vor dem Zope das beste aus beiden Welten haben.

Paulo Eduardo Neves Jan. 19, 2005, 7:42 p.m.

I've read this article through google translation, it is great, but I missed somethings. Maybe I'm asking too much, but would someone post an english translation?

Joachim Werner Jan. 19, 2005, 7:46 p.m.

Was mich in dem Zusammenhang einmal interessieren würde: Ist Zope nun besonders resourcenintensiv oder verleitet es nur zu einem recht verschwenderischen Umgang mit dynamischen Seitenelementen? Will meinen: Wenn ich nun genau die selbe Funktionalität mit PHP, mod_perl oder Java implementiere, habe ich dann die gleichen Probleme?

Mein Eindruck ist bisher, dass Zope (zumindest ohne Plone) recht effizient programmiert ist. Aber es ist halt damit auch kinderleicht, eine Methode zu schreiben, die 200 Objekte zu je ein paar Megabyte "aufweckt", nur um eine Übersichtsseite zu generieren.

hugo Jan. 19, 2005, 7:55 p.m.

Sorry für die Verwirrung in der Kommentarkiste. Ich hab OSA bis auf weiteres wieder ausgeschaltet, es verwirrt die Leute doch zu sehr :-/

Also es sind tatsächlich zwei Sachen. Zum Einen sind es natürlich durchaus die Programmierer die Mist bauen - leider viel zu oft und viel zu gern. Der ganze Bereich in dem zum Beispiel zu komplexer Code in DTML statt in Python Scripts oder Products geschrieben wird kann man den Programmierern anlasten.

Aber es ist durchaus ein Problem der Zope Architektur selber beteiligt. Bestimmte Sachen - wie zum Beispiel das global Lock im Python Interpreter, die statische Listener-Konfiguration, das Problem mit den Sicherheitslayern, die In-Memory-Konstruktion von Objekten und noch einiges andere ist durchaus ein Problem der Plattform. Zum Einen Python (das globale Lock) und der Rest Zope.

Andererseits hat Zope einfach den unschlagbaren Vorteil, das es durch die integrierte Umgebung super einfach ist damit loszuhacken. Und man hat alles an einem Platz - klar, die Programmierumgebung eines Webformulars mag nicht jeder, aber es ist durchaus praktisch - man kann jederzeit und von überall mal eben schnell was am System ändern.

Bengt Giger Sept. 19, 2005, 1:53 p.m.

Erfahrungen unter ZEO (9 ZEO-Knoten):

Ich habe auf jedem SMP-ZEO-Knotensystem mehrere Zope-Prozesse laufen. Vorgelagert zu Zope ist ein Apache fürs Caching und URL-Rewriting, nochmal davor ein Load-Balancer. Das System beherrbergt mehrere Hundert virtuelle Hosts.

1. Stickyness: der Loadbalancer sorgt mit einem Cookie dafür, dass Folgerequests eines Webbesucher auf dem gleichen ZEO-Knoten landen. Die Objekte für diese Site sind somit beim 2. Request schon geladen.

2. Die Zope-Prozesse sind in Gruppen gegliedert, und virtuelle Hosts diesen Gruppen zugeordnet. Apache ist nun so konfiguriert, dass Requests für gewisse virtuelle Hosts immer in den gleichen Zope-Prozessen landen.

3. Autoren werden auf eigene Prozesse geführt. Unser CMS (Silva) verwendet die Methode 'edit', also lenkt apache alles, was mit '/edit' endet, auf spezielle Prozesse. Autoren sind somit vom normalen Betrieb weitgehend getrennt (alle greifen aber auf die gleiche ZODB zu).

Der Grund hinter allen 3 Massnahmen: Cache-Trashing reduzieren. Um alle Objekte dauernd im RAM halten zu können, müsste ich Maschinen mit min. 5 GB einsetzen. Zwar gibt es die heutzutage, aber das Suchen im RAM-Cache ist auch nicht kostenlos.