Каждый Unix-программист рано или поздно сталкивается с проблемой работы с разными версиями одной и той же программы, рассчитанными на разные варианты ОС. Если повезло и у производителя есть нужный загрузочный код программы, то запустить ее удастся. Если нет — начинается кошмар. Статья посвящена особенностям процедуры сборки сложных приложений с использованием возможностей Виртуальных Сред (Virtual Environment).

Многим знакома ситуация, когда при отсутствии у производителя нужного бинарного кода программы приходится самостоятельно разыскивать исходные файлы и пытаться их собрать (конечно, если они доступны). Версия make отличается, описание структуры лежит не в том системном включаемом файле, компилятор собирает программу, руководствуясь своими собственными представлениями, а не представлениями программиста, библиотеки отсутствуют или слабо совместимы — неполный перечень проблем, возникающих при такой ручной сборке. Именно поэтому уважающие себя компании обычно пытаются предоставить пользователю все возможные версии программ в бинарном виде.

Особую актуальность проблема разнообразия версий приобрела для быстро развивающихся операционных систем, в частности, для Linux. Большое количество разработчиков генерируют множество разнообразных вариантов; например, процедура поиска по дистрибутивам Linux находит их уже более чем 160 штук. При этом многие из них достаточно слабо совместимы друг с другом, особенно если говорить о бинарных версиях. Даже способы упаковки бинарной версии в дистрибутив («пакетирование») от одного дистрибутива к другому существенно разнятся. Наиболее известным способом пакетирования в мире Linux является использование RedHat Packet Manager (RPM), но существует и несколько других достаточно распространенных способов сборки (например, пакеты дистрибутивов Debian, файлы с расширением .tgz, или установки непосредственно с сервера компании-распространителя). Кстати, RPM тоже имеет свои версии, и не факт, что пакет, собранный его более новой версией, будет обслуживаться старой.

Что же тогда делать разработчику, который хочет, чтобы его программой, адресованной широкому кругу пользователей, действительно могло воспользоваться максимальное число людей? Как собрать программу в бинарном виде так, чтобы она была пригодна для установки в максимальное количество дистрибутивов? Ответ, увы, прост и очевиден, но, к сожалению, малоприятен: придется собирать программу в большом наборе дистрибутивов, версий библиотек libc/glibc/gcc и т.д. Конечно, собирать надо не в 160 экземплярах, а, как правило, в трех-четырех основных дистрибутивах, и, возможно, каждый из них в двух-трех разновидностях.

Чаще всего проблемы возникают в связи с версиями библиотек (речь идет в частности, о библиотеках общего пользования glibc, libstdc++, db{1-3}, libcrypto, libssl, pam, которые используются очень большим количеством прикладных программ). Например, пакет из дистрибутива RedHat Linux 7.2, собранный в среде дистрибутива 7.1, скорее всего, откажется запускаться в среде 7.2 из-за несоответствия версий. Другой пример — язык С++ и его компилятор gcc. Известно, что из-за разного именования объектов языка (mangling) программа, собранная версией компилятора 2.95, не будет работать в рамках среды с установленными библиотеками от версии компилятора 2.96. Еще один актуальный пример взят из жизни «писателей» прикладных Web-программ на языке perl: модули, собранные с версией 5.6.0, при установке размещаются в каталог, носящий имя соответствующей версии, и perl 5.6.1 их уже не найдет.

Сборка программ в ОС Linux обычно осуществляется с помощью программы rpm, которая последовательно выполняет операции, заложенные разработчиком, например, путем вызова make. Но, оказывается и при этом простом процессе есть свои особенности, скажем, вопрос об эффективности использования многопроцессорной архитектуры. rpm запускает сборку по очереди, отрабатывая соответствующие файлы make, и оказывается, что загрузка многопроцессорной машины далека от оптимальной. GNU make имеет возможность вызова в параллельном режиме, с опцией «-j», когда на выполнение запускается сразу несколько «мишеней» makefile. В таком случае одновременно активизируется несколько процессов компиляции, и ресурсы машины используются более полно, что приводит к фактическому ускорению всей процедуры сборки (так, в случае ожидания обмена с диском машина отдает часть центрального процессора другому процессу компиляции). Кстати, опыт показывает, что даже на однопроцессорной машине иногда можно добиться уменьшения физического времени сборки. Для этого используется, например, опция «-j2» для запуска двух процессов в параллель, что приводит к большей полезной загрузке ресурсов компьютера. Интересно, что в новой серии процессоров Pentium 4 Xeon MP используется технология многонитевой обработки Hyper-Threading, благодаря которой каждый физический процессор перед операционной системой и приложениями предстает как два логических, что, по утверждению представителей Intel, может поднять производительность вплоть до 40% (хотя, пожалуй, на программах наподобие Java benchmark улучшение производительности оказывается в районе 15% — А.Т.).

Однако такой режим работы make не всегда можно использовать, например, из за ошибок в компиляторе или при не совсем корректном файле makefile (так, из-за ошибки в компиляторе gcc 2.96 ядро Linux не могло собираться в параллельном режиме). Использование пакета rpm для параллельной сборки тоже требует достаточно больших усилий от создателя пакета.

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

Возможный путь — так называемая консолидация серверов. Хотелось бы получить возможность создавать несколько разнотипных сред на одной или нескольких машинах, с независимой конфигурацией, с возможностью эффективного управления и независимой установки разных дистрибутивов, разных версий библиотек и т.д.

Как представляется, использование для решения этой задачи средств VMware Workstation/VMware GSX Server не оптимально. Даже на мощной машине удается запустить всего две-три копии эмулятора; одновременно работающие экземпляры VMware практически не дают возможности совместного использования ни диска, ни оперативной памяти. При этом управление процессом сборки затруднено. Нельзя управлять «вторжением» скриптов хост-машины внутрь операционной системы, запущенной в экземплярах эмуляторов VMware. Нельзя говорить и об эффективности использования ресурсов компьютера, что особенно существенно при сборке больших приложений, которая длится сутками, и 20-30% потерь производительности оборачиваются лишними десятками часов.

Казалось бы, более простое решение — создание на одной машине так называемых chroot-сред, которые содержали бы разные дистрибутивы. Однако клонирование таких сред оказывается не самой простой и удобной процедурой. Проблема состоит в больших административных сложностях управления подобным хозяйством, особенно для нескольких машин: непросто создать такую среду корректным образом, непросто создать скрипты, которые обеспечивали бы запуск процесса и т.д. Хорошим примером таких сред и связанных с ними трудозатрат является небольшой хостинг-провайдер, предоставляющий ряду клиентов несколько почтовых и Web-серверов. Даже ля поддержки такой конфигурации обычно требуется два-три человека, не говоря уже о ее развертывании «с нуля». Возникают накладные расходы и другого рода: при создании слегка отличающихся друг от друга сред в случае параллельного запуска нескольких процедур сборки в оперативной памяти будет одновременно находиться несколько экземпляров программ типа glibc, gcc и т.д.

Другой путь — применение технологии виртуализации, принятой в операционной системе Linux Virtuozzo компании SWsoft [1]. Для каждого варианта сборки создается своя собственная среда Virtual Environment (VE), которая изначально содержит один из дистрибутивов Linux, поставляемых вместе с ядром Virtuozzo. Затем в каждую среду вносятся требуемые модификации для организации процесса сборки: версия компилятора gcc, библиотек glibc или rpm. Затем размещаются исходные тексты программ совместно с необходимыми для сборки скриптами (обычно makefile). Существует две возможности установить исходные тексты: их можно скопировать в каждую среду или разместить в так называемый «главный шаблон» файловой системы Virtuozzo VZFS, который разделяют разные виртуальные среды. В последнем случае все экземпляры виртуальных сред будут видеть исходные файлы из общего дерева, вместе с тем обеспечивая за счет VZFS внесение своих собственных изменений и хранение полученных объектных и бинарных файлов в одном дереве с исходными файлами. При большом количестве виртуальных сред это может обеспечить существенный выигрыш в размере занятого дискового пространства.

Для обслуживания процедур сборки также возможно несколько вариантов процедур управления. Например, запуск процедуры сборки можно поставить в стартовые скрипты, выполняющиеся при запуске каждой среды (они являются полным аналогом соответствующих процедур ОС Linux; так, эти скрипты обычно расположены в /etc/init.d). Но это не очень удобно, особенно при каких-либо сбоях процедуры сборки, поэтому обычно применяется вариант, когда управление процессом осуществляется из «главной операционной системы» host OS, используя возможность технологии Virtuozzo по запуску внешнего процесса внутри соответствующей среды. Таким образом, например, можно выполнить команду типа «make» для запуска сборки, и сделать это одновременно для многих сред. Возможность параллельного запуска всех процедур сборки позволяет эффективно использовать возможности машины: физическое время сборки всех комплектов уменьшается, облегчается процедура управления системой за счет возможностей по конфигурированию VE, упрощается процедура создания новых сред для сборки системы.

Финалом процесса построения является обычно соединение всех собранных данных в единый пакет (например, в ISO-образ CD-ROM). Поскольку все виртуальные среды VE доступны через специальный интерфейс командной строки для любых административных действий, возможна эффективная реализация сборки уже собранных бинарных файлов. Они попросту копируются из каждой среды в одно место, где и осуществляется сборка ISO-образов с дистрибутивом (впрочем, можно собирать ISO-образы и непосредственно внутри VE). То же самое касается и регистрационных файлов, которые автоматически собираются и анализируются на предмет ошибок.

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

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

Литература

[1] Александр Тормасов, Виртуализация операционных систем. // Открытые системы, 2002, № 1

Александр Тормасов (tor@sw.ru) — руководитель НИОКР компании SWsoft (Москва).