Richtlinien für Lasttests von Backend-Diensten mit Application Load Balancern

Wenn Sie einen Backend-Dienst in einen Anwendungs-Load-Balancer einbinden, ist es wichtig, die Leistung eines Backend-Dienstes allein zu messen, wenn kein Load-Balancer vorhanden ist. Lasttests unter kontrollierten Bedingungen helfen Ihnen, Kompromisse im Rahmen der Kapazitätsplanung zwischen verschiedenen Leistungsdimensionen wie Durchsatz und Latenz abzuwägen. Da eine sorgfältige Kapazitätsplanung die tatsächliche Nachfrage weiterhin unterschätzen kann, empfehlen wir, über Lasttests proaktiv zu bestimmen, wie sich die Verfügbarkeit eines Dienstes auf die Überlastung auswirkt.

Lasttestziele

Bei einem typischen Lasttest wird das extern sichtbare Verhalten des Backend-Dienstes unter verschiedenen Lastdimensionen gemessen. Zu den wichtigsten Dimensionen dieser Tests gehören:

  • Anfragedurchsatz: Die Anzahl der pro Sekunde verarbeiteten Anfragen.
  • Nebenläufigkeit von Anfragen: Die Anzahl der Anfragen, die gleichzeitig verarbeitet werden.
  • Verbindungsdurchsatz: Die Anzahl der pro Sekunde von Clients initiierten Verbindungen. Die meisten Dienste, die Transport Layer Security (TLS) verwenden, haben für jede Verbindung, die unabhängig von der Anfrageverarbeitung ist, einen gewissen Netzwerktransport- und TLS-Verhandlungsaufwand.
  • Verbindungs-Nebenläufigkeit: Die Anzahl der Clientverbindungen, die gleichzeitig verarbeitet werden.

  • Anfragelatenz: Die Gesamtzeit zwischen dem Beginn der Anfrage und dem Ende der Antwort.

  • Fehlerrate: Gibt an, wie oft Anfragen Fehler verursachen, darunter HTTP 5xx-Fehler und vorzeitig geschlossene Verbindungen.

Zur Bewertung des Zustands des Servers unter Last kann ein Lasttestverfahren auch folgende interne Dienstmesswerte erfassen:

  • Nutzung von Systemressourcen: Systemressourcen, darunter Werte für CPU, RAM und Dateihandles (Sockets), werden normalerweise in Prozent angegeben.

    Die Bedeutung dieser Messwerte hängt davon ab, wie der Dienst implementiert ist. Wenn Anwendungen ihre Ressourcen erschöpfen, kommt es zu einer geringeren Leistung, einer verminderten Last oder einem Absturz. Daher ist es wichtig, die Verfügbarkeit von Ressourcen zu ermitteln, wenn der Host stark ausgelastet ist.

  • Nutzung anderer begrenzter Ressourcen: Nicht-Systemressourcen, die unter Last erschöpft sind, z. B. auf Anwendungsebene.

    Beispiele für solche Ressourcen:

    • Ein begrenzter Pool von Worker-Threads oder ‑prozessen.
    • Bei einem Anwendungsserver, der Threads verwendet, ist in der Regel die Anzahl der gleichzeitig ausgeführten Worker-Threads begrenzt. Thread-Pool-Größenlimits sind nützlich, um zu verhindern, dass Arbeitsspeicher und CPU überlastet werden. Die Standardeinstellungen sind jedoch oft sehr konservativ. Zu niedrige Limits können die angemessene Nutzung von Systemressourcen verhindern.
    • Einige Server verwenden Prozess- anstelle von Threadpools. Beispiel: Ein Apache-Server weist bei Einrichtung mit dem Prefork Multi-Processing-Modell jeder Clientverbindung einen Prozess zu. Die Größenbeschränkung des Pools bestimmt also die Obergrenze für die Gleichzeitigkeit von Verbindungen.
    • Ein Dienst, der als Frontend für einen anderen Dienst mit einem Backend-Verbindungspool von begrenzter Größe bereitgestellt wird.

Kapazitätsplanung und Überlastungstests

Mit Lasttesttools können Sie verschiedene Skalierungsdimensionen einzeln messen. Legen Sie für die Kapazitätsplanung Lastgrenzwerte für die akzeptable Leistung in verschiedenen Dimensionen fest. Anstatt beispielsweise das absolute Limit einer Dienstanfrage vollständig zu messen, könnten Sie Folgendes messen:

  • Die Anfragerate, mit der der Dienst mit einer Latenz des 99. Perzentils verarbeitet werden kann, die unter einer angegebenen Anzahl an Millisekunden liegt. Die Anzahl wird durch den SLO-Wert des Dienstes festgelegt.
  • Die maximale Anfragerate, bei der die Systemressourcenauslastung die optimalen Werte nicht überschreitet. Die optimale Auslastung variiert je nach Anwendung und kann deutlich unter 100 % liegen. Bei einer Spitzenauslastung von 80 % kann die Anwendung beispielsweise kleinere Lastspitzen besser bewältigen als die Spitzenauslastung von 99 %.

Es ist zwar wichtig, die Ergebnisse von Lasttests zu verwenden, um Entscheidungen zur Kapazitätsplanung zu bilden, aber es ist ebenso wichtig zu verstehen, wie sich ein Dienst verhält, wenn die Last die Kapazität überschreitet. Zu den Serververhalten, die häufig mit Überlastungstests bewertet werden, zählen:

  • Load-Shedding: Wenn ein Dienst übermäßig viele eingehende Anfragen oder Verbindungen erhält, kann er deswegen alle Anfragen verlangsamt oder einige Anfragen ablehnt, um für die übrigen Anfragen eine akzeptable Leistung zu erhalten. Wir empfehlen den zweiten Ansatz, um Zeitüberschreitungen des Clients vor dem Empfang einer Antwort zu verhindern und das Risiko einer Speicherausschöpfung durch die Verringerung der gleichzeitigen Anfrage auf dem Server zu reduzieren.

  • Sicherheit vor Ressourcenüberlastung: Ein Dienst vermeidet im Allgemeinen das Abstürzen der Ressourcen, da es für ausstehende Anfragen schwierig ist, weitere Fortschritte zu machen, wenn der Dienst abstürzt. Wenn ein Backend-Dienst viele Instanzen hat, ist die Robustheit der einzelnen Instanzen für die Gesamtverfügbarkeit des Dienstes entscheidend. Während eine Instanz nach einem Absturz neu gestartet wird, können andere Instanzen eine höhere Last haben, was zu Kaskadenfehlern führen kann.

Allgemeine Testrichtlinien

Berücksichtigen Sie bei der Definition Ihrer Testfälle folgende Richtlinien.

Begrenzte Tests erstellen

Erstellen Sie begrenzte Tests, um die Leistungsgrenzen des Servers zu messen. Bei übermäßiger Serverkapazität besteht das Risiko, dass ein Test nicht die Leistungsgrenzen des Dienstes selbst aufdeckt, sondern Engpässe in anderen Systemen erkennt, z. B. in den Clienthosts oder in der Netzwerkebene.

Die besten Ergebnisse erzielen Sie mit einem Testfall, der eine einzelne VM-Instanz oder einen GKE-Pod (Google Kubernetes Engine) verwendet, um den Dienst unabhängig zu testen. Sie können bei Bedarf mehrere VMs verwenden, um eine vollständige Auslastung des Servers zu erzielen. Denken Sie jedoch daran, dass dies die Erfassung von Leistungsdaten komplizieren kann.

Lademuster für Open-Loop-Systeme auswählen

Die meisten Lastgeneratoren verwenden das Closed-Loop-Muster, um die Anzahl der gleichzeitigen Anfragen zu begrenzen und neue Anfragen zu verzögern, bis die vorherigen abgeschlossen wurden. Wir raten von diesem Ansatz jedoch ab, da die Produktionsclients des Dienstes möglicherweise kein solches Drosselungsverhalten aufweisen.

Im Gegensatz dazu ermöglicht es das Open-Loop-Muster Load-Generatoren, die Produktionslast zu simulieren. Dazu werden Anfragen mit einer konstanten Rate gesendet, unabhängig von der Rate, mit der Serverantworten eingehen.

Wir empfehlen folgende Lastgeneratoren für die Lasttests des Backend-Dienstes:

Nighthawk

Nighthawk ist ein Open-Source-Tool, das in Zusammenarbeit mit dem Envoy-Projekt entwickelt wurde. Sie können damit Clientlasten generieren, Benchmarks visualisieren und die Serverleistung für die meisten Lasttestszenarien von HTTPS-Diensten messen.

HTTP/1 testen

Testen Sie HTTP/1 mit folgendem Befehl:

nighthawk_client URI \
    --duration DURATION \
    --open-loop \
    --no-default-failure-predicates \
    --protocol http1 \
    --request-body-size REQ_BODY_SIZE \
    --concurrency CONCURRENCY \
    --rps RPS \
    --connections CONNECTIONS

Ersetzen Sie Folgendes:

  • URI: Die zu vergleichende URI
  • DURATION: Die gesamte Testlaufzeit in Sekunden
  • REQ_BODY_SIZE: Größe der POST-Nutzlast pro Anfrage
  • CONCURRENCY: Die Gesamtzahl der gleichzeitigen Ereignisschleifen

    Diese Zahl sollte mit der Anzahl der Kerne der Client-VM übereinstimmen

  • RPS: Zielrate der Anfragen pro Sekunde und Ereignisschleife

  • CONNECTIONS: Anzahl der gleichzeitigen Verbindungen pro Ereignisschleife

Sehen Sie sich folgendes Beispiel an:

nighthawk_client http://10.20.30.40:80 \
    --duration 600 --open-loop --no-default-failure-predicates \
    --protocol http1 --request-body-size 5000 \
    --concurrency 16 --rps 500 --connections 200

Die Ausgabe der einzelnen Testläufe enthält je ein Histogramm der Antwortlatenzen. Im Beispiel aus der Nighthawk-Dokumentation beträgt die Latenz des 99. Perzentils etwa 135 µs.

Initiation to completion
    samples: 9992
    mean:    0s 000ms 113us
    pstdev:  0s 000ms 061us

    Percentile  Count       Latency
    0           1           0s 000ms 077us
    0.5         4996        0s 000ms 115us
    0.75        7495        0s 000ms 118us
    0.8         7998        0s 000ms 118us
    0.9         8993        0s 000ms 121us
    0.95        9493        0s 000ms 124us
    0.990625    9899        0s 000ms 135us
    0.999023    9983        0s 000ms 588us
    1           9992        0s 004ms 090us

HTTP/2 testen

Testen Sie HTTP/2 mit folgendem Befehl:

nighthawk_client URI \
    --duration DURATION \
    --open-loop \
    --no-default-failure-predicates \
    --protocol http2 \
    --request-body-size REQ_BODY_SIZE \
    --concurrency CONCURRENCY \
    --rps RPS \
    --max-active-requests MAX_ACTIVE_REQUESTS \
    --max-concurrent-streams MAX_CONCURRENT_STREAMS

Ersetzen Sie Folgendes:

  • URI: Die zu vergleichende URI
  • DURATION: Die gesamte Testlaufzeit in Sekunden
  • REQ_BODY_SIZE: Größe der POST-Nutzlast pro Anfrage
  • CONCURRENCY: Die Gesamtzahl der gleichzeitigen Ereignisschleifen

    Diese Zahl sollte mit der Anzahl der Kerne der Client-VM übereinstimmen

  • RPS: Die Zielrate von Anfragen pro Sekunde pro Ereignisschleife

  • MAX_ACTIVE_REQUESTS: Die maximale Anzahl gleichzeitiger aktiver Anfragen für die einzelnen Ereignisschleife

  • MAX_CONCURRENT_STREAMS: die maximale Anzahl gleichzeitiger Streams, die für die einzelnen HTTP/2-Verbindung zulässig sind

Sehen Sie sich folgendes Beispiel an:

nighthawk_client http://10.20.30.40:80 \
    --duration 600 --open-loop --no-default-failure-predicates \
    --protocol http2 --request-body-size 5000 \
    --concurrency 16 --rps 500 \
    --max-active-requests 200 --max-concurrent-streams 1

ab (Apache Benchmark-Tool)

ab ist eine weniger flexible Alternative zu Nighthawk, die aber als Paket in fast jeder Linux-Distribution verfügbar ist. ab wird nur für schnelle und einfache Tests empfohlen.

Verwenden Sie zum Installieren von ab den folgenden Befehl:

  • Führen Sie unter Debian oder Ubuntu sudo apt-get install apache2-utils aus.
  • Führen Sie für RedHat-basierte Distributionen sudo yum install httpd-utils aus.

Führen Sie nach der Installation von ab folgenden Befehl aus, um es auszuführen:

ab -c CONCURRENCY \
    -n NUM_REQUESTS \
    -t TIMELIMIT \
    -p POST_FILE URI

Ersetzen Sie Folgendes:

  • CONCURRENCY: Anzahl der nebenläufig auszuführenden Anfragen
  • NUM_REQUESTS: Anzahl der auszuführenden Anfragen
  • TIMELIMIT: Maximale Anzahl an Sekunden, die für Anfragen aufgewendet werden sollen
  • POST_FILE: Lokale Datei mit der HTTP-POST-Nutzlast
  • URI: Die zu vergleichende URI

Sehen Sie sich folgendes Beispiel an:

ab -c 200 -n 1000000 -t 600 -P body http://10.20.30.40:80

Der Befehl im vorherigen Beispiel sendet Anfragen mit einer Gleichzeitigkeit von 200 (Geschlossen-Schleife-Muster) und wird nach 1.000.000 (oder 1 Million) Anfragen oder 600 Sekunden verstrichener Zeit beendet. Der Befehl enthält auch den Inhalt der Datei body als HTTP-POST-Nutzlast.

Der Befehl ab erstellt Antwortlatenz-Histogramme, die denen von Nighthawk ähneln. Die Höchstauflösung ist jedoch Millisekunden statt Mikrosekunden:

Percentage of the requests served within a certain time (ms)
    50%     7
    66%     7
    75%     7
    80%     7
    90%    92
    95%   121
    98%   123
    99%   127
    100%  156 (longest request)

Nächste Schritte