Разберемся с регуляторами подсистемы хранения данных и посмотрим, что они позволяют делать в смысле блочного ввода-вывода.
Особенно интересно здесь то, что мы вступаем на территорию, где изменения настроек, которые вносятся уже после запуска системы, гораздо менее важны, чем решения, которые принимаются еще до ее развертывания.
Взгляните на рисунок ниже.
На нем представлены четыре основных ресурса, которые нужны современному компьютеру для полноценной работы. Настройка производительности – это искусство оптимально распределять эти ресурсы между процессами приложений. Причем, все эти ресурсы не безграничны и не равноценны в смысле влияния на производительность.
Производительность подсистемы хранения данных сводится к производительности используемых в ней технологий хранения: жесткие диски, SSD, SAN, NAS – они могут сильно варьироваться по скорости доступа и пропускной способности. И мощный процессор и куча памяти не спасут ситуацию, если устройства хранения не отвечают требованиям решаемых задач.
Если вы, как специалист по Linux, можете влиять на принятие решений, связанных с оборудованием, постарайтесь сделать так, чтобы ваша организация имела адекватную (или превосходную) платформу хранения данных. Это избавит от многих проблем в будущем.
А теперь давайте посмотрим, что можно сделать с помощью регуляторов ввода-вывода (I/O).
Официально регулятор ввода-вывода называется blkio, но в хорошем настроении он отзывается на Blocky. Как и у CPU-регулятора, у Blocky есть два режима работы:
На скрине ниже показаны параметры, которые можно регулировать с помощью команды systemctl. Здесь мы воспользовались магией автоподсказок по клавише Tab, чтобы вывести на экран список параметров. Это называется bash-completion, и если вы все еще не пользуетесь этой функцией, самое время установить соответствующий PRM.
Относительные I/O-шары регулируются параметрами BlockIODeviceWeight и BlockIOWeight. Прежде чем играться с этими регуляторами, нужно уяснить вот что: они работают, только если для устройства хранения включен планировщик ввода-вывода CFQ.
Что такое планировщик ввода-вывода? Давайте начнем издалека и вспомним, что ядро Linux отвечает за то, чтобы все аппаратные компоненты компьютера общались друг с другом правильно. А поскольку все эти компоненты одновременно хотят разного, тут не обойтись без упорядочивания. Ну, как мы, люди, к примеру, упорядочиваем свою жизнь, структурируя ее на работу, отдых, сон и прочее.
Если говорить об устройствах хранения, то за упорядочивание их работы в рамках ядра отвечает планировщике ввода-вывода. Это просто программный код, который задает способ управления потоком данных для блочных устройств, начиная от USB-флешек и жестких дисков, и заканчивая виртуальными дисками, которые на самом деле представляют собой файлы где-то на устройствах ISCI в сети SAN.
И поверх всех этих устройств, которые можно использовать в Linux, есть различные задачи, которые должен выполнять компьютер. Кроме того, в реальной жизни существует то, что мы в Red Hat называем «сценарии использования». Именно поэтому и существуют разные планировщики, ориентированные на разные сценарии. Называются эти планировщики noop, deadline и cfq. В двух словах каждый из них можно охарактеризовать следующим образом:
Дополнительные сведения о планировщиках можно найти в руководстве по настройке производительности Red Hat Enterprise Linux 7.
К чему были все эти рассуждения про планировщики? К тому, что на большинстве компьютеров cfq по умолчанию НЕ ИСПОЛЬЗУЕТСЯ, если у них нет накопителей SATA. Не зная этого, вы можете до посинения менять BlockIOWeight без какого бы то ни было эффекта. К сожалению, systemd не скажет вам: «Извините, вы зря пытаетесь поменять данный параметр. Это не сработает, поскольку на устройстве используется не тот планировщик».
Так откуда же можно узнать об этой «интересной» особенности? Как обычно, из документации по cgroups, о которой мы писали в прошлом посте. С ней всегда полезно ознакомиться, прежде чем пользоваться теми или иными регуляторами.
Снова переходим от общих слов к конкретике: позвольте представить вам господина Крякина.
Он занимается общепитом, и у него на сервере приложений есть две базы данных для отслеживания заказов. Г-н Крякин настаивает, что БД заказов на блюда из утки гораздо важнее базы для блюд из гуся, поскольку гуси – это самозванцы на троне водоплавающих.
Обе БД настроены в качестве сервисов, а их юнит-файлы выглядят следующим образом:
На самом деле вызываемые в них сценарии (duck.sh и goose.sh) не выполняют никакой реальной работы в БД, а лишь имитируют чтение и запись с помощью циклов команды dd. Оба сценария используют файловую систему /database, которая лежит на своем виртуальном диске.
Давайте запустим duck и goose и посмотрим, где они приземлятся в иерархии cgroup:
А теперь, поскольку мы знаем PID’ы процессов dd, обратимся к команде iotop, чтобы посмотреть, что происходит в подсистеме хранения:
М-да, 12-14 Мбайт/с… не быстро. Похоже, г-н Крякин не очень-то вкладывался в систему хранения данных. Хотя у нас уже были вопросы насчет его адекватности, так что удивляться особо нечему.
Теперь смотрим на две наших задачи: PID 3301 (goose) и PID 3300 (duck). Каждая использует ввод-вывод где-то на 6 Мбайт/с. На скрине выше немного другие цифры, но в реальности они постоянно скачут, и в среднем эти две задачи поровну делят пропускную способность устройства хранения.
Г-н Крякин хочет, чтобы duck имел по меньшей мере в 5 раз больше пропускной способности ввода-вывода, чем goose, чтобы заказы на утку всегда обрабатывались в первую очередь. Попробуем использовать для этого параметр BlockIOWeight с помощью следующих команд:
Смотрим iotop и видим, что не сработало:
Давайте-ка проверим планировщик ввода-вывода для устройства /dev/vdb:
Интересно… Мы пробуем сменить планировщик на cfq и ничего не выходит. Почему?
Дело в том, что наша система работает на KVM-виртуалке, и, оказывается, начиная с версии 7.1 в Red Hat Enterprise Linux больше нельзя поменять планировщик. И это не баг, а фича, связанная с улучшением механизмов работы с виртуализованными устройствами ввода-вывода.
Но не будем отчаиваться. У нас есть еще два параметра, которые можно поменять: BlockIOReadBandwidth и BlockIOWriteBandwidth работают на уровне блочного устройства и игнорируют планировщик ввода-вывода. Поскольку мы знаем пропускную способность устройства /dev/vdb (где-то 14 Мбайт/с на прием и на отдачу), то ограничив goose на уровне 2 Мбайт/с, мы, похоже, сможем реализовать пожелание г-на Крякина. Давайте попробуем:
Смотрим: PID 3426, он же goose, теперь использует ввод-вывод где-то на 2 Мбайт/с, а PID 3425, то бишь duck, – почти на все 14!
Ура, мы сделали то, что хотел заказчик, а значит, спасли не только энное количество гусей, но и свою репутацию Linux-гуру.
(Продолжение следует)
Особенно интересно здесь то, что мы вступаем на территорию, где изменения настроек, которые вносятся уже после запуска системы, гораздо менее важны, чем решения, которые принимаются еще до ее развертывания.
Взгляните на рисунок ниже.
На нем представлены четыре основных ресурса, которые нужны современному компьютеру для полноценной работы. Настройка производительности – это искусство оптимально распределять эти ресурсы между процессами приложений. Причем, все эти ресурсы не безграничны и не равноценны в смысле влияния на производительность.
Производительность подсистемы хранения данных сводится к производительности используемых в ней технологий хранения: жесткие диски, SSD, SAN, NAS – они могут сильно варьироваться по скорости доступа и пропускной способности. И мощный процессор и куча памяти не спасут ситуацию, если устройства хранения не отвечают требованиям решаемых задач.
Если вы, как специалист по Linux, можете влиять на принятие решений, связанных с оборудованием, постарайтесь сделать так, чтобы ваша организация имела адекватную (или превосходную) платформу хранения данных. Это избавит от многих проблем в будущем.
А теперь давайте посмотрим, что можно сделать с помощью регуляторов ввода-вывода (I/O).
Все дело в устройствах хранения
Официально регулятор ввода-вывода называется blkio, но в хорошем настроении он отзывается на Blocky. Как и у CPU-регулятора, у Blocky есть два режима работы:
- Регулировка с помощью относительных I/O-шар (shares), которые позволяют управлять производительностью на уровне всех или выбранных устройств блочного хранения путем установки значений в интервале от 10 до 1000. По умолчанию используется 1000, поэтому любые изменения лишь уменьшают I/O-шары выбранного пользователя или службы. Почему 1000, а не 1024, как в случае CPU? Хороший вопрос. Видимо, это тот случай, когда открытая природа Linux не идет ему на пользу.
- Регулировка абсолютной пропускной способности, позволяющая ограничить скорость чтения и/или записи для заданного пользователя или службы. По умолчанию этот режим отключен.
На скрине ниже показаны параметры, которые можно регулировать с помощью команды systemctl. Здесь мы воспользовались магией автоподсказок по клавише Tab, чтобы вывести на экран список параметров. Это называется bash-completion, и если вы все еще не пользуетесь этой функцией, самое время установить соответствующий PRM.
Относительные I/O-шары регулируются параметрами BlockIODeviceWeight и BlockIOWeight. Прежде чем играться с этими регуляторами, нужно уяснить вот что: они работают, только если для устройства хранения включен планировщик ввода-вывода CFQ.
Что такое планировщик ввода-вывода? Давайте начнем издалека и вспомним, что ядро Linux отвечает за то, чтобы все аппаратные компоненты компьютера общались друг с другом правильно. А поскольку все эти компоненты одновременно хотят разного, тут не обойтись без упорядочивания. Ну, как мы, люди, к примеру, упорядочиваем свою жизнь, структурируя ее на работу, отдых, сон и прочее.
Если говорить об устройствах хранения, то за упорядочивание их работы в рамках ядра отвечает планировщике ввода-вывода. Это просто программный код, который задает способ управления потоком данных для блочных устройств, начиная от USB-флешек и жестких дисков, и заканчивая виртуальными дисками, которые на самом деле представляют собой файлы где-то на устройствах ISCI в сети SAN.
И поверх всех этих устройств, которые можно использовать в Linux, есть различные задачи, которые должен выполнять компьютер. Кроме того, в реальной жизни существует то, что мы в Red Hat называем «сценарии использования». Именно поэтому и существуют разные планировщики, ориентированные на разные сценарии. Называются эти планировщики noop, deadline и cfq. В двух словах каждый из них можно охарактеризовать следующим образом:
- Noop – хорошо подходит для блочных устройств хранения, у которых нет вращающихся частей (флеш, ssd и прочее).
- Deadline – легковесный планировщик, ориентированный на минимизацию задержек. По умолчанию отдает приоритет чтению в ущерб записи, поскольку большинство приложений спотыкаются именно на чтении.
- Cfq – ориентирован на справедливое распределение пропускной способности ввода-вывода на общесистемном уровне. И как мы уже сказали выше, это единственный планировщик, который поддерживает относительные параметры ввода-вывода для cgroups.
Дополнительные сведения о планировщиках можно найти в руководстве по настройке производительности Red Hat Enterprise Linux 7.
К чему были все эти рассуждения про планировщики? К тому, что на большинстве компьютеров cfq по умолчанию НЕ ИСПОЛЬЗУЕТСЯ, если у них нет накопителей SATA. Не зная этого, вы можете до посинения менять BlockIOWeight без какого бы то ни было эффекта. К сожалению, systemd не скажет вам: «Извините, вы зря пытаетесь поменять данный параметр. Это не сработает, поскольку на устройстве используется не тот планировщик».
Так откуда же можно узнать об этой «интересной» особенности? Как обычно, из документации по cgroups, о которой мы писали в прошлом посте. С ней всегда полезно ознакомиться, прежде чем пользоваться теми или иными регуляторами.
Переходим к сценарию использования
Снова переходим от общих слов к конкретике: позвольте представить вам господина Крякина.
Он занимается общепитом, и у него на сервере приложений есть две базы данных для отслеживания заказов. Г-н Крякин настаивает, что БД заказов на блюда из утки гораздо важнее базы для блюд из гуся, поскольку гуси – это самозванцы на троне водоплавающих.
Обе БД настроены в качестве сервисов, а их юнит-файлы выглядят следующим образом:
На самом деле вызываемые в них сценарии (duck.sh и goose.sh) не выполняют никакой реальной работы в БД, а лишь имитируют чтение и запись с помощью циклов команды dd. Оба сценария используют файловую систему /database, которая лежит на своем виртуальном диске.
Давайте запустим duck и goose и посмотрим, где они приземлятся в иерархии cgroup:
А теперь, поскольку мы знаем PID’ы процессов dd, обратимся к команде iotop, чтобы посмотреть, что происходит в подсистеме хранения:
М-да, 12-14 Мбайт/с… не быстро. Похоже, г-н Крякин не очень-то вкладывался в систему хранения данных. Хотя у нас уже были вопросы насчет его адекватности, так что удивляться особо нечему.
Теперь смотрим на две наших задачи: PID 3301 (goose) и PID 3300 (duck). Каждая использует ввод-вывод где-то на 6 Мбайт/с. На скрине выше немного другие цифры, но в реальности они постоянно скачут, и в среднем эти две задачи поровну делят пропускную способность устройства хранения.
Г-н Крякин хочет, чтобы duck имел по меньшей мере в 5 раз больше пропускной способности ввода-вывода, чем goose, чтобы заказы на утку всегда обрабатывались в первую очередь. Попробуем использовать для этого параметр BlockIOWeight с помощью следующих команд:
Смотрим iotop и видим, что не сработало:
Давайте-ка проверим планировщик ввода-вывода для устройства /dev/vdb:
Интересно… Мы пробуем сменить планировщик на cfq и ничего не выходит. Почему?
Дело в том, что наша система работает на KVM-виртуалке, и, оказывается, начиная с версии 7.1 в Red Hat Enterprise Linux больше нельзя поменять планировщик. И это не баг, а фича, связанная с улучшением механизмов работы с виртуализованными устройствами ввода-вывода.
Но не будем отчаиваться. У нас есть еще два параметра, которые можно поменять: BlockIOReadBandwidth и BlockIOWriteBandwidth работают на уровне блочного устройства и игнорируют планировщик ввода-вывода. Поскольку мы знаем пропускную способность устройства /dev/vdb (где-то 14 Мбайт/с на прием и на отдачу), то ограничив goose на уровне 2 Мбайт/с, мы, похоже, сможем реализовать пожелание г-на Крякина. Давайте попробуем:
Смотрим: PID 3426, он же goose, теперь использует ввод-вывод где-то на 2 Мбайт/с, а PID 3425, то бишь duck, – почти на все 14!
Ура, мы сделали то, что хотел заказчик, а значит, спасли не только энное количество гусей, но и свою репутацию Linux-гуру.
(Продолжение следует)