Linux багатоликий: як працювати на будь-якому дистрибутиві

Linux багатоликий: як працювати на будь-якому дистрибутиві

Створити додаток для резервного копіювання, що працює на будь-якому дистрибутиві - завдання складне. Щоб забезпечити роботу Veeam Agent for Linux на дистрибутивах від Red Hat 6 та Debian 6, до OpenSUSE 15.1 та Ubuntu 19.04 доводиться вирішувати спектр проблем, особливо якщо врахувати, що до складу програмного продукту входить модуль ядра.

Статтю створено за матеріалами виступу на конференції LinuxPiter 2019.

Linux - це не просто одна з найпопулярніших ОС. По суті це платформа, на базі якої можна зробити щось унікальне, щось своє. Завдяки цьому у Linux безліч дистрибутивів, що відрізняються набором програмних компонентів. І тут постає проблема: щоб програмний продукт функціонував на будь-якому дистрибутиві, доводиться враховувати особливості кожного.

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

Почнемо із очевидної проблеми поширення продукту для різних дистрибутивів.
Найбільш типовий спосіб розповсюдження програмних продуктів - це викласти пакет на репозиторій, щоб вбудований у систему пакетний менеджер зміг його звідти встановити.
Однак популярних форматів пакетів у нас два: оборотів в хвилину и деб. Значить, доведеться підтримати кожен.

У світі deb-пакетів рівень сумісності вражаючий. Один і той же пакет однаково добре ставиться та працює як на Debian 6, так і на Ubuntu 19.04. Стандарти процесу побудови пакетів та роботи з ними, закладені у старих Debian дистрибутивах, залишаються актуальними і у новомодних Linux Mint та elementary OS. Тому у випадку Veeam Agent for Linux достатньо одного deb-пакету для кожної апаратної платформи.

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

Проблема залежностей

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

Для EL7:
Для SLES 12:

  • libblkid
  • libgcc
  • libstdc ++
  • ncurses-libs
  • fuse-libs
  • file-libs
  • veeamsnap = 3.0.2.1185
  • libblkid1
  • libgcc_s1
  • libstdc ++ 6
  • libmagic1
  • libfuse2
  • veeamsnap-kmp = 3.0.2.1185

В результаті перелік залежностей виявляється унікальним для дистрибутива.

Найгірше буває, коли під старим ім'ям пакета починає ховатися оновлена ​​версія.

Приклад:

У Fedora 24 оновився пакет ncurses c версії 5 до версії 6. Наш продукт збирався саме з 5 версією, для забезпечення сумісності зі старими дистрибутивами. Щоб скористатися старою 5-тою версією бібліотеки на Fedora 24, довелося використовувати пакет ncurses-compat-libs.

У результаті Fedora з'являється два пакети, з різними залежностями.

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

В результаті для деяких дистрибутивів довелося відмовитись від явної залежності від ncurses-libs, а продукт виправити так, щоб він міг працювати з будь-якою версією бібліотеки.

До речі, у 8-й версії Red Hat більше немає мета-пакету пітон, який посилався на старий добрий пітон 2.7. є python2 и пітон3.

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

Проблема із залежностями стара і давно очевидна. Згадати хоч би Dependency hell.
Об'єднати різноманітні бібліотеки та програми так, щоб усі вони стабільно працювали і не конфліктували — власне, це завдання й намагається вирішувати будь-який дистриб'ютор Linux.

Зовсім інакше намагається вирішувати цю проблему. Швидко від Canonical. Основна ідея: додаток виконується в ізольованій та захищеній від основної системи пісочниці. Якщо програмі потрібні бібліотеки, вони поставляються разом із самим додатком.

Flatpak також дозволяє запускати програми у пісочниці, використовуючи Linux Containers. Ідею пісочниці використовує та AppImage.

Ці рішення дозволяють створювати один пакет будь-яких дистрибутивів. У випадку з Flatpak встановлення та запуск програми можливе навіть без відома адміністратора.

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

Друга проблема – популярні в enterprise-середовищі дистрибутиви від Red Hat та SUSE ще не містять підтримки Snappy та Flatpak.

У зв'язку з цим Veeam Agent for Linux немає ні на snapcraft.io ні на flathub.org.

На завершення питання пакетних менеджерів зауважу, що є варіант відмовитися зовсім від пакетних менеджерів, об'єднавши в один пакет бінарні файли і скрипт для їх установки.

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

Проблема оновлень

Linux багатоликий: як працювати на будь-якому дистрибутиві
Навіть якщо всі проблеми із залежностями вирішені, програма може працювати по-різному на тому самому дистрибутиві. Справа в поновленнях.

Є 3 стратегії оновлення:

  • Найпростіша – не оновлювати ніколи. Налаштував сервер та забув. Навіщо поновлення, якщо все працює? Проблеми починаються при першому зверненні до служби підтримки. Творець дистрибутива підтримує лише оновлений реліз.
  • Можна довіритися дистриб'ютору та настроїти автоматичне оновлення. У цьому випадку дзвінок до служби підтримки можливий одразу після невдалого оновлення.
  • Варіант ручного оновлення тільки після його обкатки на тестовій інфраструктурі – найвірніший, але найдорожчий і трудомісткий. Не всі здатні його собі дозволити.

Так як різні користувачі застосовують різні стратегії оновлення, то і підтримувати потрібно і найсвіжіший реліз, і раніше випущені. Це ускладнює і процес розробки, і процес тестування, додає головний біль службі підтримки.

Різноманітність апаратних платформ

Різні апаратні платформи – це проблема, значною мірою специфічна саме для native-коду. Як мінімум, доводиться збирати бінарники для кожної платформи, що підтримується.

У проекті Veeam Agent for Linux ми не можемо підтримати хоч щось таке RISC-овое.

Докладно зупинятись на цьому питанні не буду. Позначу лише основні проблеми: платформозалежні типи, такі як size_t, вирівнювання структур та byte order.

Статична та/або динамічна лінківка

Linux багатоликий: як працювати на будь-якому дистрибутиві
А ось питання «Як лінкуватися з бібліотеками — динамічно чи статично?» варто обговорити.

Як правило, С/С++ програми під Linux використовують динамічне лінкування. Це добре працює в тому випадку, якщо програма зібрана спеціально під конкретний дистрибутив.

Якщо ж стоїть завдання охопити різноманітні дистрибутиви одним бінарним файлом, то доводиться орієнтуватися на найстаріший підтримуваний дистрибутив. Для нас це Red Hat 6. Він містить gcc 4.4, який навіть стандарт С++11 не підтримує повністю.

Ми збираємо проект за допомогою gcc 6.3, який повністю підтримує C++14. Природно, у такому разі на Red Hat 6 бібліотеку libstdc++ та boost доводиться тягнути із собою. Найпростіше лінкуватися з ними статично.

Але, на жаль, не з усіма бібліотеками можна лінкуватися статично.

По-перше, системні бібліотеки, такі як libfuse, libblkid необхідно лінкувати динамічно, щоб бути впевненим у сумісності їх із ядром та його модулями.

По-друге, є тонкість із ліцензіями.

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

Загалом використання динамічного лінкування убезпечить від необхідності щось надавати.

Складання С/С++ додатків

Для складання С/С++ додатків для різних платформ та дистрибутивів достатньо підібрати або зібрати gcc підходящої версії та скористатися крос-компіляторами для специфічних архітектур, зібрати весь набір бібліотек. Ця робота цілком реалізована, але досить клопітка. І немає жодних гарантій, що вибраний компілятор та бібліотеки забезпечать працездатний варіант.

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

На противагу цьому варіанту можна просто підготувати ферму, тобто кілька машин для складання. Кожна така машина забезпечуватиме компіляцію програми та складання пакету для конкретного дистрибутива та певної архітектури. У разі компіляція виробляється тими засобами, які підготував дистриб'ютор. Тобто етап підготовки компілятора та підбір бібліотек відпадає. Крім того, процес складання може бути легко розпаралельний.

Є, щоправда, і мінус такого підходу: для кожного дистрибутива в рамках однієї архітектури доведеться збирати свій набір бінарних файлів. Також мінусом є те, що така безліч машин потрібно обслуговувати, виділяти велику кількість дискового простору та оперативної пам'яті.

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

Open Build Service

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

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

Linux багатоликий: як працювати на будь-якому дистрибутиві

Реалізований в OpenBuildService планувальник сам визначить, скільки віртуальних машин може запустити для оптимальної швидкості складання пакетів. Вбудований механізм підпису сам підпише пакети та викладе їх на вбудований репозиторій. Вбудована система версійного контролю збереже історію змін та складання. Залишається просто додати до цієї системи свої вихідні джерела. Навіть сервер самому піднімати не обов'язково, а можна скористатися відкритим.

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

Крім того, підтримка інших дистрибутивів - наприклад, Red Hat - реалізована досить мізерно, що цілком зрозуміло.

Перевагою такого сервісу є швидка підтримка чергової версії дистрибутива SUSE. До офіційного оголошення про дозвіл необхідні для складання пакети викладаються на публічний репозиторій. У списку доступних дистрибутивів OpenBuildService з'являється новий. Виставляємо галочку, і він додається до плану складання. Таким чином додавання нової версії дистрибутива виконується практично в один клік.

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

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

kernel ABI

Модулі ядра Linux історично поширювалися як вихідних текстів. Справа в тому, що творці ядра не обтяжують себе турботою про підтримку стабільного API для модулів ядра, а тим більше на бінарному рівні, далі kABI.

Щоб зібрати модуль для ванільного ядра, обов'язково потрібні header-и саме цього ядра, і працюватиме він тільки на цьому ядрі.

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

Однак така ситуація не надто влаштовує Enterprise-сегмент. Розповсюджувачі пропрієтарного коду хочуть розповсюджувати продукт у вигляді зібраних бінарників.

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

Суть такого рішення є досить простою. Для конкретної версії дистрибутива API ядра freeze-ється. Дистриб'ютор заявляє, що він використовує саме ядро, наприклад, 3.10, і вносить тільки виправлення та поліпшення, які ніяк не зачіпають інтерфейси ядра, а зібрані для першого ядра модулі можуть бути використані для всіх наступних без перекомпіляції.

Red Hat заявляють про kABI сумісність дистрибутива протягом усього життєвого циклу. Тобто зібраний модуль для rhel 6.0 (реліз листопада 2010 року) повинен також працювати і на версії 6.10 (реліз червня 2018 року). А це майже вісім років. Звичайно, завдання це досить складне.
Ми зафіксували кілька випадків, коли через проблеми з kABI сумісністю модуль veeamsnap переставав працювати.

Після того, як модуль veeamsnap, зібраний для RHEL 7.0, виявився несумісним з ядром з RHEL 7.5, але при цьому завантажувався і гарантовано упускав сервер, ми відмовилися від використання kABI сумісності для RHEL 7 взагалі.

В даний момент KMOD пакет для RHEL 7 містить складання для кожної версії релізу та скрипт, який забезпечує завантаження модуля.

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

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

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

Патчі та бекпорти

Незважаючи на те, що дистриб'ютори намагаються забезпечити kABI сумісність та стабільність ядра, вони ще й намагаються покращити продуктивність та усунути дефекти цього стабільного ядра.

При цьому, крім власної «роботи над помилками», розробники ядра enterprise linux відстежують зміни у ванільному ядрі і переносять їх у своє «стабільне».

Іноді це призводить до нових помилок.

В останньому релізі Red Hat 6 в одному з мінорних оновлень була допущена помилка. Вона призводила до того, що модуль veeamsnap гарантовано валив систему при звільненні снапшота. Порівнявши вихідні джерела ядра до і після оновлення, ми з'ясували, що провиною всьому був backport. Аналогічний фікс був зроблений у ванільному ядрі версії 4.19. Ось тільки у ванільному ядрі цей фікс працював нормально, а при перенесенні його в «стабільне» 2.6.32 виникла проблема зі спін-блокуванням.

Звичайно, помилки зустрічаються у всіх і завжди, але чи варто було тягти код з 4.19 до 2.6.32, ризикуючи стабільністю?.. Я не впевнений.

Найгірше, коли до перетягування каната «стабільність» «модернізація» підключається маркетинг. Відділу маркетингу потрібно, щоб ядро ​​оновленого дистрибутива був стабільним, з одного боку, і в той же час було кращим за продуктивністю і мало нові функції. Це призводить до дивних компромісів.

Коли я спробував зібрати модуль на ядрі 4.4 SLES 12 SP3, я з подивом виявив у ньому функціонал з ванільного 4.8. На мій погляд, реалізація блокового введення/виведення ядра 4.4 від SLES 12 SP3 більше схожа на ядро ​​4.8, ніж на попередній реліз стабільного 4.4 ядра від SLES12 SP2. Який був відсоток перенесеного коду з ядра 4.8 в SLES-івський 4.4 для SP3 я судити не беруся, проте назвати ядро ​​все тим самим стабільним 4.4 у мене мова не повертається.

Найнеприємніше в цьому те, що при написанні модуля, який однаково добре працював би на різних ядрах, більше не можна спиратися на версію ядра. Доводиться ще враховувати дистрибутив. Добре, що іноді можна розпочати дефайн, який з'являється разом з новим функціоналом, проте така можливість з'являється не завжди.

В результаті код обростає химерними директивами умовної компіляції.

Трапляються ще й патчі, які змінюють задокументоване API ядра.
Натрапив на дистрибутив KDE neon 5.16 і був дуже здивований, побачивши, що виклик lookup_bdev у цій версії ядра змінив перелік вхідних параметрів.

Щоб зібратися, довелося в makefile додавати скрипт, який перевіряє, чи параметр mask у функції lookup_bdev.

Підпис модулів ядра

Але повернемося до питання розповсюдження пакетів.

Одна з переваг стабільного kABI в тому, що модулі ядра у вигляді бінарного файлу можна підписати. У цьому випадку розробник може бути впевнений, що модуль був випадково пошкоджений або навмисно змінений. Перевірити це можна командою modinfo.

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

Проблема в тому, що сертифікати можуть бути або вбудовані в ядро ​​(їх використовують дистриб'ютори), або повинні бути записані в енергонезалежну пам'ять EFI за допомогою утиліти. мокутил. Утиліта мокутил під час встановлення сертифіката вимагає перезавантажити систему і ще до завантаження ядра операційної системи пропонує адміністратору дозволити завантаження нового сертифіката.

Таким чином, додавання сертифіката потребує фізичного доступу адміністратора до системи. Якщо машина знаходиться десь у хмарі або просто у віддаленій серверній і доступ є тільки по мережі (наприклад, по ssh), то додати сертифікат буде неможливо.

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

Незважаючи на те, що вже давно EFI підтримується практично всіма творцями материнських плат, при встановленні системи адміністратор може не задуматися про необхідність EFI, і він може бути вимкнений.

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

Однак у дефолтній конфігурації цей функціонал для Linux машин вимкнено, а отже, сертифікат встановити не можна.

У vSphere 6.5 виставити опцію безпечне завантаження можна лише у старій версії веб-інтерфейсу, який працює через Flash. Web UI на HTML-5 поки сильно відстає.

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

Ну і насамкінець розглянемо питання експериментальних дистрибутивів та дистрибутивів без офіційної підтримки. З одного боку, такі дистрибутиви навряд чи зустрінеш на серверах серйозних організацій. Офіційної підтримки такі дистрибутиви не мають. Тому й забезпечуватиме тих. Підтримку продукту на такому дистрибутиві не можна.

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

Тож якщо щось не працює на експериментальному дистрибутиві — це привід розібратися в проблемі та вирішити її. Потрібно бути готовим до того, що цей функціонал незабаром з'явиться на production-серверах користувачів.

На даний момент список офіційно підтримуваних дистрибутивів для версії 3.0 ви можете вивчити тут. Але реальний список дистрибутивів, на яких наш продукт здатний працювати, набагато ширший.

Особисто мені був цікавий експеримент із ОС «Ельбрус». Після доопрацювання veeam пакет наш продукт встановився і запрацював. Про цей експеримент я писав на Хабре в статті.

Ну а підтримка нових дистрибутивів продовжується. Очікуємо на світ версії 4.0. Ось-ось має з'явитися бета, так що слідкуйте за що нового!

Джерело: habr.com

Додати коментар або відгук