Хабрахабр

Linux многоликий: как работать на любом дистрибутиве

Чтобы обеспечить работу Veeam Agent for Linux на дистрибутивах от Red Hat 6 и Debian 6, до OpenSUSE 15. Создать приложение для резервного копирования, работающее на любом дистрибутиве — задачка не простая. 04 приходится решать спектр проблем, особенно если учесть, что в состав модуля входит модуль ядра. 1 и Ubuntu 19.

По сути, это платформа, на базе которой можно сделать что-то уникальное, что-то своё. Статья создана по материалам выступления на конференции LinuxPiter 2019.
Linux — это не просто одна из самых популярных ОС. И тут возникает проблема: чтобы программный продукт функционировал на любом дистрибутиве, приходится учитывать особенности каждого. Благодаря этому у Linux множество дистрибутивов, которые отличаются набором программных компонентов.

Пакетные менеджеры. .deb vs .rpm

Начнём с очевидной проблемы распространения продукта для разных дистрибутивов.
Самый типичный способ распространения программных продуктов — это выложить пакет на репозиторий, чтобы встроенный в систему пакетный менеджер смог его оттуда установить.
Однако популярных форматов пакетов у нас два: rpm и deb. Значит, придётся поддержать каждый.

Один и тот же пакет одинаково хорошо ставится и работает как на Debian 6, так и на Ubuntu 19. В мире deb-пакетов уровень совместимости поразителен. Стандарты процесса построения пакетов и работы с ними, заложенные в старых Debian дистрибутивах, остаются актуальными и в новомодных Linux Mint и elementary OS. 04. Поэтому в случае Veeam Agent for Linux достаточно одного deb-пакета для каждой аппаратной платформы.

Во-первых, из-за того, что есть два совершенно независимых дистрибьютера Red Hat и SUSE, для которых совершенно не нужна совместимость. А вот в мире rpm-пакетов различия велики. поддержкой и экспериментальные. Во-вторых, у этих дистрибьютеров есть дистрибутивы с тех. У нас получилось, что для el6, el7 и el8 свои пакеты. Между ними совместимость тоже не нужна. Пакеты для SLES11 и 12 и отдельный для openSUSE. Отдельно пакет для Fedora. Основная проблема — в зависимостях и именах пакетов.

Проблема зависимостей

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

Хуже бывает, когда под старым именем пакета начинает прятаться обновлённая версия.

Пример:

Наш продукт собирался именно с 5-той версией, для обеспечения совместимости со старыми дистрибутивами. В Fedora 24 обновился пакет ncurses c версии 5 до версии 6. Чтобы воспользоваться старой 5-той версией библиотеки на Fedora 24, пришлось использовать пакет ncurses-compat-libs.

В результате для Fedora появляется два пакета, с разными зависимостями.

После очередного обновления дистрибутива пакет ncurses-compat-libs с 5-той версией библиотеки оказывается недоступным. Дальше интереснее. Спустя некоторое время проблема повторилась и в дистрибутивах SUSE. Для дистрибьютера накладно тянуть старые библиотеки в новую версию дистрибутива.

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

7. Кстати, в 8-й версии Red Hat больше нет мета-пакета python, который ссылался на старый добрый python 2. Есть python2 и python3.

Альтернатива пакетным менеджерам

Проблема с зависимостями стара и давно очевидна. Вспомнить хотя бы Dependency hell.
Объединить разнообразные библиотеки и приложения так, чтобы все они стабильно работали и не конфликтовали — собственно, эту задачу и пытается решать любой дистрибьютор Linux.

Основная идея: приложение выполняется в изолированной и защищённой от основной системы песочнице. Совсем иначе пытается решать эту проблему пакетный менеджер Snappy от Canonical. Если приложению нужны библиотеки, то они поставляются вместе с самим приложением.

Идею песочницы использует и AppImage. Flatpak также позволяет запускать приложения в песочнице, используя Linux Containers.

В случае с Flatpak установка и запуск приложения возможна даже без ведома администратора. Эти решения позволяют создавать один пакет для любых дистрибутивов.

Некоторым нужно прямое обращение к платформе. Основная проблема в том, что не все приложения могут работать в песочнице. Я уж не говорю про модули ядра, которые жёстко зависят от ядра и никак не вписываются в концепцию песочницы.

Вторая проблема – популярные в enterprise-среде дистрибутивы от Red Hat и SUSE ещё не содержат поддержку Snappy и Flatpak.

В связи с этим Veeam Agent for Linux нет ни на snapcraft.io ни на flathub.org.

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

Я сталкивался с такими пакетами для Linux только от VMware. Такой bundle позволяет создать один общий пакет для разных дистрибутивов и платформ, производить интерактивный процесс установки, осуществляя необходимую кастомизацию.

Проблема обновлений


Даже если все проблемы с зависимостями решены, программа может работать по-разному на одном и том же дистрибутиве. Дело в обновлениях.

Есть 3 стратегии обновления:

  • Самая простая – не обновлять никогда. Настроил сервер и забыл. Зачем обновления, если всё работает? Проблемы начинаются при первом же обращении в службу поддержки. Создатель дистрибутива поддерживает только обновлённый релиз.
  • Можно довериться дистрибьютеру и настроить автоматическое обновление. В этом случае звонок в службу поддержки вероятен сразу после неудачного обновления.
  • Вариант ручного обновления только после его обкатки на тестовой инфраструктуре – самый верный, но дорогой и трудоёмкий. Далеко не все способны его себе позволить.

Так как разные пользователи применяют разные стратегии обновления, то и поддерживать нужно и самый свежий релиз, и все ранее выпущенные. Это усложняет и процесс разработки, и процесс тестирования, добавляет головной боли службе поддержки.

Разнообразие аппаратных платформ

Различные аппаратные платформы – это проблема, в значительной степени специфичная именно для native-кода. Как минимум, приходится собирать бинарники для каждой поддерживаемой платформы.

В проекте Veeam Agent for Linux мы всё никак не можем поддержать хоть что-нибудь такое RISC-овое.

Обозначу лишь основные проблемы: платформозависимые типы, такие как size_t, выравнивание структур и byte order. Подробно останавливаться на этом вопросе не буду.

Статическая и/или динамическая линковка


А вот вопрос «Как линковаться с библиотеками — динамически или статически?» стоит обсудить.

Это отлично работает в том случае, если приложение собрано специально под конкретный дистрибутив. Как правило, С/С++ приложения под Linux используют динамическую линковку.

Для нас это Red Hat 6. Если же стоит задача охватить разнообразные дистрибутивы одним бинарным файлом, то приходится ориентироваться на самый старый поддерживаемый дистрибутив. 4, который даже стандарт С++11 поддерживает не полностью. Он содержит gcc 4.

3, который полностью поддерживает C++14. Мы собираем наш проект с помощью gcc 6. Проще всего линковаться с ними статически. Естественно, в таком случае на Red Hat 6 библиотеку libstdc++ и boost приходится тащить с собой.

Но увы, не со всеми библиотеками можно линковаться статически.

Во-первых, системные библиотеки, такие как libfuse, libblkid необходимо линковать динамически, чтобы быть уверенным в совместимости их с ядром и его модулями.

Во-вторых, есть тонкость с лицензиями.

MIT и BSD разрешают статическую линковку и позволяют включать библиотеки в проект. Линцензия GPL в принципе разрешает линковать библиотеки только с opensource кодом. А вот LGPL вроде как не противоречит статической линковке, но требует предоставить в общий доступ файлы, необходимые для связывания.

В общем случае использование динамической линковки обезопасит от необходимости что-то предоставлять.

Сборка С/С++ приложений

Для сборки С/С++ приложений для разных платформ и дистрибутивов достаточно подобрать или собрать gcc подходящей версии и воспользоваться кросс-компиляторами для специфичных архитектур, собрать весь набор библиотек. Работа эта вполне реализуемая, но довольно хлопотная. И нет никаких гарантий, что выбранный компилятор и библиотеки обеспечат работоспособный вариант.

Кроме того, достаточно собрать один набор бинарных файлов для одной архитектуры и можно паковать их в пакеты для разных дистрибутивов. Очевидный плюс: сильно упрощается инфраструктура, так как весь процесс сборки можно выполнить на одной машине. Именно так собираются пакеты veeam для Veeam Agent for Linux.

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

Также минусом является то, что такое множество машин нужно обслуживать, выделять большое количество дискового пространства и оперативной памяти. Есть, правда, и минус у такого подхода: для каждого дистрибутива в рамках одной архитектуры придётся собирать свой набор бинарных файлов.

Таким образом собираются KMOD пакеты модуля ядра veeamsnap под дистрибутивы Red Hat.

Open Build Service

Коллеги из SUSE попробовали реализовать некоторую золотую середину в виде специального сервиса для компиляции приложений и сборки пакетов — openbuildservice.

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

Встроенный механизм подписи сам подпишет пакеты и выложит их на встроенный репозиторий. Реализованный в OpenBuildService планировщик сам определит, сколько виртуальных машин он может запустить для оптимальной скорости сборки пакетов. Остаётся просто добавить в эту систему свои исходники. Встроенная система версионного контроля сохранит историю изменений и сборок. Даже сервер самому поднимать не обязательно, а можно воспользоваться открытым.

Например, версионный контроль не нужен, у нас уже есть свой для исходников. Тут, правда, есть проблема: такой комбайн трудно вписывается в существующую инфраструктуру. Репозиторий тоже не нужен. Механизм подписи у нас отличается: используется специальный сервер.

Кроме того, поддержка других дистрибутивов — к примеру, Red Hat — реализована довольно скудно, что вполне объяснимо.

До официального объявления о релизе необходимые для сборки пакеты выкладываются на публичный репозиторий. Достоинством такого сервиса является быстрая поддержка очередной версии дистрибутива SUSE. Выставляем галочку, и он добавляется в план сборки. В списке доступных дистрибутивов на OpenBuildService появляется новый. Таким образом, добавление новой версии дистрибутива выполняется практически в один клик.

В нашей инфраструктуре с использованием OpenBuildService собирается всё многообразие KMP пакетов модуля ядра veeamsnap для дистрибутивов SUSE.

Далее я хотел бы остановиться на вопросах, специфичных именно для модулей ядра.

kernel ABI

Модули ядра Linux исторически распространялись в виде исходных текстов. Дело в том, что создатели ядра не обременяют себя заботой о поддержке стабильного API для модулей ядра, а тем более на бинарном уровне, далее kABI.

Чтобы собрать модуль для ванильного ядра, обязательно нужны header-ы именно этого ядра, и работать он будет только на этом ядре.

В результате пользователи репозитория Debian (и его многочисленных родственников) используют модули ядра либо из репозитория дистрибьютера, либо собираемые из исходников с помощью DKMS. DKMS позволяет автоматизировать процесс сборки модулей при обновлении ядра.

Распространители проприетарного кода хотят распространять продукт в виде собранных бинарников. Однако такая ситуация не особо устраивает Enterprise-сегмент.

Дистрибьютеры Enterprise Linux — такие, как Red Hat и SUSE — решили, что для своих пользователей стабильное kABI они смогут поддержать. Администраторы не хотят держать средства разработки на production-серверах из соображений безопасности. В результате появились KMOD пакеты для Red Hat и KMP пакеты для SUSE.

Для конкретной версии дистрибутива API ядра freeze-ится. Суть такого решения довольно проста. 10, и вносит только исправления и улучшения, которые никак не затрагивают интерфейсы ядра, а собранные для самого первого ядра модули могут быть использованы для всех последующих без перекомпиляции. Дистрибьютор заявляет, что он использует именно ядро, например, 3.

То есть собранный модуль для rhel 6. Red Hat заявляют о kABI совместимости для дистрибутива на протяжении всего жизненного цикла. 10 (релиз июня 2018). 0 (релиз ноября 2010) должен также работать и на версии 6. Естественно, задача это довольно сложная.
Мы зафиксировали несколько случаев, когда из-за проблем с kABI совместимостью модуль veeamsnap переставал работать. А это почти 8 лет.

0, оказался несовместим с ядром из RHEL 7. После того, как модуль veeamsnap, собранный для RHEL 7. 5, но при этом загружался и гарантированно ронял сервер, мы отказались от использования kABI совместимости для RHEL 7 вообще.

В настоящий момент KMOD пакет для RHEL 7 содержит сборку для каждой версии релиза и скрипт, который обеспечивает загрузку модуля.

Они обеспечивают kABI совместимость только в рамках одного service pack. SUSE к задаче kABI совместимости подошли более осторожно.

А SLES 12 SP1 уже в декабре 2015, то есть прошло чуть больше года. Например, релиз SLES 12 состоялся в сентябре 2014. 12, они kABI несовместимы. Несмотря на то, что оба релиза используют ядро 3. Годовой цикл обновления модуля ядра не должен вызывать проблем у создателей модулей. Очевидно, что поддерживать kABI совместимость в течение всего лишь года значительно проще.

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

Патчи и бэкпорты

Несмотря на то, что дистрибьютеры стараются обеспечить kABI совместимость и стабильность ядра, они ещё и стараются улучшить производительность и устранить дефекты этого стабильного ядра.

При этом кроме собственной «работы над ошибками» разработчики ядра enterprise linux отслеживают изменения в ванильном ядре и переносят их в своё «стабильное».

Иногда это приводит к новым ошибкам.

Она приводила к тому, что модуль veeamsnap гарантированно валил систему при освобождении снапшота. В последнем релизе Red Hat 6 в одном из минорных обновлений была допущена ошибка. Аналогичный фикс был произведён в ванильном ядре версии 4. Сравнив исходники ядра до и после обновления, мы выяснили, что виной всему был backport. Вот только в ванильном ядре этот фикс работал нормально, а при переносе его в «стабильное» 2. 19. 32 возникла проблема со спин-блокировкой. 6.

19 в 2. Конечно, ошибки встречаются у всех и всегда, но стоило ли тащить код из 4. 32, рискуя стабильностью?.. 6. Я не уверен…

Отделу маркетинга нужно, чтобы ядро обновлённого дистрибутива был стабильным, с одной стороны, и в тоже время было лучше по производительности и имело новые функции. Хуже всего, когда к перетягиванию каната «стабильность»<->«модернизация» подключается маркетинг. Это приводит к странным компромиссам.

4 от SLES 12 SP3, я с удивлением обнаружил в нём функционал из ванильного 4. Когда я попробовал собрать модуль на ядре 4. На мой взгляд, реализация блочного ввода/вывода ядра 4. 8. 8, чем на предыдущий релиз стабильного 4. 4 от SLES 12 SP3 больше походит на ядро 4. Каков был процент перенесённого кода из ядра 4. 4 ядра от SLES12 SP2. 4 для SP3 я судить не берусь, однако назвать ядро всё тем же стабильным 4. 8 в SLES-овский 4. 4 у меня язык не поворачивается.

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

В результате код обрастает причудливыми директивами условной компиляции.

16 и был очень удивлён, увидев что вызов lookup_bdev в этой версии ядра изменил перечень входных параметров. Встречаются ещё и патчи, которые меняют задокументированное API ядра.
Наткнулся на дистрибутив KDE neon 5.

Чтобы собраться, пришлось в makefile добавлять скрипт, который проверяет, есть ли параметр mask у функции lookup_bdev.

Подпись модулей ядра

Но вернёмся к вопросу распространения пакетов.

В этом случае разработчик может быть уверен, что модуль не был случайно повреждён или намеренно изменён. Одно из достоинств стабильного kABI в том, что модули ядра в виде бинарного файла можно подписать. Проверить это можно командой modinfo.

Сертификат представляет собой публичный ключ, которым подписывается модуль. Дистрибутивы Red Hat и SUSE позволяют проверять подпись модуля и загружать его, только если в системе зарегистрирован соответствующий сертификат. Мы распространяем его в виде отдельного пакета.

Утилита mokutil при установке сертификата требует перезагрузить систему и ещё до загрузки ядра операционной системы предлагает администратору разрешить загрузку нового сертификата. Проблема тут в том, что сертификаты могут быть либо встроенные в ядро (их используют дистрибьютеры), либо должны быть записаны в энергонезависимую память EFI с помощью утилиты mokutil.

Если машина располагается где-то в облаке либо просто в удалённой серверной и доступ есть только по сети (например, по ssh), то добавить сертификат будет невозможно. Таким образом, добавление сертификата требует физического доступа администратора к системе.

EFI на виртуальных машинах

Несмотря на то, что уже давно EFI поддерживается практически всеми создателями материнских плат, при установке системы администратор может не задуматься о необходимости EFI, и он может быть отключён.

VMWare vSphere поддерживает EFI, начиная с версии 5.
Microsoft Hyper-V также получил поддержку EFI, начиная с Hyper-V for Windows Server 2012R2. Не все гипервизоры поддерживают EFI.

Однако в дефолтной конфигурации этот функционал для Linux машин выключен, а значит, сертификат установить нельзя.

5 выставить опцию Secure Boot можно только в старой версии веб-интерфейса, который работает через Flash. В vSphere 6. Web UI на HTML-5 пока сильно отстаёт.

Экспериментальные дистрибутивы

Ну и напоследок рассмотрим вопрос экспериментальных дистрибутивов и дистрибутивов без официальной поддержки. С одной стороны, такие дистрибутивы навряд ли встретишь на серверах серьёзных организаций. Официальной поддержки у таких дистрибутивов нет. Поэтому и обеспечивать тех. поддержку продукта на таком дистрибутиве нельзя.

Например, Fedora, OpenSUSE Tumbleweed или Unstable версии Debian. Однако такие дистрибутивы становятся удобной площадкой для пробы новых экспериментальных решений. В них всегда новые версии программ и всегда новое ядро. Они довольно стабильны. Через год этот экспериментальный функционал может оказаться в обновлённом RHEL, SLES или Ubuntu.

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

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

После доработки пакета veeam наш продукт установился и заработал. Лично мне был интересен эксперимент с ОС «Эльбрус». Про этот эксперимент я писал на Хабре в статье.

Ожидаем появления на свет версии 4. Ну а поддержка новых дистрибутивов продолжается. Вот-вот должна появиться бэта, так что следите за whats-new! 0.

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»