L'utilizzo della memoria di build di Ember-cli rischia il fallimento (OOM) sulla dimensione minima dell'istanza

Durante l’aggiornamento, di gran lunga la maggiore sollecitazione sulla memoria (RAM+swap) si verifica quando viene eseguito il processo ‘ember’. Penso che ogni volta che ho eseguito un aggiornamento, sia stato più grande del precedente, e si sta avvicinando al punto di non poter più funzionare su computer con dimensioni minime raccomandate.

Potrebbe essere utile esaminare questo aspetto prima che si verifichi effettivamente un guasto. (Speriamo che, per motivi di costo, la risposta non sia quella di aumentare la dimensione minima raccomandata. Aumentare lo swap aiuterebbe, se lo spazio su disco lo consente. In linea di principio, si potrebbe migrare temporaneamente a un’istanza più costosa con più RAM.)

Gestisco due forum di modeste dimensioni su istanze piccole, entrambe entro i minimi raccomandati, credo. In entrambi i casi, RAM+swap=3G. In un caso un’istanza Digital Ocean con 1G di RAM e 2G di swap, nell’altro caso un’istanza Hetzner con 2G di RAM e 1G di swap.

Ecco tre snapshot del processo ember, sulla macchina DO, utilizzando ps auxc

USER       PID %CPU %MEM      VSZ    RSS TTY   STAT START   TIME COMMAND
1000     10342 87.7 65.1 32930460 657936 ?     Rl   16:57   2:23 ember

USER       PID %CPU %MEM      VSZ    RSS TTY   STAT START   TIME COMMAND
1000     10342 84.9 60.7 43572204 612668 ?     Rl   16:57   2:57 ember

USER       PID %CPU %MEM      VSZ    RSS TTY   STAT START   TIME COMMAND
1000     10342 81.2 55.2 43405220 557128 ?     Rl   16:57   3:40 ember

Ovviamente la dimensione del processo di 43 GB non è interamente presente nella memoria virtuale, poiché abbiamo solo 3 GB disponibili. Utilizzare il 65% della dimensione della RAM per RSS è impressionante, ma di per sé non è un problema. La quantità di memoria libera e swap libero mostra che la macchina è vicina a una condizione di Out of Memory (OOM), che molto probabilmente comporterebbe l’uccisione di qualche processo e una conclusione disordinata dell’aggiornamento.

Ecco free come snapshot puntuale:

# free
              total        used        free      shared  buff/cache   available
Mem:        1009140      863552       72768        6224       72820       34868
Swap:       2097144     1160628      936516

Per cercare di catturare la situazione al suo punto più critico, ho usato vmstat 5

# vmstat 5 5
procs -----------memory----------    ---swap-- -----io----  -system-- ------cpu-----
 r  b   swpd    free   buff  cache    si    so    bi    bo   in    cs us sy id wa st
 3  0 1392140  61200  11632  76432    41    32   117    93    0     1  2  1 97  0  0
 1  1 1467220  63416    324  67284  8786 20499 13178 20567 2539  8924 77 13  0 10  0
 0  2 1593340  57916   1096  53832 24262 46868 29986 46889 5377 18534 44 22  0 34  0
 4  0 1155632 120680   2772  86280 39111 35424 54768 37824 6987 25174 38 27  0 35  0
 3  0 1102988  74096   2852  85276 11261   246 12610   271 1879  6365 86  6  0  8  0

Noterai un gran numero di context switch (cs), molta attività del disco (bi, bo) e molta attività di swap (si, so), ma la cosa più importante è l’utilizzo dello swap fino a 1,6G con la memoria libera scesa a 60M e solo 54M di utilizzo dei buffer. Ciò significa che circa 2,6G dei 3G disponibili di memoria virtuale sono in uso. Si tratta dell’87% della capacità. (Potrebbe essere un po’ peggio, poiché stiamo campionando solo ogni 5 secondi.)

Si noti che la situazione era preoccupante (con circa 2G di utilizzo, quindi non così vicina al critico come oggi) quando ho aggiornato ad agosto:

# vmstat 5 5
procs -----------memory----------    ---swap-- -----io----  -system-- ------cpu-----
 r  b    swpd   free   buff  cache    si    so    bi    bo   in    cs us sy id wa st
 3  0  700404  62740   1956  48748    35    29   108    92    3     8  2  1 96  0  1
 1  0  741000  65996   1880  44360  3708 11190  3982 11191  643  1437 92  4  0  3  1
 1  0  834836  70452   1480  53856   528 18969  4274 18974  532  1575 93  6  0  1  0
 4  1 1010144  82192   4644  44400 30065 38803 35455 39946 4432 19267 28 26  0 39  7
 1  0  644116 307764   1644  55348 24406 21154 27724 21945 2551  8672 52 22  0 21  6
4 Mi Piace

Ciao @Ed_S - quale versione di Discourse stavi usando per questi test? Aggiorniamo regolarmente ember-cli e i suoi add-on, quindi voglio solo assicurarmi che stiamo guardando la stessa cosa.

Inoltre, quanti core CPU hanno le tue VM? 1? (puoi controllare eseguendo lscpu nella console)

In modo che tutti lavoriamo con gli stessi dati, potresti provare a eseguire:

/var/discourse/launcher enter app
cd /var/www/discourse/app/assets/javascripts/discourse
apt-get update && apt-get install time
NODE_OPTIONS='--max-old-space-size=2048' /usr/bin/time -v yarn ember build -prod

Sul mio droplet di test (1 CPU, 1 GB di RAM, 2 GB di swap), vedo questo:

Command being timed: "yarn ember build -prod"
	User time (seconds): 369.74
	System time (seconds): 22.62
	Percent of CPU this job got: 81%
	Elapsed (wall clock) time (h:mm:ss or m:ss): 8:02.73
	Average shared text size (kbytes): 0
	Average unshared data size (kbytes): 0
	Average stack size (kbytes): 0
	Average total size (kbytes): 0
	Maximum resident set size (kbytes): 774912
	Average resident set size (kbytes): 0
	Major (requiring I/O) page faults: 253770
	Minor (reclaiming a frame) page faults: 1158920
	Voluntary context switches: 519269
	Involuntary context switches: 383328
	Swaps: 0
	File system inputs: 7521784
	File system outputs: 316304
	Socket messages sent: 0
	Socket messages received: 0
	Signals delivered: 0
	Page size (bytes): 4096
	Exit status: 0

Stiamo usando strumenti ember abbastanza standard qui, quindi non sono sicuro che ci sia molto che possiamo fare in termini di configurazione per ridurre l’utilizzo della memoria. Il nostro obiettivo a lungo termine è passare all’uso di Embroider, che potrebbe darci più opzioni.

1 Mi Piace

Grazie @david, apprezzo che Ember sia una cosa a sé stante.

Ho appena eseguito quei comandi.

# /var/discourse/launcher enter app
Rilevata architettura x86_64.

ATTENZIONE: Stiamo per iniziare a scaricare l'immagine base di Discourse
Questo processo potrebbe richiedere da pochi minuti a un'ora, a seconda della velocità della tua rete

Per favore, sii paziente

2.0.20220720-0049: Estrazione da discourse/base
Digest: sha256:7ff397003c78b64c9131726756014710e2e67568fbc88daad846d2b368a02364
Stato: Immagine più recente scaricata per discourse/base:2.0.20220720-0049
docker.io/discourse/base:2.0.20220720-0049

Questa è un’installazione di produzione, quindi, da ieri, era aggiornata. Attualmente riporta:

Installato 2.9.0.beta12 (8f5936871c)

È un’istanza a CPU singola, come la tua ha 1G di RAM e 2G di swap.

Il risultato del comando time è stato

Fatto in 303.21s.

Comando temporizzato: "yarn ember build -prod"
Tempo utente (secondi): 222.71
Tempo di sistema (secondi): 17.17
Percentuale di CPU ottenuta da questo job: 78%
Tempo trascorso (orologio) (h:mm:ss o m:ss): 5:04.15
Dimensione media del testo condiviso (kbytes): 0
Dimensione media dei dati non condivisi (kbytes): 0
Dimensione media dello stack (kbytes): 0
Dimensione totale media (kbytes): 0
Dimensione massima del set residente (kbytes): 702292
Dimensione media del set residente (kbytes): 0
Errori di pagina principali (richiedenti I/O): 348190
Errori di pagina minori (recupero di un frame): 1152689
Commutazioni di contesto volontarie: 617736
Commutazioni di contesto involontarie: 774189
Swap: 0
Input del file system: 5001936
Output del file system: 318280
Messaggi di socket inviati: 0
Messaggi di socket ricevuti: 0
Segnali consegnati: 0
Dimensione pagina (byte): 4096
Stato di uscita: 0

Immediatamente prima, avevo aggiornato l’host e riavviato, quindi tutto nel container sarebbe stato appena riavviato.

Il peggio dell’utilizzo della memoria come riportato da un vmstat in esecuzione in un’altra finestra:

# vmstat 1
r  b    swpd   free   buff  cache    si     so    bi     bo    in    cs us sy id wa st
 2  0  704000 136044  24136 158144  1517   3503  8256   4377   886  3564 43  8 43  6  0
...
 5  0 1451436  71604   1248  50196 55016 110236 73204 121060 13152 45971 29 60  0 10  1
2 Mi Piace

Sembra che abbiamo aumentato esplicitamente l’heap consentito di Node da 500M a 2G - forse questo è un passo troppo lontano, e 1,5G sarebbe meglio:

Vale la pena notare che Ember non è l’unica cosa in esecuzione sulla macchina e stiamo raggiungendo il limite globale di RAM+swap. Quindi la cronologia della macchina e le esigenze di tutti gli altri processi in esecuzione entrano in gioco. Il mio riavvio potrebbe aver aiutato qui a raggiungere un punto di riferimento più basso rispetto a ieri.

La pull request sopra è stata citata in
Failed to upgrade discourse instance to Feb 15 2022
dove notiamo anche che qualcuno ha avuto una carenza di memoria che è stata risolta da un riavvio.

È sfortunato che il comando time non riporti l’utilizzo massimo della memoria. Forse, su una macchina con almeno 3G di RAM e senza swap, il conteggio RSS ci direbbe l’utilizzo massimo di Ember. O forse potremmo usare un’altra tattica - diverse sono delineate qui e ci sono alcune idee qui anche.

Ciò che è scomodo è che siamo veramente interessati all’utilizzo della memoria qui, mentre in molti casi le persone sono interessate all’utilizzo della RAM, che è una domanda diversa.

3 Mi Piace

Il motivo per cui abbiamo aggiunto quel flag è che l’OOM killer di Node stesso stava uccidendo la build - 500M non era abbastanza. Sono felice di provare a modificarlo a 1.5G - l’ho appena provato sul mio droplet e sembra funzionare bene. Infatti, sembra che anche 1.0G sia sufficiente.

Ho provato a tracciare l’utilizzo della memoria con diverse dimensioni di max_heap:

(while(true); do (free -m -t | grep Total | awk '{print $3}') && sleep 0.5; done) | tee 1000mb.csv

Mostra questo utilizzo durante la build:

C’è stata pochissima differenza nel tempo di build, ma i limiti di 1GB e 1.5GB producono chiaramente un minor utilizzo complessivo. Come previsto, l’output di time mostra significativamente meno “Major page faults” quando il limite di Node è inferiore.

È curioso che la differenza tra 1.5GB e 1GB sia così piccola… :face_with_monocle:

In ogni caso, sono d’accordo che diminuire il limite sia una buona idea. Per assicurarci che non influenzi le prestazioni di build su macchine con specifiche più elevate, penso che dovremmo sovrascrivere il limite solo quando sappiamo che è troppo basso. Altrimenti, possiamo lasciare che Node utilizzi il suo default.

Ecco una PR - cercheremo di farla unire presto. Grazie per averla sollevata @Ed_S!

4 Mi Piace

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.