Por que os aplicativos em um contêiner LXC com memória limitada estão gravando arquivos grandes no disco sendo mortos pela OOM?

10

EDIT2: Este problema parece existir também sob a versão 3.8.0-25-genérica # 37-Ubuntu SMP

EDIT: Eu modifiquei a questão do título original de "Por que o gerenciador Linux Out of Memory foi acionado por gravar em um arquivo com o dd?" para refletir melhor que estou preocupado com a questão geral descrita abaixo:

Estou me deparando com um cenário problemático em que o assassino da OOM é difícil matando processos no meu contêiner LXC quando eu escrevo um arquivo com tamanho excedendo a limitação de memória (definida para 300MB). O problema não ocorre quando eu executo o aplicativo em uma máquina virtual Xen (um EC2 t1.micro) que na verdade tem apenas 512 MB de RAM, portanto, parece haver algum problema com o armazenamento em buffer do arquivo, respeitando o limite de memória dos contêineres. p>

Como um exemplo simples, posso demonstrar como um arquivo grande gravado pelo dd causará problemas. Novamente, esse problema afeta todos os aplicativos. Eu estou olhando para resolver o problema geral de cache do aplicativo ficando muito grande; Eu entendo como posso fazer o "dd" funcionar.

Cenário:

Eu tenho um contêiner LXC onde memory.limit_in_bytes é definido como 300 MB.

Eu tentei adicionar um arquivo de ~ 500 MB da seguinte forma:

dd if=/dev/zero of=test2 bs=100k count=5010

Aproximadamente 20% do tempo, o gerenciador Linux OOM é acionado por este comando e um processo é morto. Escusado será dizer que isso é altamente não intencional comportamento; dd destina-se a simular um arquivo "útil" real escrito por um programa rodando dentro do container.

Detalhes: Enquanto os caches de arquivo ficam grandes (260 MB), o rss e o mapa de arquivos parecem ficar bastante baixo. Aqui está um exemplo de como o memory.stat pode parecer durante a gravação:

cache 278667264
rss 20971520
mapped_file 24576
pgpgin 138147
pgpgout 64993
swap 0
pgfault 55054
pgmajfault 2
inactive_anon 10637312
active_anon 10342400
inactive_file 278339584
active_file 319488
unevictable 0
hierarchical_memory_limit 300003328
hierarchical_memsw_limit 300003328
total_cache 278667264
total_rss 20971520
total_mapped_file 24576
total_pgpgin 138147
total_pgpgout 64993
total_swap 0
total_pgfault 55054
total_pgmajfault 2
total_inactive_anon 10637312
total_active_anon 10342400
total_inactive_file 278339584
total_active_file 319488
total_unevictable 0

Aqui está uma pasta do dmesg onde o OOM acionou uma morte. Eu não sou muito familiarizado com as distinções entre os tipos de memória; uma coisa que destaca-se que, enquanto "Nó 0 Normal" é muito baixo, há uma abundância de Nó 0 DMA32 livre de memória. Alguém pode explicar por que uma gravação de arquivo está causando a OOM? Como evito que isso aconteça?

O log:

[1801523.686755] Task in /lxc/c-7 killed as a result of limit of /lxc/c-7
[1801523.686758] memory: usage 292972kB, limit 292972kB, failcnt 39580
[1801523.686760] memory+swap: usage 292972kB, limit 292972kB, failcnt 0
[1801523.686762] Mem-Info:
[1801523.686764] Node 0 DMA per-cpu:
[1801523.686767] CPU    0: hi:    0, btch:   1 usd:   0
[1801523.686769] CPU    1: hi:    0, btch:   1 usd:   0
[1801523.686771] CPU    2: hi:    0, btch:   1 usd:   0
[1801523.686773] CPU    3: hi:    0, btch:   1 usd:   0
[1801523.686775] CPU    4: hi:    0, btch:   1 usd:   0
[1801523.686778] CPU    5: hi:    0, btch:   1 usd:   0
[1801523.686780] CPU    6: hi:    0, btch:   1 usd:   0
[1801523.686782] CPU    7: hi:    0, btch:   1 usd:   0
[1801523.686783] Node 0 DMA32 per-cpu:
[1801523.686786] CPU    0: hi:  186, btch:  31 usd: 158
[1801523.686788] CPU    1: hi:  186, btch:  31 usd: 114
[1801523.686790] CPU    2: hi:  186, btch:  31 usd: 133
[1801523.686792] CPU    3: hi:  186, btch:  31 usd:  69
[1801523.686794] CPU    4: hi:  186, btch:  31 usd:  70
[1801523.686796] CPU    5: hi:  186, btch:  31 usd: 131
[1801523.686798] CPU    6: hi:  186, btch:  31 usd: 169
[1801523.686800] CPU    7: hi:  186, btch:  31 usd:  30
[1801523.686802] Node 0 Normal per-cpu:
[1801523.686804] CPU    0: hi:  186, btch:  31 usd: 162
[1801523.686806] CPU    1: hi:  186, btch:  31 usd: 184
[1801523.686809] CPU    2: hi:  186, btch:  31 usd:  99
[1801523.686811] CPU    3: hi:  186, btch:  31 usd:  82
[1801523.686813] CPU    4: hi:  186, btch:  31 usd:  90
[1801523.686815] CPU    5: hi:  186, btch:  31 usd:  99
[1801523.686817] CPU    6: hi:  186, btch:  31 usd: 157
[1801523.686819] CPU    7: hi:  186, btch:  31 usd: 138
[1801523.686824] active_anon:60439 inactive_anon:28841 isolated_anon:0
[1801523.686825]  active_file:110417 inactive_file:907078 isolated_file:64
[1801523.686827]  unevictable:0 dirty:164722 writeback:1652 unstable:0
[1801523.686828]  free:445909 slab_reclaimable:176594
slab_unreclaimable:14754
[1801523.686829]  mapped:4753 shmem:66 pagetables:3600 bounce:0
[1801523.686831] Node 0 DMA free:7904kB min:8kB low:8kB high:12kB
active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB
unevictable:0kB isolated(anon):0kB isolated(file):0kB present:7648kB
mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:0kB
slab_reclaimable:0kB slab_unreclaimable:0kB kernel_stack:0kB pagetables:0kB
unstable:0kB bounce:0kB writeback_tmp:0kB pages_scanned:0
all_unreclaimable? no
[1801523.686841] lowmem_reserve[]: 0 4016 7048 7048
[1801523.686845] Node 0 DMA32 free:1770072kB min:6116kB low:7644kB
high:9172kB active_anon:22312kB inactive_anon:12128kB active_file:4988kB
inactive_file:2190136kB unevictable:0kB isolated(anon):0kB
isolated(file):256kB present:4112640kB mlocked:0kB dirty:535072kB
writeback:6452kB mapped:4kB shmem:4kB slab_reclaimable:72888kB
slab_unreclaimable:1100kB kernel_stack:120kB pagetables:832kB unstable:0kB
bounce:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? no
[1801523.686855] lowmem_reserve[]: 0 0 3031 3031
[1801523.686859] Node 0 Normal free:5660kB min:4616kB low:5768kB
high:6924kB active_anon:219444kB inactive_anon:103236kB
active_file:436680kB inactive_file:1438176kB unevictable:0kB
isolated(anon):0kB isolated(file):0kB present:3104640kB mlocked:0kB
dirty:123816kB writeback:156kB mapped:19008kB shmem:260kB
slab_reclaimable:633488kB slab_unreclaimable:57916kB kernel_stack:2800kB
pagetables:13568kB unstable:0kB bounce:0kB writeback_tmp:0kB
pages_scanned:0 all_unreclaimable? no
[1801523.686869] lowmem_reserve[]: 0 0 0 0
[1801523.686873] Node 0 DMA: 2*4kB 3*8kB 0*16kB 2*32kB 4*64kB 3*128kB
2*256kB 1*512kB 2*1024kB 2*2048kB 0*4096kB = 7904kB
[1801523.686883] Node 0 DMA32: 129*4kB 87*8kB 86*16kB 89*32kB 87*64kB
65*128kB 12*256kB 5*512kB 2*1024kB 13*2048kB 419*4096kB = 1769852kB
[1801523.686893] Node 0 Normal: 477*4kB 23*8kB 1*16kB 5*32kB 0*64kB 3*128kB
3*256kB 1*512kB 0*1024kB 1*2048kB 0*4096kB = 5980kB
[1801523.686903] 1017542 total pagecache pages
[1801523.686905] 0 pages in swap cache
[1801523.686907] Swap cache stats: add 0, delete 0, find 0/0
[1801523.686908] Free swap  = 1048572kB
[1801523.686910] Total swap = 1048572kB
[1801523.722319] 1837040 pages RAM
[1801523.722322] 58337 pages reserved
[1801523.722323] 972948 pages shared
[1801523.722324] 406948 pages non-shared
[1801523.722326] [ pid ]   uid  tgid total_vm      rss cpu oom_adj
oom_score_adj name
[1801523.722396] [31266]     0 31266     6404      511   6       0
    0 init
[1801523.722445] [32489]     0 32489    12370      688   7     -17
-1000 sshd
[1801523.722460] [32511]   101 32511    10513      325   0       0
    0 rsyslogd
[1801523.722495] [32625]     0 32625    17706      838   2       0
    0 sshd
[1801523.722522] [32652]   103 32652     5900      176   0       0
    0 dbus-daemon
[1801523.722583] [  526]     0   526     1553      168   5       0
    0 getty
[1801523.722587] [  530]     0   530     1553      168   1       0
    0 getty
[1801523.722593] [  537]  2007   537    17706      423   5       0
    0 sshd
[1801523.722629] [  538]  2007   538    16974     5191   1       0
    0 python
[1801523.722650] [  877]  2007   877     2106      157   7       0
    0 dd
[1801523.722657] Memory cgroup out of memory: Kill process 538 (python)
score 71 or sacrifice child
[1801523.722674] Killed process 538 (python) total-vm:67896kB,
anon-rss:17464kB, file-rss:3300kB

Eu estou rodando em Linux ip-10-8-139-98 3.2.0-29-virtual # 46-Ubuntu SMP Fri Jul 27 17:23:50 UTC 2012 x86_64 x86_64 x86_64 GNU / Linux no Amazon EC2.

    
por UsAaR33 15.06.2013 / 17:35

3 respostas

13

Editar: manteremos minha resposta original abaixo, mas tentarei explicar o que está acontecendo aqui e fornecer uma solução geral para você.

Editar 2: Fornece outra opção.

O problema que você está atingindo aqui tem a ver com como o kernel gerencia a E / S. Quando você faz uma gravação em seu sistema de arquivos, essa gravação não é imediatamente confirmada no disco; isso seria incrivelmente ineficiente. Em vez disso, as gravações são armazenadas em cache em uma área de memória conhecida como cache de páginas e, periodicamente, gravadas em blocos no disco. A seção "suja" do seu log descreve o tamanho do cache desta página que ainda não foi gravado no disco:

dirty:123816kB

Então, o que esvazia esse cache sujo? Por que ele não está funcionando?

'Flush' no Linux é responsável por escrever páginas sujas no disco. É um daemon que acorda periodicamente para determinar se as gravações em disco são necessárias e, em caso afirmativo, as executa. Se você é do tipo C, comece por aqui . Flush é incrivelmente eficiente; Ele faz um ótimo trabalho de liberar o material para o disco quando necessário. E está funcionando exatamente como deveria.

O flush é executado fora do seu contêiner LXC, já que seu contêiner LXC não possui seu próprio kernel. Os contêineres LXC existem como um constructo em torno de cgroups , que é um recurso do kernel do Linux que permite melhores limitações e isolamento de grupos de processos , mas não seu próprio kernel ou daemon flush.

Como o seu LXC tem um limite de memória menor que a memória disponível no kernel, coisas estranhas acontecem. O Flush assume que tem a memória completa do host para gravar em cache. Um programa no seu LXC começa a gravar um arquivo grande, ele armazena buffers ... e, eventualmente, atinge seu limite máximo, e começa a chamar o gerenciador de OOM. Esta não é uma falha de nenhum componente em particular; é o comportamento esperado. Mais ou menos. Esse tipo de coisa deve ser tratado pelos cgroups, mas não parece ser assim.

Isso explica completamente o comportamento que você vê entre os tamanhos das instâncias. Você vai começar a flushing para o disco muito mais cedo na micro instância (com 512MB de RAM) vs em uma grande instância

Ok, isso faz sentido. Mas é inútil. Eu ainda preciso me escrever um arquivo big-ass.

Bem, o flush não está ciente do seu limite de LXC. Então, ao invés de corrigir o kernel, existem algumas opções aqui para coisas que você pode tentar ajustar:

/proc/sys/vm/dirty_expire_centiseconds

Isso controla por quanto tempo uma página pode ser mantida no cache sujo e gravada no disco. Por padrão, são 30 segundos; tente configurá-lo mais baixo para começar a empurrá-lo mais rápido.

/proc/sys/vm/dirty_background_ratio

Isso controla qual porcentagem do esvaziamento da memória ativa pode ser preenchida antes de começar a forçar gravações. Há um pouco de mexer na classificação do total exato aqui, mas o mais fácil A explicação é apenas olhar para a sua memória total. Por padrão, é 10% (em algumas distros é 5%). Defina isto mais baixo; forçará as gravações no disco mais cedo e poderá impedir que o seu LXC fique fora dos limites.

Não posso simplesmente estragar um pouco com o sistema de arquivos?

Bem, sim. Mas certifique-se de testar isso .. você pode afetar o desempenho. Nas suas montagens em / etc / fstab onde você vai escrever isso, adicione a opção de montagem ' sync ' .

Resposta original:

Try reducing the blocksize used by DD:

dd if=/dev/zero of=test2 bs=512 count=1024000

You can only write one sector at a time (512 bytes on older HDDs, 4096 on newer). If DD is pushing writes to disk faster than the disk can accept them, it will start caching the writes in memory. That's why your file cache is growing.

    
por 15.06.2013 / 21:08
3

O seu arquivo está sendo gravado em / tmp? Em caso afirmativo, pode não estar em um sistema de arquivos real, mas residente no disco. Assim, à medida que você escreve, mais e mais memória é retirada para identificar as necessidades do arquivo. Eventualmente, você fica sem memória + espaço de troca e seu desempenho se deteriora a ponto de frustração total.

    
por 15.06.2013 / 19:38
2

a menos que você esteja gravando no disco RAM, você pode evitar o armazenamento em cache usando oflag = direct

dd if=/dev/zero of=test2 bs=100k oflag=direct count=5010
    
por 16.06.2013 / 08:42