Skip to content

Webserver-Tuning

Alle paar Monate wieder turnt ein - weniger rücksichtsvoller - Crawler vorbei und spidert sich zügig durch alle Einträge meines Blogs. Damit brachte er meinen bisherigen Server gerne an den Rand seiner Leistungsfähigkeit, durfte dieser doch für jeden Link einen php-cgi-Prozess starten. Man merkte das recht schnell an den steigenden Antwortzeiten, und auch interaktiv auf der Shell machte sich die steigende load bemerkbar. Am Ende blieben meist einige php-cgi-Prozesse übrig, die man dann manuell mittels kill entsorgen durfte. (Vielleicht lag auch hier das eigentliche Problem, dass nämlich die genutzten Ressourcen nicht wieder freigegeben wurden. Hinter die Einzelheiten bin ich nie so recht gekommen.)

Die brandneue Maschine hingegen sollte durch FastCGI und massenhaft RAM sowie schnelle SSDs einem solchen Ansturm (auch bei weiterer Verwendung des doch eher schwergewichtigen Apachen) besser gewachsen sein - dachte ich mir. Bis eines Freitagmorgens die E-Mails des Monitoring-System eingingen, die mir mitteilten, dass der Webserver nicht mehr auf Anfragen reagiert. Gar nicht mehr.

Der Grund dafür fand sich schnell im Log:

[mpm_event:error] [pid 1365:tid 139856442496192] AH00484: server reached MaxRequestWorkers setting, consider raising the MaxRequestWorkers setting

Alle Threads des Webservers waren mit Anfragen ausgelastet (und offensichtlich wurden sie auch nicht mehr freigegeben - oder der Apache hatte sich “verschluckt”). Ein service apache2 restart löste daher das Problem; Zeit aber für einen Blick in die Konfiguration - vielleicht sollte man etwas an den Limits drehen (“consider raising the MaxRequestWorkers setting “).

Apache Event-MPM

Für das verwendete MPM findet sich die entsprechende Konfiguration (unter Debian) in /etc/apache2/mods-enabled/mpm_event.conf; sie sieht per default so aus:

# event MPM
# StartServers: initial number of server processes to start
# MinSpareThreads: minimum number of worker threads which are kept spare
# MaxSpareThreads: maximum number of worker threads which are kept spare
# ThreadsPerChild: constant number of worker threads in each server process
# MaxRequestWorkers: maximum number of worker threads
# MaxConnectionsPerChild: maximum number of requests a server process serves
<IfModule mpm_event_module>
       StartServers              2
       MinSpareThreads          25
       MaxSpareThreads          75
       ThreadLimit              64
       ThreadsPerChild          25
       MaxRequestWorkers       150
       MaxConnectionsPerChild    0
</IfModule>

Es werden also standardmäßig zwei Serverprozesse (StartServers)gestartet; jeder hat 25 Threads (ThreadsPerChild), also können 50 parallele Anfragen abgearbeitet werden. Sobald weniger als 25 freie Threads zur Verfügung stehen (MinSpareThreads), werden weitere Serverprozesse gestartet, bis zum Maximum von 150 Threads (MaxRequestWorkers) - das entspricht sechs Prozessen (mit je 25 Threads). Sobald mehr als 75 Threads unbeschäftigt sind (MaxSpareThreads), werden Serverprozesse wieder zurückgefahren.

150 Prozesse für eine Vielzahl von Webpräsenzen erschien mir wirklich nicht viel; ich habe (angesichts des zur Verfügung stehenden Speichers) daher etwas nachgelegt:

<IfModule mpm_event_module>
        ServerLimit               40
        StartServers               5
        MinSpareThreads          100
        MaxSpareThreads          250
        ThreadLimit               64     
        ThreadsPerChild           25
        MaxRequestWorkers       1000
        MaxConnectionsPerChild  1000
</IfModule>

Wir fangen jetzt also mit mit 125 Threads an (fünf Server mit je 25 Threads) und halten minimal 100 Threads bereit, maximal 250 - eine Lastspitze kann daher besser abgefedert werden, weil von Anfang an mehr Ressourcen bereitstehen und schneller zusätzliche Ressourcen hochgefahren werden). Maximal können dann 1.000 Threads laufen (statt vorher 150) - zu diesem Zweck muss auch ServerLimit erhöht werden, das per Default auf 16 steht - denn 1.000 Threads brauchen bei 25 Threads pro Prozess 40 Prozesse (mit dem Default von 16 Prozessen kommt man daher maximal auf 400 Threads).

Außerdem lasse icn jeden Thread nach 1.000 beantworteten Anfragen neu starten (MaxConnectionsPerChild); so können sich Speicherlecks nicht zu Problemen auswachsen.

Seitdem war der Webserver jedenfalls nicht mehr komplett ausgelastet; schauen wir mal, ob das schon der richtige Mittelweg zwischen der Bereitstellung ausreichender Ressourcen und dem Schutz des Gesamtsystems vor Überlast war oder mir als nächstes die ganze Maschine in die Knie geht, wenn es mal richtig rundgeht …

FPM-Konfiguration

Für Webapplikationen in PHP ist die Apache-Konfiguration nicht das einzige limitierende Element - vielmehr muss jede Anfrage auch von einem FastCGI-Prozess beantwortet werden, und auch die sind limitiert: per Default (bei Debian) auf 50 pro Pool, konfiguriert (unter Strech) in /etc/php/7.0/fpm/pool.d/www.conf, wobei statt www.conf eben der jeweilige Pool einzusetzen ist.

Das erscheint mir ebenfalls nicht besonders viel; ich habe daher den Pool für mein Blog etwas nach oben angepasst:

pm.max_children = 250
pm.start_servers = 30
pm.min_spare_servers = 10
pm.max_spare_servers = 50

Zu lesen ist das ähnlich wie oben beim Apachen: 30 Prozesse stehen von Anfang an zur Verfügung, 10 müssen mindestens immer arbeitslos sein, sonst werden Prozesse nachgelegt, bis zur Maximalzahl von 250. Ab 50 “arbeitslosen” Prozessen werden überzählige Prozesse gestoppt.

Erfahrungswerte

Ich habe bisher, muss ich gestehen, nicht die geringste Erfahrung mit solchen “Tuning-Aufgaben”, und werde daher abwarten müssen, ob sich die Werte bewähren. Aus dem Log kann ich jedenfalls sehen, dass 50 Prozesse viel zu wenig waren und auch 150 mehrfach nicht ausreichten:

WARNING: [pool blog] server reached pm.max_children setting (150), consider raising it

Andererseits habe ich bisher zumindest aus Munin - das allerdings nicht sehr fein auflöst - keine Hinweise für Lastspitzen (CPU-Nutzung, RAM-Nutzung und Load); da scheint im Tagesverlauf überall noch viel, viel Luft nach oben zu sein.

Hat jemand aus der werten Leserschaft Erfahrungen und Tips, die er gerne teilen möchte? Sonst berichte ich, wenn das nächste Mal etwas danebengeht. :-)

Trackbacks

Keine Trackbacks

Kommentare

Ansicht der Kommentare: Linear | Verschachtelt

tux- am :

tux-

Ich versuche auf von mir administrierten Servern aus genau diesem Grund auf Apache zu verzichten - nginx oder OpenBSDs httpd machen sehr viele Dinge sehr viel performanter.

Thomas Hochstein am :

Thomas Hochstein

Tja, was der Bauer (oder hier in diesem Fall: das Szaf) nicht kennt, frisst er (hier: es) nicht … Will sagen, einen neuen Webserver samt dessen Konfiguration kennen zu lernen, steht wenn, dann erst später[tm] auf der Agenda.

Bisher stellt sich ja auch nicht so sehr die Frage, ob der Apache zu schwergewichtig ist, sondern wie man ihn am besten konfiguriert …

Bernd am :

Bernd

nginx oder OpenBSDs httpd machen sehr viele Dinge sehr viel performanter.

Für Apache 2.4 ist diese Aussage mal richtig und dann auch mal wieder falsch, wie bei so vielen Benchmarks. Alles ein klarer Fall von "kommt darauf an": Auf die Config, die Art der Requests, die Hardware, … Wir haben ältere Dual Core-Server mit Apache mod_proxy, die bei einer hoch siebenstelligen Anzahl von Requests pro Tag sehr entspannt arbeiten. Falls der Server nicht gerade auf extrem schwachbrüstiger Hardware läuft oder richtige Hochlast bewältigen muss, ist die wichtigste Empfehlung immer noch etwas zu nehmen, was man kennt, gerade wenn das im Internet hängt… Das spricht nicht gegen nginx&Co, aber wie Thomas schon schrieb, muss man dafür nicht unbedingt Zeit für neues Wissen investieren, wenn die eh schon ein knappes Gut ist.

Hat jemand aus der werten Leserschaft Erfahrungen und Tips, die er gerne teilen möchte?

mod_event und FPM sieht schon mal gut aus. Mit Benchmarkwerkzeugen wie jmeter oder ab darauf herum zu hämmern reicht IMHO meistens nur, um grobe Fehler zu finden (die ich hier nicht sehe), weil es zu weit weg von realer Last ist. Das andere Ende der Skala sind komplexere Tools oder Mitschnitte mit goReplay oder ähnlichem. Das ist aber Overkill für privat, weil die nicht nur Zeit fressen, sondern auch die Infrastruktur gedoppelt werden muss, um die "Produktion" nicht zu opfern.

Oberste Regel beim Tuning: Messen, messen, messen. Zweite Regel: Möglichst nicht zu viele Parameter zwischen zwei Durchläufen ändern. Sonst wird es schwierig bis unmöglich zu sehen, was geholfen hat. Und drittens erfassen, was man konkret geändert hat. Config-Management, git-Repos, Changelog per Textdatei, Hauptsache irgendwie. Klingt so simpel, wird aber genauso gerne vergessen. Solange kleine Probleme tolerabel sind, lernt man wirklich am meisten durch Iteration am lebenden Objekt. Falls das nicht tolerabel ist, geht man eh mit ganz anderen Geschützen ran, siehe oben.

Wenn ich das richtig interpretiere, erfasst du noch nicht die Ausgaben von mod_status? Laut google kann munin das. (mod_status mit ExtendedStatus On, über den "auto"-Parameter maschinenlesbar abzufragen. Sollte besser nicht von außen erreichbar sein.) Damit siehst du ziemlich schnell, wie viele Prozesse und Threads überhaupt wann genutzt wurden. Viel hilft da nicht immer viel.

Thomas Hochstein am :

Thomas Hochstein

QUOTE:
muss man dafür nicht unbedingt Zeit für neues Wissen investieren, wenn die eh schon ein knappes Gut ist.

Ja, das ist - leider - ein nicht unwichtiger Punkt: Zeit habe ich ohnehin für alles (zu) wenig, und ich beschäftigte mich ja nicht hauptberuflich mit diesem Themen, sondern dilettiere nur hobbymäßig ein wenig herum … (nginx steht aber tatsächlich - noch deutlich vor varnish - seit Jahren auf der Liste der Dinge, mit denen ich mich einmal beschäftigen möchte, wenn ganz viel Zeit ist.)

QUOTE:
Mit Benchmarkwerkzeugen wie jmeter oder ab darauf herum zu hämmern reicht IMHO meistens nur, um grobe Fehler zu finden (die ich hier nicht sehe), weil es zu weit weg von realer Last ist.

Danke, das ist ein wertvoller Hinweis für mich; ich hatte nämlich schonmal überlegt, ob ich nicht einfach mal Last simulieren sollte.

QUOTE:
Config-Management, git-Repos, Changelog per Textdatei, Hauptsache irgendwie. Klingt so simpel, wird aber genauso gerne vergessen.

Ein Changelog als Text führe ich seit über einem Jahrzehnt; in den letzten Jahren auch so zuverlässig und ausführlich, dass es oft tatsächlich hilfreich ist. :-) Außerdem dokumentiere ich neuerdings die Standardschritte zur Installation und Konfiguration dienstbezogen gesondert, damit ich mir das nicht immer aus den Changelogs herausfischen und dabeu nach dem aktuellsten Stand suchen muss. Irgendwann[tm] wäre ein Tool fürs Konfigurationsmanagement und ggf. Automatisierung (Puppet, Ansible, Chef o.ä.) sicherlich mal nett, einerseits, um etwas Neues zu lernen, andererseits, um nicht jede Änderung von Hand drei, vier oder fünfmal nachzuvollziehen, aber das steht auch sehr weit unten auf der "nice-to-have"-Liste.

Seit letztem Jahr habe ich auf allen Maschinen etckeeper installiert, vergesse aber noch mit einer Regelmäßigkeit von an die 100% das committen - was nicht zur Übersicht beiträgt, aber (schon durch die Autocommits bei Änderungen) wenigstens rudimentär einen Verlauf dokumentiert.

QUOTE:
Wenn ich das richtig interpretiere, erfasst du noch nicht die Ausgaben von mod_status?

Doch, das ist in der Überwachung von Munin mit drin; man sah auch sehr schön, dass die Threads steil anzogen, das Limit (150) überschritten - und dann keine Daten mehr erfasst wurden. :-)

Bernd am :

Bernd

nginx steht aber tatsächlich - noch deutlich vor varnish - seit Jahren auf der Liste der Dinge, mit denen ich mich einmal beschäftigen möchte

Bei mir sind das aktuell traefik und bessere Kenntnisse in haproxy, wenn auch aus anderer Motivation. Caddy sieht ebenfalls interessant aus. Es gibt immer genug zu lernen. :-)

[Benchmark-Tools]

Danke, das ist ein wertvoller Hinweis für mich; ich hatte nämlich schonmal überlegt, ob ich nicht einfach mal Last simulieren sollte.

Gerne. Ich will dir das auf keinen Fall ausreden. Die Werkzeuge sind an sich wertvoll und schaden kann das nie. Mir ging es bei dem Hinweis übertrieben formuliert darum, Enttäuschungen zu vermeiden. Das Problem an der Sache: Je simpler, desto weiter weg sind die Werkzeuge manchmal von der realen Welt. Will man komplexere Situationen simulieren, braucht man Zeit um die Parameter zu finden, zu lernen und alles mit realistischen Werten zu füttern. Am Beispiel eines Bots: Wie ist der zeitliche Abstand zwischen Anfragen, wie viele Requests kommen parallel, lässt er bestimmte Seiten/Ressourcen aus weil er sich an die robots.txt hält oder wird die ignoriert, sind gleichzeitig noch Benutzer aktiv und so weiter. Mir hat mal jemand stolz gezeigt, wie sich sein Klickroboter pro Benutzer mit 20Hz durch eine GUI arbeitet. Den Sachbearbeiter der das auch schafft hat er bis heute nicht vorzeigen können…

Wenn das Interesse noch da ist :-): Kris hat hier sein Vorgehen erklärt. Wie immer lesenswert.

Puppet, Ansible, Chef

Macht Spaß, muss aber für privat gar nicht sein. Erlaubt ist, was Zeit spart. Besagte Werkzeuge entwickeln ihre Stärken vor allem, wenn viele Server konsistent halten oder ständig neue hochziehen muss. Beides ist für privat selten gegeben.

man sah auch sehr schön, dass die Threads steil anzogen, das Limit (150) überschritten - und dann keine Daten mehr erfasst wurden.

Das war ja dann noch die default-Config. Wie entwickelt sich der Verlauf jetzt, mit mehr Reserven? Oder war der Crawler noch nicht wieder da? :-)

Um das Thema noch eben rund zu machen:

Munin - das allerdings nicht sehr fein auflöst

Ich kenne Munin nicht. Bei der Visualisierung können auch die Werkzeuge beliebig viel falsch machen, wenn sie zu stark oder falsch verdichten. Für deinen Anwendungsfall trifft das vermutlich nicht zu, aber empfehlenswert ist ein weiterer Artikel von Kris zu irreführenden Graphen, den google aber leider gerade nicht ausspucken möchte. Ich diskutiere regelmäßig mit Kunden und Chefetage, wenn die sich mal wieder vom arithmetischen Mittel oder schlechter Visualisierung in die Irre führen lassen…

Thomas Hochstein am :

Thomas Hochstein

QUOTE:
Das war ja dann noch die default-Config. Wie entwickelt sich der Verlauf jetzt, mit mehr Reserven? Oder war der Crawler noch nicht wieder da? :-)

Genau da liegt das Problem. :-)

Normalerweise bekomme ich keine echte Last; 4.000 Requests pro Tag auf das Blog (die meisten vermutlich auf die Feeds), und alles andere sind inzwischen statische Seiten. Nur ab und an, alle paar Monate, passierte irgendwas[tm], was die alte Maschine, auf der der Webserver lief, an ihre Grenzen brachte (*) und diesmal den neuen in die Limits getrieben hat - vermutlich ein schlechter Crawler, vielleicht auch irgendwas anderes. Insofern ist das eher Tuning mit sehr größen Zeitabständen zwischen den Iterationen.

(*) Meist sah man da hinterher aber auch nicht viel davon; nur wenn man es live mitbekam, war alles … zäh.

Insofern vielleicht doch einmal was für einen Lasttest. Kommt Zeit, kommt Rat. (Und schlimmstenfalls geht halt mal das Blog ein paat Stunden nicht. So etwas ärgert mich immer ganz furchtbar, aber wirklich relevant ist das dann auch nicht.)

Bernd am :

Bernd

Normalerweise bekomme ich keine echte Last

Ich bin da nicht so im Thema, aber mindestens den google-Bot kann man IIRC über die Webmaster-Tools auffordern vorbeizuschauen. Vielleicht reicht das ja zum Üben?! :-)

Insofern vielleicht doch einmal was für einen Lasttest.

Du kannst ja sehr einfach starten. Ein beherztes "ab -c 50 -n 1000 URL" macht 1000 Requests, davon 50 gleichzeitig. Das ist weit weg von realem Verhalten eines Crawlers, aber man kann schon mal sehen wie die Maschine damit umgeht. Vorher aber am besten mit "-c 1 -n 100" o.ä. die "Baseline" für einzelne Requests anschauen.

Ein paar Kleinigkeiten können ab (aus dem Apache Lieferumfang) oder siege noch, aber bald kommt man an die Grenzen. Dann fallen mir für Lasttests JMeter, Grinder, gatling, artillery, locust und vegeta ein. In die gleiche Kategorie fallen Dinge wie blazemeter, das es in der kleinen Version kostenlos gibt. Das sind dann aber die Zeitfresser…

mitch am :

mitch

Vielen Dank für den Artikel!

Ich habe mich mit dem Thema noch nie beschäftigt (auf meinem Server ist noch viel weniger Last ;-), aber nachdem ich gerade festgestellt habe, dass Debian mod_status für localhost standardmäßig aktiv hat, habe ich meine RRD-Statistiken um die Anzahl der Busy/Idle-Worker erweitert. Man kann nie genug bunte Graphen haben :-)

Thomas Hochstein am :

Thomas Hochstein

QUOTE:
Man kann nie genug bunte Graphen haben

Allerdings! :-)

Ich suche auch immer nach neuen Plugins, um noch etwas neues zu tracken.

Bernd am :

Bernd

Man kann nie genug bunte Graphen haben

Volle Zustimmung. Wobei man - so banal wie das klingt - immer darauf achten sollte, ob die auch zeigen was man braucht. Standard-Beispiel: Sucht man Ausreißer im Antwortzeitverhalten, sind das arithmetische Mittel oder automatisch geglätte Kurven für diese Zeiten Murks, weil einzelne Patzer prinzipbedingt rausfallen. Leider machen das erstaunliche viele Monitoring-Anwendungen zumindest in den Standard-Graphen.

Wenn man gar keine Ahnung hat, wie man das später braucht, bewahrt man zur Not Rohdaten auf und visualisiert nach Bedarf. Wer sich mal sowas wie den Stand der Technik im Open Source Umfeld ansehen möchte, muss einen Blick auf Grafana werfen. Das ist ein reines Frontend zur Visualisierung und überschwemmt IMHO durch die Kombination folgender Punkte seit letztem Jahr alle einschlägigen Blogs mit Tutorials und Danksagungen:

  1. Einfache Gestaltung hübscher Dashboard. Versteht man sehr schnell und das Ergebnis ist ansprechend.
  2. Es mehrere Datentöpfe parallel und davon sehr viele verschiedene. Der Fokus liegt auf den moderneren Werkzeugen, aber bei denen ist die Abdeckung sehr gut.
  3. Es gibt ein Plugin-Konzept mit guter und steigender Auswahl.
  4. Brauchbare Dokumentation.

Das Gesamtpaket ist scheinbar so überzeugend, das einzelne Monitoring-Lösungen wie Prometheus empfehlen, einfach Grafana für Metriken zu benutzen, weil sie es auch nicht besser hinbekommen. :-) Nein, ich bekomme keine Provision und fluche auch manchmal darüber, möchte es aber nicht mehr missen. Elastic? Influx? Kairos? Prometheus? Cloudwatch? Egal, einfach Grafana davor setzen…

Dennis am :

Dennis

Hi,

genau so etwas habe ich gesucht. Ich habe meinen Server gerade anhand deiner Beschreibung getunt. Danke für deine Mühe.

Kommentar schreiben

HTML-Tags werden in ihre Entities umgewandelt.
Markdown-Formatierung erlaubt
Standard-Text Smilies wie :-) und ;-) werden zu Bildern konvertiert.
BBCode-Formatierung erlaubt
Gravatar, Identicon/Ycon Autoren-Bilder werden unterstützt.
Formular-Optionen