Чому додатки в контейнер LXC з обмеженою пам'яттю записують великі файли на диск, який вбиває OOM?


10

EDIT2: Здається, ця проблема існує також під загальною версією 3.8.0-25-generic # 37-Ubuntu SMP

EDIT: Я змінив питання з оригінальної назви "Чому запускається менеджер Linux з пам'яті, записуючи у файл з dd?" щоб краще відобразити, що мене хвилює загальне питання, описане нижче:

Я стикаюся з неприємним сценарієм, коли вбивця OOM важко вбиває процеси в мій контейнер LXC, коли я записую файл розміром, що перевищує обмеження пам'яті (встановлено на 300 МБ). Проблема не виникає, коли я запускаю додаток на віртуальній машині Xen (EC2 t1.micro), яка фактично має лише 512 Мб оперативної пам’яті, тому, мабуть, виникає проблема з буферизацією файлів з дотриманням обмеження пам’яті контейнерів.

Як простий приклад, я можу продемонструвати, як великий файл, написаний dd, спричинить проблеми. Знову ж таки, це питання зачіпає всі програми. Я хочу вирішити загальну проблему, якщо кеш програми стає занадто великим; Я розумію, як я можу змусити "дд" працювати.

Сценарій:

У мене є контейнер LXC, де пам'ять.limit_in_bytes встановлена ​​на 300 Мб.

Я намагаюся створити файл ~ 500 Мб наступним чином:

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

Приблизно в 20% часу менеджер OOM Linux спрацьовує за допомогою цієї команди і процес вбивається. Потрібно говорити, що це дуже ненавмисна поведінка; dd призначений для імітації фактичного "корисного" запису файлів програмою, що працює всередині контейнера.

Докладніше: Хоча файлові кеші (260 МБ), 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

Ось паста з dmesg, де OOM спровокував вбивство. Я не надто знайомий з відмінностями між типами пам’яті; Єдине, що вирізняється, це те, що хоча "Node 0 Normal" дуже низький, у пам'яті Node 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 Пт 27 липня 17:23:50 UTC 2012 x86_64 x86_64 x86_64 GNU / Linux на Amazon EC2.


1
Короткий підсумок для всіх, хто читає, це помилка ядра Linux
UsAaR33

Відповіді:


13

Редагувати: Я збережу свою оригінальну відповідь нижче, але спробую пояснити, що відбувається тут, і запропонувати загальне рішення для вас.

Редагування 2: надано інший варіант.

Проблема, з якою ви потрапляєте тут, пов'язана з тим, як ядро ​​управляє входом / виводом. Коли ви записуєте у свою файлову систему, це запис не приймається відразу на диск; це було б неймовірно неефективно. Натомість записи запитуються в кеш-пам'ять в області пам’яті, що називається кешем сторінки, і періодично записуються фрагментами на диск. У "брудному" розділі вашого журналу описується розмір кешу сторінки, який ще не був записаний на диск:

dirty:123816kB

То що спорожняє цей брудний кеш? Чому він не робить свою роботу?

'Flush' в Linux відповідає за виведення брудних сторінок на диск. Це демон, який періодично прокидається, щоб визначити, чи потрібні записи на диск, і, якщо так, виконує їх. Якщо ви хлопець типу С, починайте тут . Флеш неймовірно ефективний; вона виконує велику роботу з промивання матеріалів на диску, коли це необхідно. І працює саме так, як і належить.

Flush працює поза вашим контейнером LXC, оскільки ваш контейнер LXC не має власного ядра. LXC контейнери існують як конструкт навколо контрольних груп , яка є особливістю Linux ядра , що дозволяє краще обмеження та ізоляції груп процесів, а не його власне ядро або флеш - демон.

Оскільки у Вашого LXC обмеження пам’яті нижче, ніж у пам’яті, яке має ядро, трапляються дивні речі. Flush передбачає, що у нього є повна пам'ять хоста, на який записується кеш. Програма у вашому LXC починає писати великий файл, він буферизує ... буфери ... і врешті-решт потрапляє в жорсткий ліміт і починає дзвонити менеджеру OOM. Це не збій якогось конкретного компонента; це очікувана поведінка. Типу. Такими речами повинні займатися групи, але, схоже, це не так.

Це повністю пояснює поведінку, яку ви бачите між розмірами екземплярів. Ви почнете промивати на диску набагато швидше на мікроаптеці (з 512 Мб оперативної пам’яті) проти великого екземпляра

Гаразд, це має сенс. Але це марно. Мені все-таки потрібно написати мені файл з великими дупами.

Що ж, флеш не знає вашу межу LXC. Тож замість виправлення ядра, тут є кілька варіантів речей, які можна спробувати налаштувати:

/proc/sys/vm/dirty_expire_centiseconds

Це керує тим, як довго сторінка може зберігатися в брудному кеші і записуватися на диск. За замовчуванням це 30 секунд; спробуйте встановити його нижче, щоб почати швидше виштовхувати його.

/proc/sys/vm/dirty_background_ratio

Це дозволяє контролювати, який відсоток активного потоку пам'яті дозволено заповнювати до того, як він починає примусово записувати. Існує трохи хитрощів, які спрямовані на сортування точної загальної кількості тут, але найпростіше пояснення - просто подивитися на вашу загальну пам'ять. За замовчуванням це 10% (у деяких дистрибутивах - 5%). Встановіть це нижче; це змусить швидше виписувати на диск і може не допустити, щоб ваш LXC не вичерпався.

Чи не можу я трохи закрутити файлову систему?

Ну так. Але переконайтесь, що ви протестуєте це. Ви можете вплинути на продуктивність. На вашій монтажі в / etc / fstab, куди ви будете писати це, додайте опцію " синхронізувати ".

Оригінальна відповідь:

Спробуйте зменшити розмір блоків, використовуваний DD:

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

За один раз можна писати лише один сектор (512 байти на старих жорстких дисках, 4096 - на новіших). Якщо DD натискає записи на диск швидше, ніж диск може їх прийняти, він почне кешування записів у пам'яті. Ось чому ваш кеш файлів зростає.


Слід зазначити, що якщо я запускаю подібні тести в python, де я вручну промиваю файловий об’єкт, помилка все ж виникає з подібною ймовірністю. Кеш, звичайно, зростає, але про те, що слід очищати, можна було б думати, а не вбивати процес.
UsAaR33

1
Я б все одно дав йому постріл. Я виявив, що змушує fsync () з Python не завжди робити те, що ви очікуєте.
alexphilipp

1
@ UsAaR33 Отримайте швидший диск.
Тінь

1
@ UsAaR33 Додаток буде писати якомога швидше; він очікує, що ядро ​​обробляє IO. Я раніше не використовував контейнер LXC, але з короткого погляду виглядає, що він не забезпечує його власне ядро ​​в створеному хроноту? Якщо це так, ядро ​​надає IO припущення, що воно має повну пам'ять хост-системи. Поняття не має, що ви обмежили його 300МБ. Після досягнення цієї межі OOM починає вбивати процеси.
alexphilipp

1
@ UsAaR33: Погане налаштування призводить до поганих результатів. Одну частину системи повідомляється, що велика кількість пам'яті може використовуватися як кеш, інша частина системи наказана знищувати процеси, якщо кеш занадто великий. Чому слід чекати диска, коли є багато оперативної пам’яті? А якщо є достатня кількість оперативної пам’яті, чому б не дозволити її використовувати?
Девід Шварц

3

Ваш файл пише в / tmp? Якщо це так, воно може бути не у фактичній файловій системі, а знаходиться на диску. Таким чином, коли ви пишете до нього, все більше пам'яті забирається для задоволення потреб файлу. Врешті-решт у вас не вистачає пам’яті + місця для обміну, і ваша продуктивність погіршується до повної розлади.


Він пише в $ HOME, який знаходиться на кріпленні AUFS, який запускає запис на базовий диск. (EC2 EBS)
UsAaR33

2

якщо ви не пишете на диск RAM, ви можете уникнути кешування, використовуючи oflag = direct

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

direct викликає помилку "Недійсний аргумент", але використання oflag = dsync працює.
UsAaR33

Вибачте, якщо це не спрацювало для вас, як на сторінці чоловіка "Пряме використання прямого вводу / виводу для даних"
Кевін Паркер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.