| Author | Enrico Kern, phantom@h07.org |
| Version | 0.1, 05 April 2004 |
| URL | http://www.h07.org |
| Lang | DE |
Dieses Dokument beschreibt das Tunen und optimieren einer Apache 2.x
Installation unter Linux oder Unix-aehnlichen Distributionen und behandelt
auch die Optimierung f. dynamischen Content (PHP-Caching, MySQL Tuning).
Wenn moeglich sollte es vermieden werden binaere Apache-Packete einzuspielen (z.B ueber das Packet-System der Distribution).
Diese Packete sind meistens dazu ausgelegt auf der groesstmoeglichen Anzahl von Systemkonfigurationen zu laufen. Damit werden natuerlich keine Optmierungen wie die Unterstuetzung von erweiterten CPU-Befehlen Ihres Prozessors verwendet.
downloaden Sie die neueste Apache2-Version von httpd.apache.org und entpacken Sie das Archiv:
tar xzvf httpd-2.0.49.tar.gz
cd httpd-2.0.49
Die wichtigste Veraenderung am Apache2 ist das neue Thread-Modell, dieses
ermoeglicht es viele Anfragen mit wenigen Prozessen zu
behandeln. Standardmaessig benutzt Apache2 jedoch das aus der 1er Version
bekannte pre-fork Modell welches fuer jede Verbindung
einen neuen Prozess erstellt und somit natuerlich unmengen an Resourcen verbraucht.
Mit Apache2 gibt es sog. Multi-Processing Modules (MPM`s) wir werden uns in der Konfiguration fuer eines entscheiden muessen.
* MPM-Worker
Das Worker-Modul benutzt mehrere Kind-Prozesse wo jedes Kind viele Threads
erstellt. Jeder Thread behandelt eine Verbindung.
Dieses Modul ist vorallen fuer High-Traffic Server zu empfehlen weil es besser
mit dem zur verfuegung stehenden Speicher umgeht
als das prefork Modul.
* MPM-Prefork
Das Prefork-Modul benutzt mehrere Kind-Prozesse wo jeder "EINEN"
Thread hat. Jeder Prozess behandelt eine Verbindung, das heist
bei 100 Clients hat man 100 Prozesse mit 100 Threads. Auf vielen Systemen
ist dieses Modul schneller als der Worker
jedoch verbraucht es unmengen speicher was diesen Vorteil vernichtet. Dieses
Modul wird jedoch gebraucht wenn
nicht threadsichere Apache-Module oder Bibliotheken benutzt werden. Wenn man
es nicht wirklich umbedingt benoetigt sollte
man auf keinen Fall Prefork einsetzen.
fuer den worker uebergeben Sie dem Konfigurationsscript:
--with-mpm=worker
fuer das Prefork-Modull:
--with-mpm=prefork (NICHT EMPFOHLEN)
Unter Linux (x86) und Solaris (Sparc/x86) bringt die Option --enable-nonportable-atomics
auch noch einen kleinen schub.
Diese Option veranlasst Apache dazu bessere atomic operations (z.B integer
inkrementierung) durchzuführen. Auf SPARC
Plattformen ist das messbar, unter der normalen x86-Architektur faellt das
kam auf, da es lediglich alte 386er Instructions
und 486er Instructions gibt. Wobei 486er welche mit --enable-nonportable-atomics
aktiviert werden immer noch
schneller sind als der 386er standard wert.
Apache kann nun uebersetzt werden (nach ./configure mit den eigenen
und optimierungsparametern) was mit make && make install
geschieht. Fuer detalierte Informationen ueben den Uebersetzungs- Vorgang
sollten Sie sich http://httpd.apache.org/docs-2.0/
bedienen.
HostnameLookups sollte auf jeden Fall auf "off" stehen. Dies verhindert
das Apache fuer jede IP den Hostname aufloest,
was immens viel Zeit spart. Fuer statistiken kann mittlerweile jedes Stats-Programm
selber aufloesen.
HostnameLookups off
Wenn Symlinks nicht wirklich gebraucht werden (wozu es eigentlich auch kaum
Gruende gibt) sollte man auf keinem Fall "Options
FollowSymLinks" und/oder "SymLinksIfOwnerMatch"
auf Directory-Direktiven angeben. Ist so eine Option gesetzt veranlasst das
Apache fuer jede
Anfrage Dateisystem-Operationen auszufuehren was bei enorm vielen zugriffen
bei langsamen Festplatten immense Performance-
Einbussen bringt.
1.2.3 .htaccess Files und AllowOverride
Wenn bei Directory-Direktiven die Option "AllowOverride
all" angegeben wurde, prueft Apache bei jeder Anfrage ob eine
.htaccess Datei vorhanden ist, wenn nicht notwendig also abschalten.
Die direktive "DirectoryIndex" sollte auf keinen fall mit Wildcards bestueckt sein wie index.* index etc. sondern eine Liste aller index-Dateien enthalten die vorkommen koennen z.B "DirectoryIndex index.html index.cgi index.php" ansonsten prueft Apache jedesmal in einem komplizierten Syscall ob entsprechende Dateien vorhanden sind.
Unter umstaenden mapt Apache Dateien in den Speicher, z.B beim parsen von
ServerSide Includes. Auf einem Multiprozessor
System sollte das deaktiviert werden da Apache die Dateien so meist schneller
liefern kann.
Wird Apache in einer NFS-Umgebung betrieben sollte auf keinen Fall MemoryMapping
aktiviert sein da Apache sich unter
umstaenden aufhaengen kann wenn 2 Prozesse gleichzeitig auf eine Datei zugreifen.
1.2.7 Prozess-erstellung, Prozesskonfiguration
Das ist der wohl wichtigste Teil der Konfiguration und auch der dynamischste
von allen. Die Einstellungen des MPM-Workers
sind sehr Systembezogen und koennen nur richtig eingestellt werden wenn immens
getestet wird.
Hierzu ein einleitender Auszug aus der Apache 2 Dokumentation der bitte sorgfaeltig zu lesen ist.
"Ein einzelner Steuerprozess (der Elternprozess) ist
für den Start der Kindprozesse verantwortlich. Jeder Kindprozess erstellt
eine
feste Anzahl von Server-Threads, wie durch die ThreadsPerChild- Direktive
angegeben, sowie einen "Listener-Thread", der auf
Verbindungen wartet und diese an einen Server-Thread zur Bearbeitung weiterreicht,
sobald sie eintreffen.
Der Apache versucht immer, einen Vorrat von freien oder
unbeschäftigten Threads zu verwalten, die zur Bedienung
hereinkommender Anfragen bereit stehen. Auf diese Weise brauchen Clients nicht
auf die Erstellung eines neuen Threads oder Prozesses
zu warten, bevor ihre Anfrage bedient werden kann.
Die Anzahl der Prozesse, die anfangs gestartet wird, wird
mit der Direktive StartServers festgelegt. Dann, während des Betriebes,
berechnet der Apache die Gesamtzahl der unbeschäftigten Threads und forkt
oder beendet Prozesse, um diese Anzahl innerhalb der durch
MinSpareThreads und MaxSpareThreads angegebenen Grenzen zu halten. Da dieser
Prozess sehr selbstregulierend ist, ist es nur selten
notwendig, die Voreinstellung dieser Direktiven zu ändern.
Die maximale Anzahl Clients, die gleichzeitig bedient werden
kann (d.h. die maximale Gesamtzahl der Threads in allen Prozessen),
wird mit der Direktive MaxClients festgelegt. Die maximale Anzahl der aktiven
Kindprozesse ergibt sich aus MaxClients dividiert durch
ThreadsPerChild.
Zwei Direktiven legen harte Limits für die Anzahl
der aktiven Kindprozesse fest und können nur geändert werden, indem
der Server
komplett gestoppt und dann wieder neu gestartet wird. ServerLimit stellt die
obere Grenze für die Anzahl der aktiven Kindprozesse dar
und muss größer oder gleich dem Quotienten aus MaxClients und ThreadsPerChild
sein. ThreadLimit ist die obere Grenze für die
Anzahl der Server-Threads und muss größer oder gleich ThreadsPerChild
sein. Sofern für diese Direktiven keine
Voreinstellungen verwendet werden, sollten sie vor allen anderen worker-Direktiven
platziert werden."
klingt etwas unverstaendlich wird mit dem herumexperimentieren jedoch einfacher
zu verstehen.
Kleines Beispiel:
ThreadLimit 60
ServerLimit 30
StartServers 2
MaxClients 1800
MinSpareThreads 30
MaxSpareThreads 60
ThreadsPerChild 60
diese Konfiguration kann 1800 Clients gleichzeitig bedienen.
2 Apache-Instanzen werden beim hochfahren gestartet wovon
jeder mindestens 30 und maximal 60 Kinder haben kann. Jeder Kindprozess kann
maximal 60 Threads erstellen, wobei jeder
Thread 60 Clients verwaltet.
Rechnung dazu: 30*60=1800
Teilen Sie Also soviele Threads und Childs ein wie Sie benoetigen, dazu
mus spaeter bei groesseren Clients auch das ServerLimit
erhoeht werden. Dieses gibt an wieviele Apache-Server maximal gestartet werden
koennen (hier 30).
Apache bringt bereits ein simples Benchmark-Tool mit welches vollkommen ausreicht
um kleine Pruefungen vorzunehmen.
Das Apache-Benchmark genannte Programme finden Sie im bin Verzeichnis des
Apache-Prefixes z.B
/usr/local/apache2/bin/ab
Um z.B 1000 gleichzeitige Clients zu simulieren die jeweils 100 requests
abschicken (gesamt: 100.000 requests) tippen wir
/usr/local/apache2/bin/ab -n 100 -c 1000 hostname/document.html
ein.
Der Benchmark auf einen Server mit der in 1.2.7 erwaehnten
MPM-Beispiel Konfiguration auf eine php-datei die phpinfo();
aufruft sieht folgendermassen aus:
Server Software: Apache/2.0.49 Server Hostname: beispielhost.de Server Port: 80 Document Path: /info.php Document Length: 41604 bytes Concurrency Level: 1000 Time taken for tests: 1.614566 seconds Complete requests: 100 Failed requests: 47 (Connect: 0, Length: 47, Exceptions: 0) Write errors: 0 Total transferred: 7372639 bytes HTML transferred: 7317892 bytes Requests per second: 61.94 [#/sec] (mean) Time per request: 16145.660 [ms] (mean) Time per request: 16.146 [ms] (mean, across all concurrent requests) Transfer rate: 4458.78 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 230 237 5.1 238 245 Processing: 91 572 240.2 543 1331 Waiting: 59 455 168.0 466 803 Total: 332 810 239.1 781 1572
Seit Apache 2 als Stable verfuegbar ist fragt man sich warum PHP nicht
fuer diese Version empfohlen wird, bietet sie doch soviele
Vorteile.
Das PHP-Team raet davon ab PHP in Apache 2.0-Produktiv Umgebungen einzusetzen.
Das liegt am neuen Thread-Modell welches in Apache
uebernommen wurde und dazu dient effektiver viele Anfragen zu bearbeiten.
Die Zend-Core von PHP ist zwar threadsafe, aber viele Bibliotheken auf die PHP benutzt sind es nicht.
Lange Rede kurzer Sinn. Wenn man diese Zeilen liest faengt man nun wahrscheinlich
an nachzudenken ob PHP ueberhaupt Sinnvoll ist
unter Apache 2, ich sag ja. Auch wenn es nicht von den Entwicklern empfohlen
wird so laeuft PHP mit eigentlich allen Features mittlerweile Reibunglos
unter Apache 2. Es lohnt auf keinen Fall nur wegen PHP auf eine Apache 1.3
Umgebung umzuschalten.
Caching Technologien auf dem eigenen Server können eine Menge ausmachen. Solche Cache-Software wirbt mit teilweise 400%iger Beschleunigung des Codes - was natürlich sehr attraktiv ist.
Und tatsaechlich bringen diese Beschleuniger etwas.
Das ganze funktioniert recht simpel: Jedes PHP Skript muss vor der Ausführung
(logischerweise) kompiliert werden. Die Cache-Software hält
das Skript im Kompilierten Zustand in einem Cache und stellt bei erneutem
Aufruf des Skript die bereits kompilierte Version direkt zur
Verfügung. Der gesamte Kompilierungs-Overhead muss somit nicht wiederholt
werden.
Die zwei besten Beschleuniger sind der ZendOptimizer von Zend Inc. und der nicht-kommerzielle (OpenSource) MMcache.
- http://www.zend.com/store/products/zend-optimizer.php
- http://turck-mmcache.sourceforge.net/
Der ZendOptimizer bringt ein einfaches Installations-Script mit sich welches
den Optimizer selber installiert, und die MMcache Version
ist einfach zu kompilieren und ueber die php.ini einzubinden.
Ich empfehle MMcache weil diese Version Frei ist und mehr Einstellungsmoeglichkeiten
bietet als der ebenfalls "frei erhaeltliche" ZendOpti.
Erfahrungsgemaess steigt MySQL bei einem Besucheransturm auf eine dynamische
Seite am ersten aus. Dies liegt daran das ein Grossteil
der Installationen die Standard-Konfiguration nicht veraendert was u.a schon
die Einschraenkung bringt das MySql nur 100 gleichzeitige
Verbindungen erlaubt.
Die folgenden Tips sollten einen erheblichen Performance-Schub bringen.
Hinweis:
Alle Konfigurationsparameter entweder in /etc/my.cnf in der Sektion mysqld eintragen z.B
[mysqld]
set-variable = max_connections=2500
oder als Parameter uebergeben zB.
safe_mysqld --max_connections=2500 &
2.3.1 Einstellen der max. gleichzetigen Verbindungen:
Wenn Sie stark frequentierte Webseiten haben die bei jedem Aufruf selects
durchfuehren oder persistente Verbindungen zur
Datenbank halten ist MySQL in der std. Konfiguration mit 100 gleichzeitigen
Verbindungen schnell am Ende.
MySQL ist auch auf schwachen Rechnern in der Lage viele Verbindungen zu verabrieten.
Die Variable max_connections sollte mit dem
Wert "1000" oder "2500" auf einen akzeptablen Wert gebracht
werden oder jedoch
dem was Ihre Websiten an gleichzeitigen Benutzern entspricht.
2.3.2 der Table-Cache und Key-Buffer:
key_buffer_size und table_cache sind die wohl wichtigsten performance-bezogenen
Variablen. Diese geben an wieviel
Speicher MySQL zur verfuegung steht um Tabellen und Abfrage Keys zu speichern.
Auf Systemen mit 2GB-Ram oder mehr und vielen Datenbanken sind folgende Einstellungen zu empfehlen:
key_buffer=128M und table_cache=512 sowie sort_buffer=15M
Fuer kleinere Systeme (256MB + Ram) oder wenig Datenbanken
ist ein:
key_buffer=64M und ein table_cache=256 (fuer =>
256MB RAM) oder table_cache=128 (fuer < 256MB Ram) ausreichend.
Desweiteren
reicht hier auch ein kleiner sort_buffer von 4 oder
8MB.
Fuer Systeme mit 128MB Ram oder Weniger sollten sie
den table_cache nicht einstellen sondern die Standard-Werte
der small.cnf verwenden , jedoch den key_buffer auf 16 oder 32M stellen.
Kleinere Systeme zu verwalten macht fuer einen stark frequentierten Server
keinen Sinn.
2.4 Mysql & Apache Temp. Files
Sowohl der Apache als auch der MySQL-Server legen temporaere Dateien auf
der Festplatte ab. Meistens handelt es sich um
PHP-Sessions oder Auslagerungen grosser Suchanfragen bei einem belasteten
System.
Diese meist kleinen Dateien kann man sinnvoll in einem tmpfs (shared memory filesystem) ablegen. Auch Kleinvieh macht mist.
dazu mountet man z.B ein 150MB grosses tmpfs mit:
/bin/mount -t tmpfs /dev/shm /tmp/ramtmp/ -o size=150m
und uebergibt mysql mit --tempdir=/tmp/ramtmp/ dieses Verzeichnis,
PHP kann man den Session-Speicherpfad in der php.ini (/etc) angeben.
session.save_path = /tmp/ramtmp/
so long live the fish ;)