28 января 2009

I am RHCA


Подготовка к более чем двадцати четырем часам лабораторных, разбитых на пять экзаменов, заняла чуть больше года. Сегодня пришли результаты последнего экзамена по настройке производительности, который иначе как "black magic" назвать нельзя :) Для интересующихся: интервью с первым Red Hat Certified Architect Флорианом Брендом, обзор сертификации и FAQ.

21 января 2009

20 января 2009

Управление динамическим распределением задач на уровне ОС

"Большие системы" для конечного пользователя означают значительные инвестиции в вычислительные ресурсы, и сразу же возникает вопрос эффективного управления этими мощностями. На этот раз оставим виртуализацию в стороне и рассмотрим динамическое управление ресурсами в пределах одной отдельно взятой операционной системы. Где это необходимо? Примеры:
  • Веб-сервер, поддерживающий несколько экземпляров одного и того же приложения;
  • Сервер, поддерживающий разные приложения (например СУБД и Apache) или HPC-приложения, каждое из которых должно работать с заданными параметрами быстродействия;
  • Сервер, поддерживающий приложения, нагружающие разные ресурсы (подсистемы) этого сервера, но требующие заданный уровень сервиса.
В замечаниях к выпуску Enterprise Linux 5 можно встретить такую фразу:

"...Cpuset обеспечивает механизм назначения узлов процессоров и памяти наборам задач, тем самым ограничивая распределение памяти и процессоров в пределах заданного cpuset. Механизм Cpuset является основным стандартом управления динамическим распределением задач в больших системах..."

Про Cpuset "в двух словах" я и хочу рассказать в этом посте. Данный механизм позволяет "на лету" распределять вычислительные ресурсы между различными задачами, ограничивая использование ОЗУ и ЦП "вычислительным доменом", которому присвоена задача. Впервые поддержка Cpuset появилась в ядре 2.6.12. Патч, включавший данный функционал, был предложен в 2005 году, привносил достаточно небольшой объем кода, дополнительно не нагружая планировщик процессов. Cpuset расширяет возможности уже существовавших в ядре Linux механизмов, позволявших задать с использованием какого ЦП (sched_setaffinity) и ОЗУ (mbind, set_mempolicy) может выполняться задача.

Поскольку управление Cpuset осуществляется при помощи виртуальной файловой системы, самый простой способ проверить, скомпилировано ли ядро вашей системы с поддержкой cpusets - это команда:

[root@server1 ~]# cat /proc/filesystems | grep cpuset
nodev cpuset
Начать работать с cpuset максимально просто:

1) Создадим и смонтируем специальную файловую систему cpuset

[root@server1 ~]# echo "cpuset /cs cpuset defaults 0 0" >> /etc/fstab
[root@server1 ~]# mkdir /cs
[root@server1 ~]# mount -a
В итоге все процессы "привязаны" к корневому и единственному существующему на данный момент "вычислительным домену". У каждого процесса есть файл /proc/PID/cpuset, просмотрев который можно увидеть, к какому cpuset он привязан. В настоящий момент для любого процесса мы увидим картину:

[root@server1 ~]# cat /proc/1/cpuset
/
Кроме того, если посмотреть на специальную файловую систему cpuset, то внутри нашей директории /cs мы обнаружим ряд файлов, среди которых:

- cpus: список ЦП в этом cpuset

[root@server1 ~]# cat /cs/cpus
0-7
- mems: список узлов ОЗУ в этом cpuset

[root@server1 ~]# cat /cs/mems
0
- tasks: список процессов, привязанных к этому cpuset

[root@server1 ~]# cat /cs/tasks | wc -l
151
2) Теперь создадим "дочерний" cpuset. Это делается при помощи создания поддиректории внутри файловой системы cpuset:

[root@server1 ~]# mkdir /cs/cs1
Внутри /cs/cs1 мы увидим те же файлы, что и внутри корня файловой системы cpuset. Далее необходимо привязать ресурсы нашему новому вычислительному домену:

[root@server1 ~]# echo 0,3,7 > /cs/cs1/cpus
[root@server1 ~]# echo 0 > /cs/cs1/mems
3) Присваиваем какую-либо задачу домену cs1:

[root@server1 ~]# echo $(pidof syslogd) > /cs/cs1/tasks
В итоге мы видим:

[root@server1 ~]# cat /proc/$(pidof syslogd)/cpuset
/cs1
и изменившуюся маску ЦП для процесса syslogd:

[root@server1 ~]# cat /proc/$(pidof syslogd)/status | grep Cpus
Cpus_allowed: 00000089
Должен заметить, что наиболее полный из доступных источников по теме - это описание /usr/share/doc/kernel-doc-*/Documentation/cpusets.txt, к которому я и отсылаю вас за дополнительной информацией.

19 января 2009

Новая версия Spacewalk 0.4


На днях вышла новая версия open source решения для обслуживания всего жизненного цикла Linux-инфраструктуры - Spacewalk 0.4. Spacewalk представляет собой открытый по лицензии GPL2 продукт Red Hat Network Satellite Server, имеющий давнюю, с 2001 года, историю разработки.

Основные усовершенствования:
  • интеграция с Cobbler/Koan (о Cobbler я писал в блоге пару недель назад). Требуется версия cobbler >=1.4, которая пока что присутствует в репозитории epel-testing. Предполагается, что Cobbler заменит соответствующую подсистему в Spacewalk, а в дальнейшем, видимо, и в Red Hat Network Satellite Server;
  • начата работа над реализацией доверительных отношений между Организациями и реализацией "общих каналов" программного обеспечения (Multi-Org II);
  • улучшения в поддержке разных архитектур в пределах одного сервера для 64-разрядных систем;
  • Набор новых API (channel.access.*, channel.org.*, package.*);
  • Модули SELinux для компонентов Spacewalk (Ура! Раньше первый пункт при установке звучал как "выключите SELinux" :) ;
  • Поиск по online-документации в Spacewalk. (Мелочь, а приятно);
  • Справочная документация обновлена с учетом последних изменений;
  • Продолжается миграция частей кода с perl на java;
  • Улучшенна интеграция с базовой системой. satellite-httpd удален, поскольку теперь используется стандартный httpd;
  • С момента выхода версии 0.3 исправлено более сотни различных ошибок.
В качестве платформы все еще необходимо использовать RHEL/CentOS/etc, но уже начата работа по созданию репозитория для Fedora 10.

13 января 2009

И снова о памяти в Linux - /proc/meminfo

В продолжение поста о команде free, посмотрим на более полный источник об использовании памяти - специальную виртуальную файловую систему /proc, являющуюся общей точкой доступа к структурам данных ядра, а именно - на информацию /proc/meminfo. Вывод команды cat:
[andrey@server1 ~]$  cat /proc/meminfo
MemTotal: 1945312 kB
MemFree: 48980 kB
Buffers: 2248 kB
Cached: 171092 kB
SwapCached: 256 kB
Active: 622380 kB
Inactive: 32504 kB
HighTotal: 1048256 kB
HighFree: 3176 kB
LowTotal: 897056 kB
LowFree: 45804 kB
SwapTotal: 2048276 kB
SwapFree: 2047868 kB
Dirty: 2696 kB
Writeback: 400 kB
AnonPages: 481592 kB
Mapped: 137244 kB
Slab: 34244 kB
PageTables: 7276 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
CommitLimit: 2433156 kB
Committed_AS: 1277084 kB
VmallocTotal: 114680 kB
VmallocUsed: 14232 kB
VmallocChunk: 99948 kB
Пройдемся сверху вниз с некоторыми комментариями по части из этих значений.

MemTotal - Доступный объем оперативной памяти. Часть физически доступной памяти резервируется во время запуска системы ядром и не входит в указанный здесь объем:
[root@server1 ~]# grep Memory: /var/log/dmesg
Memory: 1941740k/1965760k available
(2097k kernel code, 22716k reserved,
877k data, 228k init, 1048256k highmem)
MemFree - Какой объем памяти не используется и доступен для немедленного выделения процессам. Ядро Linux старается использовать доступную память максимально эффективно, и, по умолчанию, достаточно большой объем ОЗУ, потенциально доступный для приложений, может быть занят под кэш и буферы. Объем свободной памяти в несколько десятков мегабайт на машинах с гигабайтами оперативной памяти - вполне типичная картина для Linux. В 32-разрядной операционной системе MemFree=LowFree+HighFree.

Buffers - область ОЗУ, занятая хранением данных, ожидающих записи на диск. Буфер позволяет приложениям продолжать выполнение своей задачи не дожидаясь момента когда данные будут физически записаны на диск. Обычно размер около 20Мб.

Cached - Объем занятый в ОЗУ под кэш чтения страниц с диска (файлы, директории, файлы блочных устройств, данные, относящиеся к механизму IPC, данные процессов уровня пользователя, сброшенных в область подкачки). Не включает в себя SwapCached.

SwapCached - Объем памяти, который однажды был помещен в область подкачки, но потом перенесен обратно в ОЗУ. Однако данные все еще присутствуют в swap, и при необходимости этот объем памяти может быть вновь освобожден без необходимости тратить ресурсы на "дорогие" операции ввода/вывода.

Active - Объем памяти, занятый в ОЗУ наиболее часто используемыми страницами памяти. Иными словами, эти страницы памяти активно используются процессами и будут освобождаться только в случае крайней необходимости.

Inactive - Объем памяти, занятый в ОЗУ не используемыми в настоящий момент страницами. Эти страницы считаются наиболее подходящими для выгрузки в swap и освобождения в случае необходимости.

High{Total,Free}, Low{Total,Free} - MemTotal=HighTotal+LowTotal. Вывод команды cat /proc/meminfo получен на 32-разрядной операционной системе. Модель использования памяти в 32-разрядном и 64-разрядном ядрах отличается. Ниже я привел рисунок, показывающий эти отличия:


В 32-р архитектуре ядро Linux может напрямую адресовать только первый гигабайт физической памяти. На рисунке ZONE_NORMAL - это и есть область памяти объемом LowTotal:897056 kB. Число меньше гигабайта за счет зарезервированных ядром областей памяти. Из оставшегося объема нужно вычесть 1Мб, используемый BIOS и устройствами ввода/вывода, а также 16Mб ZONE_DMA для совместимости с ограничениями устройств на шине ISA. Память "выше" первого гигабайта - ZONE_HIGHMEM (HighTotal:1048256 kB). Доступ к ней осуществляется через отображение на первый гигабайт ОЗУ. Отображение "прозрачно" для приложений, но вызывает небольшую потерю производительности. ZONE_NORMAL используется для тех же нужд что и зона ZONE_HIGHMEM, плюс для собственных структур ядра. В 64-разрядных архитектурах зона ZONE_HIGHMEM всегда пуста.

Swap{Total, Free} - SwapTotal - это общий объем области подкачки (как в разделе подкачки, так и в swap-файлах, если они используются). Как и в случае с ОЗУ, ядро Linux старается использовать область подкачки максимально эффективно. Иногда факт использования части области подкачки еще не означает того, что память является "узким местом" производительности системы. Один из способов влиять на агрессивность использования swap - это парметр vm.swappiness. Чем больше процент swappiness, тем активнее будет выгрузка в swap. По умолчанию в RHEL5 это число 60%. Предпочтительно использовать более агрессивную политику выгрузки в область подкачки, когда вы, например, на рабочей станции большую часть времени работаете с одним большим пакетом ПО и редко переключаетесь на другие задачи. Пример из области серверов - машина с ограниченным ОЗУ, выполняющая какие-то пакетные задания (процессы, находящиеся долгое время в состоянии S). На серверах с большим объемом ОЗУ увеличения swappiness может свести все выгоды использования swap "на нет" из-за активного использования дисковой подсистемы и процессорного времени для поиска и сброса на диск неактивных страниц памяти.

Dirty - Измененные ("грязные") страницы, находящиеся в ОЗУ, но еще не сброшенные на диск. За процедуру записи на диск отвечает группа потоков ядра pdflush. В системе должно работать минимум два и максимум восемь потоков pdflush. Посмотреть текущее число потоков можно:
[root@server1 ~]# cat /proc/sys/vm/nr_pdflush_threads
2
Дополнительные потоки создаются в зависимости от текущей нагрузки ввода/вывода. Команда sync и "магическая" комбинация Alt-SysRq-S сбрасывает все "грязные" страницы и буферы. Число, хранящееся в vm.dirty_background_ratio (по умолчанию 10%), задает какой процент от ОЗУ потоки pdflush должны сбрасывать на диск. Чем больше процент, тем реже происходит обращение к диску. Второй из важных параметров, влияющих на pdflush, это процент от объема ОЗУ, при котором стартует сам процесс сброса на диск - vm.dirty_ratio.

Writeback - Страницы памяти, которые в настоящий момент сбрасываются на диск.

AnonPages - Анонимные страницы - это, как правило, данные, используемые программами и не ассоциированные с каким-либо файлом. Наряду со страницами, объем которых указан в Inactive, это первые кандидаты на попадание в область подкачки. Анонимные страницы нередко используются несколькими процессами. Самый распространенный пример - fork() при создании нового процесса. Число анонимных страниц для конкретного процесса можно вычислить как разницу между размером резидентной части (resident) и разделяемыми страницами (share) в выводе /proc/PID/statm (информация о состоянии памяти в страницах). Например:
[root@server1 vm]# cat /proc/$(pidof sshd)/statm
1763 258 161 93 0 130 0
Получается что число анонимных страниц для sshd = 258 - 161 = 97 или 388Кб. Подробнее - man proc.

Mapped - Общий объем памяти, привнесенный в виртуальное адресное пространство процессов при помощи mmap (например, библиотеки).

Slab - объем памяти, занятый под различные структуры ядра небольшого объема, для которых не оптимально выделять по целой странице памяти. По умолчанию в 32-р системах размер страницы - 4Кб, и этот объем является квантом памяти при ее выделении. Подробнее информацию о slab-кэше можно посмотреть при помощи утилиты vmstat с ключем -m, утилиты slabtop или через /proc/slabinfo.

PageTables - Объем памяти, зарезервированный под Таблицу Страниц.

NFS_Unstable - Данный параметр относится к клиенту NFS v3+, реализованному в ядре Linux, и показывает, какой объем данных, отправленных клиентом серверу, еще не был записан на диск. Клиент должен кэшировать эти данные до поступления подтверждения от сервера.

CommitLimit - Объем памяти, который может быть выделен системой. Вычисляется на основе vm.overcommit_ratio (по умолчанию - 50%) и размера области подкачки. Формула имеет следующий вид CommitLimit = ( vm.overcommit_ratio * объем_ОЗУ) + область_подкачки. Этот лимит соблюдается только при "строгой" политике выделения памяти (vm.overcommit_memory=2). По умолчанию используется "эвристическая" политика (vm.overcommit_memory=0).

Committed_AS - Сколько памяти выделено всем процессам, даже если они эту память не используют в полном объеме. Иными словами, данный параметр показывает, сколько при текущей загрузке системы требуется ОЗУ, если процессы реально захотят использовать выделенную память для того, чтобы избежать core dump по причине отсутствия памяти (OOM). Дело в том что, когда процесс требует - ядро подтверждает выделение требуемого объема памяти (только если не используется "строгая" политика. В этом случае проверяем, не исчерпан ли CommitLimit) без выделения ОЗУ. Реально память выделяется постранично только лишь когда процесс пытается что-то записать в выделенную до этого память. Этот механизм называется overcommitment. Более подробно см. /usr/share/doc/kernel-doc-*/Documentation/vm/overcommit-accounting.

VmallocTotal - общее число виртуального пространства, доступного для vmalloc.

VmallocUsed - объем использованного пространства vmalloc.

VmallocChunk - наибольший свободный непрерывный блок внутри пространства vmalloc.

Литература:
  • Ядро Linux. 3-е издание, Даниель Бовет, Марко Чезати, БХВ-Петербург, 2007
  • Optimizing Linux® Performance: A Hands-On Guide to Linux® Performance Tools, Phillip G. Ezolt, Pearson PTR, 2005
  • Linux System Programming, Robert Love, O’Reilly, 2007
  • /usr/share/doc/kernel-doc-*/Documentation/

08 января 2009

Команда /bin/free "в картинках"

Команда free выводит информацию об общем числе свободной и использованной памяти, включая swap. Для того, чтобы было понятнее, как трактовать вывод команды, на приведенном ниже рисунке я показал результат исполнения команды free в Dom0 на моем рабочем ноутбуке:

Ключ -m говорит о том, что размер памяти нужно выводить в мегабайтах. Еще один ключ -l позволяет посмотреть сколько памяти использовано в каждой из зон:
[andrey@server1 ~]$ free -l
total used free shared buffers cached
Mem: 1819648 1807228 12420 0 56504 1257716
Low: 738260 728896 9364
High: 1081388 1078332 3056
-/+ buffers/cache: 493008 1326640
Swap: 2048276 8 2048268
Если бы я отдал эту же команду на установленной 64-разрядной ОС, то в строке, относящейся к ZONE_HIGMEM, я бы увидел 0. Обзор модели памяти Linux можно почитать в статье на IBM developerWorks Россия. Если же вы готовы к "глубокому погружению", то ничего лучше "Ядро Linux. 3-е издание" Бовет, Чезати на русском языке я не видел.

Cobbler: новое видео в Red Hat Magazine


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

Такой способ есть, и он заключается в использовании ПО Cobbler, которое упрощает сетевую установку операционных систем, что особенно необходимо при масштабных инсталляциях, например, в дата-центрах. Вчера в Red Hat Magazine появилось видео-интервью с разработчиком Cobbler - Michael DeHaan.

Добавлю, что в релизе 0.4 open source решения для управления Linux-инфраструктурой Spacewalk планируется сделать первые шаги для интеграции c Cobbler/Koan. Предполагается, что Cobbler заменит соответствующую подсистему в Spacewalk, а в дальнейшем, видимо, и в Red Hat Network Satellite Server.

Подробнее с Cobbler можно познакомиться на сайте проекта. А все тот же Michael DeHaan описал процесс настройки Cobbler в статье "How to set up a network boot server in 10 minutes".

06 января 2009

Приоритет ввода/вывода процесса и ionice

В планировщике ввода/вывода CFQ (подробнее о планировщиках ввода/вывода можно почитать тут), который используется по умолчанию, начиная с ядра версии 2.6.18 (кстати, в последнем издании Understanding the Linux Kernel описано ядро 2.6.11, и там в соответствующей главе пишут про упреждающий конвейер - Anticipatory) есть интересная возможность вручную присваивать приоритет ввода/вывода конкретному процессу. На практике эти манипуляции осуществляются при помощи утилиты ionice.

Можно задать три класса ввода/вывода:

3. Idle - получает доступ к диску только тогда, когда другие процессы не требуют ввода/вывода. При "нормальной" работе системы такой процесс не должен испытывать проблем с производительностью.

2. Best effort - класс "по умолчанию". Для вычисления приоритета ввода/вывода используется соответствующее значение nice планировщика ЦП для этого процесса. Таким образом, при помощи команд nice и renice вы, помимо косвенного изменения приоритета процесса в планировщике ЦП, косвенно же влияете на планировщик ввода/вывода.

1. Real time - процессы с этим классом имеют приоритетный доступ к жесткому диску вне зависимости от того, что происходит в системе. Вместе с классом 1 как и с классом 2 можно передать и параметр-приоритет (0-7).

Первое, что приходит в голову глядя на классы - запуск некоторых задач в cron с пониженным приоритетом ionice -c3. Приоритет наследуется от родительского процесса, таким образом, все, что вы напишете после

#!/bin/bash
ionice -c3 -p$$

будет работать с приоритетом ввода/вывода Idle. Видимо, наилучшего прироста производительности можно добиться экспериментируя с ionice, когда вы вынуждены запускать одновременно несколько процессов с разными требованиями к дисковой подсистеме.

Подробнее:
  • man-страница ionice(1)
  • /usr/share/doc/kernel-doc-*/Documentation/block/ioprio.txt
Хотя не существует однозначного ответа на вопрос "Какой планировщик лучше?", и каждый раз необходимо тестировать конкретное приложение/систему, в общем случае ionice и конвейер CFQ не стоит применять для работы с /dev/xvdX в виртуальных машинах, где рекомендуется использовать планировщик NOOP, предоставив заботиться о планировании операций ввода/вывода Dom0. Для этого нужно добавить elevator=noop в строку параметров ядра grub:

title Red Hat Enterprise Linux Server (2.6.18-53.el5xen)
root (hd0,0)
kernel /vmlinuz-2.6.18-53.el5xen ro root=/dev/vol0/root rhgb quiet elevator=noop
initrd /initrd-2.6.18-53.el5xen.img

В итоге:

[root@vm02 ~]# dmesg | grep schedule
io scheduler noop registered (default)
io scheduler anticipatory registered
io scheduler deadline registered
io scheduler cfq registered

Впрочем, если вы помимо виртуальных дисков работаете с внешним хранилищем, где оптимальнее использовать планировщик "по умолчанию", то можно обойтись и /etc/rc.d/rc.local, меняя планировщик на уровне устройства:

echo noop > /sys/block/xvdX/queue/scheduler