Benchmark Apache2 mit PHP via mod_fcgid und SuExec

Webserver Apache2 mit PHP via mod_fcgid und SuExec setze ich nun schon seit einiger Zeit ein, aber ich hatte mir noch nie wirklich Gedanken über die Performance Situation der Server gemacht. Sie funktionierten und mit den kleineren Problemen konnte ich leben. Letzte Woche schaute ich mir die Performance des Apache mit Apache Benchmark ab doch mal genauer an und war etwas schockiert über die schlechten Werte. Hier nun also die Benchmarks und die Anpassungen die mir bei einer signifikanten Performancesteigerung geholfen haben. Zum testen habe ich jeweils Apache Benchmark mit 100.000 Requests, 20 gleichzeitigen Verbindungen und KeepAlive laufen lassen. Nach jeder Configanpassung wurde der Apache neu gestartet. Das angeforderte Script enthielt eine einfache php echo Anweisung und der Server war ein Hetzner EQ4. Der Apache ist nach meiner Anleitung Apache2 Worker mit PHP und fcgid SuExec auf Debian Lenny installiert.
Ausgangssituation / Benchmark 1
Dies war meine Ausgangssituation, wie ich sie mir aus Manuals und Anleitung zusammen kopiert hatte.
fcgid-starter
#!/bin/sh export PHPRC="/var/www/vh-default/conf/" exec /usr/bin/php5-cgi
fcgid.conf
SpawnScoreUpLimit 10 SpawnScore 1 TerminationScore 1 MaxProcessCount 500 MaxRequestsPerProcess 500
Benchmark
$ ab -n 100000 -c 20 -k http://IP-DES-APACHE2/index.php Time taken for tests: 389.727 seconds Complete requests: 100000 Failed requests: 198 (Connect: 0, Receive: 0, Length: 198, Exceptions: 0) Requests per second: 256.59 [#/sec] (mean) Time per request: 77.945 [ms] (mean) Time per request: 3.897 [ms] (mean, across all concurrent requests) Transfer rate: 46.93 [Kbytes/sec] received
Während dieses Tests war mein Server nur minimal ausgelastet. Die CPU sprang ab und an auf 5% und es dauerte ewig... Die schlechten Requests per Second Werte zeigen das ja auch...
Benchmark 2 / Erhöhen des SpawnScoreUpLimit
Als nächstes habe ich das SpawnScoreUpLimit erhöht, was zu einer schnelleren Spawngeschwindigkeit führen sollte.
fcgid-starter
#!/bin/sh export PHPRC="/var/www/vh-default/conf/" exec /usr/bin/php5-cgi
fcgid.conf
SpawnScoreUpLimit 150 SpawnScore 1 TerminationScore 1 MaxProcessCount 500 MaxRequestsPerProcess 500
Benchmark
$ ab -n 100000 -c 20 -k http://IP-DES-APACHE2/index.php Time taken for tests: 247.170 seconds Complete requests: 100000 Failed requests: 195 (Connect: 0, Receive: 0, Length: 195, Exceptions: 0) Requests per second: 404.58 [#/sec] (mean) Time per request: 49.434 [ms] (mean) Time per request: 2.472 [ms] (mean, across all concurrent requests) Transfer rate: 73.99 [Kbytes/sec] received
Die Geschwindigkeit ging spürbar nach oben, aber sie war immer noch nicht berauschend.
Benchmark 3 / Erhöhen der MaxRequestsPerProcess
In der Fcgid Anleitung sah ich dann den Teil zu PHP_FCGI_MAX_REQUESTS und MaxRequestsPerProcess. Die Annahme das PHP Prozesse immer nach 500 Anfragen getötet werden müssen hing mir im Gedächtnis, aber hier wird genau dieser Punkt anders beschrieben.
By default, PHP FastCGI processes exit after handling 500 requests, and they may exit after this module has already connected to the application and sent the next request. When that occurs, an error will be logged and 500 Internal Server Error will be returned to the client. This PHP behavior can be disabled by setting PHP_FCGI_MAX_REQUESTS to 0, but that can be a problem if the PHP application leaks resources. Alternatively, PHP_FCGI_MAX_REQUESTS can be set to a much higher value than the default to reduce the frequency of this problem. FcgidMaxRequestsPerProcess can be set to a value less than or equal to PHP_FCGI_MAX_REQUESTS to resolve the problem.
[warn] (104)Connection reset by peer: mod_fcgid: read data from fastcgi server error. [error] [client] Premature end of script headers: index.php
Das scheinen die 500er Fehler zu sein die mich schon seit langem verfolgen. Also passte ich die Werte dem entsprechend an.
fcgid-starter
#!/bin/sh export PHPRC="/var/www/vh-default/conf/" PHP_FCGI_MAX_REQUESTS=10000 export PHP_FCGI_MAX_REQUESTS exec /usr/bin/php5-cgi
fcgid.conf
SpawnScoreUpLimit 150 SpawnScore 1 TerminationScore 1 MaxProcessCount 500 MaxRequestsPerProcess 10000
Benchmark
$ ab -n 100000 -c 20 -k http://IP-DES-APACHE2/index.php Time taken for tests: 11.946 seconds Complete requests: 100000 Failed requests: 0 Requests per second: 8371.04 [#/sec] (mean) Time per request: 2.389 [ms] (mean) Time per request: 0.119 [ms] (mean, across all concurrent requests) Transfer rate: 1520.52 [Kbytes/sec] received
Das brachte den wirklich entscheidenden Schub. Erstaunlich war auch das es keinerlei Fehler gab. Nach ein paar Wiederholungen pendelten sich die Werte bei ~6500 Requests per Second ein und einige 500er Fehler landeten auch noch im Apache Errorlog (Quote von 10/100000), aber es scheinen weniger zu werden und die Performance Werte sind definitiv besser als die Ausgangssituation.
Benchmark 4 / Weiteres erhöhen der MaxRequestsPerProcess
Als letztes habe ich die Werte PHP_FCGI_MAX_REQUESTS und MaxRequestsPerProcess auf 20000 erhöht, was die folgenden Resultate ergaben.
$ ab -n 100000 -c 20 -k http://IP-DES-APACHE2/index.php Time taken for tests: 12.898 seconds Complete requests: 100000 Failed requests: 0 Requests per second: 7753.02 [#/sec] (mean) Time per request: 2.580 [ms] (mean) Time per request: 0.129 [ms] (mean, across all concurrent requests) Transfer rate: 1408.26 [Kbytes/sec] received
$ ab -n 100000 -c 20 -k http://IP-DES-APACHE2/index.php Time taken for tests: 12.153 seconds Complete requests: 100000 Failed requests: 0 Requests per second: 8228.57 [#/sec] (mean) Time per request: 2.431 [ms] (mean) Time per request: 0.122 [ms] (mean, across all concurrent requests) Transfer rate: 1494.64 [Kbytes/sec] received
$ ab -n 100000 -c 20 -k http://IP-DES-APACHE2/index.php Time taken for tests: 13.382 seconds Complete requests: 100000 Failed requests: 0 Requests per second: 7472.78 [#/sec] (mean) Time per request: 2.676 [ms] (mean) Time per request: 0.134 [ms] (mean, across all concurrent requests) Transfer rate: 1357.37 [Kbytes/sec] received
$ ab -n 100000 -c 20 -k http://IP-DES-APACHE2/index.php Time taken for tests: 13.166 seconds Complete requests: 100000 Failed requests: 8 (Connect: 0, Receive: 0, Length: 8, Exceptions: 0) Requests per second: 7595.48 [#/sec] (mean) Time per request: 2.633 [ms] (mean) Time per request: 0.132 [ms] (mean, across all concurrent requests) Transfer rate: 1380.03 [Kbytes/sec] received
Fazit
Als abschließenden Test habe ich 1 Million Requests mit 100 gleichzeitigen Verbindungen getestet.
$ ab -n 1000000 -c 100 -k http://IP-DES-APACHE2/index.php Time taken for tests: 121.558 seconds Complete requests: 1000000 Failed requests: 42 (Connect: 0, Receive: 0, Length: 42, Exceptions: 0) Requests per second: 8226.55 [#/sec] (mean) Time per request: 12.156 [ms] (mean) Time per request: 0.122 [ms] (mean, across all concurrent requests) Transfer rate: 1494.49 [Kbytes/sec] received
Die Anpassungen haben sich gelohnt. Die allgemeine Performance ist besser und die 500er Fehler nehmen ab. Die Dauer des Tests ist von 390 Sekunden auf unter 12 Sekunden gesunken. Wenn jemand Anmerkungen hat oder ähnliche/andere Beobachtungen gemacht hat würde ich mich über ein Kommentar freuen.
7 Kommentare
4F2E4A2E
schrieb am 11.07.2010 um 11:37 Uhr
i love this tutorial, but i am really new to this benchmark stuff, so it would really help me alot if you could post the path of the files to it like this:
vim /etc/…/fcgid-starter
where can i find it anyway?
using debian lenny, apache2, ispconfig 3
kostaki
schrieb am 12.07.2010 um 13:13 Uhr
Hi,
i can’t tell where your fcgid-starter is localed. Normaly you can find out if you look into your vhost config.
You can finde the fcgid.conf in /etc/apache2/mods-available.
Florian
schrieb am 04.08.2010 um 22:25 Uhr
Noch viel mehr Performance (und ich meine Real-Performance, nicht für echo-scripte) würde es wohl bringen, wenn apache und php entsprechend in den neusten versionen selbst für die entsprechende arch kompiliert werden. Und dann natürlich auch suexec und fcgid direkt in die binary statisch reinlinken.
Die veraltete Software aus dem lenny-Repository kann man wirklich niemandem empfehlen. Zumindest nicht bei Apache, fcgid
und PHP oder dergleichen.
Dazu wäre es schön, wenn Du in solchen Anleitungen nicht die inzwischen als deprecated gesehenen fcgid-direktiven nutzten würdest, sondern die neuen – siehe entsprechend http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html .
Wieviel Ram rechnest Du im übrigen für ein MaxProcessCount von 500 eigentlich? Bei ~ 15mb pro PHP-Prozess (eher mehr wenn ich mir die Version aus dem Rep ansehe bzw. einige Module aktiviert sind) sind das ja schon 7,5GB alleine nur für PHP. Damit wäre lustigerweise nur noch 500MB platz fürs ganze restliche System inkl. apache etc. bei nem EQ4. Ist das im Ernst eine Konfiguration, die Du auf einem Produtiv-System fährst?
kostaki
schrieb am 05.08.2010 um 08:23 Uhr
Schon klar das man die Performance von einem simplen Echo Scripts nicht mit der einem kompletten Frameworks vergleichen kann, aber wenn es bei simplen Sachen besser läuft, sollten auch größere Anwendungen eine bessere Performance haben.
Ich bin kein großer Fan von selbst kompilierten Anwendungen wenn mir Debian alles liefert was ich benötige, deshalb beschreibe ich das meiste auch an den mitgelieferten Versionen und benutze dann auch die dort aktuellen Konfigurationsnamen. Sobald es hier ein Update gibt, bringe ich die Anleitungen auf den neuen Stand.
Das mit dem RAM hab ich ehrlich gesagt nicht getestet und ja ich fahre diese Konfiguration auf einem EQ4. Bisher ohne Probleme was RAM Verbrauch angeht.
Im allgemeinen würde ich jedem raten selbst zu benachmarken welche Einstellungen bei ihm am besten laufen. Es war für mich nur schwer die richtigen Stellschrauben zu finden, da es keine wirklichen resourcen dafür gibt und deshalb habe ich diese Anleitung geschrieben, mein vorgehen dokumentiert, damit es für andere eine Hilfe bietet.
voku
schrieb am 22.09.2010 um 19:33 Uhr
Hi, nach meinen Tests ist die Kombination aus Nginx (Webserver) + PHP5-fpm (+ XCache) am schnellsten und kann auch am meisten gleichzeitige Verbindungen verarbeiten, ohne Beeinträchtigung. Da PHP5-fpm als Daemon läuft und nicht immer neue PHP-Prozesse starten muss, ist es auch logisch, dass diese PHP-Version schneller ist. Jedoch muss man für jede Webseite einen eigenen Daemon starten, wenn man die PHP-Skripte jeweils mit anderen User-Rechten ausführen möchte…
-> http://suckup.de/blog/2010/07/31/nginx-php5-fpm-auf-debianubuntu/
-> http://suckup.de/blog/2010/07/26/apc-eaccelerator-xcache/
kostaki
schrieb am 29.09.2010 um 19:13 Uhr
Hi voku,
nginx klingt echt super, das wollte ich mir schon lange mal ansehen. Mal schauen wann ich Zeit finde. Zu Opcode Caches hab ich auch schon Tests fertig geschrieben, aber bin noch nicht zum posten gekommen. War auch etwas enttäuscht in der Kombination von Apache/fcgid/suexec/php und den Caches… Für jeden php Prozess ein eigenständiger Cache und beim neu starten ist der Cache wieder leer. Vielleicht kann man das mit nginx ja auch lösen.
Gruß
kostaki
voku
schrieb am 03.10.2010 um 13:48 Uhr
in der XCache Konfiguration kann man per “xcache.ttl” die Zeit einstellen für der Cache gültig ist… funktioniert sehr gut in Kombination mit php-fpm.
Man muss “php-fpm” nur neu-starten, wenn man eine Änderung an php-Dateien sofort sehen will…