
Vytvořit aplikaci pro zálohování, která funguje na jakékoli distribuci, není jednoduchý úkol. Aby Veeam Agent for Linux fungoval na distribucích od Red Hat 6 a Debian 6 až po OpenSUSE 15.1 a Ubuntu 19.04, je třeba vyřešit řadu problémů, zejména s ohledem na to, že součástí software je modul jádra.
Článek byl vytvořen na základě materiálů z vystoupení na konferenci .
Linux není jen jednou z nejpopulárnějších operačních systémů. V podstatě je to platforma, na jejímž základě lze vytvořit něco unikátního, něco vlastního. Díky tomu existuje spousta distribucí Linuxu, které se liší sadou programových komponentů. A zde se objevuje problém: aby software fungoval na jakékoli distribuci, je nutné brát v úvahu specifika každé distribuce.
Správci balíčků. .deb vs .rpm
Začněme zřejmým problémem distribuce produktu pro různé distribuce.
Nejtypičtějším způsobem distribuce softwarových produktů je umístit balíček do repozitáře, aby ho vestavěný správce balíčků v systému mohl odtud nainstalovat.
Nicméně máme dva populární formáty balíčků: rpm a deb. To znamená, že je třeba podporovat každý.
Ve světě deb-balíčků je úroveň kompatibility pozoruhodná. Tentýž balíček se stejně dobře instaluje a funguje jak na Debian 6, tak na Ubuntu 19.04. Standardy procesu vytváření balíčků a práce s nimi, které byly zavedeny ve starých Debianových distribucích, zůstávají aktuální také v moderních Linux Mint a elementary OS. Proto pro Veeam Agent for Linux stačí jeden deb-balíček pro každou hardwarovou platformu.
Naopak ve světě rpm-balíčků jsou rozdíly značné. Za prvé, proto, že existují dva zcela nezávislé distributoři Red Hat a SUSE, pro které není nutná kompatibilita. Za druhé, tito distributoři mají distribuce s technickou podporou a experimentální. Mezi nimi také není potřeba kompatibility. Získali jsme, že pro el6, el7 a el8 jsou své balíčky. Samostatný balíček pro Fedora. Balíčky pro SLES11 a 12 a samostatný pro openSUSE. Hlavní problém tkví v závislostech a názvech balíčků.
Problém se závislostmi
Bohužel, stejné balíčky se často objevují pod různými názvy v různých distribucích. Níže je uveden neúplný seznam závislostí balíčku veeam.
Pro EL7:
Pro 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
V důsledku toho je seznam závislostí jedinečný pro distribuci.
Horší je, když se pod starým názvem balíčku začne skrývat aktualizovaná verze.
Příklad:
Ve Fedora 24 došlo k aktualizaci balíčku ncurses z verze 5 na verzi 6. Náš produkt byl sestaven právě s 5. verzí, aby byla zajištěna kompatibilita se staršími distribucemi. Abychom mohli použít starou 5. verzi knihovny ve Fedora 24, museli jsme použít balíček ncurses-compat-libs.
V důsledku toho se pro Fedoru objevují dva balíčky s různými závislostmi.
Dál je to zajímavější. Po dalším aktualizaci distribuce balíček ncurses-compat-libs s 5. verzí knihovny se ukáže jako nedostupný. Pro distributora je náročné táhnout staré knihovny do nové verze distribuce. Po nějaké době se problém zopakoval i v distribucích SUSE.
V důsledku toho se některé distribuce musely vzdát explicitní závislosti na ncurses-libs, a produkt opravit tak, aby mohl fungovat s jakoukoli verzí knihovny.
Mimochodem, v 8. verzi Red Hat již není meta-balíček python, který odkazoval na starý dobrý python 2.7. Existuje python2 a python3.
Alternativa k balíčkovým manažerům
Problém se závislostmi je starý a již dlouho zřejmý. Stačí si vzpomenout na Dependency hell.
Sjednotit různé knihovny a aplikace tak, aby všechny stabilně pracovaly a nekonfliktovaly — to je úkol, který se snaží řešit každý distributor Linuxu.
Úplně jinak se tento problém snaží řešit balíčkový manažer Snappy od Canonical. Hlavní myšlenka: aplikace běží ve izolovaném a od hlavního systému chráněném písku. Pokud aplikaci potřebné knihovny, jsou dodány společně s aplikací.
Flatpak rovněž umožňuje spouštět aplikace v písku s využitím Linux Containers. Myšlenku písku používá i AppImage.
Tato řešení umožňují vytvořit jeden balíček pro jakékoli distribuce. V případě Flatpak je instalace a spuštění aplikace možné i bez vědomí administrátora.
Hlavní problém spočívá v tom, že ne všechny aplikace mohou fungovat v písku. Některé potřebují přímý přístup k platformě. O modulech jádra, které jsou pevně závislé na jádře a nikterak se nehodí do koncepce písku, ani nemluvím.
Druhým problémem je, že populární enterprise distribuce od Red Hat a SUSE dosud nepodporují Snappy a Flatpak.
V souvislosti s tím není Veeam Agent for Linux ani na ani na .
Na závěr otázky o správci balíčků bych rád poznamenal, že je možné se zcela vzdát správců balíčků a spojit do jednoho balíku spustitelné soubory a skript pro jejich instalaci.
Takový balíček umožňuje vytvořit jeden společný balík pro různé distribuce a platformy a provádět interaktivní proces instalace s potřebnou přizpůsobení. S takovými balíčky pro Linux jsem se setkával pouze od VMware.
Problémy s aktualizacemi

I když jsou všechny problémy se závislostmi vyřešeny, program může fungovat odlišně na stejné distribuci. Problémem jsou aktualizace.
Existují 3 strategie aktualizací:
- Nejjednodušší je nikdy neaktualizovat. Server je nastaven a zapomenut. Proč aktualizace, když vše funguje? Problémy začínají při prvním kontaktu se zákaznickou podporou. Tvořitel distribuce podporuje pouze aktualizovanou verzi.
- Můžete se spolehnout na distributora a nastavit automatické aktualizace. V tomto případě je pravděpodobné, že se zákaznická podpora kontaktuje ihned po neúspěšné aktualizaci.
- Možnost ruční aktualizace až po otestování na testovací infrastruktuře je nejspolehlivější, ale nákladná a pracná. Ne každý si to může dovolit.
Protože různí uživatelé používají různé strategie aktualizací, je třeba podporovat jak nejnovější verzi, tak všechny předchozí verze. To komplikuje jak proces vývoje, tak proces testování, a přidává vrásky na čelo zákaznické podpoře.
Různost hardwarových platforem
Různé hardwarové platformy jsou problémem, který je do značné míry specifický právě pro nativní kód. Minimálně je nutné sestavovat binární soubory pro každou podporovanou platformu.
V projektu Veeam Agent for Linux se nemůžeme dostat k podpoře alespoň něčeho takového jako je RISC.
Podrobně se na tuto otázku nezastavím. Jen vyznačím hlavní problémy: platformně závislé typy, jako například size_t, zarovnání struktur a byte order.
Statické a/nebo dynamické linkování

Ale otázku „Jak se linkovat s knihovnami – dynamicky nebo staticky?“ je třeba projednat.
Obecně platí, že aplikace C/C++ pod Linuxem používají dynamické linkování. To funguje skvěle, pokud je aplikace sestavena speciálně pro konkrétní distribuci.
Pokud však cílem je pokrýt různorodé distribuce jedním binárním souborem, je nutné se orientovat na nejstarší podporovanou distribuci. Pro nás je to Red Hat 6. Obsahuje gcc 4.4, který ani standard C++11 plně nepodporuje. .
Náš projekt sestavujeme pomocí gcc 6.3, který plně podporuje C++14. Samozřejmě, v takovém případě musíme na Red Hat 6 přibalit knihovny libstdc++ a boost. Nejjednodušší je na ně odkazovat staticky.
Bohužel, ne se všemi knihovnami je možné odkazovat staticky.
Za prvé, systémové knihovny, jako jsou libfuse, libblkid je nutné linkovat dynamicky, aby byla zajištěna jejich kompatibilita s jádrem a jeho moduly.
Za druhé, existují specifika týkající se licencí.
Licence GPL v zásadě povoluje linkování knihoven pouze s opensource kódem. MIT a BSD povolují statické linkování a umožňují zahrnutí knihoven do projektu. Zatímco LGPL jaksi nebrání statickému linkování, ale vyžaduje poskytnutí souborů potřebných pro vazbu veřejnosti.
Obecně použití dynamického linkování zabezpečí, že není třeba nic poskytovat.
Sestavení aplikací C/C++
Pro sestavení aplikací C/C++ pro různé platformy a distribuce stačí vybrat nebo sestavit gcc vhodné verze a použít krosové kompilátory pro specifické architektury a sestavit celý soubor knihoven. Tato práce je zcela realizovatelná, ale velmi náročná. A neexistují žádné záruky, že vybraný kompilátor a knihovny zajistí funkční variantu.
Očividnou výhodou je výrazné zjednodušení infrastruktury, protože celý proces sestavení lze provést na jednom stroji. Navíc stačí sestavit jeden soubor binárních souborů pro jednu architekturu a lze je balit do balíčků pro různé distribuce. Takto jsou sestavovány balíčky veeam pro Veeam Agent for Linux.
Na rozdíl od této možnosti je možné jednoduše připravit build farmu, tedy několik strojů pro sestavení. Každý takový stroj zajistí kompilaci aplikace a sestavení balíčku pro konkrétní distribuci a určitou architekturu. V takovém případě se kompilace provádí nástroji, které připravil distributor. To znamená, že fáze přípravy kompilátoru a výběru knihoven odpadá. Kromě toho lze proces sestavení snadno paralelizovat.
Existuje však i nevýhoda tohoto přístupu: pro každou distribuci v rámci jedné architektury bude nutné sestavit svůj vlastní soubor binárních souborů. Další nevýhodou je, že je třeba spravovat tolik strojů, vyčlenit velké množství diskového prostoru a operační paměti.
Tímto způsobem se sestavují KMOD balíčky modulu jádra veeamsnap pro distribuce Red Hat.
Open Build Service
Kolegové z SUSE se pokusili realizovat určitý zlatý střed v podobě speciálního servisního pro kompilaci aplikací a sestavení balíčků — .
V podstatě se jedná o hypervizor, který vytváří virtuální stroj, instaluje do něj všechny potřebné balíčky, provádí kompilaci aplikace a sestavení balíčku v tomto izolovaném prostředí, po čemž je tento virtuální stroj uvolněn.

Naplánovač implementovaný v OpenBuildService sám určí, kolik virtuálních strojů může spustit pro optimální rychlost sestavení balíčků. Vestavěný mechanismus podpisu sám podepíše balíčky a umístí je do vestavěného repozitáře. Vestavěný systém verzování uchová historii změn a sestavení. Stačí jednoduše přidat do tohoto systému své zdrojové kódy. Ani server nemusíte sami zřizovat, můžete využít otevřený.
Zde je však problém: takový kombinátor se obtížně integruje do stávající infrastruktury. Například verzování není potřeba, již máme svůj pro zdrojové kódy. Mechanismus podpisu se liší: používáme speciální server. Repozitář také není potřeba.
Kromě toho je podpora jiných distribucí — například Red Hat — realizována docela skromně, což je zcela pochopitelné.
Výhodou tohoto servisu je rychlá podpora další verze distribuce SUSE. Před oficiálním oznámením o vydání jsou potřebné balíčky pro kompilaci umisťovány na veřejný repozitář. V seznamu dostupných distribucí na OpenBuildService se objevuje nová verze. Zaškrtneme ji a ta se přidá do plánu kompilace. Tímto způsobem je přidání nové verze distribuce prakticky na jedno kliknutí.
V naší infrastruktuře s využitím OpenBuildService se shromažďuje veškerá rozmanitost KMP balíčků modulu jádra veeamsnap pro distribuce SUSE.
Dále bych se chtěl zaměřit na otázky specifické právě pro moduly jádra.
kernel ABI
Moduly jádra Linuxu se historicky šířily ve formě zdrojových kódů. Je to proto, že tvůrci jádra se nezatěžují péčí o podporu stabilního API pro moduly jádra, a to už vůbec ne na binární úrovni, dále kABI.
Aby bylo možné zkompilovat modul pro vanilkové jádro, jsou nezbytné hlavičkové soubory právě tohoto jádra, a pracovat bude pouze na tomto jádře.
DKMS umožňuje automatizovat proces kompilace modulů při aktualizaci jádra. V důsledku toho uživatelé repozitáře Debian (a jeho mnoha příbuzných) používají moduly jádra buď z repozitáře distributora, nebo kompilované ze zdrojových kódů pomocí DKMS.
Tato situace však příliš nevyhovuje Enterprise segmentu. Distributoři proprietárního kódu chtějí šířit produkt ve formě zkompilovaných binárek.
Administrátoři nechtějí mít vývojové nástroje na produkčních serverech z bezpečnostních důvodů. Distributoři Enterprise Linuxu — jako Red Hat a SUSE — se rozhodli, že pro své uživatele stabilní kABI budou schopni podpořit. V důsledku toho se objevily KMOD balíčky pro Red Hat a KMP balíčky pro SUSE.
Podstata takového řešení je docela jednoduchá. Pro konkrétní verzi distribuce je API jádra zmrazeno. Distributor deklaruje, že používá právě jádro, například 3.10, a provádí pouze opravy a vylepšení, které nijak nezasahují do rozhraní jádra, a moduly zkompilované pro první jádro mohou být použity pro všechna následující bez opětovné kompilace.
Red Hat uvádí, že zajišťuje kABI kompatibilitu pro distribuci po celou dobu životního cyklu. To znamená, že sestavený modul pro RHEL 6.0 (vydaný v listopadu 2010) by také měl fungovat na verzi 6.10 (vydané v červnu 2018). A to je téměř 8 let. Samozřejmě, že úkol je to poměrně složitý.
Zaznamenali jsme několik případů, kdy kvůli problémům s kABI kompatibilitou modul veeamsnap přestal fungovat.
Poté, co se modul veeamsnap, sestavený pro RHEL 7.0, ukázal jako nekompatibilní s jádrem z RHEL 7.5, ale přitom se načítal a zaručeně snižoval výkon serveru, vzdali jsme se používání kABI kompatibility pro RHEL 7 vůbec.
V současné době KMOD balíček pro RHEL 7 obsahuje sestavení pro každou verzi vydání a skript, který zajišťuje načítání modulu.
SUSE k úloze kABI kompatibility přistoupili opatrněji. Zajišťují kABI kompatibilitu pouze v rámci jednoho service packu.
Například vydání SLES 12 proběhlo v září 2014. A SLES 12 SP1 už v prosinci 2015, tedy uplynul necelý rok. I když obě vydání používají jádro 3.12, nejsou kABI kompatibilní. Je zřejmé, že udržovat kABI kompatibilitu po pouhém roce je výrazně jednodušší. Roční cyklus aktualizace jádrového modulu by neměl způsobovat problémy tvůrcům modulů.
V důsledku této politiky SUSE jsme nezjistili žádné problémy s kABI kompatibilitou u našeho modulu veeamsnap. Pravdou je, že počet balíčků pro SUSE je téměř o řád vyšší.
Opravy a backporty
I když se distributoři snaží zajistit kABI kompatibilitu a stabilitu jádra, také se snaží zlepšit výkon a odstranit vady tohoto stabilního jádra.
Přitom kromě vlastní 'opravy chyb' vývojáři jádra enterprise linux sledují změny ve vanilkovém jádře a přenášejí je do svého 'stabilního'.
Někdy to vede k novým .
V posledním vydání Red Hat 6 v jedné z menších aktualizací došlo k chybě. Ta vedla k tomu, že modul veeamsnap zaručeně zhroucil systém při uvolnění snapshotu. Porovnáním zdrojových kódů jádra před a po aktualizaci jsme zjistili, že za vším byl backport. Podobný fix byl proveden ve vanilkovém jádře verze 4.19. Jenže ve vanilkovém jádře tento fix fungoval normálně, zatímco při přenosu do 'stabilního' 2.6.32 vznikl problém se spin-lockem.
Samozřejmě, chyby se objevují u všech a vždy, ale stálo za to přenést kód z 4.19 do 2.6.32, riskovat stabilitu?.. Nejsem si jistý...
Nejhorší je, když se k přetahování lanem "stabilita" <-> "modernizace" přidává marketing. Marketingové oddělení potřebuje, aby jádro aktualizovaného distribuce bylo stabilní, na jedné straně, a zároveň mělo lepší výkon a novou funkcionalitu. To vede k podivným kompromisům.
Když jsem se pokusil sestavit modul na jádře 4.4 od SLES 12 SP3, s překvapením jsem zjistil v něm funkčnost z vanilkové 4.8. Podle mého názoru implementace blokového vstupu/výstupu jádra 4.4 od SLES 12 SP3 více odpovídá jádru 4.8 než předchozímu stabilnímu vydání 4.4 od SLES12 SP2. Jaký byl procentuální podíl přeneseného kódu z jádra 4.8 do SLES-ovského 4.4 pro SP3, si netroufám soudit, ale říkat, že jádro je stále stabilní 4.4, se mi těžko říká.
Nejpříjemnější na tom je, že při psaní modulu, který by fungoval stejně dobře na různých jádrech, už nelze spoléhat na verzi jádra. Je nutné brát v úvahu i distribuci. Je dobré, že někdy lze využít definici, která se objeví s novou funkcionalitou, ale tato možnost se neobjevuje vždy.
V důsledku toho kód nabírá podivné direktivy podmínkového kompilace.
Existují také patche, které mění zdokumentované API jádra.
Narazil jsem na distribuci 5.16 a byl jsem velmi překvapen, když jsem viděl, že volání lookup_bdev v této verzi jádra změnilo seznam vstupních parametrů.
Abych to sestavil, musel jsem do makefile přidat skript, který kontroluje, zda má funkce lookup_bdev parametr mask.
Podpis modulů jádra
Ale vraťme se k otázce distribuce balíčků.
Jednou z výhod stabilního kABI je, že moduly jádra ve formě binárního souboru mohou být podepsány. V tomto případě si vývojář může být jistý, že modul nebyl náhodně poškozen nebo úmyslně změněn. Ověřit to lze příkazem modinfo.
Distribuce Red Hat a SUSE umožňují ověřit podpis modulu a načíst jej, pouze pokud je v systému registrován odpovídající certifikát. Certifikát je veřejný klíč, kterým se modul podepisuje. Distribuujeme ho jako samostatný balíček.
Problém je v tom, že certifikáty mohou být buď integrované v jádře (používají je distributoři), nebo musí být zapsány do nevolatilní paměti EFI pomocí utilit. mokutil. Nástroj mokutil při instalaci certifikátu žádá o restartování systému a ještě před spuštěním jádra operačního systému nabízí administrátorovi povolit načtení nového certifikátu.
Takže přidání certifikátu vyžaduje fyzický přístup administrátora k systému. Pokud je stroj umístěn v cloudu nebo jen v vzdáleném serveru a přístup je pouze přes síť (například přes ssh), bude přidání certifikátu nemožné.
EFI na virtuálních strojích
I když je EFI podporováno prakticky všemi výrobci základních desek, může si administrátor při instalaci systému neuvědomit potřebu EFI, a ten může být vypnut.
Ne všichni hypervizory podporují EFI. VMWare vSphere podporuje EFI od verze 5.
Microsoft Hyper-V také získal podporu EFI počínaje Hyper-V pro Windows Server 2012R2.
Ve výchozí konfiguraci je však tato funkce pro Linux stroje vypnutá, což znamená, že certifikát nelze nainstalovat.
Ve vSphere 6.5 je možnost Secure Boot možno nastavit pouze ve staré verzi webového rozhraní, které funguje přes Flash. Webové UI na HTML-5 se zatím značně opozdilo.
Experimentální distribuce
A nakonec se podíváme na otázku experimentálních distribucí a distribucí bez oficiální podpory. Na jedné straně se takové distribuce těžko nacházejí na serverech vážných organizací. Tyto distribuce nemají oficiální podporu. Proto nelze zajišťovat technickou podporu produktu na takové distribuci.
Tyto distribuce však stále více slouží jako pohodlná platforma pro vyzkoušení nových experimentálních řešení. Například Fedora, OpenSUSE Tumbleweed nebo nestabilní verze Debiana. Jsou poměrně stabilní. Vždy obsahují nové verze programů a nové jádro. Za rok se tato experimentální funkce může objevit ve vylepšené verzi RHEL, SLES nebo Ubuntu.
Takže pokud něco nefunguje na experimentální distribuci — je to důvod k tomu, abyste se zabývali problémem a vyřešili ho. Je třeba být připraven na to, že tato funkce se brzy objeví na produkčních serverech uživatelů.
Aktuální seznam oficiálně podporovaných distribucí pro verzi 3.0 si můžete prohlédnout . Skutečný seznam distribucí, na kterých náš produkt dokáže fungovat, je však mnohem širší.
Osobně mě zajímal experiment s OS "Elbrus". Po úpravě balíčku veeam se náš produkt nainstaloval a začal fungovat. O tomto experimentu jsem psal na Habr v .
Podpora nových distribucí pokračuje. Očekáváme vydání verze 4.0. Beta by měla být co nevidět, tak sledujte !
Zdroj: habr.com
