Benchmark Apache2 mit PHP via mod_fcgid und SuExec

2010-04-18 - kostaki 7 Kommentare »

Blatt

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.

Ähnliche Artikel

  1. 7 Kommentare

  2. 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

  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.

  4. 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?

  5. 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.

  6. 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/

  7. 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

  8. 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… :-)

Kommentar schreiben

*

*