Sziasztok,
Van néhány, JVM11 felett futó appunk k8s-en. Néhány hete történt, hogy a k8s egyszerre lőtte ki egy deployment összes podját, OOMKilled okkal a tesztkörnyezetben. Szerintem ennek nem kellett volna megtörténnie - a Javas appok a saját igényeiknek beállított `-Xmx`-szel futnak (3200m), a pod memória requestje 4Gi, limitje 5.
Utólagos vizsgálatok alapján az alábbiakat sikerült megszülnöm:
- A memóriahasználat a pod indulásától kúszik fel, normál esetben megáll valahol 4.5 Gb környékén
- A bekötött micrometer szerint a heap max tökéletesen bennemarad a 3200m-ben, de a nonheap memória max ~1.2 Gb.
- lásd a képet lejjebb, de amivel el vagyok veszve, hogy a JVM kapásból befoglalja a micrometer által a `jvm_memory_max_bytes`-ban mondott értéket?
- ha igen, akkor részben boldog vagyok, mert a JVM Heap max + JVM Non-Heap max kiadja azt, amit a k8s szerint a pod memóriában lefoglal a nap végére
- cserébe mintha a heapnél nem így működne, lásd a nap elejét. Tehát memória usage = JVM Heap Committed + JVM NonHeap max?
- miért?
- ha nem, akkor miért mond a k8s akkora memória használatot, ami a `sum(jvm_memory_max_bytes)`-nak felel meg?
- ha igen, akkor részben boldog vagyok, mert a JVM Heap max + JVM Non-Heap max kiadja azt, amit a k8s szerint a pod memóriában lefoglal a nap végére
Namost, ha helyesen értem az interneten talált doksikat, és a micrometer metrikáit, akkor a nonheap áll:
- CodeHeap 'non-nmethods' (a jvm_memory_max_bytes 5 megabájtot mond rá)
- CodeHeap 'non-profiled nmethods' (a jvm_memory_max_bytes ~117 megabájtot mond rá)
- CodeHeap 'profiled nmethods' (a jvm_memory_max_bytes ~117 megabájtot mond rá)
- Compressed Class Space (a jvm_memory_max_bytes 1 gigabájtot mond rá)
- Metaspace (a jvm_memory_max_bytes -1-et mond rá)
Odáig eljutottam a doksi alapján, hogy a Compressed Class Space méretét a `-XX:CompressedClassSpaceSize` kapcsolóval tudom állítani, és hogy a default értéke 1 gigabájt. A kérdésem az, hogy ezt az 1 gigabájtot a JVM lefoglalja egyből induláskor, akár rak oda adatot, akár nem? A jvm_memory_used_bytes metrika szerint ebből a Compressed Class Space-ből az utóbbi ~30 napaban sosem volt több, mint ~30 mega használva, és az is szélsőérték.
TLDR két kérdésem van, hátha valaki nagyon otthon van JVM memory tuningban:
- Ha hagyom a `-XX:CompressedClassSpaceSize`-ot defaulton (~nem adom meg), akkor ezt az 1 gigabájtot a JVM lefoglalja egyből induláskor, akár rak oda adatot, akár nem?
- Ha lejjebb veszem az `-XX:CompressedClassSpaceSize`-ot (de még mindig a valós használat többszörösére, mondjuk 256 megára, szép kerek szám), akkor milyen veszélyeket rántok magamra?
Megpróbáltam végigtúrni az internetet, de nem nagyon találtam semmi hasznosat elsőre. Ha van jó doksid, vagy releváns tapasztalatod, megköszönöm.
Köszi előre is,
- 825 megtekintés
Hozzászólások
Biztos, hogy a podokkal volt valami baj, nem a futtató node(ok) memóriája fogyott el? A leírt jelenség alapján gyanús, hogy de. Részben pont emiatt érdemes a Java-s podok memory request-jét és limit-jét ugyanarra az értékre állítani.
- A hozzászóláshoz be kell jelentkezni
Én mindig hagyok rá egy keveset, azért, mert simán lehet, hogy a Java-s cucc fog valami külső processt spawnolni, aminek amúgy kell memória. Opcionális natív függőségeknek is kellhet memória, amit nem feltétlen a heapból adja ki. Szerintem a fenti beállítás nem feltétlen rossz.
Illetve, a Xmx-hez húzásnak nincs hatása arra, hogy a node-on elfogy a memória. De amúgy, úgy rémlik, hogy akkor a pod eventjei között is van olyan bejegyzés, hogy evicted, mert a node-on elfogyott a memória.
Amit én tuningolnék, az az, hogy limitálni kellene, hány példány futhat adott node-on, mert ha egy node-on indul az összes, akkor bizony lehet, hogy az összes legyilkolódik.
- A hozzászóláshoz be kell jelentkezni
Igen, a JVM memóriaigénye felett kell még hagyni a podban memóriát, de ez szerintem nem mond ellent annak, hogy a pod memory request-je és limit-je legyen ugyanakkora. Jobb, ha már a scheduling közben elhasal a pod, mintha később jön egy OOM.
Na várjuk meg, hogy mit mond Vilmos, addig meg aludjunk egyet :D
- A hozzászóláshoz be kell jelentkezni
Attól függ, ha csak nagy ritkán, peakekben használ mondjuk 8G-t, de az idő 95%-ban elpöfög 2G-n, akkor szerintem bőven elég a request-nek 2G. Persze, ha nem egy példányban futtatod. A proxy el fogja rejteni a pod restartját.
- A hozzászóláshoz be kell jelentkezni
Ez általában igaz, de a JVM-ek tipikusan úgy működnek, hogy ha egyszer már elkérték a memóriát (8 GB-ot a példádban a peak maitt) az OS-től, akkor azt sose adják vissza. Ha a pod limitje 8 GB alatti, akkor nem tudja megcsinálni a peak alatt csinált dolgát, ha pedig 8 GB feletti, akkor úgy marad 8 GB-nál a memóriafoglalás.
Ha ekkor jön a következő pod, elindul, schedulable az alacsonyabb request miatt, de node OOM-re jutunk végül. Hogy ilyenkor mi történik pontosan, az erősen helyzetfüggő, és egyáltalán nem biztos, hogy a 8 GB-os podot fogja kilőni a kubelet - pontosan ez történik:
https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-e…
Ezek alapján szerintem nincs olyan scenario, amikor megérné így beállítani a dolgot, kivéve, ha vannak még más garanciák is (pl. csak kétféle pod van a k8s-en vagy ilyesmi).
- A hozzászóláshoz be kell jelentkezni
ha egyszer már elkérték a memóriát (8 GB-ot a példádban a peak maitt) az OS-től, akkor azt sose adják vissza
1.8 + G1 ota mar igen. Nem azonnal persze, kell hozza egy major/compacting GC, de visszaadja.
- A hozzászóláshoz be kell jelentkezni
Attól függ, ha csak nagy ritkán, peakekben használ mondjuk 8G-t, de az idő 95%-ban elpöfög 2G-n, akkor szerintem bőven elég a request-nek 2G.
A requests nem ezt jelenti. A requests azt jelenti, hogy amikor egy pod indítás előtti elhelyezése történik, akkor a scheduler megnézi, hogy a node erőforrása elég lesz-e a rajta lévő pod-ok igényeihez és az oda kerülő pod igényeihez.
Ha van egy node, amiben van 8GB használható memória és neked vannak pod-ok, amiknek 2 GB a requests memóriából, akkor erre a node-ra fel fog kerülni akár 4 darab pod. Hiába akarsz egyszer-egyszer 8GB memóriát használni, nem lesz -> OOMKill. Ha tudod, hogy lesz 8 GB memóriaigényed, akkor legyen annyi a requests is. Ha a requests kisebb, mint a limits, azzal overbooking-ot kockáztatsz és lesznek OOMKill jelenségek, szélsőséges esetben például a 8 GB peak igény mindig OOMKill eredményű lesz, mert a scheduler olyan node-ot néz neki, amin 2 GB szabad erőforrás van.
- A hozzászóláshoz be kell jelentkezni
Á, értem, köszi. A limits-sel kevertem.
- A hozzászóláshoz be kell jelentkezni
A limits se azt jelenti ebből a szempontból, nem garantált, hogy lesz ott annyi, amennyi a limits-ben meg van határozva... ha azt akarod, hogy ne legyen overbooking, akkor a requests és a limits legyen egyforma méretű és annyi, amennyivel a pod minden körülmények között működni fog, mert akkor oda fogja kitenni a scheduler, ahol biztos lesz annyi erőforrás, amennyi a requests és a requests annyi, amennyi kell a pod-nak.
A request / limits arány gyakorlatilag az overbooking aránya, a limits-nél több erőforrást nem fog kapni a pod, de a requests-nél több szabad erőforrással rendelkező node fogja futtatni. Érdemes játszani az overbooking kapcsán ezekkel, mert meg lehet spórolni egy csomó általában nem használt erőforrást, csak arra kell figyelni az affinity és anti-affinity paraméterekkel, hogy ne kerüljenek egy node-ra olyan pod-ok, amelyekről tudjuk, hogy kb. azonos időben fognak többlet erőforrást használni és kritikusak az infrastruktúránkban.
- A hozzászóláshoz be kell jelentkezni
Amit én tuningolnék, az az, hogy limitálni kellene, hány példány futhat adott node-on, mert ha egy node-on indul az összes, akkor bizony lehet, hogy az összes legyilkolódik.
megvan már, két külön node-on lettek legyilkolva egyszerre :\
- A hozzászóláshoz be kell jelentkezni
- Alapvetően látszik annak a két podnak a metrikáiban, hogy az `OOMKilled`-kor bőven túllőtt a memóriahasználata a limiten: https://snipboard.io/dhnmF5.jpg
- A micrometers dashboardján az egyik podnak a restart látszik (piros vonal), de az már nem, hogy a memória usage-ot mi lőtte ennyire túl: https://snipboard.io/q71pMk.jpg
- Lehet, hogy a node-on nem volt ennyi szabad memória (bár jobban örülnék, ha előbb a cache-t dobná ki ilyenkor bármi): https://snipboard.io/J7xtq4.jpg
De alapvetően ha a request=limit, az sem lett volna itt megoldás, ha jól értem a szitut, mert a limitjén is túllőtt az adott pod - valószínűleg az jobban alkalmazásspecifikus, hogy miért lőtt túl ennyire hirtelen.
De a fenti kutakodásokból kiindulva, találtam podonként ~700 mega fölös foglalást, amivel ennek az esélye csökkenthető, vagy rosszul gondolok valamit?
- A hozzászóláshoz be kell jelentkezni
A java memory management egy eleg bonyi dolog. Amikor erre rakeresel, tipikusan a kulonbozo GC-krol fogsz infot talalni, egyszeruen azert, mert az esetek 98%-aban azzal van a gond.
A JVM -Xmx parametere a _heap_ -et _maximalja_. Ezt fontos megerteni. Ezen felul van meg:
- stack
- non-heap
- class
- ... (meg par egyeb kisebb dolog, tipikusan aprosagok)
Ezeket _nem_ befolyasolja az -Xmx. Van par eldugott/experimental parameter, amikkel lehet ezt is szabalyozni, de erosen ellenjavallt. A JVM ugyanis nem ok nelkul hasznalja, amit hasznal. A GC egy dolog, ott az -Xmx -el kvazi azt allitod, hogy mennyire 'ugorjon' a GC, kvazi egyfajta mem-cpu tradeoff bizonyos fokig. A tobbi viszont, a class, a stack, a non-heap, azokra egyszeruen szukseg van. Teszem azt, egy nginx-ben sem kezdesz el hibat keresni, ha 1G-t eszik 600M helyett, nemde? Hanem megnezed, mi okozza az extra memoria-fogyasztast es azon probalsz valtoztatni, esetleg elfogadod, ahogy van.
Hogy mennyi hely kell 'extraban' a JVM-nek? Na, ez teljesen app fuggo. Siman lehet olyat irni, ami szinte 0 stacket, non-heap-et es class-t hasznal, de 64G heap kell neki, es viszont is. Pl. tipikusan az unsafe/native lib -es cuccok hasznaljak a non-heapet (netty pl., de igazabol sima JDK is lehetoseget ad a java.nio packageben barkinek erre; pl. nehany performance cache lib is hasznalja, hogy megkerulje a GC-t). A class-t meg a nyaklo nelkuli lib-hasznalat tudja megdobni, illetve meg egy dolog: ha valaki afterburner -t meg hasonlo runtime kodgeneralo lib-eket rosszul hasznal, az tud pl. class memory leak-et is csinalni. De manapsag mar egy sima spring is cglib-el manipulalja a javac altal forditott bytecode-ot. Generalisan nem szokott ezekkel gond lenni; ha igen, az bug. (nb, a class az a hely, ahol a jvm kvazi a java kodot magat tarolja, amit tenylegesen vegrehajt).
Okolszabalykent nalunk azt csinaljak, hogy 4G-s VM-hez -Xmx2G -t adnak meg. Pazarlas? Igen. Viszont ez egy rendes VM, szoval az OS-nek is kell nemi hely. Ha ragoogle-zol, azt fogod talalni, hogy kiserletezd ki, mi mukodik nalad. Celszeru esetleg k8s canary modon tobb parameterrel is kiprobalni elesben, aztan megnezni, hogy a gyakorlatban mekkora -Xmx -el milyen gyakran hasal el. Aztan amelyik stabilnak tunik, meg egy hajszalnyit visszavenni, es az lesz a gyakorlatban is megbizhato darab.
BTW ez nem kulonbozik barmilyen masik dockerizalt cucctol; a legtobb ugyanugy nagyon hasznalat-fuggo, hogy mennyi memoriat zabal, es neked kell kitokolni kezzel, hogy a te use case -d eseten mi a legjobb.
Ahogy lentebb gabrielakos irta, az egyebkent igaz: a standard JVM parametereken kivul mast nem erdemes bizgetni, mert java upgrade eseten szivas lesz, de nagy. Nem csak a parameterek valtoznak release-nkent, hanem az egesz JVM belso mukodese is, szoval ha nem akarsz par evente mindent 0-rol ujra kitokolni, maradj az -Xmx/-Xms/... parametereknel.
(oh btw, ha nem jott volna at, a java GC kizarolag a heap-en mukodik, illetve van nemi takaritas a class-on es non-heapen belul is, de a low-level jellegbol adodoan kozel sem olyan hulyebiztos, mint a heap).
(btw #2, nalunk a dev-ek konfigoljak a k8s-t is, pont ezert. ok jobban tudjak, mibol mennyi kell, es ha megfekszik, az o dolguk amugy is kitokolni, hogy miert, es hogyan lehet javitani.)
- A hozzászóláshoz be kell jelentkezni
Akkor nem node memória, oké.
A többiekhez hasonlóan én sem tekergetném a JVM különböző kapcsolóit emiatt a probléma miatt. Összesen négy dolog van, amit javaslok, hogy tekergetni kell/lehet:
- ha valami dobozos szoftver jön JVM kapcsolókkal, akkor legyen úgy, ahogy kérik, biztos jobban tudják
- heap memória maximális mérete (-Xmx vagy -XX:MaxRAMPercentage)
- GC stratégia (-XX:+UseG1GC - ha nem lenne default, pl. 8-as JVM-nél)
- GC logolás beállítása (-XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:./logs/gc.log -XX:-UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M)
Minden más általában csak ront a helyzeten, és nem javasolt piszkálni.
A konkrét esetedre lefordítva én azt csinálnám, hogy adnék még memóriát a podnak, és egyelőre elengedném a dolgot. Ha újra előjön a gond, van ugrás a non-heap memória fogyasztásában, akkor az esélyes, hogy szoftver bug, ekkor utána kell menni, hogy mi van (és minimálisan esélyes, de nem kizárt, hogy JVM bug, ilyenkor meg érdemes másik JVM-mel próbálkozni, vagy sok energiát beletenni a debugba).
- A hozzászóláshoz be kell jelentkezni
Lásd itt. A konkrét esetet megoldjuk a fejlesztőkkel, főleg ha előfordul még párszor.
Ami zavaró, hogy a jelenlegi megértésem szerint bármelyik Javas pod lefoglal ~1 gigabájt memóriát a Compressed Class Space számára, akár van rá szüksége, akár nincs.
- A hozzászóláshoz be kell jelentkezni
én még az Xms -is beállítanám, tipikusan ugyanarra mint az Xmx.
zászló, zászló, szív
- A hozzászóláshoz be kell jelentkezni
Ujabb java kepes a cgroup-tol lekerni a memoria mennyiseget es ahhoz beloni a heapet: https://medium.com/@yortuc/jvm-memory-allocation-in-docker-container-a2…
- A hozzászóláshoz be kell jelentkezni
Küldtél egy cikket, miszerint a JVM 8.x-től, a heapet a cgrouphoz méretezni. Kösz.
Akkor most válaszolhatnál azokra a kérdésekre, hogy a posztban szereplő JVM11-nek (amiben benne van ez a feature), a nonheap egy részét hogyan érdemes méretezni.
- A hozzászóláshoz be kell jelentkezni
ja és ha a cgroup alapján akarod beállíttatni akkor se Xms se Xmx -et ne adjál meg mert azzal gyanítom felülbírálod a cgroup alapján történő beállítást.
zászló, zászló, szív
- A hozzászóláshoz be kell jelentkezni
Nem akarod méretezni. Hidd el.
- A hozzászóláshoz be kell jelentkezni
Nem hiszi el :)
Én drukkolok h sikerüljön és ha tényleg akkor pliz oszd meg a tudást itt.
zászló, zászló, szív
- A hozzászóláshoz be kell jelentkezni
Nálunk volt olyan extrém példa, hogy Azure felhőben futó OpenShift-en a pod 128 GB memóriát látott. Még egy dolog, amit azóta nem hiszek el.
- A hozzászóláshoz be kell jelentkezni
JVM tuning kapcsolókkal hosszú távon biztosan rosszul jársz. Ha máskor nem akkor a következő upgrade-nél amikor "csak egy picit" változik a jelentése.
Nagyon pontosan kell tudni mit csinálsz, különben csak a baj van vele rövid távon is.
Lehet h megköveznek de pont ezért én valamennyi swapet minden gépnek adok mindig, látsszon már valahol hogy tényleg a fizikai memória fogyott el.
Úgy is kaphatsz OOMKill-t, hogy nagyobb szegmenst akart egyben foglalni (duplázni) mint amekkora éppen a gépen elérhető volt, nem feltétlen fogyott el tényleg az utolsó bit memória is.
zászló, zászló, szív
- A hozzászóláshoz be kell jelentkezni
Po-tay-to, po-tah-to...
Mármint a „ha máskor nem akkor a következő upgrade-nél amikor "csak egy picit" változik a jelentése.” alapon, ha a következő upgrade-nél:
- változik a nonheap default méret, ugyanúgy felsültem, ha k8s-ben van limit állítva (pl.: ha a compressed class space a JVM17-től 2 giga defaultból, akkor ugyanúgy elcsúszik a k8s limitemtől)
- változik a default, hogy a heapet hogyan méretezi a cgrouphoz képest, akkor ugyancsak elcsúszik a k8s limittől
- stb
Szóval vagy nem állítok _sehol_ memória-related dolgokat (se JVM, se k8s, és bízom benne, hogy just works), vagy úgyis benne van ez, hogy ha változik a JVM, akkor szétcsúszhatnak a dolgok.
- A hozzászóláshoz be kell jelentkezni
Az X és az XX kapcsolók stabilitása között van egy klasszis különbség. Én XX-et nem használnék, sehol.
Amúgy igen, ha változik a JVM akkor "bármi lehet", mint ahogy volt is.
zászló, zászló, szív
- A hozzászóláshoz be kell jelentkezni
Vedd figyelembe, hogy az Xmx az a heap size, a JVM ennél többet tud enni, a JVM körüli egyéb dolgok meg pláne, ha pedig ezen felül a Java process allokál Unsafe memóriát magának, akkor abból könnyen OOM lehet.
- A hozzászóláshoz be kell jelentkezni
Vedd figyelembe, hogy az Xmx az a heap size, a JVM ennél többet tud enni, a JVM körüli egyéb dolgok meg pláne, ha pedig ezen felül a Java process allokál Unsafe memóriát magának, akkor abból könnyen OOM lehet.
Ezt a részét értem. Ami zavar, hogy ha van tizeniksz Javas podunk (és nem néztem meg részletesen, de tegyük fel, hogy a többi is hasonlóan pazarló a compressed class space-szel), akkor az azt jelenti, hogy ~10 giga memória van lefoglalva fölöslegesen?
Mármint érted, erre az egy példára nekem nagyon úgy tűnik, hogy a Compressed Class Space úgy 1 giga (és le is foglalja azt az 1 gigát), hogy a JVM sosem eszik többet ott ~30 megánál. Ez azt jelenti, hogy a lefoglalt memória ~3%-a van használatban - még ha negyedelem is (256 megára), akkor is csak a ~15%-a lesz használatban. Kicsit pazarlónak tűnik ez.
De lehet, nagyon félreértem a fenti szitut, de nekem nagyon úgy tűnik, hogy a Compressed Class Space 1 gigáját defaultból elkéri az OS-től a JVM, akár kihasználja, akár nem.
- A hozzászóláshoz be kell jelentkezni
~10 giga memória van lefoglalva fölöslegesen
nem folosleges. ott tarolja a java bytecode-ot, tudod, amit a .jar fileokbol toltott be a jvm. az az egyik legfontosabb memoria resz.
de igen, ha a jdk stdlib ~50 mega, a jar-od meg 40, zipelve, akkor bizony nagy lesz a class space is. Ugye ott nemcsak a parse-olt bytecode kell, hanem a JIT forditas eredmenye is oda megy. Es akkor meg a runtime code generation-rol nem is beszeltunk.
ha nagyon zavar a merete, vannak jvm tool-ok, amivel lehet analizalni. mondjuk en heap-et szoktam, de biztos van code space-hez meg non-heap-hez is.
btw, 3-4G egy java containernek manapsag eleg alap am, ennel csak tobb lesz. ha gond, beszelj a dev-ekkel, hogy mit is csinal ez az ize. Egy alap spring boot app pl. elfutkos 256M -n siman. De aztan jon a rest, json de/ser, sql, ORM, mapperek, cache, schedule-k, encryption, metrics, logging, ..., aztan hirtelen mar 2G kell neki minimum.
A manapsag divo async / event frameworkok hatulutoje, amit nem reklamoznak ugye, szinten pont ez, hogy zabalja a (gyakran non-heap/class) memoriat.
- A hozzászóláshoz be kell jelentkezni
Haladunk a cél felé, remélem lassan sikerül elolvasni az OP-t is. ;)
Kiemelek még egy infót, ami szerepel az OP-ben: a micrometer azt mondja, hogy ~20 megabájt van használva a Compressed Class Space-ből.
- A hozzászóláshoz be kell jelentkezni
a micrometer is csak siman System.* -bol nyeri az adatokat. hogy pontosan mi micsoda, gyakran jopar retegen kell atturnod magad.
btw a jvm_memory_max_bytes -nak eleg sok tag-je van am, amik abszolut nem osszekeverendok.
osszefoglalva, csak latszolag osztottal meg sok adatot OP-ban, amugy nemigen. pl. jvm paramok?
es nem, a jvm alapbol nem foglal le 1G-t csak ugy per hecc. Persze lehet, hogy micrometer ezt jelenti, mert a default max. -ot kapod vissza, de a process tenyleges, memory-mapped memoriahasznalata a lenyeges. Amugy meg turd vegig, honnan veszi a jvm-bol a micrometer az a metric-et, ami erdekel, aztan meg nezd meg, hogy specifikusan a JVM11 alatt ez mit jelent.
- A hozzászóláshoz be kell jelentkezni
De lehet, nagyon félreértem a fenti szitut, de nekem nagyon úgy tűnik, hogy a Compressed Class Space 1 gigáját defaultból elkéri az OS-től a JVM, akár kihasználja, akár nem.
Az szerintem nincs fizikailag belapozva a memóriába, hanem csak virtuálisan van ott és swap-mechanizmussal, ha kell, akkor betölti ténylegesen és kilapoz más régebbi dolgot, de úgy látszik, mintha memóriát foglalna közben.
- A hozzászóláshoz be kell jelentkezni
Ez éppen AKS-en fut, ahol nézem, nincs defaultból swap. De ez jó ötlet, a többi cloudnál is érdemes lehet ezt megnéznünk. Van-e olyan java metrika framework, ami ezt segít monitorozni, hogy mi van a swap-en, és mi van a fizikai memóriában vajon?
(Bár ha csak a swapen van lefoglalva, akkor is pazarlásnak érzem az ~1Gb-ot odaadni JVM-enként neki, miközben a tapasztalat szerint nem foglal többet ~30Mb-nál. De első körben ezt szeretném megérteni, hogy jók-e a feltevéseim, a micrometeres adatokból jó következtetéseket vonok-e le, vagy teljesen tévút a Compressed Class Space környékén kutatni ennyi memóriát...)
- A hozzászóláshoz be kell jelentkezni
Ez éppen AKS-en fut, ahol nézem, nincs defaultból swap.
Az a trükk, hogy nem kell legyen swap, hogy legyen swap... :D
Tehát például van 3GB executable halmazod fájlokban, akkor az már swap, amikor elindítod, akkor nem tölti be az egészet a memóriába a kernel, hanem megjelöli, mint swap, és annyit tölt be, amennyi kell a futáshoz, a több ki van lapozva. Akkor is, ha explicit nincs swap, ha kell, akkor betölti fizikailag is a memóriába és "kilapoz" onnan olyan területeket, amelyeket éppen nem használ. Azért nem swap, mert írásvédett a tartalom, kockázat nélkül el tudja dobni és bármikor újra beolvasni a fájlrendszerből.
Pölö, ezen a gépen nincs swap, de mégis van ~2,7 GB virtuális memória amellett, hogy a process ténylegesen ~940 MB körül foglal (512 MB Xmx mellett):
total used free shared buff/cache available
Mem: 7755508 5154044 169352 413176 2432112 1883616
Swap: 0 0 0
...
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1285615 java 20 0 2770488 941044 33020 S 4.0 12.1 2745:01 /usr/lib/jvm/java/bin/java -D[Standalone] -server -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m
- A hozzászóláshoz be kell jelentkezni
a pod memória requestje 4Gi, limitje 5.
valamelyest off, de a k8s nem ugy mukodik, ahogy te hiszed, hogy mukodik :D
A request/limit egy faek egyszerusegu dolog, amivel a k8s beosztja a vasakra a docker containereket. nem fog neked autoskalazni igeny szerint vagy ilyesmi; egyszeru doboz-pakolasi problema neki, ennyi. A gyakorlatban a hasznalhatosaga kozel 0.
(de lehet, rosszul tudom; legutobb, mikor rakerestem, ez lett az eredmenye, en is meglepodtem)
- A hozzászóláshoz be kell jelentkezni
Szerintem az van hogy a 3200-as mx-hez nem minden esetben elég a 4g, de lehet hogy az 5 se.
Meg kell nézni jó nagy limittel (8) hogy mekkorát fűrészfogazik és arra beállítani a tényleges limitet.
Vagy a 3200-as mx-et kisebbre venni, legföljebb többet fog gc-zni.
De még JVM bug is lehet ráadásként a dologban, volt ilyen a múltban bőven.
zászló, zászló, szív
- A hozzászóláshoz be kell jelentkezni
Szerintem az van hogy a 3200-as mx-hez nem minden esetben elég a 4g, de lehet hogy az 5 se.
Meg kell nézni jó nagy limittel (8) hogy mekkorát fűrészfogazik és arra beállítani a tényleges limitet.
Ha az Xmx3200m, és tegyük fel, de k8s szinten kap a pod 8Gi request/limitet, akkor mi fűrészfogazna? A nonheap? Melyik része?
- A hozzászóláshoz be kell jelentkezni
A heap fűrészfogaz, látszik az ábrán is. Ez a fűrészfogazás az Xmx-en belül történik.
zászló, zászló, szív
- A hozzászóláshoz be kell jelentkezni
Okay, akkor:
Szerintem az van hogy a 3200-as mx-hez nem minden esetben elég a 4g, de lehet hogy az 5 se.
Meg kell nézni jó nagy limittel (8) hogy mekkorát fűrészfogazik és arra beállítani a tényleges limitet.Vagy a 3200-as mx-et kisebbre venni, legföljebb többet fog gc-zni.
Milyen limitet kellene feljebb venni nagyra? Mert a fentiből, amit írtál, nekem az jön le, hogy te az Xmx és a k8s memory request/limit közötti gap-et szélesítenéd.
Ez most 800 mega, a memory request 80%-ig van engedve a Heap. Hogyan függ össze, hogy a heapen mekkora a GC-kor felszabadított hely azzal, hogy a nonheapnek mennyi garantált helye van? (Főleg, hogy bármilyen metrika alapján, lásd korábban, a nonheap ~100 mega környékén van használatban, és kb. konstans a nap folyamán)
- A hozzászóláshoz be kell jelentkezni
Várjál, a K8s `requests` azt jelenti, hogy olyan node-ra teszi rá a pod-ot, ahol a pod-ok összeadogatott `requests` értékei kisebbek, mint a node-on elérhető erőforrások. Ha vannak ott olyan pod-ok, amelyeknél nincs a `requests` definiálva, de nyilván fogyasztanak erőforrásokat, akkor borul az egész, mert overbooking lesz a node-on és olyan esetben is lesz OOMKill, ha az adott pod még nem érte el a limitjeit, de már nincs szabad memória példáu. A tényleges limit a `limits`, de az se jelent garantált foglalást, az azt jelenti, hogy annál többet nem kap.
Tipikusan akkor jön K8s pod-ra OOMKill, ha overbooking van memóriából.
- A hozzászóláshoz be kell jelentkezni
Kettébontanám a témát:
- egyszer megállt egyszerre az összes pod a deploymentünkből. fun story, nekiálltunk kutatni
- WAAAAIT, WHAAAAAAAAAAT, itt lehet, hogy van 1 giga úgy lefoglalva, hogy max ~20-30 megát rak bele a JVM?
Az előbbit értem, megoldjuk házon belül, az app hibától kezdve sokmindent el tudok képzelni. Még azt is, hogy tényleg mellette volt valami rakoncátlan pod.
Az utóbbira viszont se az internet, se a semmi más nem ad hasznos találatot, ha ezzel van valakinek tapasztalata, megköszönöm ;) A fun story, csak a felvezető, hogy hogyan jutottam idáig ;)
- A hozzászóláshoz be kell jelentkezni
WAAAAIT, WHAAAAAAAAAAT, itt lehet, hogy van 1 giga úgy lefoglalva, hogy max ~20-30 megát rak bele a JVM?
Nem foglalja le, maximum úgy látod, mintha lefoglalná, de az amúgy virtuális memória. Azt nézd, hogy mennyi a RES az adott process esetén, van n+1 tool, ami megmondja, a többi csak látszólagos és memória-menedzsment trükk, ami meg tud zavarni.
Például egy `jcmd <PID> GC.heap_info` nálad mit mond?
- A hozzászóláshoz be kell jelentkezni
attol, hogy micrometer valamit visszaad, meg nem feltetlen igaz. utana kell nezni, akar forraskodban is, hogy pontosan mit jelent, honnan szedi a JVM-bol, es az az adott JVM-ben mit jelent.
(ez utobbi fontos - pl. kulonbozo GC-k alatt mas a jelentese a free/used/total memoria metric-eknek)
- A hozzászóláshoz be kell jelentkezni
Bizomány. Sőt, OOMKill-t úgy is kaphat egy szegény processz hogy ő még a saját limitjén belül volt.
zászló, zászló, szív
- A hozzászóláshoz be kell jelentkezni
" Hogyan függ össze, hogy a heapen mekkora a GC-kor felszabadított hely azzal, hogy a nonheapnek mennyi garantált helye van? "
Erre az a konkrét válasz hogy sehogy.
Azzal a memóriával amit a GC felszabadít te nem számolhatsz sehogy, mert azt bármikor újra lefoglalhatja.
A JVM amikor memóriát foglal akkor a "mindenféle más" területeket valamilyen algoritmus szerint számolja ki, általában van egy minimum és van egy heap %-ában megadott érték amit foglel ezeknek.
Ha beállítod az ms-t és az mx-et és a processzed elindul akkor igen jó eséllyel sose fog memória booking miatt megdögleni.
Másfajta resource allokációs hiba miatt megdögölhet persze.
Az hogy a nonheap max 100MB legyen azt az eddigiek alapján szinte kizártnak tartom, valamit ott benézel.
zászló, zászló, szív
- A hozzászóláshoz be kell jelentkezni
"az Xmx és a k8s memory request/limit közötti gap-et szélesítenéd."
Igen, azt szélesíteném.
A requestet és a limitet beállítanám egyformára, esetleg a requestet kicsit nagyobbra. A limitet meg a heap duplájára legalább.
Amúgy milyen microservice az aminek 3GB-nál több heap kell és megsértődik ha elpusztul?
zászló, zászló, szív
- A hozzászóláshoz be kell jelentkezni
A requestet és a limitet beállítanám egyformára, esetleg a requestet kicsit nagyobbra.
Ez utóbbit a Kubernetes nem igazán díjazza... :D
Amúgy milyen microservice az aminek 3GB-nál több heap kell és megsértődik ha elpusztul?
Hát monolitikus microservice, nem láttál még olyat?!
- A hozzászóláshoz be kell jelentkezni
De, láttam. Azt érdemes tudni h amúgy a k8s nem ezekre van kitalálva ergo amit a k8s "best practices"-ben olvasol az ide pont nem érvényes.
Ebben az esetben van egy túlbonyolítottan konfigurálandó dockeres környezetünk ahol legjobban tesszük ha saját kis kezünkkel számolgatunk és kb. mindent fixen ledrótozunk.
zászló, zászló, szív
- A hozzászóláshoz be kell jelentkezni
https://kubernetes.io/docs/concepts/configuration/manage-resources-cont…
Ez alapján nem látom miért ne díjazná.
Nincs sok gyakorlati tapasztalatom, nem lehet h ez implementáció függő?
zászló, zászló, szív
- A hozzászóláshoz be kell jelentkezni
The Pod "test" is invalid: spec.containers[0].resources.requests: Invalid value: "512Mi": must be less than or equal to memory limit
- A hozzászóláshoz be kell jelentkezni
megsértődik ha elpusztul?
ha tudnám, hogy hol olvastad, hogy megsértődik, ha elpusztul.
én sértődök meg, ha egyszerre pusztul el az összes instance belőle. innen indultunk, hogy egyszerre pusztult el mind.
- A hozzászóláshoz be kell jelentkezni
nonheap-nek nincs 'garantalt' helye.
a GC meg egy kulon bestia, abba most ne menjunk bele. legyen eleg annyi, hogy a j11 es afolott a GC intelligens, es csak annyit GC-zik, amennyit muszaj. Ergo, ha pl. -Xmx4G -t adsz meg neki, es par nap utan 'neheznek talalja' osszeszedni 1-2G garbage-t, akkor nem szedi ossze, hanem marad ott, ahol van, amig nincs 'pressure', hogy osszeszedje. Akkor jon az un. major GC, ami stop-the-world -ot is csinalja a JVM-ben.
En anno drotoztam ejszakara egy force-olt GC-t, mert akkor nem gond, ha akar par sec-re is megall a JVM. De ezt tenyleg csak vegso esetben ajanlom; altalaban mukodik jol a GC, kihasznalja a rendelkezesre allo memoriat, hogy minel kevesebb cpu-t egyen, ennyi.
(ez G1-ra vonatkozott; azota vannak durvabb GC-k is)
- A hozzászóláshoz be kell jelentkezni
Tudom hogy teljesen counter-intuitive de pont ezt nem szabad csinálni.
Ehelyett kevesebb memóriát kell neki adni, viszont 1-2 cpu maggal többet.
Akkor majd elkezd GC-zni és intenzív CPU terhelés mellett is meg fogja tudni csinálni (G1GC).
Inkább legyen pár másodpercenként pár msec GC mint ilyen stop-the-world scenario-k, amiknek vagy vége van vagy nincs a megadott limitben.
zászló, zászló, szív
- A hozzászóláshoz be kell jelentkezni
ja, csak pocsekolod a CPU-t a gc-kkel... a throughput nagy ur!
- A hozzászóláshoz be kell jelentkezni
Összességében nem pocsékolod és pause-k sincsenek.
Uszkve 12 évig üzemeltettem ilyen motyókat, van tapasztalat.
zászló, zászló, szív
- A hozzászóláshoz be kell jelentkezni
de, pocsekolod. van egy sweet spot, ahol nincs is tul gyakran GC+nem hasznal sok cycle-t, de jelentosen tobb ramot se hasznal a jvm.
Ha tul keveset adsz, csak azert ered el, hogy feleslegesen scanneli vegig a heap-et szegeny GC ujra es ujra. sot, rosszabb, mert a generational GC eseten minden object, ami tulelt egy GC-t, 'idosebb' lesz, es egyre hatrebb kerul a sorban, egyre kesobb lesz csak felszabaditva.
De olvass el par cikket, mert meg a G1 is durvan valtozott, miota megjelent (folyamatosan javitgatjak), a shenandoah/zgc meg total mas.
- A hozzászóláshoz be kell jelentkezni
Szerintem túlgondolod amit írtam. Az egy jó gondolat hogy van egy sweet spot: igen, azt jó megtalálni.
Azt kell látni csak, hogy a gc problémáknak nem feltétlen az a jó megoldása hogy odagórok neki még memóriát, sőt néha kifejezetten kontraproduktív.
Ez főleg a régivágású nagy alkalmazásokra igaz, a rendesen megírt mondjuk springbootos microservice-k tök jól elvannak alap beállításokkal.
GC-ről szívesen olvasnék mert szerettem a témát de mostanában mással foglalkozom, erre már nem jut idő.
Szerintem az változatlanul igaz, hogy ha elkezd szépen fűrészfogazni és nem nőnek a fogak akkor oké a konfig.
zászló, zászló, szív
- A hozzászóláshoz be kell jelentkezni
Szerintem az változatlanul igaz, hogy ha elkezd szépen fűrészfogazni és nem nőnek a fogak akkor oké a konfig.
Nade nem azert futtatod a containert, hogy fureszfogazzon neked, hanem valami hatarozott cellal. Azt kell merni, hogy abban hogyan teljesit. Rest service eseten ugye throughput+latency, pl.
- A hozzászóláshoz be kell jelentkezni
"Ehelyett kevesebb memóriát kell neki adni, viszont 1-2 cpu maggal többet."
Kezdjük úgy, hogy by definitionem, nem adsz cpu magot semminek. Se konténernek, se VM-nek, semminek. Csak CPU időt adsz neki, amit - az egyszerűség kedvéért - vCPU-nak hívunk, de a CPU "mag" az egyik legkönnyebben oversellingelhető dolog a világon.
Aztán, ha kevesebb memóriát adsz neki, akkor egyrészt ahogy a kollega fentebb mondta, nagyon gyakoriak lesznek a GC műveletek, ami rengeteg terhelést rak a CPU-ra - ezzel más, hasznosabb alkalmazások elől véve el a CPU időt.
De a nagyobb baj az, hogy ha van egy peak terhelésed, akkor nem él túl az alkalmazás.
Nincs értelme spórolni a memóriával, még a mostani árak mellett is bőven nagyon olcsó a memória, legalábbis olcsóbb, mintha elpusztul az alkalmazásod.
- A hozzászóláshoz be kell jelentkezni
ez nem mukodik, mert mar jo regota (mar j8 G1-el is), a minor GC, amikor eleri a target time-ot, es van eleg szabad heap, akkor megall es nem szabadit fel tobbet, akkor se, ha lehetne.
- A hozzászóláshoz be kell jelentkezni
Pontosan ezért kell a kisebb memória hogy gyakrabban legyen gc és azon hamarabb végigérjen. És nem az van, hogy nem szabadít fel többet, hanem az van, hogy nem szabadít fel _semmit_.
És ha már G1 akkor azért jó a plusz cpu mert akkor tud a GC-re külön magot szánni.
zászló, zászló, szív
- A hozzászóláshoz be kell jelentkezni
ez borzalmas, legyszives ne terjessz hulysegeket. nem erre valo a GC, hogy egy magot folyamatosan elvigyen. arrol nem is beszelve, hogy mar regota multithread a java GC egyre nagyobb resze.
- A hozzászóláshoz be kell jelentkezni
Jól tudod, ráadásul kizárólag a request-ek alapján pakol. Általában meglep mindenkit, hogy se a limit-ek, se a tényleges kihasználtság nincs figyelembe véve a scheduling során, semmilyen formában.
(A dolog hátterében egyébként az van, hogy a Kubernetes úgy van kitalálva, hogy szépen kezeljen 5000 node-ot és 150000 podot egy clusteren belül. 150000 pod tényleges CPU/memória használatát folyamatosan mérni, és az alapján döntéseket hozni meg elég drága mulatság lenne, de legalábbis sokkal drágább, mint egy 150000 elemű statikus elemeket tartalmazó listával dolgozni. A 2 node-os otthoni clusteren persze ezek nem valódi problémák :-) )
- A hozzászóláshoz be kell jelentkezni
metaspace hogy áll?
- A hozzászóláshoz be kell jelentkezni
jvm_memory_used_bytes/jvm_memory_committed_bytes szerint ~80 mega, a jvm_memory_max_bytes -1-et mond rá.
A piros vonalnál lett OOMKilled a pod: https://snipboard.io/sRcqUk.jpg
- A hozzászóláshoz be kell jelentkezni
Van esetleg intezív file-kezelés a történetben? Jártam már úgy, hogy a RedHat másodrendű hibátlanságú kernelének cgroup-ja beleszámolta a disk-cache-t a processz által foglalt memóriába.
- A hozzászóláshoz be kell jelentkezni
Nincs, network van sok. Azon túl startupkor felolvas memóriába mindent a pod, amire szüksége van, AFAIK.
- A hozzászóláshoz be kell jelentkezni
Honnan jott az -Xmx3.2G btw? gondolom, nem az ujjadbol szoptad :)
De ha kubernetes kevesli a ramot, akkor vagy az -Xmx -et csokkentsd, vagy a kubernetes memoriat noveld.
Ha java-nak keves a heap, akkor 2 dolog lehet:
- eszeveszett CPU hasznalat - ez azert, mert keves a szabad heap, es folyamatosan GC-zni kenyszerul. tipikusan hamar torkollik a masodik pontba, ami:
- JVM OOM. ilyenkor kap egy OutOfMemoryError-t az app (ami durvan leegyszerusitve "ronda exception"), aztan tole fugg, hogyan kezeli le. Tipikusan folytatjak, amit csinaltak, ergo ez is cpu -t fog zabalni, de sok-sok errort logol melle.
- A hozzászóláshoz be kell jelentkezni
Három dolgot bekommentelek még ide, nem tudom melyik szálra kéne, de lehet hasznos.
Az egyik, hogy a non-heap memória a Java 8 újabb verziói óta ügyesen működik, és az OS-től csak annyit kér el a JVM, amennyit ténylegesen használ. Tehát a Compressed Class Space 1 GB-ját nem kéri el, a kihasznált Compressed Class Space 32 MB-ját elkéri.
A másik, hogy a non-heap memória tuningolásával elég sokan próbálkoznak a piacon, pl. adnak MaxMetaspaceSize értéket, vagy lentebb veszik a Compressed Class Space méretét. Ezzel azt érik el, hogy a tipikusan dinamikusan jól méreteződő memóriaterületeknek adnak felső korlátokat, amelyekbe jól bele lehet ütközni, amitől a JVM leáll. Azt nem érik el, hogy valójában kevesebb memória kelljen bárminek is.
A harmadik, hogy a non-heap memória jellegzetes mérete egy Spring Boot-os alkalmazásnál 100-300 MB körüli átlagos library használat mellett, irgalmatlan extrém kilengésekkel (láttam már 3 GB-ot igénylő appot is). A nagyobb dobozos szoftvereknél (Weblogic, Forgerock cuccai, Atlassian cuccai, stb.) jellemzően 1-3 GB non-heap memória kell, és ezek viszont nem annyira szoktak felfelé kilógni ebből.
Amúgy időtlen idők óta ez az első értelmes thread a HUP-on, gratula hozzá! :-D
- A hozzászóláshoz be kell jelentkezni
Én is próbáltam bűvészkedni ezekkel, de jobb a JDK/JRE frissítése, amiben jó sansszal reszeltek még a GC-n, ha az alkalmazás engedi, mint a beállításokkal játszani.
Színes vászon, színes vászon, fúj!
Kérem a Fiátot..
- A hozzászóláshoz be kell jelentkezni
Nalunk fut ugy 3000 javas pod productionban, az altalanos tapasztalatok a kovetkezok:
- pod memory request es limit legyen egyenlo, ez a pod mozgatasokat gyakorlatilag megszunteti nagy worker node-ok eseten ( >= 64GB ram), jot tesz a stabilitasnak, mindenki boldog. Ha ~500 pod felett van egy clusterben akkor ennek hatvanyozottan megno a fontossaga, ha raesik valami peak terheles a clusterre az csunya hullamokat tud produkalni, mivel nagyon rovid idon belul sok pod lesz evictelve, a friss podok egybol szinten peak load-ot fognak kapni, es egy onmagat folyamatosan erosito csunya eviction/rescheduling kaosz lesz a vege. Fontos tudni hogy ilyenkor a pod Evicted lesz es nem OOMKilled, tehat konnyu megallapitani hogy mi tortent pontosan, raadasul a K8S eventsben is benne lesz ha a node memorypressure miatt evictelt podokat.
- hasznos szabaly hogy ha tobb eroforras kell akkor nem a podokat kell felhizlalni hanem tobb kell beloluk, sokkal jobban ki lesznek hasznalva a worker node-ok es a k8s schedulernek sokkal egyszerubb lesz a dolga.
- egy adott deploymenten belul az altalad leirt kaszkadolt OOMKilled jelenseg akkor fordul elo ha megerkezik a terheles es az appban nincsen rendes feldolgozasi limitacio (maximum szalak vagy ilyesmi frontend eseten, kotegelt feldolgozas backendben, stb.), a java megprobal mindent kiszolgalni, elfogy a ram, elszall az elso pod, a terheles a tobbire elosztodik, es jon a domino effektus. Ennek minositett esete hogy vegleg lerohad az app mert a pod indulas utan szinte azonnal maximumra ugik a load es megint OOMKilled lesz, vegtelen ciklusban. Ilyenkor nagy segitseg a horizontal pod autoscaler, ha rendesen tudja monitorozni a podokat akkor egybol feltekeri a deploymentben a podok szamat es elkerulheto a teljes leallas. Egy jol beallitott pod autoscaler nem mellesleg ingyen soroket tud generalni a devopsnak ha a fejlesztok eppen megusztak egy komolyabb leallast :)))
- rendesen megirt javas appok (ertsd nem memory leakel, rendesen limitalt a feldolgozas hogy ne tudjon megfutni, stb.) -XX:MaxRAMPercentage=65.0 beallitassal nagyon stabilak nagy terheles alatt is, mi ezt talaltuk ugymond sweet spot-nak, alkalmazas tipusatol fuggetlenul.
- az esetek tobbsegeben a GC beallitasokkal valo kuzdesnek sok haszna nincs, szinte mindig kiderult hogy idopocsekolas volt.
- A hozzászóláshoz be kell jelentkezni
köszönöm szépen, az ` -XX:MaxRAMPercentage=65.0`-tel futunk egy kört (mondjuk ahogy számolom, az 5G-s limittel és a 3200m Xmx-szel ahogy számolom, pont ugyanitt vagyunk, de a request emelést feldobom itt bent).
- A hozzászóláshoz be kell jelentkezni
de a request emelést feldobom itt bent
Mint írtam, a requests esetén arra kell figyelni, hogy minden pod esetén be legyen állítva és arra is figyelni kell, hogy nem jelent erőforrás lefoglalást, csak egy scheduling segítség, hogy a K8s hova tegye a pod-ot.
- A hozzászóláshoz be kell jelentkezni
(feliratkozok)
- A hozzászóláshoz be kell jelentkezni