Uso de memória do build do Ember-cli arrisca falha (OOM) no tamanho mínimo da instância

Durante a atualização, a maior sobrecarga de memória (RAM+swap) ocorre quando o processo ‘ember’ é executado. Acho que cada vez que executei uma atualização, ela foi maior do que antes, e está chegando perto de não conseguir ser executada em computadores com o tamanho mínimo recomendado.

Pode ser bom investigar isso antes que realmente falhe. (Esperançosamente, por razões de custo, a resposta não será aumentar o tamanho mínimo recomendado. Aumentar o swap ajudaria, se o espaço em disco permitir. Em princípio, poder-se-ia migrar temporariamente para uma instância maior com mais RAM, mais cara.)

Eu executo dois fóruns de tamanho modesto em instâncias pequenas - ambos dentro dos mínimos recomendados, acredito. Em ambos os casos, RAM+swap=3G. Em um caso, uma instância Digital Ocean com 1G de RAM e 2G de swap, no outro caso, uma instância Hetzner com 2G de RAM e 1G de swap.

Aqui estão três instantâneos do processo ember, na máquina DO, usando 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

Obviamente, os 43 GB de tamanho do processo não estão todos presentes na memória virtual, pois temos apenas 3 GB disponíveis. Usar 65% do tamanho da RAM para RSS é impressionante, mas não é um problema em si. A quantidade de memória livre e swap livre mostra que a máquina está perto de uma condição de falta de memória (OOM), o que provavelmente resultaria em algum processo sendo encerrado e um fim desorganizado para a atualização.

Aqui está free como um instantâneo em um determinado momento:

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

Para tentar capturar a situação mais próxima da falha, usei 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

Você notará muitas trocas de contexto (cs), muita atividade de disco (bi, bo) e muita atividade de swap (si, so), mas o mais importante é o uso de swap de até 1,6G com a memória livre caindo para 60M e apenas 54M de uso de buffer. Isso significa que cerca de 2,6G dos 3G disponíveis de memória virtual estão em uso. Isso é 87% da capacidade. (Pode ser um pouco pior, pois estamos amostrando apenas a cada 5 segundos.)

Note que a situação era preocupante (com cerca de 2G usados, não tão perto do crítico quanto hoje) quando atualizei em 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 curtidas

Olá @Ed_S - qual versão do Discourse você estava usando para esses testes? Nós atualizamos regularmente o ember-cli e seus add-ons, então só quero ter certeza de que estamos olhando para a mesma coisa.

Além disso, quantos núcleos de CPU suas VMs têm? 1? (você pode verificar executando lscpu no console)

Para que todos estejamos trabalhando com os mesmos dados, você poderia tentar executar:

/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

No meu droplet de teste (1 CPU, 1GB RAM, 2GB swap), eu vejo isso:

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

Estamos usando ferramentas ember bastante padrão aqui, então não tenho certeza se há muito que possamos fazer em termos de configuração para reduzir o uso de memória. Nosso objetivo de longo prazo é migrar para o uso do Embroider, o que pode nos dar mais opções.

1 curtida

Obrigado @david - aprecio que o Ember seja uma coisa por si só.

Acabei de executar esses comandos.

# /var/discourse/launcher enter app
x86_64 arch detectado.

AVISO: Vamos começar a baixar a imagem base do Discourse
Este processo pode levar de alguns minutos a uma hora, dependendo da velocidade da sua rede

Por favor, seja paciente

2.0.20220720-0049: Puxando de discourse/base
Digest: sha256:7ff397003c78b64c9131726756014710e2e67568fbc88daad846d2b368a02364
Status: Imagem mais nova baixada para discourse/base:2.0.20220720-0049
docker.io/discourse/base:2.0.20220720-0049

Esta é uma instalação de produção, então, a partir de ontem, estava atualizada. Atualmente relatando:

Instalado 2.9.0.beta12 (8f5936871c)

É uma instância de um único CPU, como a sua, tem 1G de RAM e 2G de swap.

O resultado do comando time foi

Concluído em 303,21s.

Comando sendo cronometrado: "yarn ember build -prod"
Tempo do usuário (segundos): 222,71
Tempo do sistema (segundos): 17,17
Porcentagem da CPU que este trabalho obteve: 78%
Tempo decorrido (relógio de parede) (h:mm:ss ou m:ss): 5:04.15
Tamanho médio de texto compartilhado (kbytes): 0
Tamanho médio de dados não compartilhados (kbytes): 0
Tamanho médio da pilha (kbytes): 0
Tamanho total médio (kbytes): 0
Tamanho máximo do conjunto residente (kbytes): 702292
Tamanho médio do conjunto residente (kbytes): 0
Falhas de página principais (requerendo I/O): 348190
Falhas de página secundárias (reclamando um frame): 1152689
Trocas de contexto voluntárias: 617736
Trocas de contexto involuntárias: 774189
Swaps: 0
Entradas do sistema de arquivos: 5001936
Saídas do sistema de arquivos: 318280
Mensagens de socket enviadas: 0
Mensagens de socket recebidas: 0
Sinais entregues: 0
Tamanho da página (bytes): 4096
Status de saída: 0

Imediatamente antes, eu havia atualizado o host e reiniciado, então tudo no contêiner teria sido reiniciado recentemente.

O pior uso de memória, conforme relatado por um vmstat rodando em outra janela:

# vmstat 1
procs  -----------memory----------    ---swap--  -----io----   -system-- ------cpu-----\n r  b    swpd   free   buff  cache    si     so    bi     bo    in    cs us sy id wa st\n 2  0  704000 136044  24136 158144  1517   3503  8256   4377   886  3564 43  8 43  6  0\n...\n 5  0 1451436  71604   1248  50196 55016 110236 73204 121060 13152 45971 29 60  0 10  1\n```
2 curtidas

Parece que aumentamos explicitamente a memória heap permitida do Node de 500M para 2G - possivelmente isso é um exagero e 1,5G seria melhor:

Vale a pena notar que Ember não é a única coisa rodando na máquina, e estamos atingindo o limite global de RAM + swap. Portanto, o histórico da máquina e as necessidades de todos os outros processos em execução entram em jogo. Meu reinício pode ter ajudado aqui a atingir uma marca d’água mais baixa em comparação com ontem.

O pull request acima foi referenciado em
Falha ao atualizar a instância do Discourse para 15 de fevereiro de 2022
onde também observamos que alguém teve falta de memória que foi resolvida com um reinício.

É lamentável que o comando time não relate o uso máximo de memória. Possivelmente, em uma máquina com pelo menos 3G de RAM e sem swap, a contagem RSS nos diria o pico de uso da Ember. Ou possivelmente poderíamos usar outra tática - várias são descritas aqui e há algumas ideias aqui também.

O que é complicado é que realmente estamos interessados no uso de memória aqui, enquanto em muitos casos as pessoas estão interessadas no uso de RAM, que é uma questão diferente.

3 curtidas

O motivo pelo qual adicionamos essa flag foi que o próprio OOM killer do Node estava matando a compilação - 500M não era suficiente. Ficarei feliz em tentar ajustá-lo para 1.5G - acabei de tentar no meu droplet e parece funcionar bem. Na verdade, parece que até 1.0G é suficiente.

Tentei rastrear o uso de memória com diferentes tamanhos de max_heap:

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

Mostra este uso durante a compilação:

Houve muito pouca diferença no tempo de compilação, mas os limites de 1GB e 1.5GB claramente produzem menos uso geral. Como esperado, a saída do time mostra significativamente menos “Falhas de página principais” quando o limite do Node é menor.

É curioso que a diferença entre 1.5GB e 1GB seja tão pequena… :face_with_monocle:

De qualquer forma, concordo que diminuir o limite é uma boa ideia. Para garantir que isso não afete o desempenho da compilação em máquinas com especificações mais altas, acho que devemos substituir o limite apenas quando soubermos que ele é muito baixo. Caso contrário, podemos deixar o Node usar o padrão.

Aqui está um PR - tentaremos mesclá-lo em breve. Obrigado por levantar isso, @Ed_S!

4 curtidas

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