Ressourcenzuordnungen von lokaler Hardware zu Google Cloud

In diesem Dokument wird gezeigt, wie Sie geeignete Ressourcenzuordnungen von lokaler Hardware zu Google Cloud finden. Wenn Ihre Anwendungen auf Bare-Metal-Servern ausgeführt werden und Sie sie zu Google Cloud migrieren möchten, sollten Sie sich folgende Fragen stellen:

  • Wie werden physische Kerne (pCPUs) virtuellen CPUs (vCPUs) in Google Cloud zugeordnet? Wie ordnen Sie beispielsweise vier physische Kerne des Bare-Metal-Xeon E5 den vCPUs in Google Cloud zu?
  • Wie schlagen sich Leistungsunterschiede zwischen verschiedenen CPU-Plattformen und Prozessorgenerationen nieder? Ist beispielsweise ein Sandy Bridge-Prozessor mit 3,0 GHz eineinhalbmal schneller als ein Skylake-Prozessor mit 2,0 GHz?
  • Wie wird die Größe von Ressourcen auf Basis der Arbeitslasten richtig angepasst? Wie lässt sich beispielsweise eine arbeitsspeicherintensive Single-Threaded-Anwendung optimieren, die auf einem Mehrkern-Server ausgeführt wird?

Sockets, CPUs, Kerne und Threads

Die Begriffe Socket, CPU, Kern und Thread werden häufig synonym verwendet, was bei einer Migration zwischen verschiedenen Umgebungen zu Verwirrung führen kann.

Einfach ausgedrückt, kann ein Server einen oder mehrere Sockets haben. Ein Socket (auch als CPU-Socket oder CPU-Steckplatz bezeichnet) ist der Anschluss auf dem Motherboard, der einen CPU-Chip enthält und physische Verbindungen zwischen der CPU und der Platine herstellt.

Mit CPU ist der eigentliche integrierte Schaltkreis (Integrated Circuit, IC) gemeint. Die Hauptaufgabe einer CPU besteht darin, eine Folge von gespeicherten Befehlen auszuführen. Allgemein gesprochen durchlaufen CPUs die Schritte "Abrufen", "Decodieren" und "Ausführen", die insgesamt als Befehlszyklus bezeichnet werden. In komplexeren CPUs können mehrere Befehle gleichzeitig abgerufen, decodiert und ausgeführt werden.

Jeder CPU-Chip kann einen oder mehrere Kerne haben. Ein Kern besteht im Wesentlichen aus einer Ausführungseinheit, die Befehle empfängt und Aktionen auf Basis dieser Befehle ausführt. In einem Hyper-Threaded-System können die Ressourcen eines physischen Prozessorkerns als mehrere logische Prozessoren zugewiesen werden. Genau gesagt wird jeder Kern des physischen Prozessors dem Betriebssystem als zwei virtuelle (oder logische) Kerne präsentiert.

Das folgende Diagramm ist eine grobe Darstellung einer Quad-Core-CPU mit aktiviertem Hyper-Threading.

Diagramm: Quad-Core-CPU mit aktiviertem Hyper-Threading.

In Google Cloud wird jede vCPU als einzelner Hyper-Thread auf einer der verfügbaren CPU-Plattformen implementiert.

Mit der folgenden Formel können Sie die Gesamtzahl der logischen CPUs (vCPUs) in Ihrem System ermitteln:

vCPUs = Threads pro physischem Kern × physische Kerne pro Socket × Anzahl der Sockets

Der Befehl lscpu erfasst Informationen wie die Anzahl der Sockets, CPUs, Kerne und Threads. Außerdem gibt er Informationen über die Caches und Cache-Freigaben der CPU sowie über deren Familie, Modell und BogoMips zurück. Hier ist eine typische Ausgabe:

...
Architecture:           x86_64
CPU(s):                 1
On-line CPU(s) list:    0
Thread(s) per core:     1
Core(s) per socket:     1
Socket(s):              1
CPU MHz:                2200.000
BogoMIPS:               4400.00
...

Wenn Sie CPU-Ressourcen zwischen Ihrer vorhandenen Umgebung und Google Cloud zuordnen, sollten Sie wissen, wie viele physische oder virtuelle Kerne Ihr Server hat. Weitere Informationen finden Sie im Abschnitt Ressourcen zuordnen.

CPU-Taktrate

Damit ein Programm ausgeführt werden kann, muss es in eine Reihe von Befehlen unterteilt werden, die der Prozessor versteht. Sehen Sie sich dazu das folgende C-Programm an, das zwei Zahlen addiert und das Ergebnis anzeigt:

#include <stdio.h>
int main()
{
        int a = 11, b = 8;
        printf("%d \n", a+b);
}

Bei der Kompilierung wird das Programm in den folgenden Assembler-Code konvertiert:

...
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movl    $11, -8(%rbp)
        movl    $8, -4(%rbp)
        movl    -8(%rbp), %edx
        movl    -4(%rbp), %eax
        addl    %edx, %eax
        movl    %eax, %esi
        movl    $.LC0, %edi
        movl    $0, %eax
        call    printf
        movl    $0, %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
...

Jeder Assembler-Befehl in der vorherigen Ausgabe entspricht einem einzelnen Maschinenbefehl. Der Befehl pushq gibt beispielsweise an, dass der Inhalt des RBP-Registers in den Programmstack verschoben werden soll. Eine CPU kann während jedes CPU-Zyklus einen grundlegenden Vorgang ausführen, z. B. einen Befehl abrufen, auf den Inhalt eines Registers zugreifen oder Daten schreiben. In diesem CPU-Simulator können Sie die einzelnen Phasen des Zyklus zum Addieren von zwei Zahlen durchlaufen.

Beachten Sie, dass jeder CPU-Befehl die Ausführung mehrerer Taktzyklen erfordern kann. Die durchschnittliche Anzahl der Taktzyklen, die pro Befehl für ein Programm erforderlich sind, wird durch die Zyklen pro Befehl (Cycles Per Instruction, CPI) definiert:

Zyklen pro Befehl = Anzahl der verwendeten CPU-Zyklen ÷ Anzahl der ausgeführten Befehle

Die meisten modernen CPUs können über Befehlspipelines mehrere Befehle pro Taktzyklus ausführen. Die durchschnittliche Anzahl der Befehle, die pro Zyklus ausgeführt werden, wird durch die Befehle pro Zyklus (Instructions Per Cycle, IPC) definiert:

Befehle pro Zyklus = Anzahl der ausgeführten Befehle ÷ Anzahl der verwendeten CPU-Zyklen

Die CPU-Taktrate definiert die Anzahl der Taktzyklen, die der Prozessor pro Sekunde ausführen kann. Beispielsweise kann ein Prozessor mit 3,0 GHz drei Milliarden Taktzyklen pro Sekunde ausführen. Dies bedeutet, dass die Ausführung jedes Taktzyklus etwa 0,3 Nanosekunden in Anspruch nimmt. Während jedes Taktzyklus kann eine CPU einen oder mehrere Befehle wie durch den IPC-Wert definiert ausführen.

Taktraten werden häufig zum Vergleich der Prozessorleistung herangezogen. Aus ihrer Definition (Anzahl der pro Sekunde ausgeführten Zyklen) könnte man schließen, dass eine höhere Anzahl von Taktzyklen bedeutet, dass die CPU mehr Arbeit bewältigen und somit eine bessere Leistung erzielen kann. Diese Schlussfolgerung kann beim Vergleich von CPUs in derselben Prozessorgeneration Gültigkeit haben. Taktraten sollten jedoch nicht als alleiniger Leistungsindikator für den Vergleich von CPUs aus verschiedenen Prozessorfamilien dienen. Eine CPU der neuen Generation bietet möglicherweise eine bessere Leistung, auch wenn sie mit einer niedrigeren Taktrate ausgeführt wird als CPUs früherer Generationen.

Taktraten und Systemleistung

Damit Sie die Leistung eines Prozessors besser verstehen, ist es wichtig, dass Sie nicht nur die Anzahl der Taktzyklen berücksichtigen, sondern auch die Arbeitslast, die eine CPU pro Zyklus bewältigen kann. Die Gesamtausführungszeit eines CPU-gebundenen Programms hängt nicht nur von der Taktrate ab, sondern auch von anderen Faktoren wie der Anzahl der auszuführenden Befehle, den Zyklen pro Befehl oder den Befehlen pro Zyklus, der Befehlssatzarchitektur, den Planungs- und Weiterleitungsalgorithmen sowie der verwendeten Programmiersprache. Diese Faktoren können von Prozessorgeneration zu Prozessorgeneration stark variieren.

Am Beispiel eines einfachen faktoriellen Programms können Sie sehen, wie sich die CPU-Ausführung bei zwei verschiedenen Implementierungen unterscheiden kann. Eines der folgenden Programme ist in C und das andere in Python geschrieben. Perf (ein Profilerstellungstool für Linux) wird verwendet, um einige CPU- und Kernelmesswerte zu erfassen.

C-Programm

#include <stdio.h>
int main()
{
    int n=7, i;
    unsigned int factorial = 1;
    for(i=1; i<=n; ++i){
            factorial *= i;
    }

    printf("Factorial of %d = %d", n, factorial);
}

Performance counter stats for './factorial':

...
0             context-switches       #    0.000 K/sec
0             cpu-migrations         #    0.000 K/sec
45            page-faults            #    0.065 M/sec
1,562,075     cycles                 #    1.28 GHz
1,764,632     instructions           #    1.13  insns per cycle
314,855       branches               #    257.907 M/sec
8,144         branch-misses          #    2.59% of all branches
...

0.001835982 seconds time elapsed

Python-Programm

num = 7
factorial = 1
for i in range(1,num + 1):
  factorial = factorial*i
print("The factorial of",num,"is",factorial)

Performance counter stats for 'python3 factorial.py':

...
7              context-switches      #    0.249 K/sec
0              cpu-migrations        #    0.000 K/sec
908            page-faults           #    0.032 M/sec
144,404,306    cycles                #    2.816 GHz
158,878,795    instructions          #    1.10  insns per cycle
38,254,589     branches              #    746.125 M/sec
945,615        branch-misses         #    2.47% of all branches
...

0.029577164 seconds time elapsed

Der hervorgehobene Teil der Ausgabe zeigt die Gesamtzeit, die zur Ausführung des jeweiligen Programms benötigt wurde. Das in C geschriebene Programm wurde etwa 15-mal schneller ausgeführt als das in Python geschriebene Programm (1,8 Millisekunden im Vergleich zu 30 Millisekunden). Hier einige zusätzliche Vergleiche:

  • Kontextwechsel: Wenn der Systemplaner ein anderes Programm ausführen muss oder ein Interrupt eine laufende Ausführung auslöst, speichert das Betriebssystem den CPU-Registerinhalt des laufenden Programms und bereitet ihn für die neue Programmausführung vor. Während bei Ausführung des C-Programms keine Kontextwechsel erfolgt sind, wurden bei Ausführung des Python-Programms sieben Kontextwechsel vorgenommen.

  • CPU-Migrationen: Das Betriebssystem versucht, den Arbeitslastausgleich zwischen den verfügbaren CPUs in Mehrprozessorsystemen aufrechtzuerhalten. Dieser Ausgleich erfolgt regelmäßig und jedes Mal, wenn eine CPU-Ausführungswarteschlange leer ist. Während des Tests wurde keine CPU-Migration beobachtet.

  • Befehle: Das C-Programm ist auf 1,7 Millionen Befehlen hinausgelaufen, die in 1,5 Millionen CPU-Zyklen ausgeführt wurden (IPC = 1,13, CPI = 0,88), während das Python-Programm 158 Millionen Befehle zur Folge hatte, die in 144 Millionen Zyklen ausgeführt wurden (IPC = 1,10, CPI = 0.91). Beide Programme haben die Pipeline gefüllt, sodass die CPU mehrere Befehle pro Zyklus ausführen konnte. Allerdings ist die Anzahl der für Python generierten Befehle im Vergleich zu C etwa 90-mal größer.

  • Seitenfehler: Jedes Programm hat ein Slice des virtuellen Arbeitsspeichers, das alle zugehörigen Befehle und Daten enthält. Normalerweise ist es nicht effizient, all diese Befehle auf einmal in den Hauptarbeitsspeicher zu kopieren. Ein Seitenfehler tritt immer dann auf, wenn der Inhalt des virtuellen Arbeitsspeichers eines Programms teilweise in den Hauptarbeitsspeicher kopiert werden muss. Ein Seitenfehler wird von der CPU durch einen Interrupt angezeigt.

    Da die ausführbare Interpreter-Datei für Python viel größer ist als für C, zeigt sich der zusätzliche Overhead sowohl in Bezug auf die CPU-Zyklen (1,5 Millionen für C, 144 Millionen für Python) als auch in Bezug auf die Seitenfehler (45 für C, 908 für Python).

  • Verzweigungen und falsch vorhergesagte Verzweigungen: Bei bedingten Befehlen versucht die CPU, den Ausführungspfad vorherzusagen, bevor die Verzweigungsbedingung ausgewertet wird. Ein solcher Schritt ist nützlich, um die Befehlspipeline gefüllt zu halten. Dieser Vorgang wird als spekulative Ausführung bezeichnet. Die spekulative Ausführung war in den obigen Ausführungen recht erfolgreich: Falsche Verzweigungsvorhersagen haben nur 2,59 % der Zeit für das C-Programm und 2,47 % der Zeit für das Python-Programm beansprucht.

Andere Faktoren als CPU

Bisher haben Sie verschiedene Aspekte von CPUs und deren Auswirkungen auf die Leistung betrachtet. Es kommt jedoch selten vor, dass die gesamte Ausführung einer Anwendung ununterbrochen in der CPU stattfindet. Sehen Sie sich als einfaches Beispiel den folgenden tar-Befehl an, der ein Archiv aus dem home-Verzeichnis eines Nutzers in Linux erstellt:

$ time tar cf archive.tar /home/"$(whoami)"

Sie erhalten folgende Ausgabe:

real  0m35.798s
user  0m1.146s
sys   0m6.256s

Diese Ausgabewerte sind so definiert:

Echtzeit
Die Echtzeit (real) ist die Zeit, die die Ausführung von Anfang bis Ende dauert. Diese verstrichene Zeit umfasst Zeitabschnitte, die von anderen Prozessen belegt werden, sowie die Zeit, in der der Prozess blockiert ist, z. B. beim Warten auf den Abschluss von E/A-Vorgängen.
Nutzerzeit
Die Nutzerzeit (user) ist die CPU-Zeit, die für die Ausführung von User-Space-Code im Prozess benötigt wird.
Systemzeit
Die Systemzeit (sys) ist die CPU-Zeit, die für die Ausführung von Kernel-Space-Code im Prozess benötigt wird.

Im vorherigen Beispiel beträgt die Nutzerzeit 1,0 Sekunden, während die Systemzeit bei 6,3 Sekunden liegt. Die Differenz von ca. 28 Sekunden zwischen der Zeit von real und der Zeit von user + sys ist die Zeit, die der tar-Befehl außerhalb der CPU verbracht hat.

Wenn für eine Ausführung viel Zeit außerhalb der CPU aufgewendet wird, bedeutet dies, dass der Prozess nicht CPU-gebunden ist. Eine Berechnung ist immer dann an etwas gebunden, wenn die jeweilige Ressource den Engpass für das Erreichen der erwarteten Leistung darstellt. Daher ist es beim Planen einer Migration wichtig, eine ganzheitliche Sicht auf die Anwendung zu haben und alle Faktoren zu berücksichtigen, die einen erheblichen Einfluss auf die Leistung haben können.

Rolle der Zielarbeitslast bei der Migration

Damit Sie einen vernünftigen Ausgangspunkt für die Migration finden, ist es wichtig, ein Benchmarking für die zugrunde liegenden Ressourcen auszuführen. Für das Leistungsbenchmarking haben Sie verschiedene Möglichkeiten:

  • Tatsächliche Zielarbeitslast: Stellen Sie die Anwendung in der Zielumgebung bereit und vergleichen Sie die Werte der Leistungskennzahlen (Key Performance Indicators, KPIs). KPIs für eine Webanwendung können beispielsweise Folgendes enthalten:

    • Ladezeit der Anwendung
    • Endnutzerlatenzen für End-to-End-Transaktionen
    • Unterbrochene Verbindungen
    • Anzahl der Bereitstellungsinstanzen für niedrige, durchschnittliche und maximale Zugriffszahlen
    • Ressourcennutzung (CPU, RAM, Laufwerk, Netzwerk) der Bereitstellungsinstanzen

    Die vollständige (oder teilweise) Bereitstellung einer Zielanwendung kann jedoch komplex und zeitaufwendig sein. Daher werden für das Benchmarking im Vorfeld programmbasierte Benchmarks im Allgemeinen bevorzugt.

  • Programmbasierte Benchmarks: Bei programmbasierten Benchmarks stehen die einzelnen Komponenten der Anwendung im Mittelpunkt und nicht der Anwendungsfluss von Anfang bis Ende. Diese Benchmarks führen einen Mix aus Testprofilen aus, wobei jedes Profil auf eine Komponente der Anwendung ausgerichtet ist. Testprofile für eine LAMP-Stack-Bereitstellung können beispielsweise Apache Bench zum Benchmarking der Webserverleistung und Sysbench zum Benchmarking von MySQL umfassen. Diese Tests sind in der Regel einfacher einzurichten als tatsächliche Zielarbeitslasten und sehr gut auf verschiedene Betriebssysteme und Umgebungen übertragbar.

  • Kernelbenchmarks oder synthetische Benchmarks: Zum Testen wichtiger rechenintensiver Aspekte aus echten Programmen können Sie synthetische Benchmarks wie die Matrixfaktorisierung oder FFT verwenden. Sie führen diese Tests in der Regel in der frühen Designphase der Anwendung aus. Die Tests eignen sich vor allem, um das Benchmarking nur für bestimmte Aspekte einer Maschine auszuführen, beispielsweise VM- und Laufwerkbelastung, E/A-Synchronisierungen und Cache-Seitenflattern.

Anwendung verstehen

Viele Anwendungen sind durch CPU, Arbeitsspeicher, Laufwerk- und Netzwerk-E/A oder eine Kombination dieser Faktoren gebunden. Wenn beispielsweise Konflikte auf Laufwerken der Grund für eine langsame Anwendung sind, wird die Leistung durch das Hinzufügen von mehr Kernen zu den Servern nicht unbedingt verbessert.

Die Beobachtbarkeit von Anwendungen in großen, komplexen Umgebungen aufrechtzuerhalten, kann eine anspruchsvolle Aufgabe sein. Es gibt jedoch spezialisierte Monitoringsysteme, die alle verteilten, systemweiten Ressourcen verfolgen können. In Google Cloud steht Ihnen beispielsweise Cloud Monitoring zur Verfügung, das Ihnen volle Transparenz über Ihren Code, Ihre Anwendungen und Ihre Infrastruktur liefert. Ein Cloud Monitoring-Beispiel wird weiter unten in diesem Abschnitt erläutert. Zum besseren Verständnis soll aber zuerst auf das Monitoring typischer Systemressourcen auf einem eigenständigen Server eingegangen werden.

Viele Dienstprogramme wie top, IOStat, VMStat und iPerf können Ihnen einen Überblick über die Ressourcen eines Systems verschaffen. Wenn Sie beispielsweise top auf einem Linux-System ausführen, erhalten Sie eine Ausgabe wie diese:

top - 13:20:42 up 22 days,  5:25,         18 users,        load average: 3.93 2.77,3.37
Tasks:  818 total,        1 running,      760 sleeping,    0 stopped,       0 zombie
Cpu(s): 88.2%us,          0.0%sy,         0.0%ni,          0.3%id,          0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem:    49375504k total,  6675048k used,  42700456k free,  276332k buffers
Swap:   68157432k total,  0k used,        68157432k free,  5163564k cached

Ein System mit hoher Last und hoher prozentualer Wartezeit ist ein Anzeichen dafür, dass die Anwendung E/A-gebunden ist. Wenn die prozentuale Nutzer- oder Systemzeit bzw. beide Messwerte sehr hoch sind, ist die Anwendung wahrscheinlich CPU-gebunden.

Im vorherigen Beispiel liegt die durchschnittliche Last (für eine VM mit vier vCPUs) in den letzten 5, 10 und 15 Minuten bei 3,93, 2,77 bzw. 3,37. Wenn Sie diese Durchschnittswerte in Kombination mit dem hohen Prozentsatz der Nutzerzeit (88,2 %), der niedrigen Inaktivitätszeit (0,3 %) und der Wartezeit von 0,0 % betrachten, liegt der Schluss nahe, dass das System CPU-gebunden ist.

Obwohl sich diese Tools gut für eigenständige Systeme eignen, sind sie in der Regel nicht für das Monitoring großer, verteilter Umgebungen ausgelegt. Für das Monitoring von Produktionssystemen sind Tools wie Cloud Monitoring, Nagios, Prometheus und Sysdig vorzuziehen, die detaillierte Analysen zur Ressourcennutzung Ihrer Anwendungen liefern können.

Durch das Leistungsmonitoring Ihrer Anwendung über einen ausreichend langen Zeitraum können Sie Daten aus mehreren Messwerten wie CPU-Auslastung, Arbeitsspeichernutzung, Laufwerk-E/A, Netzwerk-E/A, Roundtrip-Zeiten, Latenzen, Fehlerraten und Durchsatz erfassen. Die folgenden Cloud Monitoring-Diagramme zeigen beispielsweise die CPU-Lasten und -Auslastungsgrade sowie die Arbeitsspeicher- und Laufwerknutzung für alle Server, die in einer von Google Cloud verwalteten Instanzgruppe ausgeführt werden. Weitere Informationen zu dieser Konfiguration finden Sie in der Übersicht zum Cloud Monitoring-Agent.

Diagramm: CPU-Lasten und -Auslastungsgrade sowie Arbeitsspeicher- und Laufwerknutzung für alle Server, die in einer verwalteten Instanzgruppe ausgeführt werden.

Der Zeitraum der Datenerfassung zu Analysezwecken sollte lang genug sein, um die Höchst- und Tiefstwerte der Ressourcennutzung aufzuzeigen. Die anschließende Analyse der erfassten Daten kann dann als Ausgangspunkt für die Kapazitätsplanung in der neuen Zielumgebung dienen.

Ressourcen zuordnen

Dieser Abschnitt führt Sie Schritt für Schritt durch die Größenbestimmung von Ressourcen in Google Cloud. Zuerst nehmen Sie eine anfängliche Größenschätzung anhand der vorhandenen Ressourcennutzungsgrade vor. Anschließend führen Sie anwendungsspezifische Tests für das Leistungsbenchmarking aus.

Nutzungsbasierte Größenanpassung

Führen Sie folgende Schritte aus, um die Anzahl der vorhandenen Kerne eines Servers den vCPUs in Google Cloud zuzuordnen.

  1. Ermitteln Sie die aktuelle Anzahl der Kerne. Verwenden Sie dazu den lscpu-Befehl aus dem obigen Abschnitt.

  2. Ermitteln Sie die CPU-Auslastung des Servers. Die CPU-Auslastung bezieht sich auf die Zeit, die die CPU benötigt, wenn sie sich im Nutzermodus (%us) oder Kernelmodus (%sy) befindet. Nice-Prozesse (%ni) gehören ebenfalls zum Nutzermodus, während Software-Interrupts (%si) und Hardware-Interrupts (%hi) im Kernelmodus verarbeitet werden. Wenn keiner dieser Vorgänge von der CPU verarbeitet wird, ist die CPU entweder inaktiv oder wartet auf den Abschluss eines E/A-Vorgangs. Ein Prozess, der auf den Abschluss eines E/A-Vorgangs wartet, schlägt sich nicht in den CPU-Zyklen nieder.

    Führen Sie folgenden top-Befehl aus, um die aktuelle CPU-Nutzung eines Servers zu berechnen:

    ...
    Cpu(s): 88.2%us,  0.0%sy,  0.0%ni,  0.3%id,  0.0%wa,  0.0%hi,  0.0%si, 0.0%st
    ...
    

    Die CPU-Nutzung ist so definiert:

    CPU Usage = %us + %sy + %ni + %hi + %si
    

    Alternativ können Sie ein beliebiges Monitoringtool wie Cloud Monitoring verwenden, das das erforderliche CPU-Inventar und die CPU-Auslastung erfassen kann. Für Anwendungsbereitstellungen ohne Autoscaling (also mit einem einzigen Server oder einer festen Anzahl von Servern) sollten Sie die Spitzenauslastung zum Festlegen der CPU-Größe heranziehen. Mit diesem Ansatz werden Anwendungsressourcen vor Unterbrechungen geschützt, wenn Arbeitslasten die Spitzenauslastung erreichen. Bei Bereitstellungen mit Autoscaling (auf Basis der CPU-Nutzung) ist die durchschnittliche CPU-Auslastung eine sichere Referenz für die Größenbestimmung. In diesem Fall können Sie zur Bewältigung von Trafficspitzen die Anzahl der Server für die Dauer der Spitzen hochskalieren.

  3. Weisen Sie genügend Puffer für eventuelle Spitzen zu. Beziehen Sie beim Festlegen der CPU-Größe ausreichend Puffer ein, um ungeplante Verarbeitungsvorgänge zu bewältigen, die zu unerwarteten Spitzen führen können. Beispielsweise können Sie die CPU-Kapazität so planen, dass ein zusätzlicher Toleranzbereich von 10 bis 15 % zuzüglich zur erwarteten Spitzenauslastung zur Verfügung steht und die maximale CPU-Auslastung insgesamt 70 % nicht überschreitet.

  4. Verwenden Sie die folgende Formel, um die erwartete Anzahl von Kernen auf der GCP zu berechnen:

    vCPUs in Google Cloud = 2 × CEILING[(core-count × utilization%) ÷ (2 × threshold%)]

    Diese Werte sind so definiert:

    • core-count: die vorhandene Anzahl von Kernen (wie in Schritt 1 berechnet).
    • utilization%: die CPU-Auslastung des Servers (wie in Schritt 2 berechnet).
    • threshold%: die maximale CPU-Nutzung, die auf dem Server zulässig ist, nachdem ein ausreichender Toleranzbereich berücksichtigt wurde (wie in Schritt 3 berechnet).

Hier ein konkretes Beispiel: Sie müssen die Anzahl der Kerne eines lokal ausgeführten Bare-Metall-Servers vom Typ Xeon E5640 mit vier Kernen den vCPUs in Google Cloud zuordnen. Die Xeon E5640-Spezifikation ist öffentlich verfügbar. Sie können diese jedoch auch mit einem Befehl wie lscpu auf dem System bestätigen. Die Zahlen sehen so aus:

  • Vorhandene Anzahl lokaler Kerne = Sockets (1) × Kerne (4) × Threads pro Kern (2) = 8.
  • Die CPU-Auslastung (utilization%), die während des Spitzentraffics beobachtet wird, beträgt 40 %.
  • Als zusätzlicher Puffer sind 30 % vorgesehen. Das heißt, die maximale CPU-Auslastung (threshold%) sollte 70 % nicht überschreiten.
  • vCPUs in Google Cloud = 2 × CEILING[(8 × 0,4)/(2 × 0,7)] = 6 vCPUs
    (das heißt, 2 × CEILING[3,2/1,4] = 2 × CEILING[2,28] = 2 × 3 = 6)

Sie können ähnliche Schätzungen für RAM, Laufwerke, Netzwerk-E/A und andere Systemressourcen vornehmen.

Leistungsbasierte Größenanpassung

Im vorstehenden Abschnitt wurde die Zuordnung von pCPUs zu vCPUs anhand von aktuellen und erwarteten CPU-Nutzungsgraden ausführlich behandelt. Nun soll die Anwendung betrachtet werden, die auf dem Server ausgeführt wird.

In diesem Abschnitt führen Sie eine standardmäßige, kanonische Gruppe von Tests (programmbasierte Benchmarks) aus, um die Leistung zu vergleichen. Fahren Sie mit dem Beispielszenario fort und gehen Sie davon aus, dass Sie auf der Xeon E5-Maschine einen MySQL-Server ausführen. In diesem Fall können Sie die Datenbankleistung mithilfe von Sysbench OLTP vergleichen.

Ein einfacher Lese-/Schreibtest für MySQL mit Sysbench liefert folgende Ausgabe:

OLTP test statistics:
  queries performed:
    read:              520982
    write:             186058
    other:             74424
    total:             781464
  transactions:        37211 (620.12 per sec.)
  deadlocks:           2 (0.03 per sec.)
  read/write requests: 707040 (11782.80 per sec.)
  other operations:    74424 (1240.27 per sec.)

Test execution summary:
  total time:          60.0061s
  total number of events: 37211
  total time taken by event execution: 359.8158
  per-request statistics:
    min:            2.77ms
    avg:            9.67ms
    max:            50.81ms
    approx. 95 percentile: 14.63ms

Thread fairness:
  events (avg/stddev):         6201.8333/31.78
  execution time (avg/stddev): 59.9693/0.00

Wenn Sie diese Benchmark ausführen, können Sie die Leistung in Bezug auf die Anzahl der Transaktionen pro Sekunde, die Gesamtzahl der Lese-/Schreibvorgänge pro Sekunde und die End-to-End-Ausführungszeit zwischen Ihrer aktuellen Umgebung und Google Cloud vergleichen. Führen Sie diese Tests möglichst mehrmals aus, um Ausreißer auszuschließen. Sie können die Tests auch mit unterschiedlichen Parametern wie Gleichzeitigkeitsgrade, Testdauer, Anzahl der simulierten Nutzer oder verschiedene Erzeugungsraten ausführen, um Leistungsunterschiede bei variierenden Last- und Trafficmustern zu beobachten.

Die Leistungszahlen aus dem Vergleich zwischen der aktuellen Umgebung und Google Cloud helfen Ihnen, die anfängliche Kapazitätsschätzung zu präzisieren. Wenn die Benchmarkingtests in Google Cloud ähnliche oder bessere Ergebnisse liefern als in der vorhandenen Umgebung, können Sie die Skalierung der Ressourcen auf Basis der Leistungssteigerungen weiter anpassen. Fallen die Benchmarks in Ihrer vorhandenen Umgebung jedoch besser aus als in Google Cloud, sollten Sie so vorgehen:

  • Nehmen Sie die anfängliche Kapazitätsschätzung noch einmal vor.
  • Beobachten Sie Systemressourcen.
  • Ermitteln Sie mögliche Konfliktbereiche (z. B. CPU- und RAM-Engpässe).
  • Passen Sie die Größe von Ressourcen entsprechend an.

Führen Sie anschließend Ihre anwendungsspezifischen Benchmarktests noch einmal aus.

End-to-End-Leistungsbenchmarking

Bisher haben Sie sich ein vereinfachtes Szenario angesehen, bei dem nur die MySQL-Leistung zwischen der lokalen Umgebung und Google Cloud verglichen wurde. In diesem Abschnitt beschäftigen Sie sich mit der folgenden verteilten 3-Tier-Anwendung.

Diagramm: Verteilte 3-Tier-Anwendung.

Im Diagramm sehen Sie, dass mehrerer Benchmarks ausgeführt wurden, um die aktuelle Umgebung und Google Cloud vernünftig beurteilen zu können. Allerdings ist es nicht immer einfach, die Untergruppe von Benchmarks zu bestimmen, mit der sich die Anwendungsleistung am genauesten einschätzen lässt. Darüber hinaus kann sich die Verwaltung des Testprozesses – vom Abhängigkeitsmanagement über die Testinstallation und -ausführung bis hin zur Ergebniszusammenfassung – als mühsam erweisen, insbesondere wenn diese über verschiedene cloudbasierte oder cloudfremde Umgebungen hinweg erfolgen muss. Für solche Szenarien können Sie PerfKitBenchmarker verwenden. PerfKit enthält verschiedene Sätze von Benchmarks zum Messen und Vergleichen verschiedener Angebote in mehreren Clouds. Das Tool kann auch bestimmte Benchmarks lokal über statische Maschinen ausführen.

Angenommen, Sie möchten für die 3-Tier-Anwendung im vorherigen Diagramm Tests ausführen, um die Bootzeit von Clustern, CPU und Netzwerkleistung von VMs in Google Cloud zu vergleichen. In diesem Fall können Sie mit PerfKitBenchmarker relevante Testprofile in mehreren Iterationen ausführen, die folgende Ergebnisse liefern:

  • Bootzeit von Clustern: stellt die Bootzeit von VMs grafisch dar. Dies ist besonders wichtig, wenn die Anwendung flexibel ist und erwartet, dass Instanzen im Rahmen des Autoscalings hinzugefügt oder entfernt werden. Die folgende Grafik zeigt, dass die Bootzeiten einer Compute Engine-Instanz vom Typ n1-standard-4 mit 40 bis 45 Sekunden ziemlich konstant bleiben (bis zum 99. Perzentil).

    Grafik: Bootzeiten einer Compute Engine-Instanz vom Typ "n1-standard-4".

  • Stress-ng: misst und vergleicht die Leistung bei rechenintensiven Vorgängen, indem der Prozessor, das Arbeitsspeicher-Subsystem und der Compiler eines Systems unter Last gestellt werden. Im folgenden Beispiel führt stress-ng mehrere Stresstests wie bsearch, malloc, matrix, mergesort und zlib auf einer Compute Engine-Instanz vom Typ n1-standard-4 aus. Stress-ng misst den Stresstest-Durchsatz mithilfe von simulierten Vorgängen pro Sekunde. Wenn Sie simulierte Vorgänge über verschiedene Stresstestergebnisse hinweg normalisieren, erhalten Sie die folgende Ausgabe, die das geometrische Mittel der pro Sekunde ausgeführten Vorgänge zeigt. In diesem Beispiel reichen die simulierten Vorgänge von etwa 8.000 pro Sekunde beim 50. Perzentil bis zu etwa 10.000 pro Sekunde beim 95. Perzentil. Simulierte Vorgänge werden im Allgemeinen nur verwendet, um die Leistung von eigenständigen CPUs zu vergleichen. Diese Vorgänge sind möglicherweise nicht repräsentativ für Ihre Anwendung.

    Grafik: Geometrisches Mittel der pro Sekunde ausgeführten Vorgänge.

  • Netperf: misst die Latenz mithilfe von Anfrage- und Antworttests. Anfrage- und Antworttests werden in der Anwendungsschicht des Netzwerkstacks ausgeführt. Diese Methode zum Testen der Latenz bezieht alle Schichten des Stacks ein und wird gegenüber Ping-Tests zum Messen der Latenz zwischen VMs bevorzugt. Die folgende Grafik zeigt die TCP-Anfrage- und -Antwortlatenzen (TCP_RR) zwischen einem Client und einem Server, die in derselben Google Cloud-Zone ausgeführt werden. TCP_RR-Werte reichen von etwa 70 Mikrosekunden beim 50. Perzentil bis etwa 130 Mikrosekunden beim 90. Perzentil.

    Grafik: TCP-Anfrage- und -Antwortlatenzen zwischen Client und Server, die in derselben Google Cloud-Zone ausgeführt werden.

Je nach Art der Zielarbeitslast können Sie andere Testprofile mit PerfKit ausführen. Weitere Informationen finden Sie in der Liste der unterstützten Benchmarks in PerfKit.

Best Practices in Google Cloud

Bevor Sie einen Migrationsplan in Google Cloud erstellen und ausführen, empfehlen wir, dass Sie die Best Practices für die Migration befolgen. Diese Vorgehensweisen sind allerdings nur ein Ansatzpunkt. Möglicherweise müssen Sie viele weitere Aspekte Ihrer Anwendung berücksichtigen, z. B. die Entkoppelung von Abhängigkeiten, die Einführung von Fehlertoleranz und das Hoch- und Herunterskalieren auf Basis der Komponentenarbeitslast. Außerdem müssen Sie sich überlegen, wie diese Aspekte jeweils in Google Cloud zugeordnet werden.

  1. Informieren Sie sich über die Limits und Kontingente von Google Cloud. Bevor Sie offiziell mit der Kapazitätsschätzung beginnen, sollten Sie einige wichtige Punkte der Ressourcenplanung in Google Cloud überdenken. Beispiele:

    Listen Sie die IaaS-Komponenten (Infrastructure as a Service) und PaaS-Komponenten (Platform as a Service) auf, die während der Migration benötigt werden, und informieren Sie sich genau über die Kontingente und Limits sowie die Feinabstimmungsoptionen, die für die einzelnen Dienste verfügbar sind.

  2. Behalten Sie Ressourcen kontinuierlich im Blick. Durch ein kontinuierliches Ressourcenmonitoring können Sie Muster und Trends in der System- und Anwendungsleistung erkennen. Das Monitoring hilft nicht nur beim Ermitteln der Referenzleistung, sondern zeigt auch, wann Upgrades und Downgrades der Hardware erforderlich sind. Google Cloud bietet verschiedene Optionen für die Bereitstellung von End-to-End-Monitoringlösungen:

  3. Passen Sie die Größe Ihrer VMs entsprechend an. Sie müssen erkennen können, wann eine VM über- bzw. unterdimensioniert ist. Wenn Sie ein grundlegendes Monitoring wie zuvor erläutert einrichten, sollten Sie die entsprechenden Informationen problemlos erhalten. Google Cloud bietet auch Empfehlungen für die richtige Größe, die auf der bisherigen Nutzung einer Instanz basieren. Wenn die vordefinierten Maschinentypen Ihre Anforderungen nicht erfüllen, können Sie außerdem eine Instanz mit benutzerdefinierten virtualisierten Hardwareeinstellungen erstellen, die Ihrer Arbeitslast entsprechen.

  4. Verwenden Sie die richtigen Tools. Stellen Sie in großen Umgebungen automatisierte Tools bereit, um den manuellen Aufwand zu minimieren. Beispiele:

Kernpunkte:

Die Migration von Ressourcen von einer Umgebung zu einer anderen erfordert sorgfältige Planung. Es ist wichtig, Hardwareressourcen nicht isoliert zu betrachten, sondern einen umfassenden Überblick über die Anwendung zu erlangen. Statt nur herausfinden zu wollen, ob ein Sandy Bridge-Prozessor mit 3,0 GHz doppelt so schnell sein wird wie ein Skylake-Prozessor mit 1,5 GHz, sollten Sie sich darauf konzentrieren, in welchem Maße sich die Leistungskennzahlen Ihrer Anwendung von einer Computing-Plattform zur anderen ändern.

Wenn Sie die Anforderungen an die Ressourcenzuordnung zwischen verschiedenen Umgebungen beurteilen, sollten Sie Folgendes berücksichtigen:

  • Systemressourcen, die Ihre Anwendung einschränken (z. B. CPU, Arbeitsspeicher, Laufwerk oder Netzwerk).
  • Auswirkungen der zugrunde liegenden Infrastruktur (z. B. Prozessorgeneration, Taktrate, HDD oder SSD) auf die Leistung der Anwendung.
  • Auswirkungen von Entscheidungen im Software- und Architekturdesign (z. B. Single- oder Multi-Threaded-Arbeitslasten, Bereitstellungen mit oder ohne Autoscaling) auf die Leistung der Anwendung.
  • Aktuelle und erwartete Nutzungsgrade von Rechen-, Speicher- und Netzwerkressourcen.
  • Am besten geeignete Leistungstests, die für Ihre Anwendung repräsentativ sind.

Das Erfassen von Daten für diese Messwerte durch kontinuierliches Monitoring hilft Ihnen bei der anfänglichen Kapazitätsplanung. Danach können Sie die ersten Größenschätzungen mithilfe eines angemessenen Leistungsbenchmarking weiter präzisieren.

Nächste Schritte