Linux polivalent: cum să lucrezi pe orice distribuție

Linux polivalent: cum să lucrezi pe orice distribuție

Crearea unei aplicații de backup care să funcționeze pe orice distribuție este o sarcină complicată. Pentru a asigura funcționarea Veeam Agent for Linux pe distribuțiile de la Red Hat 6 și Debian 6, până la OpenSUSE 15.1 și Ubuntu 19.04, este necesar să rezolvi o serie de probleme, mai ales având în vedere că software-ul conține un modul de kernel.

Articolul a fost creat pe baza materialelor prezentate la conferință LinuxPiter 2019.

Linux nu este doar un sistem de operare popular. De fapt, este o platformă pe care poți construi ceva unic, ceva propriu. Datorită acestui fapt, Linux are multe distribuții care se diferențiază prin setul de componente software. Și aici apare problema: pentru ca produsul software să funcționeze pe orice distribuție, trebuie să consideri specificitățile fiecărei distribuții.

Gestionare de pachete. .deb vs .rpm

Să începem cu problema evidentă a distribuirii produsului pentru diferite distribuții.
Cel mai tipic mod de a distribui produsele software este de a publica un pachet în repository, astfel încât managerul de pachete integrat în sistem să-l poată instala de acolo.
Totuși, avem două formate populare de pachete: rpm și deb. Asta înseamnă că va trebui să susținem fiecare.

În lumea pachetelor deb, nivelul de compatibilitate este uimitor. Același pachet se instalează și funcționează la fel de bine atât pe Debian 6, cât și pe Ubuntu 19.04. Standarde ale procesului de construire a pachetelor și lucrul cu acestea, stabilite în vechile distribuții Debian, rămân relevante și în noile Linux Mint și elementary OS. Prin urmare, în cazul Veeam Agent for Linux, este suficient un singur pachet deb pentru fiecare platformă hardware.

În schimb, în lumea pachetelor rpm, diferențele sunt mari. În primul rând, din cauză că există doi distribuitori complet independenți, Red Hat și SUSE, pentru care compatibilitatea nu este necesară. În al doilea rând, acești distribuitori au distribuții cu suport tehnic și experimentale. Între acestea, compatibilitatea nu este necesară nici ea. Am ajuns la situația în care pentru el6, el7 și el8 avem pachete proprii. Un pachet separat pentru Fedora. Pachete pentru SLES11 și 12 și unul separat pentru openSUSE. Problema principală sunt dependențele și numele pachetelor.

Problema dependențelor

Din păcate, aceleași pachete apare frecvent sub denumiri diferite în diferite distribuții. Mai jos se află o listă incompletă a dependențelor pachetului veeam.

Pentru EL7:
Pentru 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

Ca urmare, lista dependențelor devine unică pentru distribuție.

Mai rău este când o versiune actualizată începe să se ascundă sub un nume vechi de pachet.

Exemplu:

În Fedora 24 s-a actualizat pachetul ncurses de la versiunea 5 la versiunea 6. Produsul nostru a fost compilat exact cu versiunea 5, pentru a asigura compatibilitatea cu distribuțiile vechi. Pentru a folosi vechea versiune 5 a bibliotecii pe Fedora 24, a fost necesar să se utilizeze pachetul ncurses-compat-libs.

Ca rezultat, pentru Fedora apar două pachete, cu dependențe diferite.

Apoi devine mai interesant. După o nouă actualizare a distribuției, pachetul ncurses-compat-libs cu versiunea 5 a bibliotecii devine inaccesibil. Pentru distribuitor, este costisitor să tragă biblioteci vechi în noua versiune a distribuției. După un timp, problema s-a repetat și în distribuțiile SUSE.

Ca urmare, pentru unele distribuții a fost necesară abandonarea dependenței exprese de ncurses-libs, iar produsul a fost ajustat pentru a putea funcționa cu orice versiune a bibliotecii.

Apropo, în versiunea a 8-a Red Hat nu mai există metapachetul python, care se referea la vechiul bun python 2.7. Există python2 și python3.

Alternativa managerelor de pachete

Problema dependențelor este veche și bine cunoscută. Să ne amintim măcar de Dependency hell.
A uni diverse biblioteci și aplicații astfel încât toate să funcționeze stabil și să nu intre în conflict este, de fapt, problema pe care orice distribuitor Linux încearcă să o rezolve.

Complet diferit încearcă să abordeze această problemă managerul de pachete Snappy de la Canonical. Ideea principală: aplicația rulează într-un sandbox izolat și protejat de sistemul principal. Dacă aplicației îi sunt necesare biblioteci, acestea sunt furnizate împreună cu aplicația în sine.

Flatpak permite de asemenea rularea aplicațiilor în sandbox, folosind Linux Containers. Conceptul de sandbox este folosit și de AppImage.

Aceste soluții permit crearea unui singur pachet pentru orice distribuție. În cazul Flatpak instalarea și rularea aplicației sunt posibile chiar și fără cunoștința administratorului.

Principala problemă este că nu toate aplicațiile pot funcționa în sandbox. Unele au nevoie de acces direct la platformă. Nu mai vorbesc despre modulele kernel-ului care depind strict de kernel și nu se încadrează în conceptul de sandbox.

O a doua problemă este că distribuțiile populare în mediul enterprise de la Red Hat și SUSE nu suportă încă Snappy și Flatpak.

Din acest motiv, Veeam Agent for Linux nu este disponibil pe snapcraft.io nici pe flathub.org.

În încheierea acestei întrebări despre managerii de pachete, voi menționa că există opțiunea de a renunța complet la managerii de pachete, combinând într-un singur pachet fișierele binare și un script pentru instalarea acestora.

Acest bundle permite crearea unui pachet comun pentru diferite distribuții și platforme, efectuând un proces de instalare interactiv, realizând personalizările necesare. M-am întâlnit cu astfel de pachete pentru Linux doar de la VMware.

Problema actualizărilor

Linux polivalent: cum să lucrezi pe orice distribuție
Chiar dacă toate problemele de dependență sunt rezolvate, programul poate funcționa diferit pe aceeași distribuție. Motivul sunt actualizările.

Există 3 strategii de actualizare:

  • Cea mai simplă este să nu actualizezi niciodată. Configurezi serverul și uiți de el. De ce sunt necesare actualizările dacă totul funcționează? Problemele încep la primul apel în serviciul de suport tehnic. Creatorul distribuției susține doar versiunea actualizată.
  • Poți avea încredere în distribuitor și să configurezi actualizări automate. În acest caz, apelul în serviciul de suport este probabil imediat după o actualizare eșuată.
  • Opțiunea de actualizare manuală doar după testarea pe infrastructura de testare este cea mai sigură, dar și costisitoare și laborioasă. Nu toți își permit acest lux.

Deoarece diferiți utilizatori aplică diferite strategii de actualizare, este necesar să susținem atât cea mai recentă versiune, cât și toate versiunile anterioare. Acest lucru complică atât procesul de dezvoltare, cât și cel de testare, adăugând dureri de cap echipei de suport.

Diversitatea platformelor hardware

Diverse platforme hardware reprezintă o problemă, în mare parte specifică codului nativ. Cel puțin, trebuie să compilăm binare pentru fiecare platformă suportată.

În proiectul Veeam Agent for Linux, nu reușim să suportăm nimic de genul RISC.

Nu voi detalia prea mult asupra acestei probleme. Voi sublinia doar principalele dificultăți: tipuri dependente de platformă, cum ar fi size_t, alinierea structurilor și ordinea byte-urilor.

Linierea statică și/sau dinamică

Linux polivalent: cum să lucrezi pe orice distribuție
Întrebarea "Cum să ne leagă de biblioteci – dinamic sau static?" merită discutată.

În general, aplicațiile C/C++ pentru Linux folosesc legătura dinamică. Acest lucru funcționează excelent dacă aplicația este compilată special pentru o distribuție specifică.

Dacă însă dorim să acoperim diferite distribuții cu un singur fișier binar, va trebui să ne orientăm după cea mai veche distribuție suportată. Pentru noi, aceasta este Red Hat 6. Aceasta conține gcc 4.4, care nici măcar nu suportă complet standardul C++11. complet.

Noi compilăm proiectul nostru folosind gcc 6.3, care suportă complet C++14. Evident, în acest caz, pe Red Hat 6 trebuie să aducem biblioteca libstdc++ și boost împreună. Cel mai simplu este să ne legăm static de acestea.

Dar din păcate, nu cu toate bibliotecile se poate face legătura statică.

În primul rând, bibliotecile de sistem, cum ar fi libfuse, libblkid trebuie legate dinamic, pentru a fi siguri că sunt compatibile cu kernelul și modulele sale.

În al doilea rând, există o nuanță legată de licențe.

Licența GPL permite în principiu legarea bibliotecilor doar cu cod sursă open source. MIT și BSD permit legarea statică și permit inclusiv includerea bibliotecilor în proiect. Pe de altă parte, LGPL nu pare să se opună legării statice, dar solicită furnizarea fișierelor necesare pentru legare în mod liber.

În general, utilizarea legării dinamice va asigura că nu trebuie să oferim nimic.

Compilarea aplicațiilor C/C++

Pentru a compila aplicații C/C++ pentru diferite platforme și distribuții, este suficient să alegem sau să compilăm un gcc de versiune corespunzătoare și să folosim compilatoare încrucișate pentru arhitecturi specifice, compilând toată gama de biblioteci. Acest lucru este realizabil, dar destul de complicat. Și nu există nicio garanție că compilatorul și bibliotecile alese vor produce o variantă funcțională.

Un avantaj evident: infrastructura este mult simplificată, deoarece întregul proces de compilare poate fi efectuat pe o singură mașină. În plus, este suficient să compilăm un set de fișiere binare pentru o singură arhitectură și le putem ambala în pachete pentru diferite distribuții. Așa sunt compilate pachetele veeam pentru Veeam Agent for Linux.

În contrast cu această opțiune, se poate prepara pur și simplu o fermă de build, adică mai multe mașini pentru compilare. Fiecare astfel de mașină va asigura compilarea aplicației și construirea pachetului pentru o distribuție specifică și o arhitectură anume. În acest caz, compilarea se realizează cu instrumentele pregătite de distribuitor. Asta înseamnă că etapa de pregătire a compilatorului și selecția bibliotecilor este eliminată. În plus, procesul de construire poate fi ușor paralelizat.

Există, totuși, și un dezavantaj al acestei abordări: pentru fiecare distribuție în cadrul aceleași arhitecturi va trebui să se construiască propriul set de fișiere binare. De asemenea, un alt dezavantaj este că un astfel de număr de mașini necesită întreținere, precum și alocarea unui volum mare de spațiu pe disc și memorie RAM.

Astfel se construiesc pachetele KMOD pentru modulul kernel veeamsnap pentru distribuțiile Red Hat.

Open Build Service

Colegi de la SUSE au încercat să implementeze un fel de soluție de mijloc sub forma unui serviciu special pentru compilarea aplicațiilor și construirea pachetelor — openbuildservice.

În esență, acesta este un hipervizor care creează o mașină virtuală, instalează toate pachetele necesare în aceasta, execută compilarea aplicației și construirea pachetului în acest mediu izolat, după care respectiva mașină virtuală este eliberată.

Linux polivalent: cum să lucrezi pe orice distribuție

Planificatorul implementat în OpenBuildService va determina singur câte mașini virtuale poate porni pentru o viteză optimă de construire a pachetelor. Mecanismul încorporat de semnare va semna automat pachetele și le va încărca în depozitul încorporat. Sistemul de control al versiunilor va păstra istoricul modificărilor și construcțiilor. Rămâne doar să adăugați sursele dvs. în acest sistem. Nu este necesar nici să operați un server, ci se poate folosi un serviciu deschis.

Aici, totuși, există o problemă: un astfel de combinator este greu de integrat în infrastructura existentă. De exemplu, controlul versiunilor nu este necesar, deoarece avem deja unul pentru surse. Mecanismul de semnare este diferit: se utilizează un server special. De asemenea, depozitul nu este necesar.

În plus, suportul pentru alte distribuții — de exemplu, Red Hat — este realizat destul de slab, ceea ce este complet explicabil.

Un avantaj al acestui serviciu este suportul rapid pentru următoarea versiune a distribuției SUSE. Până la anunțul oficial al lansării, pachetele necesare pentru compilare sunt disponibile pe repository-ul public. În lista distribuțiilor disponibile pe OpenBuildService apare un nou element. Bifăm căsuța și acesta este adăugat în planul de construire. Astfel, adăugarea unei noi versiuni a distribuției se face practic cu un singur clic.

În infrastructura noastră folosind OpenBuildService, se compilează toată diversitatea pachetelor KMP ale modulului kernel veeamsnap pentru distribuțiile SUSE.

Aș dori să mă opresc acum asupra unor întrebări specifice pentru modulele kernel.

kernel ABI

Modulele kernel Linux au fost distribuite istoric sub formă de cod sursă. Motivul este că creatorii kernel-ului nu se încarcă cu grija de a menține un API stabil pentru modulele kernel, cu atât mai puțin la nivel binar, numit mai departe kABI.

Pentru a compila un modul pentru un kernel vanilla, sunt necesare neapărat header-urile exact pentru acest kernel, iar acesta va funcționa doar pe acest kernel.

DKMS permite automatizarea procesului de compilare a modulelor la actualizarea kernel-ului. Ca urmare, utilizatorii repository-ului Debian (și al numeroaselor sale derivații) folosesc module kernel din repository-ul distribuitorului sau compilate din surse cu ajutorul DKMS.

Cu toate acestea, o astfel de situație nu este foarte satisfăcătoare pentru segmentul Enterprise. Distribuitorii de cod proprietar doresc să ofere produsul sub formă de binare compilate.

Administratorii nu doresc să păstreze instrumente de dezvoltare pe serverele de producție din motive de securitate. Distribuitorii Enterprise Linux — precum Red Hat și SUSE — au decis că pentru utilizatorii lor pot susține un kABI stabil. Ca urmare, au apărut pachete KMOD pentru Red Hat și pachete KMP pentru SUSE.

Esenta unei astfel de soluții este destul de simplă. Pentru o versiune specifică a distribuției, API-ul kernel-ului este înghețat. Distribuitorul declară că folosește specific kernel-ul, de exemplu, 3.10 și aduce doar corecții și îmbunătățiri care nu afectează interfețele kernel-ului, iar modulele compilate pentru kernel-ul inițial pot fi utilizate pentru toate cele ulterioare fără recompilare.

Red Hat declară că oferă compatibilitate kABI pentru distribuțiile sale pe întreaga durată de viață. Asta înseamnă că un modul compilat pentru rhel 6.0 (lansat în noiembrie 2010) ar trebui să funcționeze și pe versiunea 6.10 (lansată în iunie 2018). Asta înseamnă aproape 8 ani. Din păcate, această sarcină este destul de complexă.
Am înregistrat câteva cazuri în care, din cauza problemelor legate de compatibilitatea kABI, modulul veeamsnap a încetat să funcționeze.

După ce modulul veeamsnap, compilat pentru RHEL 7.0, s-a dovedit a fi incompatibil cu nucleul din RHEL 7.5, chiar dacă se încărca și provoca prăbușirea serverului, am renunțat la utilizarea compatibilității kABI pentru RHEL 7 în întregime.

În acest moment, pachetul KMOD pentru RHEL 7 include o compilare pentru fiecare versiune de lansare și un script care asigură încărcarea modulului.

SUSE a abordat problema compatibilității kABI cu mai multă precauție. Aceștia oferă compatibilitate kABI doar în cadrul unui service pack.

De exemplu, lansarea SLES 12 a avut loc în septembrie 2014. Iar SLES 12 SP1 a fost lansat în decembrie 2015, adică a trecut puțin peste un an. Deși ambele lansări folosesc nucleul 3.12, ele nu sunt compatibile kABI. Este evident că menținerea compatibilității kABI timp de doar un an este semnificativ mai ușoară. Ciclu anual de actualizare a modulului nucleului nu ar trebui să creeze probleme pentru creatorii de module.

Ca urmare a acestei politici SUSE, nu am înregistrat nicio problemă legată de compatibilitatea kABI pentru modulul nostru veeamsnap. Totuși, numărul de pachete pentru SUSE este aproape cu un ordin de mărime mai mare.

Patch-uri și backporturi

Deși distribuitorii încearcă să asigure compatibilitatea kABI și stabilitatea nucleului, aceștia încearcă de asemenea să îmbunătățească performanța și să elimine defectele acestui nucleu stabil.

În plus față de propriile „corecții de erori”, dezvoltatorii nucleului enterprise linux urmăresc schimbările din nucleul vanilă și le transferă în „stabilul” lor.

Uneori, acest lucru duce la noi erori.

În ultima lansare a Red Hat 6, într-o actualizare minoră, a fost o eroare. Aceasta a dus la faptul că modulul veeamsnap prăbușea sistemul atunci când se elibera un snapshot. Comparând sursele nucleului înainte și după actualizare, am constatat că totul era din cauza unui backport. O corectare similară a fost efectuată în nucleul vanilă versiunea 4.19. Din păcate, în nucleul vanilă această corectare a funcționat normal, dar la transferul ei în „stabilul” 2.6.32 a apărut o problemă cu blocarea spin.

Desigur, toți fac greșeli și întotdeauna, dar a meritat să tragi codul din 4.19 în 2.6.32, riscând stabilitatea?.. Nu sunt sigur…

Cel mai rău este când marketingul se implică în tragerea frânghiei „stabilitate” „modernizare”. Departamentul de marketing are nevoie ca nucleul distribuției actualizate să fie stabil, pe de o parte, și în același timp să ofere o performanță mai bună și noi funcționalități. Acest lucru duce la compromisuri ciudate.

Când am încercat să compilez un modul pe nucleul 4.4 de la SLES 12 SP3, am descoperit cu surprindere că are funcționalități din vanilia 4.8. Din punctul meu de vedere, implementarea I/O-ului pe blocuri a nucleului 4.4 de la SLES 12 SP3 se aseamănă mai mult cu nucleul 4.8 decât cu versiunea anterioară stabilă 4.4 a nucleului de la SLES 12 SP2. Nu mă voi pronunța asupra procentului de cod din nucleul 4.8 transferat în 4.4 de la SLES pentru SP3, dar nu pot să spun că nucleul este încă același stabil 4.4.

Cel mai neplăcut aspect este că, atunci când scrii un modul care să funcționeze bine pe diferite nuclee, nu mai poți să te bazezi pe versiunea nucleului. Trebuie să iei în considerare și distribuția. Este bine că uneori poți să te bazezi pe o definire care apare împreună cu noua funcționalitate, însă această opțiune nu este întotdeauna disponibilă.

Ca urmare, codul devine plin de directive de compilare condiționată ciudate.

De asemenea, apar și patch-uri care schimbă API-ul documentat al nucleului.
Am dat peste o distribuție KDE neon 5.16 și am fost foarte surprins să văd că apelul lookup_bdev în această versiune a nucleului a schimbat lista parametrilor de intrare.

Pentru a compila, a trebuit să adaug un script în makefile care verifică dacă parametrul mask este prezent în funcția lookup_bdev.

Semnătura modulelor nucleului

Dar să revenim la întrebarea distribuției pachetelor.

Unul dintre avantajele stabilității kABI este că modulele nucleului sub formă de fișier binar pot fi semnate. În acest caz, dezvoltatorul poate fi sigur că modulul nu a fost deteriorat accidental sau modificat intenționat. Acest lucru poate fi verificat cu comanda modinfo.

Distribuțiile Red Hat și SUSE permit verificarea semnăturii modulului și încărcarea acestuia doar dacă certificatul coresponzător este înregistrat în sistem. Certificatul este cheia publică cu care este semnat modulul. O distribuim sub formă de pachet separat.

Problema aici este că certificatele pot fi fie integrate în kernel (folosite de distribuitori), fie trebuie scrise în memoria non-volatilă EFI folosind o utilitară. mokutil. Utilitară mokutil la instalarea certificatului solicită o repornire a sistemului și, înainte de a încărca kernelul sistemului de operare, îi oferă administratorului opțiunea de a permite încărcarea noului certificat.

Astfel, adăugarea certificatului necesită acces fizic al administratorului la sistem. Dacă mașina se află undeva în cloud sau pur și simplu într-un server remote și accesul este posibil doar prin rețea (de exemplu, prin ssh), atunci adăugarea certificatului va fi imposibilă.

EFI pe mașinile virtuale

Deși EFI este susținut de majoritatea producătorilor de plăci de bază de mult timp, la instalarea sistemului, administratorul poate să nu ia în considerare necesitatea EFI, iar acesta poate fi dezactivat.

Nu toate hypervizorii susțin EFI. VMWare vSphere susține EFI începând cu versiunea 5.
Microsoft Hyper-V a primit de asemenea suport pentru EFI începând cu Hyper-V pentru Windows Server 2012R2.

Cu toate acestea, în configurația default, această funcționalitate pentru mașinile Linux este dezactivată, ceea ce înseamnă că certificatul nu poate fi instalat.

În vSphere 6.5, opțiunea Secure Boot poate fi activată doar în vechea versiune a interfeței web, care funcționează prin Flash. Interfața web pe HTML-5 este încă foarte în urmă.

Distribuții experimentale

Și în final, să discutăm despre problema distribuțiilor experimentale și a celor fără suport oficial. Pe de o parte, aceste distribuții sunt puțin probabile să fie întâlnite pe serverele organizațiilor serioase. Nu există suport oficial pentru aceste distribuții. Prin urmare, nu se poate asigura suport tehnic pentru un produs pe o astfel de distribuție.

Cu toate acestea, aceste distribuții devin o platformă convenabilă pentru testarea unor soluții experimentale noi. De exemplu, Fedora, OpenSUSE Tumbleweed sau versiunile instabile ale Debian. Acestea sunt destul de stabile. Ele au întotdeauna cele mai noi versiuni ale programelor și mereu un kernel nou. În decurs de un an, această funcționalitate experimentală poate apărea în RHEL, SLES sau Ubuntu actualizate.

Așa că, dacă ceva nu funcționează pe o distribuție experimentală – este un motiv să investighezi problema și să o rezolvi. Trebuie să fii pregătit că această funcționalitate va apărea în curând pe serverele de producție ale utilizatorilor.

Lista actuală a distribuțiilor oficial acceptate pentru versiunea 3.0 o puteți studia aici. Însă lista reală a distribuțiilor pe care produsul nostru poate funcționa este mult mai extinsă.

Personal, am fost interesat de experimentul cu sistemul de operare „Elbrus”. După actualizarea pachetului veeam, produsul nostru s-a instalat și a început să funcționeze. Despre acest experiment am scris pe Habr în pe care l-ați citit.

Susținerea noilor distribuții continuă. Așteptăm lansarea versiunii 4.0. Beta ar trebui să apară în curând, așa că urmați whats-new!

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster