И снова здравствуйте! Перевод следующей статьи подготовлен специально для студентов курса «Инфраструктурная платформа на основе Kubernetes», который запускается уже в этом месяце Начнем.

В последние дни некоторые из моих подов постоянно аварийно завершали работу, оставляя в системном журнале ОС запись о том, что OOM Killer уничтожил процесс контейнера. Я решил разобраться, почему это происходит.
Проведем тест на дистрибутиве K3s. Создаем под с характерным лимитом памяти — 123 МиБ (123 Mi).
В другой консоли выясняем
На сервере, где выполняется под, узнаем параметры
128974848 — это ровно 123 МиБ (123*1024*1024). Ситуация проясняется. Выходит, в Kubernetes лимит памяти задается через cgroup. Как только под разрастется больше отведенного лимита памяти, cgroup инициирует уничтожение процесса контейнера.
Давайте установим утилиты для стресс-тестирования пода через открытый сеанс командной консоли.
В то же время будем отслеживать записи системного журнала командой
Сначала запустим утилиту стресс-тестирования, выделив ей в памяти 100 МБ. Процесс запустился успешно.
Теперь проведем второй стресс-тест.
Запуск привел к мгновенному уничтожению процесса первого стресс-теста (PID 271) по сигналу 9.
Тем временем в системном журнале появились такие записи:
Процесс с PID 32308 на хосте уничтожен в связи с нехваткой памяти (OOM). Но самое интересное скрывается в конце журнальных записей:

Здесь указаны процессы этого пода, которые отмечены как кандидаты на уничтожение компонентом OOM Killer. Базовый процесс
Выясняем объем памяти, доступной узлу:
Если объем запрашиваемой памяти не указан, по умолчанию он будет равен лимиту. Подставив значения, мы получаем следующее значение
Итак, все процессы в контейнере обладают одинаковым значением oom_score_adj. Компонент OOM Killer рассчитывает значение OOM, исходя из использования памяти, и корректирует результат с учетом оценки oom_score_adj. И, в конечном счете, он уничтожает процесс первого стресс-теста, который отъел большую часть памяти, 100 МБ, что соответствует оценке oom_score = 1718.
Kubernetes контролирует лимит памяти подов через параметры cgroup и компонент OOM Killer. Необходимо внимательно согласовывать условия OOM операционной системы и OOM подов.
Как вам материал? Всех, кто желает подробнее узнать о курсе, приглашаем 17 июня на бесплатный вебинар, где изучим возможности Kubernetes для организации практики непрерывной поставки (CI/CD) и подходы как для небольшой команды с несколькими приложениями, так и для большой организации с большим количеством систем.

В последние дни некоторые из моих подов постоянно аварийно завершали работу, оставляя в системном журнале ОС запись о том, что OOM Killer уничтожил процесс контейнера. Я решил разобраться, почему это происходит.
Лимит памяти подов и параметры памяти cgroup
Проведем тест на дистрибутиве K3s. Создаем под с характерным лимитом памяти — 123 МиБ (123 Mi).
kubectl run --restart=Never --rm -it --image=ubuntu --limits='memory=123Mi' -- sh
If you don't see a command prompt, try pressing enter.
root@sh:/#
В другой консоли выясняем
uid
пода.kubectl get pods sh -o yaml | grep uid
uid: bc001ffa-68fc-11e9-92d7-5ef9efd9374c
На сервере, где выполняется под, узнаем параметры
cgroup
, указав uid
нужного пода.cd /sys/fs/cgroup/memory/kubepods/burstable/podbc001ffa-68fc-11e9-92d7-5ef9efd9374c
cat memory.limit_in_bytes
128974848
128974848 — это ровно 123 МиБ (123*1024*1024). Ситуация проясняется. Выходит, в Kubernetes лимит памяти задается через cgroup. Как только под разрастется больше отведенного лимита памяти, cgroup инициирует уничтожение процесса контейнера.
Стресс-тест
Давайте установим утилиты для стресс-тестирования пода через открытый сеанс командной консоли.
root@sh:/# apt update; apt install -y stress
В то же время будем отслеживать записи системного журнала командой
dmesg -Tw
.Сначала запустим утилиту стресс-тестирования, выделив ей в памяти 100 МБ. Процесс запустился успешно.
root@sh:/# stress --vm 1 --vm-bytes 100M &
[1] 271
root@sh:/# stress: info: [271] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
Теперь проведем второй стресс-тест.
root@sh:/# stress --vm 1 --vm-bytes 50M
stress: info: [273] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: FAIL: [271] (415) <-- worker 272 got signal 9
stress: WARN: [271] (417) now reaping child worker processes
stress: FAIL: [271] (451) failed run completed in 7s
Запуск привел к мгновенному уничтожению процесса первого стресс-теста (PID 271) по сигналу 9.
Тем временем в системном журнале появились такие записи:
[Sat Apr 27 22:56:09 2019] stress invoked oom-killer: gfp_mask=0x14000c0(GFP_KERNEL), nodemask=(null), order=0, oom_score_adj=939
[Sat Apr 27 22:56:09 2019] stress cpuset=a2ed67c63e828da3849bf9f506ae2b36b4dac5b402a57f2981c9bdc07b23e672 mems_allowed=0
[Sat Apr 27 22:56:09 2019] CPU: 0 PID: 32332 Comm: stress Not tainted 4.15.0-46-generic #49-Ubuntu
[Sat Apr 27 22:56:09 2019] Hardware name: BHYVE, BIOS 1.00 03/14/2014
[Sat Apr 27 22:56:09 2019] Call Trace:
[Sat Apr 27 22:56:09 2019] dump_stack+0x63/0x8b
[Sat Apr 27 22:56:09 2019] dump_header+0x71/0x285
[Sat Apr 27 22:56:09 2019] oom_kill_process+0x220/0x440
[Sat Apr 27 22:56:09 2019] out_of_memory+0x2d1/0x4f0
[Sat Apr 27 22:56:09 2019] mem_cgroup_out_of_memory+0x4b/0x80
[Sat Apr 27 22:56:09 2019] mem_cgroup_oom_synchronize+0x2e8/0x320
[Sat Apr 27 22:56:09 2019] ? mem_cgroup_css_online+0x40/0x40
[Sat Apr 27 22:56:09 2019] pagefault_out_of_memory+0x36/0x7b
[Sat Apr 27 22:56:09 2019] mm_fault_error+0x90/0x180
[Sat Apr 27 22:56:09 2019] __do_page_fault+0x4a5/0x4d0
[Sat Apr 27 22:56:09 2019] do_page_fault+0x2e/0xe0
[Sat Apr 27 22:56:09 2019] ? page_fault+0x2f/0x50
[Sat Apr 27 22:56:09 2019] page_fault+0x45/0x50
[Sat Apr 27 22:56:09 2019] RIP: 0033:0x558182259cf0
[Sat Apr 27 22:56:09 2019] RSP: 002b:00007fff01a47940 EFLAGS: 00010206
[Sat Apr 27 22:56:09 2019] RAX: 00007fdc18cdf010 RBX: 00007fdc1763a010 RCX: 00007fdc1763a010
[Sat Apr 27 22:56:09 2019] RDX: 00000000016a5000 RSI: 0000000003201000 RDI: 0000000000000000
[Sat Apr 27 22:56:09 2019] RBP: 0000000003200000 R08: 00000000ffffffff R09: 0000000000000000
[Sat Apr 27 22:56:09 2019] R10: 0000000000000022 R11: 0000000000000246 R12: ffffffffffffffff
[Sat Apr 27 22:56:09 2019] R13: 0000000000000002 R14: fffffffffffff000 R15: 0000000000001000
[Sat Apr 27 22:56:09 2019] Task in /kubepods/burstable/podbc001ffa-68fc-11e9-92d7-5ef9efd9374c/a2ed67c63e828da3849bf9f506ae2b36b4dac5b402a57f2981c9bdc07b23e672 killed as a result of limit of /kubepods/burstable/podbc001ffa-68fc-11e9-92d7-5ef9efd9374c
[Sat Apr 27 22:56:09 2019] memory: usage 125952kB, limit 125952kB, failcnt 3632
[Sat Apr 27 22:56:09 2019] memory+swap: usage 0kB, limit 9007199254740988kB, failcnt 0
[Sat Apr 27 22:56:09 2019] kmem: usage 2352kB, limit 9007199254740988kB, failcnt 0
[Sat Apr 27 22:56:09 2019] Memory cgroup stats for /kubepods/burstable/podbc001ffa-68fc-11e9-92d7-5ef9efd9374c: cache:0KB rss:0KB rss_huge:0KB shmem:0KB mapped_file:0KB dirty:0KB writeback:0KB inactive_anon:0KB active_anon:0KB inactive_file:0KB active_file:0KB unevictable:0KB
[Sat Apr 27 22:56:09 2019] Memory cgroup stats for /kubepods/burstable/podbc001ffa-68fc-11e9-92d7-5ef9efd9374c/79fae7c2724ea1b19caa343fed8da3ea84bbe5eb370e5af8a6a94a090d9e4ac2: cache:0KB rss:48KB rss_huge:0KB shmem:0KB mapped_file:0KB dirty:0KB writeback:0KB inactive_anon:0KB active_anon:48KB inactive_file:0KB active_file:0KB unevictable:0KB
[Sat Apr 27 22:56:09 2019] Memory cgroup stats for /kubepods/burstable/podbc001ffa-68fc-11e9-92d7-5ef9efd9374c/a2ed67c63e828da3849bf9f506ae2b36b4dac5b402a57f2981c9bdc07b23e672: cache:0KB rss:123552KB rss_huge:0KB shmem:0KB mapped_file:0KB dirty:0KB writeback:0KB inactive_anon:0KB active_anon:123548KB inactive_file:0KB active_file:0KB unevictable:0KB
[Sat Apr 27 22:56:09 2019] [ pid ] uid tgid total_vm rss pgtables_bytes swapents oom_score_adj name
[Sat Apr 27 22:56:09 2019] [25160] 0 25160 256 1 28672 0 -998 pause
[Sat Apr 27 22:56:09 2019] [25218] 0 25218 4627 872 77824 0 939 bash
[Sat Apr 27 22:56:09 2019] [32307] 0 32307 2060 275 57344 0 939 stress
[Sat Apr 27 22:56:09 2019] [32308] 0 32308 27661 24953 253952 0 939 stress
[Sat Apr 27 22:56:09 2019] [32331] 0 32331 2060 304 53248 0 939 stress
[Sat Apr 27 22:56:09 2019] [32332] 0 32332 14861 5829 102400 0 939 stress
[Sat Apr 27 22:56:09 2019] Memory cgroup out of memory: Kill process 32308 (stress) score 1718 or sacrifice child
[Sat Apr 27 22:56:09 2019] Killed process 32308 (stress) total-vm:110644kB, anon-rss:99620kB, file-rss:192kB, shmem-rss:0kB
[Sat Apr 27 22:56:09 2019] oom_reaper: reaped process 32308 (stress), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB
Процесс с PID 32308 на хосте уничтожен в связи с нехваткой памяти (OOM). Но самое интересное скрывается в конце журнальных записей:

Здесь указаны процессы этого пода, которые отмечены как кандидаты на уничтожение компонентом OOM Killer. Базовый процесс
pause
, в котором хранится пространство сетевых имен (network namespaces), получил оценку oom_score_adj
в -998
, то есть процесс гарантированно не будет уничтожен. Остальные процессы в контейнере получили оценку oom_score_adj
в 939
. Можно проверить это значение, воспользовавшись формулой из документации Kubernetes, приведенной ниже:min(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)
Выясняем объем памяти, доступной узлу:
kubectl describe nodes k3s | grep Allocatable -A 5
Allocatable:
cpu: 1
ephemeral-storage: 49255941901
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 2041888Ki
Если объем запрашиваемой памяти не указан, по умолчанию он будет равен лимиту. Подставив значения, мы получаем следующее значение
oom_score_adj
: 1000–123*1024/2041888=938.32
, что очень близко к значению 939
, указанному в системном журнале. (Не знаю, каким образом OOM Killer получает точное значение 939.)Итак, все процессы в контейнере обладают одинаковым значением oom_score_adj. Компонент OOM Killer рассчитывает значение OOM, исходя из использования памяти, и корректирует результат с учетом оценки oom_score_adj. И, в конечном счете, он уничтожает процесс первого стресс-теста, который отъел большую часть памяти, 100 МБ, что соответствует оценке oom_score = 1718.
Заключение
Kubernetes контролирует лимит памяти подов через параметры cgroup и компонент OOM Killer. Необходимо внимательно согласовывать условия OOM операционной системы и OOM подов.
Как вам материал? Всех, кто желает подробнее узнать о курсе, приглашаем 17 июня на бесплатный вебинар, где изучим возможности Kubernetes для организации практики непрерывной поставки (CI/CD) и подходы как для небольшой команды с несколькими приложениями, так и для большой организации с большим количеством систем.
tchspprt
О чём, собственно, статья? О том, что некоторое виртуализированное/контейнеризированное окружение лимитированно через механизм cgroups, что как бы по-другому бывает редко? О том, что в доках есть какая-то формула, для которой не предоставлено ни логического разъяснения, ни механизма вычисления на самой ноде? О том, как запускать stress-ng?
Исходя из того, что это даже не Ваша статья, а перевод — начинаю очень сомневаться в квалификации OTUS и радоваться, что не лоханулся с оплатой Ваших курсов. Такое лично я (а я никому не заявлял, что квалифицирован преподавать айти-курсы) не стал бы даже писать, не то что переводить.
MaxRokatansky Автор
Если данный материал Вам не интересен, это не значит, что он не интересен другим пользователям хабра. Знаю, что многие хабровчане с большим интересом читают о различных ситуациях из практики коллег в it. Ну и судить по переводу о качестве курсов, как минимум странно. Я бы посоветовал Вам ознакомиться с программой курса, а лучше посетить бесплатный вебинар по которому уже можно делать какие-то выводы.
tchspprt
Вопрос не в том, что материал мне неинтересен, вопрос в том, что тема попросту не раскрыта. От слова совсем.
Поясню — данная статья выглядит примерно так: «берём хайповую технологию -> берём какой-то факт о ней, который на поверхности для 95% инженеров, связанных хоть как-то с этой технологией -> формулируем некую проблему, связанную с этим фактом -> не объясняем по-человечески как эта проблема выглядит изнутри, заменяя это одной строчкой, которая написана в хауту этой хайповой технологии без какого-либо глубокого анализа -> не объясняем по-человечески как бороться с этой проблемой, потому что как гласит предыдущий пункт никакого глубокого анализа и понимания как бы и нет, заменяя объяснение борьбы с проблемой одной строчкой, которая написана в хауту этой хайповой технологии».
Это — не ситуация из практики, о которой Вы сказали. Тут нет никакого вокрараунда ни для чего. Это — заявка на теорию, в которой нет теории. Это — вызубренная формулировка теоремы на экзамене вместо расписанного доказательства теоремы. Три в студенческий, два в уме, пока не отчисляем.
А судить по качеству материала, который Вы публикуете о качестве Ваших курсов — совсем не странно, потому что публикация на инженерную тему и инженерные курсы — условно одно и то же, так как и то, и другое рассчитано на то, что читатель/слушатель/зритель сможет узнать что-то новое. А что новое и кому можно узнать тут? Некоему человеку, который увидел на сайте с вакансиями слово Kubernetes и побежал читать рандомные статьи, запоминая из них баззворды, до этого не зная, условно, в чём разница между контейнеризацией и виртуализацией, а значит заранее сбив последовательность изучения технологии? Ну таких клиентов Вы конечно многому сможете обучить, безусловно.
Мне не стоило, конечно, всё под одну гребёнку — у Вас есть вполне себе неплохие бесплатные вебинары и вполне себе неплохие статьи на хабре. Просто конкретно в данном случае я сделал для себя пометочку «потенциально некомпетентны». Без обид.