Назад | Перейти на главную страницу

Как справиться с проблемами нехватки памяти в многопользовательских системах?

Когда процесс пытается выделить больше памяти, чем доступно, обычно очень трудно справиться с этой ситуацией должным образом. В некоторых случаях программы могут освободить несущественную память (например, кеши), но в большинстве случаев это приведет к фатальным исключениям (см. Также [1]), чтобы свопинг и массовое замедление системы или даже убить (довольно произвольно) процессы с помощью OOM убийца.

В однопользовательской системе все варианты плохи, но, по крайней мере, вы можете только навредить себе (в конце концов, вы несете ответственность за весь код, который запускаете на своей машине).

Однако в многопользовательской системе я бы ожидал, что можно будет заставить убийцу OOM убивать определенные процессы, возможно, принадлежащие другим пользователям. Существуют определенные ограничения, поскольку убийца OOM пытается найти «процесс, который использует большой объем памяти, но не так долго живет» (см. Вот).

Тем не менее, если представить ситуацию, когда процессу требуется x МБ памяти и он не существовал очень долгое время (низкое время ЦП) и принадлежит пользователю X, а x значительно превышает объем свободной памяти в системе, пользователь Y мог бы иметь возможность выделить больше памяти, чем доступно, и заставить систему убить процесс X (потому что он использует больше памяти).

Эта ситуация кажется более пугающей, чем аналогичные последствия для однопользовательской машины. Можно установить ограничения памяти для каждого пользователя или даже использовать контейнеры для еще большего разделения процессов. Но насколько я понимаю, устанавливаю лимит только на total_memory / number_of_users решит проблему. Но при установке такого лимита теряются все преимущества многопользовательской системы. В этом случае машина фактически похожа на несколько однопользовательских машин в одной коробке. Обычно требуется разрешить одному пользователю использовать больше памяти в часы пик, потому что большую часть времени пользователям потребуется меньше памяти, чем в среднем.

Меня больше всего интересует решение этой проблемы в ситуациях с большими вычислениями с огромными объемами данных. Я думаю, что в случае веб-серверов можно было бы лучше оценить, сколько памяти требуется, поскольку существует много мелких операций, а не несколько крупных. Но даже в этом случае я слышал, что в обычных ситуациях должно быть заполнено только 50% вашей памяти, чтобы избежать проблем с нехваткой памяти во время пиков. Разве это не пустая трата 50% вашей памяти?

В частности, я подумываю разместить хаб Jupyter или что-то подобное. Но я не хочу, чтобы пользователи убивали процессы друг друга.

Есть ли другое решение этой проблемы? Как крупные облачные провайдеры, такие как Travis CI, справляются с подобными проблемами?

Я считаю, что в настоящее время рекомендуемый способ ограничить влияние одной задачи на другие - использовать контрольную группу для ограничения памяти, которую она может использовать. Самый простой способ, который я нашел, - использовать systemd-run. Например, это начинается ls со смехотворно низким пределом памяти в 1000 байт:

sudo systemd-run --uid $USER --gid $UID -p MemoryMax=1000 ls

Запустив journalctl -f, вы увидите, что процесс был немедленно остановлен:

Memory cgroup out of memory: Kill process 3543 ((ls)) score 2503000 or sacrifice child
Killed process 3543 ((ls)) total-vm:205348kB, anon-rss:5612kB, file-rss:4276kB, shmem-rss:0kB

Тогда, конечно, вы захотите перевыполнить подписку машины, возможно, выделив каждой задаче 70% доступной памяти? Это позволит достичь пиков в потреблении памяти, в то же время быстро убивая процессы, которые используют неоправданный объем памяти.

Отключить oom killer (vm.oom-kill = 0 в файле, прочитанном sysctl) и отключите переопределение памяти (vm.overcommit_memory = 2). Любой процесс, который пытается выделить больше памяти, чем доступно, будет убит. Никакой другой процесс не будет убит. Можно использовать всю память; память не тратится.

Это не избавляет от лишней памяти. Группа памяти имеет memory.soft_limit_in_bytes настройка, которая выглядит как полезная (в сочетании с memory.limit_in_bytes и memory.memsw.limit_in_bytes), но я не нашел подробного объяснения того, как это работает.

Изменить: я смотрел, как memory.soft_limit_in_bytes используется в исходном коде ядра. Если память не может быть восстановлена, ничего не происходит.

Вы можете установить sysctl vm.oom_kill_allocating_task. Если это установлено, и запрос памяти процесса приведет к нехватке памяти, Linux убьет этот процесс.

Из документации:

Это включает или отключает завершение задачи запуска OOM в ситуациях нехватки памяти.

Если он установлен в ноль, OOM killer просканирует весь список задач и выберет задачу на основе эвристики для уничтожения. Обычно это выбирает мошенническую задачу по перегрузке памяти, которая освобождает большой объем памяти при завершении работы.

Если это значение не равно нулю, убийца OOM просто завершает задачу, которая вызвала состояние нехватки памяти. Это позволяет избежать дорогостоящего сканирования списка задач.

Если выбран panic_on_oom, он имеет приоритет над любым значением, используемым в oom_kill_allocating_task.

Значение по умолчанию - 0.