Einrichten des JVM-Anwendungsspeichers in Kubernetes / Sudo Null IT News

Freunde, hallo an alle! Wie Sie wissen, hat jeder Pod in Kubernetes ein Limit für die Speichernutzung (limits.memory), und wie die Erfahrung zeigt, ist es bei weitem nicht immer offensichtlich, wie die JVM-Anwendung diese Einstellung interpretiert, was manchmal zu OOMKill führen kann.

Ich möchte eine der Möglichkeiten zum Konfigurieren von Speicher für Java-Anwendungen in Kubernetes vorstellen. Ich muss gleich sagen, dass die endgültigen Einstellungen, zu denen wir kommen werden, nur als Beispiel gegeben werden und für jede Anwendung individuell konfiguriert werden sollten. Wir betrachten die Einstellungen und Metriken eines regulären Spring Boot-Microservices, der in Spring Boot Admin (im Folgenden einfach SBA) integriert ist.

Lassen Sie uns zunächst ein wenig Theorie über das Speichergerät in Java auffrischen. Kurz gesagt, der globale Speicher ist vereinfacht in zwei Abschnitte unterteilt:

Gesamt:

Heap (Eden, Survivor, Tenured) + Nicht-Heap (Metaspace + Code-Cache + Thread-Stack-Bereich + direkte Puffer + Symboltabellen + andere JVM-Strukturen).

Sehen wir uns nun an, wie eine Spring Boot-Anwendung ohne Speicheroptimierung mit Speicher arbeitet, indem Sie memory.limits in Kubernetes auf 1280 MB festlegen.

Wenn Native Memory Tracking (NMT) konfiguriert ist, können detaillierte Informationen mit dem Befehl jcmd 1 VM.native_memory abgerufen werden.

Native Memory TrackingTotal: reserviert=2014514KB, festgeschrieben=626614KB – Java Heap (reserviert=327680KB, festgeschrieben=259652KB) (mmap: reserviert=327680KB, festgeschrieben=259652KB) – Klasse (reserviert=1229865KB, festgeschrieben=205737KB) (Klassen #36029) (Instance-Klassen #33803, Array-Klassen #2226) (malloc=7209KB #105907) (mmap: reserviert=1222656KB, festgeschrieben=198528KB) (Metadaten:) (reserviert=174080KB, festgeschrieben=173568KB) (verwendet=169946KB) (frei= 3622 KB) ( Verschwendung = 0 KB = 0,00 %) ( Klassenraum:) ( reserviert = 1048576 KB, festgeschrieben = 24960 KB) ( belegt = 22922 KB) ( frei = 2038 KB) ( Verschwendung = 0 KB = 0,00 %) – Thread (reserviert = 134801 KB, festgeschrieben =10461KB) (Thread #82) (Stack: reserviert=134408KB, festgeschrieben=10068KB) (malloc=296KB #494) (arena=97KB #163) – Code (reserviert=251808KB, festgeschrieben=80620KB) (malloc=4120KB #15476 ) (mmap: reserviert=247688KB, festgeschrieben=76500KB) – GC (reserviert=2219KB, festgeschrieben=2003KB) (malloc=1147KB #4677) (mmap: reserviert=1072KB, festgeschrieben=856KB) – Compiler (reserviert=907KB, festgeschrieben= 907KB) (malloc=777KB #1701) (aren a=131KB #5) – Intern (reserviert=9479KB, festgeschrieben=9479KB) (malloc=9479KB #19066) – Andere (reserviert=4154KB, festgeschrieben=4154KB) (malloc=4154KB #191) – Symbol (reserviert=40259KB, festgeschrieben =40259KB) (malloc=33634KB #418136) (arena=6624KB #1) – Native Memory Tracking (reserviert=9068KB, festgeschrieben=9068KB) (malloc=32KB #432) (Tracking-Overhead=9035KB) – Arena Chunk (reserviert=2484KB , committet=2484KB) (malloc=2484KB) – Protokollierung (reserviert=4KB, committet=4KB) (malloc=4KB #189) – Argumente (reserviert=31KB, committet=31KB) (malloc=31KB #498) – Modul (reserviert =1158KB, festgeschrieben=1158KB) (malloc=1158KB #6305) – Synchronizer (reserviert=558KB, festgeschrieben=558KB) (malloc=558KB #4724) – Sicherungspunkt (reserviert=8KB, festgeschrieben=8KB) (mmap: reserviert=8KB, festgeschrieben=8KB, festgeschrieben=8KB) – Unbekannt (reserviert=32KB, festgeschrieben=32KB) (mmap: reserviert=32KB, festgeschrieben=32KB)

SBA:

Schauen wir uns nun die Daten aus dem Admin-Panel an:

Es ist ersichtlich, dass für Heap maximal nur 324 MB und für Non-Heap 1,33 GB zugewiesen werden, während dem Pod nur 1280 MB Speicher zugewiesen wurden. Wenn Sie die Heap- und Nicht-Heap-Größen hinzufügen, können Sie sehen, dass die Menge an Arbeitsspeicher, die die Anwendung verwenden kann, weit über das Containerlimit hinausgeht. Nun, OOMKill wird uns zur Verfügung gestellt 🙂

Lassen Sie uns versuchen, die Verteilung ein wenig zu optimieren. Gleichzeitig sollten Sie daran denken, dass unterschiedliche Stände (QA, Stage, Prod) für uns möglicherweise unterschiedliche Speicherkapazitäten benötigen. Um Bilder zu erstellen, verwenden wir die Bibliothek AUSLEGERmit dem Sie bequem Startoptionen für Anwendungen in entry.sh konfigurieren können.

Unsere Anwendung wird in Docker mit dem Befehl gestartet:

java \ -Xms${HEAP_SIZE_MB}M \ -Xmx${HEAP_SIZE_MB}M \ -Xss1M \ -XX:MaxMetaspaceSize=${METASPACE_SIZE_MB}M \ -XX:CompressedClassSpaceSize=${COMPRESSED_CLASS_SPACE_SIZE_MB}M \ -XX:ReservedCodeCacheSize=${ RESERVED_CODE_CACHE_SIZE_MB}M \ -XX:MaxDirectMemorySize=${DIRECT_MEMORY_SIZE_MB}M \ -XX:NativeMemoryTracking=summary \ -cp “/app/resources:/app/classes:/app/libs/*” en.example.application.DemoApplication

Hier werden die Heap- und Non-Heap-Einstellungen separat beschrieben. Versuchen wir es herauszufinden. Heap-Einstellungen:

-Xms${HEAP_SIZE_MB}M \ -Xmx${HEAP_SIZE_MB}M \

Nicht-Heap-Einstellungen:

-Xss1M \ -XX:MaxMetaspaceSize=${METASPACE_SIZE_MB}M \ -XX:CompressedClassSpaceSize=${COMPRESSED_CLASS_SPACE_SIZE_MB}M \ -XX:ReservedCodeCacheSize=${RESERVED_CODE_CACHE_SIZE_MB}M \ -XX:MaxDirectMemorySize=${DIRECT_MEMO}S

Diese Variablen können beim Start der Anwendung in der entry.sh beispielsweise mit der Formel (Beispiel) berechnet werden:

#Umwandlung eines Pod-Speicherlimits von Byte in Megabyte POD_MEM_LIMIT_MB=`expr $POD_MEM_LIMIT / 1024 / 1024` ` #Berechnung der reservierten Code-Cache-Größe #(kein Teil des Metaspace, aber relativ einfacher zu bekommen) RESERVED_CODE_CACHE_SIZE_MB=`expr $METASPACE_SIZE_MB / 3` echo “RESERVED_CODE_CACHE_SIZE_MB=”$RESERVED_CODE_CACHE_SIZE_MB #Berechnung der reservierten Code-Cache-Größe DIRECT_MEMORY_SIZE_MB= `expr $METASPACE_SIZE_MB / 16` echo “DIRECT_MEMORY_SIZE_MB=”$DIRECT_MEMORY_SIZE_MB #Berechnung der reservierten Systemnutzung und andere Zwecke / 4` #Berechnung der gesamten Nicht-Heap-Größe NON_HEAP_SIZE_MB=`expr $METASPACE_SIZE_MB + $RESERVED_CODE_CACHE_SIZE_MB + $DIRECT_MEMORY_SIZE_MB + $OTHER_USAGE_MB` #Berechnung der Heap-Größe HEAP_SIZE_MB=`expr $POD_MEM_LIMIT_MB – $NON_HEAP _SIZE_MB`

Die Größe von Metaspace kann, wie bei anderen Segmenten auch, festgelegt werden, aber lassen wir sie zum Beispiel berechnet.

Und nach einer solchen Einstellung führen wir erneut jcmd 1 VM.native_memory aus und das Bild sieht etwas anders aus:

MoreTotal: reserviert=1109330KB, festgeschrieben=961126KB – Java Heap (reserviert=618496KB, festgeschrieben=618496KB) (mmap: reserviert=618496KB, festgeschrieben=618496KB) – Klasse (reserviert=230915KB, festgeschrieben=202995KB) (Klassen #35923) (Instanz Klassen #33695, Array-Klassen #2228) (malloc=6659KB #96909) (mmap: reserviert=224256KB, festgeschrieben=196336KB) ( Metadaten: ) (reserviert=172032KB, festgeschrieben=171512KB) (verwendet=167998KB) (frei=3514KB) ( Verschwendung = 0 KB = 0,00 %) ( Klassenraum:) ( reserviert = 52224 KB, festgeschrieben = 24824 KB) ( verwendet = 22857 KB) ( frei = 1967 KB) ( Verschwendung = 0 KB = 0,00 %) – Thread (reserviert = 98449 KB, festgeschrieben = 10349 KB ) (Thread #82) (Stack: reserviert=98056KB, festgeschrieben=9956KB) (malloc=296KB #494) (arena=97KB #163) – Code (reserviert=91279KB, festgeschrieben=59095KB) (malloc=3559KB #14025) ( mmap: reserviert=87720KB, festgeschrieben=55536KB) – GC (reserviert=3134KB, festgeschrieben=3134KB) (malloc=1114KB #4516) (mmap: reserviert=2020KB, festgeschrieben=2020KB) – Compiler (reserviert=665KB, festgeschrieben=665KB) (malloc=534KB #1752) (arena=131KB #5) – Intern (reserviert=8429KB, festgeschrieben=8429KB) (malloc=8429KB #16669) – Andere (reserviert=4792KB, festgeschrieben=4792KB) (malloc=4792KB #149) – Symbol (reserviert=40213KB, festgeschrieben=40213KB) (malloc=33588KB # 415272) (arena=6624KB #1) – Native Memory Tracking (reserviert=8808KB, committet=8808KB) (malloc=27KB #341) (Tracking-Overhead=8782KB) – Arena Chunk (reserviert=2419KB, committet=2419KB) (malloc= 2419KB) – Protokollierung (reserviert=4KB, festgeschrieben=4KB) (malloc=4KB #189) – Argumente (reserviert=31KB, festgeschrieben=31KB) (malloc=31KB #498) – Modul (reserviert=1114KB, festgeschrieben=1114KB) ( malloc=1114KB #6172) – Synchronizer (reserviert=543KB, festgeschrieben=543KB) (malloc=543KB #4597) – Safepoint (reserviert=8KB, festgeschrieben=8KB) (mmap: reserviert=8KB, festgeschrieben=8KB) – Unbekannt (reserviert =32KB, festgeschrieben=32KB) (mmap: reserviert=32KB, festgeschrieben=32KB)

SBA:

Schauen wir uns nun die Daten aus dem Admin-Panel an:

Wenn wir jetzt die Segmentgrößen hinzufügen, dann haben wir jetzt alle Heap + Non-Heap-Limits niedriger als die Speichergrenze des Pods und haben etwas Spielraum für andere Ausgaben.

Ergebnisse

Der Speicher-Tuning-Prozess ist ziemlich kompliziert und erfordert die Berücksichtigung vieler kleiner und großer Details und Faktoren, von denen viele in diesem Artikel nicht erwähnt wurden. Wir haben die grundlegenden Elemente zum Einrichten des Anwendungsspeichers sowie eine der Diagnoseoptionen durchgegangen, wobei daran erinnert werden sollte, dass die angegebenen Einstellungen ungefähr sind und für jede Anwendung in einer Kampfumgebung individuell berechnet werden. Vielen Dank!

Nützliche Links:

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *