Применимость: Linux, OpenVZ
Слова для поиска: memory, limit
Статья еще не завершена
Память - наиболее важный ресурс сервера. Обычно администраторы полагаются на базовые параметры систем предусмотренные разработчиками, но нередко этого недостаточно.
От умения администратора ориентироваться в аспектах управления памятью с зависит производительность, устойчивость, а нередко и работоспособность систем.
В этой статье я пытаюсь упрощенно и кратко изложить наиболее важные для системного администратора моменты по управлению памятью применительно к использованию OpenVZ.
Для подробностей обращайтесь к соответствующей документации.
Принципы работы менеджера памяти в Linux подробно изложены на 768 страницах книги Understanding the Linux Virtual Memory Manager (Мел Горман) и в книге Ядро Linux (Бовет, Чезати).
Подсистема виртуальной памяти распределяет память между задачами (процессами). Каждая задача (процесс) считает, что ей выделен непрерывный участок памяти максимального размера, поддерживаемого на соответствующей архитектуре (для архитектуры x86 это 4GB). Из них один гигабайт (только для x86) резервируется для ядра.
Основным средством организации работы операционной системы UNIX® (Linux) и единицей многозадачности является процесс - уникальным образом идентифицируемая программа, которая нуждается в получении доступа к ресурсам компьютера. Процесс представляет собой программный код которым манипулирует Операционная система. Операционная система координирует работу разделов данных процесса и определяет среду выполнения.
Данные, связанные с процессом, также являются частью образа процесса. Некоторые из них хранятся в регистрах, обычно представленных регистрами процессора. И существуют динамические области хранения данных (куча), выделяемые процессу по ходу работы при необходимости.
Еще у процесса есть стек, содержащийся в памяти и используемый для хранения локальных переменных программы и передачи параметров. Когда процесс выполняет обращение к функции или подпрограмме, в стек отправляется новый фрейм. Одной из частей каждого фрейма является указатель на базу предыдущего фрейма, который позволяет легко вернуться из вызова функции.
Количество доступной памяти в Linux определяется совокупным значением RAM + swap (пространство подкачки на диске).
На самом же деле программа занимает только тот объем памяти, с которым она реально работает. Большинство памяти существует виртуально, но будет предоставлено программе в тот момент, когда она обратится в эту область.
Ядро распределяет память страницами фиксированного размера. Процедура, когда страница оперативной памяти объявляется частью адресного пространства процесса, называется отображением этой страницы в адресное пространство процесса.
Когда процесс обращается к некоторой странице своего адресного пространства, ядро проверяет, имеет ли он право на доступа к этой странице, и если проверка пройдена и доступ получен, то ядро переадресовывает обращение на реальный адрес этой страницы. Размер страницы фиксирован архитектурой процессора, и для x86 размер составляет 4096 байт.
Если свободных страниц больше нет, но существует файл подкачки, куда ядро может убрать одну из наиболее долго не использовавшихся страниц, и освободившуюся физическую страницу отдать запросившему память процессу.
Если нет ни незанятого пространства в файле подкачки, ни свободных страниц RAM, то развитие событий может быть следующим: либо запросивший память процесс прерван и “убит” системой, либо какой-то другой из процессов (это определяется специфическими алгоритмами) будет “убит” ядром, и освободившаяся память будет передана запросившему память процессу.
Ограничение адресного пространства в 4GB не означает, что система не сможет адресовать более этого объема памяти. На платформе x86 ядро Linux может использовать до 64GB, а ограничение в 4GB накладывается лишь на размер адресного пространства процесса.
Количество памяти в Linux определяется совокупным значением RAM + swap. Память процессам выделяется из этого пула посредством функций языка *alloc()1)
Память используется экономно, но ядро старается использовать всю память, если память не занята процессами, то ядро использует ее под буферизацию (кэширование) данных. Ценный ресурс не должен болтаться без дела.
Однако ядро позволяет выделять памяти больше чем RAM + swap. Такое поведение ядра называется перевыделение (overcommitting) памяти. Как это возможно без ущерба для устойчивости? Например выполняется некоторое количество процессов вебсервера apache. Примерно 20-30% от пространства памяти выделенного каждому процессу apache зарезервирована, но не используется и потому может быть предоставлена другим процессам. Алгоритм использования этого пространства может регулироваться параметрами ядра.
Параметры использования памяти вы можете увидеть в файлах:
Утилиты sysctl и ulimit через управление параметрами ядра позволяют ограничивать системные ресурсы. Это может помочь в ситуации когда , например, запускается слишком много процессов и система не отвечает на запросы. Или когда, напротив, заданных по умолчанию параметров недостаточно для эффективной работы приложения.
Через файловые системы sysfs и procfs ядро linux обеспечивает интерфейс управления в виде файлов каталогах /proc и /sys. Изменение значений в этих файлах позволяет вносить изменения параметров в работающее ядро без остановки или перезагрузки системы. Для взаимодействия с этими файлами обычно используют команду echo или утилиту sysctl ( man sysctl(8)). Использование текстовых редакторов для этих целей не допускается.
Просмотреть все параметры:
sysctl -a
Задать параметр:
sysctl -w параметр=значение
Заданный параметр будет действовать до перезагрузки системы.
Для внесения постоянных изменений используется файл /etc/sysctl.conf ( an sysctl.conf(8)).
ulimit - установка или определение ограничений для процессов пользователя.
Примеры использования ulimit:
ulimit -d 48000 # Ограничиваем максимальный размер сегмента данных в 48 MB ulimit -s 8192 # Ограничиваем максимальный размер стэка в 8 MB ulimit -m 48000 # Ограничиваем макс. размер резидентной части процесса (находящейся в ОЗУ) в 48 MB ulimit -u 64 # Ограничиваем максимальное часло запущенных этим пользователем процессов. ulimit -n 128 # Ограничиваем максимальное часло открытых файлов. ulimit -f 100000 # Ограничиваем максимальный размер создаваемого файла в 100 MB ulimit -v 100000 # Ограничиваем максимальный размер используемой виртуальной памяти в 100 MB
Параметры ядра следует менять с контролем влияния на общую производительность. Например популярная в интернете методика экономии памяти на размере стека не всегда дает положительный эффект. Для опредления размера стека используется команда
ulimit -s 1024
Это уменьшит размер стека и общее потребление памяти, но ухудшит стабильность и совсем не обязательно увеличит производительность. Если размер стека окажется хоть на байт меньше необходимого, то процессы будут обрываться с ошибками. Потому не торопитесь это использовать без нагрузочного тестирования.
Кроме стабильности придется побеспокоится и о безопасности. Приложение работающее на вашем сервере может быть хорошо защищено сам по себе, но вы может экспериментами с параметрами стека открыть широкую дверь злоумышленникам.
Есть несколько популярных методик преодоления защиты основанных на переполнении стека. Принцип их действия заключается в том, что в результате не предусмотренных разработчиком действий размер данных внутри программы для той или иной переменной превышают размер отведённой памяти для этой самой переменной. В результате чего часть данных попадает на чужой участок памяти и это может повлечь за собой иной исход работы программы.
ИМХО - если уменьшение размера стека не дает вам более 10% производительности (скорее всего не даст), то это вам не надо.
В контейнере OpenVZ есть особые принципы работы с памятью. Помимо общих проверок , выполняемых ядром при выделении памяти процессам, производятся проверки лимитов параметров памяти установленных для каждого контейнера. Внутри контейнера OpenVZ вы не увидите память выделенную под буферизацию (кэширование) и не увидите размер swap (кроме случая использования VSWAP). Тем не менее и то и другое обеспечивается. Но это происходит в пространстве физического сервера (HN)7) и скрыто от пользователя контейнера.
Ядро OpenVZ также позволяет использовать overcommitting памяти, но эффект от него может быть значительно большим. Однако использование overcommitting памяти требует правильного лимитирования памяти и постоянного наблюдения за фактическим использованием памяти, иначе пользователи виртуальных серверов увидят сообщение в логах
Out of Memory: Killed process 3345 (postgres)
Это означает, что сработал OOM (out-of-memory killer)8) , процесс postgres был остановлен, так как не хватило памяти. Хотя существующие соединения с базой данных будут работать, новые соединения не будут приняты. Чтобы восстановить нормальную работу, необходимо перезапустить PostgreSQL.
Для обеспечения устойчивой работы системы, справедливого распределения ресурсов и изоляции контейнеров используется интерфейс ядра Linux предоставляемый как файл /proc/user_beancounters (**UBC**) посредством файловой системы ядра procfs.
Большинство параметров (UBC) представленных в файле /proc/user_beancounters ограничивает ресурсы системы и эти параметры позволяют управлять потреблением этих ресурсов.
Исключения: - physpages (только считающий параметр) и vmguarpages (только управляющий), объяснены ниже. Ограничения должны обеспечивать достаточную производительность каждого виртуального сервера, стабильность и безопасность. Нагрузка на каждом отдельном контейнере не должна заметно влиять на все остальные.
Просмотреть список ограничений можно как изнутри контейнера так и снаружи на мастермашине (физическом сервере). Для этого достаточно прочитать содержимое файла /proc/user_beancounters с правами root.
cat /proc/user_beancounters
Можно использовать скрипт vzubc.pl или vzubc для получения информации в более гуманном виде.
./vzstats.pl
Значения колонок таблицы:
held означает текущее значение утилизации ресурса.
maxheld максимальное значение за все время работы
barrier Обозначает максимальное значение потребления ресурса, которое, в ряде случаев, может быть превышено (если на HN есть свободные ресурсы)
limit Обозначает максимальное значение потребления ресурса, которое никогда не может быть превышено контейнером
Текущее положение с выделением памяти можно оценить специальной утилитой vzmemcheck
По разнице значений в колонках commit и util вы можете оценить бонус какой вам дает система виртуализации.
Каждый параметр имеет 2 переменные, названные barrier'ом и limit'ом.
Limit - предел, который не может быть превышен ни при каких обстоятельствах. Попытку процесса получить ресурс сверх limit ядро будет отбрасывать. Попытку процесса получить ресурс сверх barrier ядро будет разрешать, но на короткое время.
Значения параметров barrier не должны превышать значенияlimit.
Параметры управляют распределением ресурсов между Виртуальными окружениями в терминах:
limits - пределы, то есть верхние границы того, что это Виртуальное окружение может потреблять, и
guarantees - гарантии, то есть механизмы, гарантирующие, что это Виртуальное окружение может получить назначенные ресурсы независимо от деятельности и количества ресурсов, требуемых другими Виртуальными окружениями.
Параметры, содержащие «guar» в названии, то есть vmguarpages и oomguarpages - это гарантии выделения памяти для Виртуального контейнера. Они гарантируют количество ресурсов и определенный уровень обслуживания до значения, определенного barrier, и не гарантируют выше значения barrier.
Однако, эти параметры не налагают ограничения использования.
Значение limit для параметров vmguarpages и oomguarpages должно устанавливаться в максимальное значение (то есть. MAX_ULONG)9).
Для некоторых ресурсов ограничивающих параметры, напирмер kmemsize, используются как barrier так и limit. Если использование ресурса превышает barrier, но не превышает limit, операции все еще могут резервировать новые ресурсы, а другие уже не могут. Промежуток между barrier и limit дает возможность процессам более гибко обращаться с ресурсами.
Для других параметров, типа numproc, ограничивающие значение barrier'а и limit'а должны иметь одинаковые значения.
Каждый параметр имеет «естественные единицы измерения» - единицы измерения значений, показанных через интерфейс /proc/user_beancounters и принятый vzctl.
Лимит на количество выделяемой (резервируемой) памяти без гарантии выделения.
Этот объем памяти выделяется только в случае наличия свободных ресурсов на HN. В случае OOM на HN процессы в этой зоне убиваются..
Параметр задает объем памяти который видит пользователь контейнера по команде free: (колонка total)
[root@www /]# free total used free shared buffers cached Mem: 2097152 470516 1626636 0 0 0 -/+ buffers/cache: 470516 1626636 Swap: 0 0 0
Единица измерения - 1 страница памяти (4 Кб )
Выбор значения:
barrier(privvmpages) >= barrier(vmguardpages)
Сумма всех privvmpages должно соответствовать физическим ресурсам и реальной загрузке физических ресурсов. Важно не позволить любому VPS получить существенную часть всей памяти, чтобы избежать серьезной деградации производительности для других VPS. Политически более верным, при желании обеспечить некоторые VPS бО'льшим количеством памяти, будет использование параметров гарантирующих выделение.
Чтобы уменьшить количество неудачных попыток выделения памяти необходим зазор между barrier и limit. Этот зазор будет доступен только процессам с высоким приоритетом, например, для расширения стека (stack expansion)
А попытки выделения памяти для процессов с нормальным приоритетом будут завершены с ошибкой в том случае, когда достигнуто значение barrier параметра privvmpages
Примечание:
Поскольку VPS может не использовать объем памяти определенный privvmpages, с умма privvmpages по всем VPS МОЖЕТ превышать RAM+swap и это, благодаря overcommitting памяти, может не вызывать проблем. Все зависит от реальной нагрузки. Не брезгуйте мониторингом.
Гарантия пространства для динамического выделения памяти.
Параметр определяет как много памяти доступно VPS Например, malloc() и другие механизмы выделения памяти в Linux будут видеть наличие свободной памяти если не исчерпана величина vmguardpages. Но в случае OOM на HN, процессы занимающие пространство в диапазоне между oomguardpages vmguardpages могут быть убиты ядром.
Единица измерения - 1 страница памяти (4 Кб )
Выбор значения: Количество гарантированной памяти указывается в значении barrier данного параметра. Значение limit должно быть установлено в максимальное значение MAX_ULONG.
Примечание:
Текущее количество выделенного размера памяти считается в параметре privvmpages, а параметр vmguarpages не имеет собственного учета (параметры held и maxheld всегда равны 0 ).
Гарантируемое количество памяти для запущенных процессов на случай перераспределения памяти (нехватки памяти OOM (out-of-memory)).
Процессы не будут убиты если cумма oomguardpages по всем VPS не превышает количество RAM+swap сервере.
Единица измерения - 1 страница памяти (4 Кб)
Выбор значения: Значение параметра barrier для oomguardpages должно обеспечивать гарантию от нехватки памяти.
Значение limit должно быть установлено в значение MAX_ULONG.
Если нужна уверенность, что процессы никогда НЕ БУДУТ убиты, нужно установить privvmpages в значение не превышающее значение oomguardpages или даже меньше для большей гарантии. Примечания:
Если текущее использование RAM+swap + ядерная память (kmemsize) +буферная память (tcpsndbuf + tcprcvbuf+othersockbuf+dgramrcvbuf ) ниже значения barrier параметра oomguardpages, то процессы на данной VPS НЕ БУДУТ экстренно завершены в случае наступления ситуации out-of-memory
Если в момент OOM на нескольких VPS наблюдается превышение oomguardpages, то будут убиваться процессы на VPS где самое большое превышение oomguardpages.
Так barrier для oomguardpages имеет другой смысл, нежели для других параметров.
И значение failcnt10) увеличивается для oomguardpages не в случае достижения barrier как обычно, а для каждого убитого процесса по причине out-of-memory.
Если суммарный размер выданных гарантий по параметру oomguardpages превышает объем физической памяти, то в данной ситуации приложения в VPS-ах с гарантированным уровнем сервиса и системные демоны (как sshd, например) могут быть принудительно завершены в случае наступления ситуации out-of-memory
Запросы процессов на выделение памяти удовлетворяются или отклоняются на основании следующих правил:
Из-за особенностей 32-битных процессоров архитектуры x86, RAM компьютера не может использоваться однородно.
Самая важная область памяти - так называемая «low memory», часть памяти, находящаяся в более нижних адресах и непосредственно доступная ядру. Для текущих ядер Linux, размер нижней области памяти - 832 МБ (или, если компьютер имеет меньше RAM, чем 832 МБ, то размер RAM).
Эта область памяти используется для нужд ядра (kmemsize) и буферизации сетевых и локальных соединений (tcprcvbuf, tcpsndbuf, dgramrcvbuf, othersockbuf). Если вы позволите себе распределить между контейнерами больше памяти чем имеет в наличии, будут проблемы.
Количество этой памяти вы можете выяснить командой
grep Low /proc/meminfo
Если вы используете 32-битный CPU, непременно изучите этот документ:
Размер несвоппируемой памяти для нужд ядра.
Эта память включает в себя все внутренние структуры данных, ассоциированные с процессами, за исключением сетевых буферов (для которых есть отдельные параметры).
Единица измерения - 1 байт
Выбор значения:
Размер kmemsize должен быть достаточен для ожидаемого количества процессов
barrier(kmemsize) >= 40 Кб * avnumproc
avnumproc - ожидаемое среднее количество процессов.
Если правило не выполняется, то приложения могут прерываться во время работы вместо того, чтобы прерываться в момент создания новых процессов.
Зазор между barrier и limit необходимо сделать приблизительно в 10%
Сумма kmemsize для всех VPS должна быть ограничена значением менее 832мб для x86 и физическим объемом RAM минус сумма лимитов для буферной памяти для x86_64.
Лимит в нижней памяти (low memory)для буферизации данных TCP-соединений. othersockbuf - для UDP или других протоколов. dgramrcvbuf определяет размер буфера для данных входящих соединений UDP или других протоколов.
Выбор значения:
limit(tcpsndbuf) - barrier(tcpsndbuf)>= 2,5 Кб %%*%% numtcpsock limit(tcprcvbuf) - barrier(tcprcvbuf)>= 2,5 Кб %%*%% numtcpsock limit(othersockbuf) - barrier(othersockbuf)>= 2,5 Кб %%*%% numothersock barrier(tcprcvbuf)>= 64 Кб barrier(tcpsndbuf)>= 64 Кб barrier(dgramrcvbuf)>= 32 Кб barrier(othersockbuf)>= 32 Кб
Если HN имеет достаточно памяти, для повышения производительности желательно:
barrier(dgramrcvbuf)>= 129 Кб barrier(othersockbuf)>= 129 Кб
В отличие от других параметров для буферной памяти сокетов для dgramrcvbuf значения barrier и limit должны быть одинаковы
Единица измерения - 1 байт
Примечание:
Если это условия выбора значения не выполняются, то некоторые сетевые соединения могут обрываться из-за невозможности отправки данных
Установка больших значений может привести к увеличению производительности, но это не обязательно. Срабатывание limit при выделении памяти для буфера сокета не имеет ярко выраженного негативного эффекта и немного снижает скорость соединений.
Параметр dgramrcvbuf обычно не нуждается в высоких значениях лимитов. Только если VPS должна принимать или отправлять очень большие датаграммы, имеет смысл увеличивать barrier как для othersockbuf, так и для dgramrcvbuf
При срабатывании лимита dgramrcvbuf некоторые датаграммы будут отброшены, что может быть или не быть важным для функционирования приложений. Так как протокол UDP неявляется протоколом с гарантированной доставкой пакетов - то данный лимит не является таким уж и критичным.
Общее количество страниц RAM, используемых процессами в этой Виртуальном окружении. Используется как счетчик реального потребления паямяти.
Единица измерения - 1 страница памяти (4 Кб)
Выбор значения:
barrier = 0
limit должно быть установлено в значение MAX_ULONG.
Примечание:
Для памяти, используемой несколькими различными VPS (отображение, планирование (mapping) разделяемых библиотек, например), только часть страницы приписывают к каждому VPS. Сумма physpages всех VPS соответствует общему количеству памяти используемой на HN всеми VPS.
Полный размер совместно используемой памяти (IPC и объекты tmpfs). Эта память учитывается в составе privvmpages.
Единица измерения - 1 страница памяти (4 Кб)
Выбор значения:
shmpages должен быть меньше privvmpages
Значение barrier должно быть установлено равным значению limit
Параметр не влияет на безопасность, стабильность или изоляцию между VPS. Его конфигурация затрагивает функциональные возможности и реакцию приложений на нехватку ресурсов только в данном Виртуальном окружении.
Общий размер заблокированных в памяти структур dentry и inode. Значения dcachesize включены в kmemsize
Единица измерения - 1 структура
Параметр dcachesize управляет памятью для операций с файлами - кеш directory entry (dentry) или inode-кеш.
Параметр dcachesize существует как отдельный параметр для того, чтобы предотвратить сбои связанные с выделением памяти для операций с файлами.
Этот параметр должен иметь зазор между значениями barrier и limit (около 10%)
Страницы, которые не могут быть перемещены в swap (страницы, заблокированные mlock (2)). Эта память учитывается в параметре kmemsize.
Единица измерения - 1 страница памяти (4 Кб)
Выбор значения:
Значение barrier может быть равно limit или несколько меньше.
Примечание:
Следует учитывать, что Web,FTP,почтовые-серверы НЕ используют механизм блокировки страниц с помощью mlock. Конфигурирование данного параметра не влияет на безопасность или стабильность системы в целом или на изоляцию между VPS.
Параметры контейнера задаются параметрами конфигурационного файла /etc/vz/conf/<VEID>.conf
(man ctid.conf(5))
Для изменения параметров используйте утилиту vzctl
(man vzctl (8))
Пример команды определения лимита для контейнера с VEID=1010:
vzctl set 1010 –oomguarpages 128M:2147483647 —save
Примечание:
vzctl не полностью проверяет корректность вводимых аргументов (например, позволяет задавать разный лимит и барьер для таких параметров, как numproc и numfile, для которых лимит и барьер обязаны совпадать), поэтому рекомендуется проводить дополнительную проверку конфигурационных файлов с помощью утилиты vzcfgvalidate:
примеры:
проверить один контейнер:
vzcfgvalidate /etc/vz/conf/1097.conf
Проверить все контейнеры:
for n in /etc/vz/conf/*.conf;do echo Check $n; vzcfgvalidate $n; done
Обратите внимание, что проверяется именно файл (с указанием полного пути), а не текущие параметры, которые могут быть другими, если «vzctl set» запускался без ключа «–save». С другой стороны, vzcfgvalidate удобен тем, что может проверять параметры, когда контейнер не запущен.
Для исправления конфигурационного файла vzcfgvalidate следует запустить с ключом «-r» («repair mode», автоматическое исправление) или «-i» («interactive repair», ручное исправление).
Память, которое приложения могут использовать — это сумма RAM+swap. Если размер используемой памяти превышает RAM, ядро Linux перемещает некоторые данные в SWAP, и загружает их обратно, когда приложение нуждается в них. Часто используемые данные имеют тенденцию оставаться в RAM, редко используемые данные в основном находятся в SWAP`е.
Внутри VPS размер доступного пространства swap не отображается.
Там потребление виртуальной памяти (ram+swap) можно узнать по сумме oomguarpages, kmemsize, tcpsndbuf, tcprcvbuf, othersockbuf, dgramrcvbuf.
Хранение данных в SWAP` тормозит работу системы до некоторой степени. Однако, если нет постоянного обмена со swap, торможение будет незаметным.
SWAP необходим для сдержания пиков нагрузки системы. И он срабатывает как демпфер в случаях DOS атак. Рост нагрузки при DOS атаке приводит к увеличению количества активных процессов которые постепенно, вытесняясь в swap, реагируют на запросы все медленнее и интенсивность атаки падает.
Настоятельно рекомендуется иметь размера SWAP больше размера RAM. Для систем со значительным количеством RAM могут быть выбраны и меньшие значения.
Рекомендации размера swap от Red Hat Knowledgebase
Для эмуляции виртуальной памяти openvz использует технологию vSWAP.
vSWAP – в отличие от swap размещается не на разделе диска, а в оперативной памяти сервера виртуализации. Эта область памяти выделяется гостям с замедлением в несколько раз, что обеспечивает возможность использования ее в качестве замедляющего буфера при перегрузке и нехватке памяти. То есть действует это подобно физическому разделу swap размещенному на диске.
Если в контейнере без vswap процесс запросивший память за пределами лимита жестоко убивается ядром и данные связанные с процессом теряются, то здесь он помещается в замедленную область памяти vswap и выполнение соответствующего запроса замедляется или ожидает освобождения памяти.Клиент выполнивший запрос терпеливо ждет своей очереди, в случае же работы без vswap клиент получает обрыв связи.
Актуальность: 2011/02/03 16:00