메모리 제한 LXC 컨테이너의 응용 프로그램이 큰 파일을 디스크에 쓰는 이유가 OOM에 의해 종료되는 이유는 무엇입니까?


10

EDIT2 :이 문제는 3.8.0-25-generic # 37-Ubuntu SMP에서도 존재합니다.

편집 : 나는 원래 제목에서 질문을 수정했습니다. 왜 Linux 메모리 부족 관리자가 dd로 파일에 쓰면 트리거됩니까? 아래에 설명 된 일반적인 문제에 대해 걱정하고 있음을 더 잘 반영합니다.

메모리 제한 (300MB로 설정)을 초과하는 크기의 파일을 쓸 때 OOM 킬러가 LXC 컨테이너에서 프로세스를 강제 종료하는 번거로운 시나리오를 겪고 있습니다. 실제로 512MB의 RAM 만있는 Xen 가상 시스템 (EC2 t1.micro)에서 응용 프로그램을 실행할 때 문제가 발생하지 않으므로 컨테이너 메모리 제한과 관련하여 파일 버퍼링에 문제가있는 것으로 보입니다.

간단한 예로, dd로 작성된 큰 파일이 어떻게 문제를 일으키는 지 보여줄 수 있습니다. 다시이 문제는 모든 응용 프로그램을 괴롭 힙니다. 응용 프로그램 캐시가 너무 커지는 일반적인 문제를 해결하려고합니다. "dd"작업을 수행하는 방법을 이해합니다.

대본:

memory.limit_in_bytes가 300MB로 설정된 LXC 컨테이너가 있습니다.

다음과 같이 ~ 500MB 파일을 dding하려고합니다.

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

대략 20 %의 시간에 Linux OOM 관리자가이 명령에 의해 트리거되고 프로세스가 종료됩니다. 말할 것도없이, 이것은 의도하지 않은 행동입니다. dd는 컨테이너 내에서 실행되는 프로그램에 의한 실제 "유용한"파일 쓰기를 시뮬레이션하기위한 것입니다.

세부 정보 : 파일 캐시가 커지지 만 (260MB) rss 및 파일 맵은 상당히 낮은 것으로 보입니다. 다음은 쓰기 도중 memory.stat의 모습에 대한 예입니다.

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

다음은 OOM이 킬을 트리거 한 dmesg의 페이스트입니다. 나는 메모리 유형들 사이의 차이점에 너무 익숙하지 않다. 눈에 띄는 한 가지는 "노드 0 정상"이 매우 낮지 만 노드 0 DMA32 메모리가 충분하다는 것입니다. 파일 쓰기로 인해 OOM이 발생하는 이유를 누구나 설명 할 수 있습니까? 이 문제가 발생하지 않도록하려면 어떻게합니까?

일지:

[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

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 Amazon EC2의 GNU / Linux.


1
이 글을 읽는 모든 사람에 대한 요약으로, 이것은 리눅스 커널 버그입니다
UsAaR33

답변:


13

편집 : 나는 원래의 대답을 아래에 유지하지만 여기서 일어나는 일을 설명하고 일반적인 해결책을 제공하려고 노력할 것입니다.

편집 2 : 다른 옵션을 제공했습니다.

여기서 겪고있는 문제는 커널이 I / O를 관리하는 방법과 관련이 있습니다. 파일 시스템에 쓸 때 해당 쓰기는 즉시 디스크에 커밋되지 않습니다. 엄청나게 비효율적입니다. 대신, 쓰기는 페이지 캐시라고하는 메모리 영역에 캐시되고 주기적으로 디스크에 청크로 작성됩니다. 로그의 "더러운"섹션은 디스크에 아직 기록되지 않은이 페이지 캐시의 크기를 설명합니다.

dirty:123816kB

이 더티 캐시를 비우는 것은 무엇입니까? 왜 일을하지 않습니까?

Linux에서 'Flush'는 더티 페이지를 디스크에 기록합니다. 디스크에 쓰기가 필요한지 확인하고 필요한 경우 수행하는 데 정기적으로 깨우는 데몬입니다. 당신이 C 유형의 사람이라면 여기 에서 시작 하십시오 . 플러시는 엄청나게 효율적입니다. 필요할 때 디스크로 물건을 플러시하는 훌륭한 작업을 수행합니다. 그리고 그것은 정확히 예상대로 작동합니다.

LXC 컨테이너에는 자체 커널이 없기 때문에 Flush는 LXC 컨테이너 외부 에서 실행됩니다 . LXC 컨테이너 는 cgroups에 대한 구성으로 존재 하는데, 이는 Linux 커널의 기능으로 프로세스 그룹을보다 효과적으로 제한하고 격리 할 수 ​​있지만 자체 커널 또는 플러시 데몬은 허용하지 않습니다.

LXC의 메모리 제한은 커널이 사용할 수있는 메모리보다 낮기 때문에 이상한 일이 발생합니다. Flush는 쓰기를 캐시하기 위해 호스트의 전체 메모리가 있다고 가정합니다. LXC의 프로그램은 큰 파일을 쓰기 시작하고 버퍼링 ... 버퍼 ... 그리고 결국 하드 한계에 도달하고 OOM 관리자를 호출하기 시작합니다. 이것은 특정 구성 요소의 고장이 아닙니다. 예상되는 동작입니다. 거의. 이런 종류의 일은 cgroup이 처리해야하지만 실제로는 그렇지 않습니다.

이것은 인스턴스 크기 사이에 나타나는 동작을 완전히 설명합니다. 마이크로 인스턴스 (512MB RAM 사용)와 큰 인스턴스에서 디스크를 훨씬 빨리 플러시하기 시작합니다.

알겠습니다. 그러나 쓸모가 없습니다. 여전히 큰 파일을 작성해야합니다.

플러시는 LXC 제한을 인식하지 못합니다. 따라서 커널을 패치하는 대신 조정할 수있는 옵션이 몇 가지 있습니다.

/proc/sys/vm/dirty_expire_centiseconds

이는 더티 캐시에 페이지를 보유하고 디스크에 쓸 수있는 시간을 제어합니다. 기본적으로 30 초입니다. 더 빨리 밀어 내려면 낮게 설정하십시오.

/proc/sys/vm/dirty_background_ratio

이것은 쓰기 강제 실행을 시작하기 전에 채워지는 활성 메모리 플러시 백분율을 제어합니다. 여기에 정확한 총계 를 정렬하는 약간의 어려움이 있지만 가장 쉬운 설명은 총 메모리를 보는 것입니다. 기본적으로 10 %입니다 (일부 배포판에서는 5 %). 이것을 낮게 설정하십시오. 디스크에 빨리 쓰기를 강제하고 LXC가 한계를 초과하지 못하게 할 수 있습니다.

파일 시스템을 약간 조일 수는 없습니까?

그래 그러나 이것을 테스트해야합니다. 성능에 영향을 줄 수 있습니다. 이것을 작성할 / etc / fstab의 마운트에서 ' sync '마운트 옵션을 추가하십시오 .

원래 답변 :

DD가 사용하는 블록 크기를 줄이십시오.

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

한 번에 하나의 섹터 만 쓸 수 있습니다 (이전 HDD의 경우 512 바이트, 최신의 경우 4096). DD가 디스크에서 허용 할 수있는 것보다 빠르게 디스크에 쓰기를 푸시하면 메모리에 쓰기 캐싱을 시작합니다. 이것이 파일 캐시가 커지는 이유입니다.


파일 객체를 수동으로 플러시하는 파이썬에서 비슷한 테스트를 실행하면 비슷한 확률로 오류가 계속 발생합니다. 캐시는 물론 커지지 만 프로세스가 종료되는 것보다는 제거되어야한다고 생각합니다.
UsAaR33 2016 년

1
어쨌든 나는 그것을 쐈다. 파이썬으로 fsync ()를 강요하는 것이 항상 원하는 것을하지는 않습니다.
alexphilipp 2016 년

1
@ UsAaR33 더 빠른 디스크를 얻으십시오.
tink

1
@ UsAaR33 응용 프로그램은 최대한 빨리 작성합니다. 커널이 IO를 처리 할 것으로 예상합니다. 이전에 LXC 컨테이너를 사용하지 않았지만 한 눈에보기에는 chroot에서 자체 커널을 제공하지 않는 것처럼 보입니다. 이 경우 커널은 사용 가능한 호스트 시스템의 전체 메모리가 있다고 가정하여 IO에 IO를 제공합니다. 300MB로 제한한다고 생각하지 않습니다. 한도에 도달하면 OOM에서 프로세스 종료를 시작합니다.
alexphilipp 2016 년

1
@ UsAaR33 : 잘못된 설정은 나쁜 결과를 초래합니다. 시스템의 한 부분은 많은 메모리가 캐시로 사용될 수 있다고 말하고, 시스템의 다른 부분은 캐시가 너무 큰 경우 프로세스를 종료하도록 지시됩니다. 사용 가능한 RAM이 많을 때 디스크를 기다려야하는 이유는 무엇입니까? 사용 가능한 RAM이 많으면 사용하지 않겠습니까?
David Schwartz

3

파일이 / tmp에 작성되고 있습니까? 그렇다면 실제 파일 시스템이 아니라 디스크에있을 수 있습니다. 따라서 파일에 쓸수록 파일의 요구를 충족시키기 위해 점점 더 많은 메모리가 제거됩니다. 결국 메모리 + 스왑 공간이 부족하여 성능이 완전히 좌절 될 수 있습니다.


기본 디스크에 대한 쓰기를 트리거하는 AUFS 마운트에있는 $ HOME에 쓰기 중입니다. (EC2 EBS)
UsAaR33 2016 년

2

RAM 디스크에 쓰지 않는 한 oflag = direct를 사용하여 캐싱을 피할 수 있습니다

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

direct는 "잘못된 인수"오류를 유발하지만 oflag = dsync를 사용하면 작동합니다.
UsAaR33 2016 년

매뉴얼 페이지 "데이터에 대한 직접 사용 직접 I / O"에 따라 작동하지 않으면 죄송합니다.
Kevin Parker
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.