Linux è multifaccettato: come lavorare su qualsiasi distribuzione.

Linux è multifaccettato: come lavorare su qualsiasi distribuzione.

Creare un'applicazione di backup che funzioni su qualsiasi distribuzione è una sfida non semplice. Per garantire il funzionamento di Veeam Agent for Linux su distribuzioni da Red Hat 6 a Debian 6, fino a OpenSUSE 15.1 e Ubuntu 19.04, è necessario affrontare una serie di problemi, soprattutto considerando che il prodotto include un modulo del kernel.

L'articolo è stato creato sulla base di un intervento alla conferenza LinuxPiter 2019.

Linux non è solo uno dei sistemi operativi più popolari. In realtà, è una piattaforma su cui si può costruire qualcosa di unico, di proprio. Per questo motivo, Linux presenta molte distribuzioni, che si differenziano per il set di componenti software. Qui si presenta un problema: affinché un prodotto software funzioni su qualsiasi distribuzione, è necessario tenere conto delle specificità di ciascuna.

Gestori pacchetti: .deb vs .rpm

Iniziamo con il problema evidente della distribuzione del prodotto per le diverse distribuzioni.
Il modo più comune di distribuire prodotti software è caricare un pacchetto su un repository affinché il gestore pacchetti integrato nel sistema possa installarlo da lì.
Tuttavia, abbiamo due formati di pacchetti popolari: rpm e deb. Quindi, dovremo sostenere ognuno.

Nel mondo dei pacchetti deb, il livello di compatibilità è impressionante. Lo stesso pacchetto si installa e funziona altrettanto bene su Debian 6 e Ubuntu 19.04. Gli standard del processo di creazione e gestione dei pacchetti, stabiliti nelle vecchie distribuzioni Debian, rimangono attuali anche nelle moderne Linux Mint e elementary OS. Pertanto, per Veeam Agent for Linux è sufficiente un solo pacchetto deb per ogni piattaforma hardware.

Nel mondo dei pacchetti rpm, invece, ci sono grandi differenze. In primo luogo, perché ci sono due distributori completamente indipendenti, Red Hat e SUSE, per i quali non è necessaria alcuna compatibilità. In secondo luogo, questi distributori hanno distribuzioni con supporto tecnico ed altre sperimentali. Tra di loro, la compatibilità non è necessaria. Risultiamo avere pacchetti diversi per el6, el7 ed el8. Un pacchetto separato per Fedora. Pacchetti per SLES11 e 12 e uno separato per openSUSE. Il problema principale riguarda le dipendenze e i nomi dei pacchetti.

Problema delle dipendenze

Purtroppo, gli stessi pacchetti si trovano spesso con nomi diversi in distribuzioni diverse. Di seguito un elenco non completo delle dipendenze del pacchetto veeam.

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

Di conseguenza, l'elenco delle dipendenze risulta unico per la distribuzione.

Può essere peggio quando una versione aggiornata si nasconde dietro un vecchio nome di pacchetto.

Esempio:

In Fedora 24, il pacchetto è stato aggiornato ncurses dalla versione 5 alla versione 6. Il nostro prodotto è stato compilato proprio con la versione 5, per garantire la compatibilità con le vecchie distribuzioni. Per utilizzare la vecchia versione 5 della libreria su Fedora 24, è stato necessario usare il pacchetto ncurses-compat-libs.

Pertanto, per Fedora, appaiono due pacchetti, con dipendenze diverse.

Diventa più interessante. Dopo un ulteriore aggiornamento della distribuzione, il pacchetto ncurses-compat-libs con la versione 5 della libreria risulta non disponibile. È oneroso per il distributore portare le vecchie librerie nella nuova versione della distribuzione. Dopo un po', il problema si è ripresentato anche nelle distribuzioni SUSE.

Di conseguenza, per alcune distribuzioni è stato necessario rinunciare alla dipendenza esplicita da ncurses-libs, e modificare il prodotto in modo che potesse funzionare con qualsiasi versione della libreria.

A proposito, nella versione 8 di Red Hat non c'è più il meta-pacchetto python, che faceva riferimento al vecchio caro python 2.7. C'è python2 e python3.

Alternativa ai gestori di pacchetti

Il problema delle dipendenze è vecchio e ormai noto. Pensiamo almeno alla dipendenza infernale.
Unire diverse librerie e applicazioni in modo che funzionino stabilmente e non conflittualmente è, in effetti, il compito che ogni distributore Linux cerca di risolvere.

In modo completamente diverso, il gestore di pacchetti Snappy di Canonical. L'idea principale: l'applicazione viene eseguita in un sandbox isolato e protetto dal sistema principale. Se l'applicazione necessita di librerie, queste vengono fornite insieme all'applicazione stessa.

Flatpak permette anche di eseguire applicazioni in un sandbox utilizzando i contenitori Linux. L'idea del sandbox è usata anche da AppImage.

Queste soluzioni permettono di creare un unico pacchetto per qualsiasi distribuzione. Nel caso di Flatpak l'installazione e l'esecuzione dell'applicazione sono possibili anche senza la conoscenza dell'amministratore.

Il problema principale è che non tutte le applicazioni possono funzionare in un sandbox. Alcune necessitano di accesso diretto alla piattaforma. Non parliamo nemmeno dei moduli del kernel, che dipendono rigidamente dal kernel e non si adattano affatto al concetto di sandbox.

Il secondo problema è che le distribuzioni popolari nell'ambito enterprise di Red Hat e SUSE non supportano ancora Snappy e Flatpak.

Pertanto, Veeam Agent per Linux non è disponibile né su snapcraft.io né su flathub.org..

In conclusione, riguardo ai gestori di pacchetti, vorrei notare che esiste la possibilità di rinunciare completamente ai gestori di pacchetti, combinando in un singolo pacchetto file binari e script per la loro installazione.

Tale bundle consente di creare un unico pacchetto per diverse distribuzioni e piattaforme, offrendo un processo di installazione interattivo con la necessaria personalizzazione. Ho incontrato pacchetti di questo tipo per Linux solo da VMware.

Il problema degli aggiornamenti.

Linux è multifaccettato: come lavorare su qualsiasi distribuzione.
Anche se tutti i problemi di dipendenze sono stati risolti, il programma può funzionare in modo diverso sulla stessa distribuzione. Questo è dovuto agli aggiornamenti.

Esistono 3 strategie di aggiornamento:

  • La più semplice è non aggiornare mai. Hai configurato il server e te ne sei dimenticato. Perché aggiornare se tutto funziona? I problemi iniziano al primo contatto con il supporto tecnico. L'autore della distribuzione supporta solo la release aggiornata.
  • È possibile fidarsi del distributore e impostare l'aggiornamento automatico. In tal caso, una chiamata al servizio di assistenza è probabile subito dopo un aggiornamento fallito.
  • L'opzione di aggiornamento manuale, applicata solo dopo una prova su un'infrastruttura di test, è la più sicura, ma costosa e laboriosa. Non tutti possono permettersi questa soluzione.

Poiché gli utenti adottano diverse strategie di aggiornamento, è necessario supportare sia l'ultima versione che tutte le versioni precedenti. Questo complica sia il processo di sviluppo che quello di testing, aumentando il carico di lavoro per il servizio di assistenza.

Diversità delle piattaforme hardware

Le diverse piattaforme hardware rappresentano un problema, particolarmente specifico per il codice nativo. Al minimo, è necessario raccogliere i binari per ciascuna piattaforma supportata.

Nel progetto Veeam Agent for Linux non possiamo ancora supportare nulla di RISC.

Non mi soffermerò su questo argomento. Indicherò solo i principali problemi: tipi dipendenti dalla piattaforma, come size_t, allineamento delle strutture e byte order.

Linking statico e/o dinamico

Linux è multifaccettato: come lavorare su qualsiasi distribuzione.
Ecco una domanda da discutere: «Come effettuare il linking con le librerie — in modo dinamico o statico?»

In genere, le applicazioni C/C++ su Linux utilizzano il linking dinamico. Questo funziona bene se l'applicazione è costruita specificamente per una distribuzione concreta.

Se invece l'obiettivo è coprire diverse distribuzioni con un unico file binario, bisogna orientarsi sulla distribuzione più vecchia supportata. Per noi, questa è Red Hat 6. Essa include gcc 4.4, che non supporta nemmeno lo standard C++11. completamente.

Compiliamo il nostro progetto usando gcc 6.3, che supporta completamente C++14. Naturalmente, in questo caso, su Red Hat 6 dobbiamo portare con noi le librerie libstdc++ e boost. È più semplice linkarle staticamente.

Purtroppo, non tutte le librerie possono essere collegate staticamente.

In primo luogo, le librerie di sistema, come libfuse, libblkid devono essere collegate dinamicamente, per garantire la compatibilità con il kernel e i suoi moduli.

In secondo luogo, c'è una questione legata alle licenze.

La licenza GPL consente di collegare le librerie solo con codice open source. MIT e BSD consentono il linking statico e permettono di includere librerie nel progetto. D'altra parte, l'LGPL non sembra opporsi al linking statico, ma richiede di fornire accesso ai file necessari per il linking.

In generale, l'uso del linking dinamico proteggerà dall'obbligo di fornire qualcosa.

Compilazione di applicazioni C/C++

Per compilare applicazioni C/C++ per diverse piattaforme e distribuzioni, è sufficiente selezionare o costruire una versione adeguata di gcc e utilizzare i cross-compiler per architetture specifiche, raccogliendo così l'intero set di librerie. Questo lavoro è realizzabile, ma piuttosto laborioso. Inoltre, non ci sono garanzie che il compilatore e le librerie scelte garantiranno un'opzione funzionante.

Un chiaro vantaggio: l'infrastruttura è notevolmente semplificata, poiché l'intero processo di compilazione può essere eseguito su una sola macchina. Inoltre, è sufficiente creare un unico set di file binari per un'architettura e poi impacchettarli in pacchetti per diverse distribuzioni. Questo è il modo in cui vengono creati i pacchetti veeam per Veeam Agent for Linux.

In questo caso, è possibile preparare una farm di build, ovvero un insieme di macchine per la compilazione. Ogni macchina si occuperà della compilazione dell'applicazione e della creazione del pacchetto per una specifica distribuzione e architettura. In questo modo, la compilazione viene effettuata utilizzando gli strumenti predisposti dal distributore. Di conseguenza, la fase di preparazione del compilatore e la selezione delle librerie non sono necessarie. Inoltre, il processo di compilazione può essere facilmente parallelizzato.

Tuttavia, c'è uno svantaggio in questo approccio: per ogni distribuzione all'interno di una stessa architettura, sarà necessario compilare il proprio insieme di file binari. Inoltre, un altro svantaggio è che è necessario gestire un gran numero di macchine, assegnare una grande quantità di spazio su disco e memoria RAM.

In questo modo vengono assemblati i pacchetti KMOD del modulo del kernel veeamsnap per le distribuzioni Red Hat.

Open Build Service

I colleghi di SUSE hanno tentato di realizzare una sorta di compromesso attraverso un servizio speciale per la compilazione delle applicazioni e la creazione dei pacchetti — openbuildservice.

In sostanza, si tratta di un hypervisor che crea una macchina virtuale, installa tutti i pacchetti necessari, compila l'applicazione e costruisce il pacchetto in quell'ambiente isolato, dopo di che tale macchina virtuale viene liberata.

Linux è multifaccettato: come lavorare su qualsiasi distribuzione.

Il pianificatore implementato in OpenBuildService determinerà autonomamente quante macchine virtuali può avviare per una velocità di costruzione ottimale dei pacchetti. Il meccanismo di firma integrato firmerà automaticamente i pacchetti e li caricherà nel repository integrato. Il sistema di controllo delle versioni incorporato salverà la cronologia delle variazioni e delle costruzioni. Non resta che aggiungere i propri sorgenti a questo sistema. Non è nemmeno necessario sollevare un server da soli, si può utilizzare un server pubblico.

C'è, tuttavia, un problema: un tale sistema è difficile da integrare nell'infrastruttura esistente. Ad esempio, il controllo delle versioni non è necessario, poiché abbiamo già il nostro per i sorgenti. Il meccanismo di firma è diverso: viene utilizzato un server speciale. Anche il repository non è necessario.

Inoltre, il supporto per altre distribuzioni, ad esempio Red Hat, è piuttosto scarso, il che è comprensibile.

Un vantaggio di questo servizio è il supporto rapido per la nuova versione della distribuzione SUSE. Prima dell'annuncio ufficiale del rilascio, i pacchetti necessari per la compilazione vengono resi disponibili nel repository pubblico. Un nuovo pacchetto appare nella lista delle distribuzioni disponibili su OpenBuildService. Basta selezionare la casella, e verrà aggiunto al piano di compilazione. In questo modo, l'aggiunta di una nuova versione della distribuzione avviene praticamente con un clic.

Nella nostra infrastruttura, utilizzando OpenBuildService, viene compilata l'intera varietà dei pacchetti KMP del modulo del kernel veeamsnap per le distribuzioni SUSE.

Ora vorrei soffermarmi su questioni specifiche relative ai moduli del kernel.

kernel ABI

I moduli del kernel Linux sono storicamente distribuiti come codice sorgente. Infatti, i creatori del kernel non si preoccupano di garantire un'API stabile per i moduli del kernel, tantomeno a livello binario, noto come kABI.

Per compilare un modulo per il kernel vanilla, sono necessari i header di quel particolare kernel, e funzionerà solo su quel kernel.

DKMS consente di automatizzare il processo di compilazione dei moduli durante l'aggiornamento del kernel. Di conseguenza, gli utenti del repository Debian (e dei suoi numerosi parenti) utilizzano i moduli del kernel o dal repository del distributore, oppure compilati dai sorgenti tramite DKMS.

Tuttavia, questa situazione non soddisfa particolarmente il segmento Enterprise. I distributori di codice proprietario desiderano fornire il prodotto sotto forma di binari già compilati.

Gli amministratori non vogliono mantenere gli strumenti di sviluppo sui server di production per motivi di sicurezza. I distributori di Enterprise Linux, come Red Hat e SUSE, hanno deciso che potrebbero supportare per i loro utenti un kABI stabile. Ne sono conseguiti pacchetti KMOD per Red Hat e pacchetti KMP per SUSE.

L'essenza di questa soluzione è piuttosto semplice. Per una specifica versione del distributivo, l'API del kernel viene congelata. Il distributore dichiara di utilizzare precisamente il kernel, ad esempio, 3.10, e apporta solamente correzioni e miglioramenti che non impattano sulle interfacce del kernel, mentre i moduli compilati per il kernel originale possono essere utilizzati per tutti i successivi senza ricompilazione.

Red Hat afferma di mantenere la compatibilità con il kABI per la distribuzione per l'intero ciclo di vita. Ciò significa che un modulo compilato per RHEL 6.0 (rilascio di novembre 2010) deve funzionare anche sulla versione 6.10 (rilascio di giugno 2018). E questo è quasi un lasso di tempo di 8 anni. Naturalmente, questa è un'operazione piuttosto complessa.
Abbiamo registrato diversi casi in cui, a causa di problemi di compatibilità con kABI, il modulo veeamsnap ha smesso di funzionare.

Dopo che il modulo veeamsnap, compilato per RHEL 7.0, è risultata incompatibile con il kernel di RHEL 7.5, ma continuava a caricarsi e sicuramente causava il crash del server, abbiamo abbandonato l'uso della compatibilità kABI per RHEL 7 del tutto.

Attualmente, il pacchetto KMOD per RHEL 7 contiene una build per ogni versione del rilascio e uno script che assicura il caricamento del modulo.

SUSE ha affrontato il problema della compatibilità kABI con maggiore cautela. Assicurano la compatibilità kABI solo all'interno di un singolo service pack.

Ad esempio, il rilascio di SLES 12 è avvenuto a settembre 2014. SLES 12 SP1 è stato invece rilasciato a dicembre 2015, poco più di un anno dopo. Nonostante entrambi i rilasci utilizzino il kernel 3.12, non sono compatibili con kABI. È chiaro che mantenere la compatibilità kABI per un periodo di solo un anno è significativamente più semplice. Un ciclo annuale di aggiornamento del modulo del kernel non dovrebbe presentare problemi per i creatori di moduli.

Grazie a questa politica di SUSE, non abbiamo riscontrato alcun problema di compatibilità kABI con il nostro modulo veeamsnap. Inoltre, il numero di pacchetti per SUSE è quasi di un ordine di grandezza superiore.

Patch e backport

Nonostante gli sforzi dei distributori per garantire la compatibilità kABI e la stabilità del kernel, si impegnano anche a migliorare le prestazioni e a correggere i difetti di questo kernel stabile.

Inoltre, oltre al loro "lavoro di correzione", gli sviluppatori del kernel enterprise Linux monitorano le modifiche nel kernel vanilla e le trasferiscono nel loro "stabile".

A volte ciò porta a nuovi errori.

Nell'ultimo aggiornamento di Red Hat 6, in uno dei minori aggiornamenti, si è verificato un errore. Questo portava al crash del sistema quando si liberava uno snapshot dal modulo veeamsnap. Confrontando i sorgenti del kernel prima e dopo l'aggiornamento, abbiamo scoperto che la colpa era di un backport. Un fix simile è stato effettuato nel kernel vanilla versione 4.19. Tuttavia, nel kernel vanilla quel fix funzionava normalmente, mentre nel portarlo nel "stabile" 2.6.32 è emerso un problema di spin-locking.

Certo, gli errori colpiscono tutti e sempre, ma valeva la pena portare il codice da 4.19 a 2.6.32, rischiando la stabilità?.. Non ne sono sicuro…

Il peggio è quando al tira e molla tra "stabilità""modernizzazione" si unisce il marketing. Il reparto marketing ha bisogno che il kernel della distribuzione aggiornata sia stabile, da un lato, e allo stesso tempo migliori in termini di prestazioni e abbia nuove funzionalità. Questo porta a strani compromessi.

Quando ho provato a compilare un modulo sul kernel 4.4 di SLES 12 SP3, sono rimasto sorpreso di trovare funzionalità del vanilla 4.8. A mio avviso, l'implementazione del blocco di I/O del kernel 4.4 di SLES 12 SP3 è più simile al kernel 4.8 che alla precedente versione stabile 4.4 di SLES 12 SP2. Non mi azzardo a giudicare quale percentuale del codice del kernel 4.8 sia stata trasferita nel 4.4 di SLES per SP3, ma non riesco a considerare il kernel come un semplice 4.4 stabile.

La cosa più sgradevole è che nella scrittura di un modulo che funzioni altrettanto bene su diversi kernel, non ci si può più affidare alla versione del kernel. Bisogna tenere in considerazione anche la distribuzione. È positivo che a volte si possa fare riferimento a una definizione che appare insieme a nuove funzionalità, ma questa opportunità non si presenta sempre.

Di conseguenza, il codice si arricchisce di strane direttive di compilazione condizionale.

Esistono anche patch che modificano l'API documentata del kernel.
Ho trovato una distribuzione KDE neon 5.16 e sono rimasto molto sorpreso di vedere che la chiamata a lookup_bdev in questa versione del kernel ha modificato l'elenco dei parametri di input.

Per configurarlo, è stato necessario aggiungere uno script al makefile che verifica se il parametro mask è presente nella funzione lookup_bdev.

Firma dei moduli del kernel

Tornando alla questione della distribuzione dei pacchetti.

Uno dei vantaggi del kABI stabile è che i moduli del kernel possono essere firmati come file binari. In questo modo, lo sviluppatore può essere certo che il modulo non sia stato accidentalmente danneggiato o modificato intenzionalmente. Questo può essere verificato con il comando modinfo.

Le distribuzioni Red Hat e SUSE consentono di controllare la firma del modulo e di caricarlo solo se il certificato corrispondente è registrato nel sistema. Il certificato rappresenta una chiave pubblica con cui viene firmato il modulo. Lo distribuiamo come pacchetto separato.

Il problema qui è che i certificati possono essere incorporati nel kernel (usati dai distributori) oppure devono essere scritti nella memoria non volatile EFI tramite l'utility mokutil. Strumento mokutil quando si installa il certificato richiede di riavviare il sistema e, prima del caricamento del kernel del sistema operativo, offre all'amministratore di consentire il caricamento del nuovo certificato.

Pertanto, l'aggiunta di un certificato richiede l'accesso fisico dell'amministratore al sistema. Se la macchina si trova in cloud o semplicemente in un server remoto e l'accesso è solo tramite rete (ad esempio, via ssh), non sarà possibile aggiungere il certificato.

EFI su macchine virtuali

Nonostante l'EFI sia supportato da quasi tutti i produttori di schede madri da molto tempo, l'amministratore potrebbe non pensare alla necessità dell'EFI durante l'installazione del sistema, e potrebbe essere disabilitato.

Non tutti gli hypervisor supportano l'EFI. VMWare vSphere supporta l'EFI a partire dalla versione 5.
Microsoft Hyper-V ha anche ricevuto supporto per l'EFI, a partire da Hyper-V per Windows Server 2012R2.

Tuttavia, nella configurazione predefinita, questa funzionalità è disattivata per le macchine Linux, quindi non è possibile installare il certificato.

In vSphere 6.5, l'opzione Secure Boot può essere impostata solo nella vecchia versione dell'interfaccia web, che funziona tramite Flash. L'interfaccia utente web in HTML-5 è ancora molto indietro.

Distribuzioni sperimentali

E infine, diamo un'occhiata alle distribuzioni sperimentali e a quelle senza supporto ufficiale. Da un lato, queste distribuzioni difficilmente si trovano sui server delle organizzazioni serie. Non c'è supporto ufficiale per tali distribuzioni. Pertanto, non è possibile fornire supporto tecnico per un prodotto basato su di esse.

Tuttavia, queste distribuzioni diventano un'ottima piattaforma per provare nuove soluzioni sperimentali. Ad esempio, Fedora, OpenSUSE Tumbleweed o le versioni instabili di Debian. Sono piuttosto stabili. Contengono sempre nuove versioni dei programmi e un nuovo kernel. Tra un anno, questa funzionalità sperimentale potrebbe diventare parte delle versioni aggiornate di RHEL, SLES o Ubuntu.

Quindi, se qualcosa non funziona su una distribuzione sperimentale, è un'opportunità per approfondire il problema e risolverlo. Bisogna essere pronti al fatto che questa funzionalità apparirà presto sui server di produzione degli utenti.

L'elenco attuale delle distribuzioni ufficialmente supportate per la versione 3.0 può essere consultato qui. Ma l'elenco reale delle distribuzioni su cui il nostro prodotto può funzionare è molto più ampio.

Personalmente, mi ha interessato l'esperimento con il sistema operativo «Elbrus». Dopo aver aggiornato il pacchetto veeam, il nostro prodotto si è installato e ha funzionato. Di questo esperimento ho scritto su Habr in articolo.

La supporto per nuove distribuzioni continua. Aspettiamo la versione 4.0. La beta dovrebbe arrivare presto, quindi seguiteci su whats-new!

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster