Перевыделение (overcommitting) оперативной памяти в Linux, особенно на рабочих серверах, является величайшим Злом, и это Зло в Linux разрешено по-умолчанию - vm.overcommit_memory=0
По-умолчанию (vm.overcommit_memory=0
) Linux выделяет памяти больше чем есть в наличии, при этом используется эвристический анализ определения выделяемой процессу памяти. Т.е. выделяя оперативки больше чем есть, Linux ещё дополнительно расходует ценные системные ресурсы на эвристику, которая не во всех случаях может давать положительный результат. Эвристический анализ - это, как пальцем в небо.
Более того, наличие возможности перевыделить памяти поощряет говнокодеров говнокодить кривые приложения в стиле "хуяк-хуяк и в продакшин" без реализации кода для надлежащей утилизации неиспользуемой приложением памяти.
Кому сложно понять что такое перевыделение памяти, то для сравнения можно привести пример с продажей билетов, скажем в кино. К примеру, в кинотеатре всего 100 мест, а мы продали 1000 в надежде, что не все купившие билеты дойдут до кинотеатра и востребуют свои места. Но, "сталося не так як гадалося":
- 100 первых пришедших в кино заняли все доступные места;
- 100 по дороге е.анулось, ещё 100 обо.ралось, и до кинотеатра не дошло (ура товарищи);
- 700 оставшихся таки дошли до кинотеатра, но понятно, что мест им не осталось, в гневе они в хлам развалили тот кинотеатр, и в конечном итоге кина не досталось никому.
Дабы подобного беспредела не происходило на Linux сервере мы обязательно должны ограничить рамки перевыделения памяти, что дополнительно избавит от необходимости тратить ресурсы на эвристику, например:
vm.overcommit_memory=2
vm.overcommit_ratio=300
Обычно, относительно "вменяемые" приложения при запуске запрашивают не более чем в 3 раза больше памяти, чем им реально требуется, а значит vm.overcommit_ratio=300
должно быть вполне достаточно в большинстве случаев. При таком раскладе будет доступно оперативки: РАМ * (overcommit_ratio/100) + SWAP. Скажем, у нас РАМ 2048 МБ и СВОП 5120, тогда получим: 2048 * 3 + 5120 = 11264 МБ около того.
vm.overcommit_memory=2
полезно даже в тех случаях, когда в системе работают более-менее "вменяемые" и проверенные приложения, - ведь не факт, что они всегда будут работать стабильно. Ведь даже самые проверенные и надёжные приложения (как и люди) время от времени, по различным причинам, могут сойти с ума начав "форкать чилдов" и запрашивать всё новые и новые порции памяти (перенаселение, голодовка, и песдец).
vm.overcommit_memory=2
совместно с vm.overcommit_ratio=300
определяет границы реальных возможностей системы виртуальной памяти, а OOM killer (OOM: out-of-memory) в свою очередь прибивая пизданутые процессы и/или их чилдов помогает остальным процессам пребывать в равновесии с системными ресурсами.
https://ru.wikipedia.org/wiki/Скрижали_Джорджии
- Пусть земное население никогда не превышает 500.000.000, пребывая в постоянном равновесии с природой
- Разумно регулируйте рождаемость, повышая ценность жизненной подготовки и многообразия человечества
- Найдите новый живой язык, способный объединить человечество.
- Проявляйте терпимость в вопросах чувств, веры, традиций и им подобных
- Пусть справедливые законы и беспристрастный суд встанут на защиту народов и наций
- Пусть каждая нация сама решает свои внутренние дела, вынося на мировой суд общенародные проблемы.
- Избегайте мелочных судебных тяжб и бесполезных чиновников.
- Поддерживайте равновесие между личными правами и общественными обязанностями.
- Превыше всего цените правду, красоту, любовь, стремясь к гармонии с бесконечностью.
- Не будьте раковой опухолью для земли, природе тоже оставьте место!
При vm.overcommit_memory=1
система считает, что в наличии неограниченное количество памяти и выделяет её без ограничений до тех пор пока она вся реально не закончится.
Когда vm.overcommit_memory=2
(overcommit 'never') нам нужно изменить значение по-умолчанию admin_reserve_kbytes
, для определения которого нужно понимать смысл аббревиатур VSZ и RSS.
VSZ and RSS Linux memory
Что такое VSZ и RSS? Ответ нам даст man ps
: VSZ - virtual memory size; RSS - resident set size, the non-swapped physical memory that a task has used (in kiloBytes).
$ man ps|grep -A 3 VSZ ... STANDARD FORMAT SPECIFIERS ... vsz VSZ virtual memory size of the process in KiB (1024-byte units). Device mappings are currently excluded; this is subject to change. (alias vsize). $ man ps|grep -A 3 RSS The SIZE and RSS fields dont count some parts of a process including the page tables, kernel stack, struct thread_info, and struct task_struct. This is usually at least 20 KiB of memory that is always resident. SIZE is the virtual size of the process (code+data+stack). -- rss RSS resident set size, the non-swapped physical memory that a task has used (in kiloBytes). (alias rssize, rsz).
Короче:
- VSZ - виртуально выделенная память, но не означает, что вся она используется. Кто в теме, тот в курсе, что Linux на запрос приложения перевыделяет (overcommitting) памяти больше, чем есть в наличии - прямо как американцы печатают баксов больше, чем фактически обеспечено золотовалютным резервом;
- RSS - фактически занимаемый в данный момент размер оперативки без учёта страниц перемещённых в swap.
man admin_reserve_kbytes
https://www.kernel.org/doc/Documentation/sysctl/vm.txt
admin_reserve_kbytes
The amount of free memory in the system that should be reserved for users with the capability cap_sys_admin.
admin_reserve_kbytes defaults to min(3% of free pages, 8MB)
That should provide enough for the admin to log in and kill a process, if necessary, under the default overcommit 'guess' mode.
Systems running under overcommit 'never' should increase this to account for the full Virtual Memory Size of programs used to recover. Otherwise, root may not be able to log in to recover the system.
How do you calculate a minimum useful reserve?
sshd or login + bash (or some other shell) + top (or ps, kill, etc.)
For overcommit 'guess', we can sum resident set sizes (RSS). On x86_64 this is about 8MB.
For overcommit 'never', we can take the max of their virtual sizes (VSZ) and add the sum of their RSS. On x86_64 this is about 128MB.
Changing this takes effect whenever an application requests memory.
Что такое cap_sys_admin и с чем его едят
cap_sys_admin
- это флаг определяющий набор административных возможностей (capabilities) приложения/программы/пользователя. Подробнее в man capabilities
Отобразить список процессов и назначенный им список capabilities-флагов можно с помощью pscap
из пакета libcap-ng-utils:
# apt-get install libcap-ng-utils or # yum install libcap-ng-utils # pscap|less ppid pid name command capabilities 1 312 root udevd full 1 950 named named net_bind_service, sys_resource + 1 966 root rngd full 20532 969 root sshd full 972 998 root su full 998 999 root bash full 999 1012 root screen full 1 1099 root mysqld_safe full 1 1884 root master full 1 1936 root crond full 1 1968 root atd full 1 2043 ntp ntpd net_bind_service, sys_time + 1 2147 root mingetty full 1 2149 root mingetty full 1 2151 root mingetty full 1 2153 root mingetty full 1 2155 root mingetty full 1 2157 root mingetty full 1 3694 root screen full 3694 3695 root bash full 3694 3710 root bash full 3694 3724 root bash full 3694 3737 root bash full 3694 3751 root bash full 3694 3764 root bash full 3764 3777 root top full 1 12799 root nginx full 1 13714 root httpd full 13714 13716 root rotatelogs full 13714 13717 root rotatelogs full 13714 13718 root rotatelogs full 13714 13719 root rotatelogs full 1 14780 root rsyslogd full 3724 17056 root less full 1 20532 root sshd full
Как распределяется admin_reserve_kbytes
Ответ: а хрен его знает, документация по данному флагу об этом умалчивает...
В сети куча постов с копи/пастой мнения, что увеличив значение admin_reserve_kbytes мы уже якобы "гарантируем" себе возможность логина в вот-вот зависающей машине, - для пользователя root возможно, но для остальных не факт!
Давайте "погульчитаем" документацию по admin_reserve_kbytes где говорится, что:
- "The amount of free memory in the system that should be reserved for users with the capability cap_sys_admin.", - резервируется размер свободной оперативки для пользователей с возможностями
cap_sys_admin
. - "How do you calculate a minimum useful reserve? sshd or login + bash (or some other shell) + top (or ps, kill, etc.) ... For overcommit 'never', we can take the max of their virtual sizes (VSZ) and add the sum of their RSS." - если перевыделение памяти запрещено (overcommit 'never'), то значение должно составлять сумму VSZ + RSS, очевидно всех процессов запущенных системой под такого пользователя и им самим же, которому присвоен флаг
cap_sys_admin
.
Замечено, что на процессы запускаемые от root-a через systemd эта самая зарезервированная в размере admin_reserve_kbytes
оперативка не выделяется, а просто себе болтается без дела, и в этом можно убедится опытным путём.
Опыт №1
Ручками сложить сумму VSZ (ака VmSize) + RSS (ака VmRSS) всех процессов из списка pscap|less по каждому отельному /proc/PID/status
будет довольно геморно, и поэтому давайте "наговнокодим" баш-скрипт автоматизирующий всю эту работу (назовём его ./vsz-total.sh):
Только после полного отключения блокировщика скриптов и рекламы на этом месте появится полезная подсказка/ссылка/код/пример конфигурации/etc!
Запустив скрипт получим сумму значений VmSize и VmRSS всех процессов выданных прогой pscap:
$ ./vsz-total.sh VmSize total: 1988240 kB VmRSS total: 178204 kB --- VmSize+VmRSS: 2166444 kB VmRSS+(VmRSS/2): 267306 kB
Из результата видим, что рекомендуемое значение для admin_reserve_kbytes
основанное на "VSZ + RSS" в данном случае составило 2166444 kB.
Увы, документация не уточняет какая именно "amount of free memory" резервируется параметром admin_reserve_kbytes = это от суммы СВОПа+РАМы или от одной РАМы? Вероятнее всего, от суммы СВОПа+РАМы.
Кроме того, сказано, что: "Changing this takes effect whenever an application requests memory.", - изменения вступают в силу, когда приложения запрашивают память.
Потому, как все системные процессы из списка pscap уже запросили память при запуске и она была им выделена, то в условиях:
# sysctl -w vm.overcommit_memory=2 # sysctl -w vm.overcommit_ratio=300 $ cat /proc/meminfo|less ... CommitLimit: 10636404 kB Committed_AS: 5546516 kB
Когда до достижения ограничения CommitLimit у нас осталось около 4,5 ГБ, мы выполним рекомендацию документации sysctl -w vm.admin_reserve_kbytes=2166444 (зарезервируем 2+ ГБ) и попробуем запустить Chromium, который при запуске перевыделяет 3+ ГБ - т.е. минимум на 0,5 превышаем допустимый CommitLimit, и получаем:
# less /var/log/messages|grep fault Jun 19 09:16:40 localhost kernel: [425230.940278] Chrome_~dThread[3925]: segfault at 0 ip af501157 sp ae877050 error 6 in libxul.so[aefb0000+63b0000] $ cat /proc/meminfo|less bash: fork: Cannot allocate memory
Вернув же sysctl -w vm.admin_reserve_kbytes=262144 обратно к 256 МБ браузер успешно запущен и занял Committed_AS более 9хххххх kB (почти до 10 ГБ).
Теперь пишем данное значение vm.admin_reserve_kbytes=2166444
в /etc/sysctl.conf, но не применяем его, а закрываем все приложения и перезагружаем машину.
Опыт №2
Теперь, после перезагрузки, все процессы, запущенные от имени пользователя root, а он имеет неограниченные права и все флаги возможностей включая cap_sys_admin (reserved for users with the capability cap_sys_admin) , казалось должы были бы использовать большую часть admin_reserve_kbytes и "болтающейся" без дела памяти у нас быть не должно.
Однако, запуская все те же процессы, что были до перезагрузки, среди которых был и браузер palemoon:
$ firejail --name=palemoon /home/test/opt/palemoon/palemoon ... fork(): Невозможно выделить память [mp3 @ 0xae719c00] overread, skip -5 enddists: -2 -2 [mp3 @ 0x8236dc00] overread, skip -5 enddists: -2 -2 [mp3 @ 0x8236cc00] overread, skip -6 enddists: -4 -4 Parent is shutting down, bye... $ cat /proc/meminfo|less ... CommitLimit: 10635300 kB Committed_AS: 8128504 kB
На значении чуть выше 8128504 кб браузер был "забит" и перед смертью выдал "fork(): Невозможно выделить память". Аналогичный результат можно получить при попытке запуска хрона:
# less /var/log/messages|grep fault Jun 19 09:16:40 localhost kernel: [425230.940278] Chrome_~dThread[3925]: segfault at 0 ip af501157 sp ae877050 error 6 in libxul.so[aefb0000+63b0000] $ cat /proc/meminfo|less bash: fork: Cannot allocate memory
Вернув же sysctl -w vm.admin_reserve_kbytes=262144 обратно к 256 МБ браузер успешно запущен и занял Committed_AS более 9хххххх kB (почти до 10 ГБ).
Итоги опытов
В итоге опытным путём мы убедились, что на процессы запускаемые от имени root через systemd зарезервированная память из admin_reserve_kbytes не выделяется, а просто себе болтается без дела как до, так и после перезагрузки.
Вероятно выделение памяти из admin_reserve_kbytes для пользователей с флагом cap_sys_admin происходит при интерактивном взаимодействии с машиной, например при авторизации по SSH/SU и т.п.
Как дать пользователю возможности cap_sys_admin
cap_sys_admin для su-пользователей CentOS 6/7
# vi /etc/security/capability.conf
cap_sys_admin testuser
Далее подключаем PAM-модуль (Pluggable Authentication Modules) pam_cap.so
. ВНИМАНИЕ! Строка подключения pam_cap.so
должна быть перед строкой подключения pam_rootok.so
!
# vi /etc/pam.d/su #%PAM-1.0 auth optional pam_cap.so auth sufficient pam_rootok.so ... ...
Авторизируемся с помощью su, проверяем capabilities для текущего пользователя:
$ su - testuser $ capsh --print Current: = cap_sys_admin+i Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,35,36 Securebits: 00/0x0/1b0 secure-noroot: no (unlocked) secure-no-suid-fixup: no (unlocked) secure-keep-caps: no (unlocked) uid=1001(testuser) gid=1001(testuser) groups=1001(testuser)
Значение для admin_reserve_kbytes
Отметим, что многие описания параметров в документации ядра не только являются не полными, но даже вводят в заблуждение, вот например: "admin_reserve_kbytes defaults to min(3% of free pages, 8MB)", - 3% от свободных страниц, не объёма, а именно страниц (4 кб страница)? А, что это за 8MB?
Значение admin_reserve_kbytes по-умолчанию
Вот, пожалуйста, не у одного меня возник подобный вопрос на данную тему:
Во время загрузки ядра - когда подсистема MM инициализируется - она вычисляет 3% оставшейся свободной памяти. Если это больше, чем 8 МБ, значение устанавливается на 8 МБ.
"Если это больше, чем 8 МБ, значение устанавливается на 8 МБ.", - да хрен там... На машине с Debian Stretch где "MemTotal: 2059828 kB" (без свопа) "default value of the admin_reserve_kbytes" составляло около 6х МБ, что таки да соответствует тем самым 3% от MemTotal: 2059828 kB / 100 * 3 = 61794,84.
На машине с Debian Stretch где "MemTotal: 250400 kB" и vm.overcommit_memory = 0
составило vm.admin_reserve_kbytes = 7616
Однако на другой машине с той же ОС Debian Stretch где "MemTotal: 5083092 kB" и vm.overcommit_memory = 0
составило vm.admin_reserve_kbytes = 8192
Возможно в других ОС и/или версиях ядра иная ситуация со значением admin_reserve_kbytes по-умолчанию и зависит от реализации функции init_admin_reserve(void)
, а также иных функций ею вызываемых global_zone_page_state(NR_FREE_PAGES)
и т.п.: https://elixir.bootlin.com/linux/v5.0/source/mm/mmap.c#L3666
Таким образом, admin_reserve_kbytes:
- defaults = как-то в расброс... То, 3% от RAM (без учёта свопа), то 8MB, а бывает и 7616 кб (Ой как бывает! Всё так зыбко и условно: https://www.youtube.com/watch?v=Ta4G51ZMZRM).
Если значение не указано явно, то admin_reserve_kbytes
по-умолчанию может быть равно 3% (без учёта пространства подкачки страниц), и как видим не факт, что 8 МБ будет минимумом даже если 3% до 8 МБ не дотягивает, - короче, как повезёт. Если не хочется гадать, установи его явно.
Оптимальное значение для admin_reserve_kbytes
Если у нас нет удалённого доступа к нашей машине и соответственно пользователей с cap_sys_admin, vm.overcommit_memory = 2
, то вполне будет достаточно символического 1 кб sysctl -w vm.admin_reserve_kbytes=1, - проверено, с таким значением ничто не загнулось, не взорвалось, ничего страшного не произошло.
А для нахождения оптимального значения admin_reserve_kbytes на удалённом сервере смотрим VmSize (virtual memory size: man proc
) + VmRSS из /proc/PID/status
, для всех основных программ с которыми мы удалённо будем работать, например:
# cat /proc/PID/status|less Name: sshd State: S (sleeping) Tgid: 20532 Pid: 20532 PPid: 1 TracerPid: 0 Uid: 0 0 0 0 Gid: 0 0 0 0 Utrace: 0 FDSize: 64 Groups: VmPeak: 87872 kB VmSize: 87868 kB VmLck: 0 kB VmHWM: 1560 kB VmRSS: 584 kB VmData: 8596 kB VmStk: 88 kB VmExe: 552 kB VmLib: 8444 kB VmPTE: 168 kB VmSwap: 868 kB Threads: 1
В случае overcommit_memory=2, сложив размер VmSize + VmRSS для базовых процессов (sshd/screen/bash) обеспечивающих удалённое взаимодествие с сервером - получим не менее 125-256 МБ, а то и все 512 МБ от общей (ака CommitLimit) системной (РАМ + СВОП + (RAM * overcommit_ratio / 100)) памяти (считать всё в kB разумеется, в МБ указано для удобства).
OOM killer
Немного про OOM killer (OOM: out-of-memory)...
oom_kill_allocating_task
This enables or disables killing the OOM-triggering task in out-of-memory situations.
If this is set to zero, the OOM killer will scan through the entire tasklist and select a task based on heuristics to kill. This normally selects a rogue memory-hogging task that frees up a large amount of memory when killed.
If this is set to non-zero, the OOM killer simply kills the task that triggered the out-of-memory condition. This avoids the expensive tasklist scan.
If panic_on_oom is selected, it takes precedence over whatever value is used in oom_kill_allocating_task.
The default value is 0.
---
Если это значение равно нулю, убийца OOM просканирует весь список задач и выберет задачу для уничтожения на основе эвристики. Обычно он выбирает задачу, которая при уничтожении освободит больше всего памяти.
Если для этого параметра установлено ненулевое значение, убийца OOM просто завершает задачу, которая вызвала состояние нехватки памяти. Это позволяет избежать дорогостоящего сканирования списка задач.
Здесь описание параметра совпадает с реальным его поведением. Если, допустим, у нас из "CommitLimit: 10635924 kB" занято "Committed_AS: 7450192 kB", и мы запустим процесс требующий более 3 ГБ, например Chromium, то OOM killer не обязательно грохнет сам Chromium, который собственно и вызвал "out-of-memory condition", а может выбрать процесс из списка уже работающих, - таким процессом для OOM killer-а оказался другой уже работающий веб-браузер Pale Moon, который занимал до 2 ГБ, в котором на тот момент было открыто много полезных страниц с несохранёнными данными. Т.е., при vm.oom_kill_allocating_task=0
делается упор на освобождение памяти от уже работающих процессов вугоду только что запущенных.
vm.oom_kill_allocating_task=1
заставит OOM killer-а тупо мочить любые процессы, которые стали причиной "out-of-memory condition", - этот вариаинт выбираю потому, что не хочу жертвовать уже работающими процессами. Данный вариант также должен гарантировать прибивание из уже работающих только "психанутых" процессов (вслучай чего с ними станется) вызвавших "out-of-memory condition", вместо наоборот завалить текстовый редактор с несохранёнными данными во благо свихнувшемуся php-cgi
например.