Linux ha molte facce: come lavorare su qualsiasi distribuzione

Linux ha molte facce: come lavorare su qualsiasi distribuzione

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

L'articolo è stato creato sulla base dei materiali di un discorso alla conferenza LinuxPietro 2019.

Linux non è solo uno dei sistemi operativi più popolari. In sostanza, si tratta di una piattaforma sulla base della quale puoi realizzare qualcosa di unico, qualcosa di tuo. Grazie a ciò, Linux ha molte distribuzioni che differiscono per il set di componenti software. E qui sorge un problema: affinché un prodotto software funzioni su qualsiasi distribuzione, è necessario tenere conto delle caratteristiche di ciascuna.

Gestori di pacchetti. .deb contro .rpm

Cominciamo con l'ovvio problema della distribuzione del prodotto tra diverse distribuzioni.
Il modo più tipico per distribuire i prodotti software è mettere il pacchetto su un repository in modo che il gestore pacchetti integrato nel sistema possa installarlo da lì.
Tuttavia, abbiamo due formati di pacchetto popolari: rpm и debuttante. Ciò significa che tutti dovranno sostenere.

Nel mondo dei pacchetti deb, il livello di compatibilità è sorprendente. Lo stesso pacchetto si installa e funziona ugualmente bene sia su Debian 6 che su Ubuntu 19.04. Gli standard per il processo di creazione dei pacchetti e per lavorare con essi, stabiliti nelle vecchie distribuzioni Debian, rimangono rilevanti nel nuovo Linux Mint e nel sistema operativo elementare. Pertanto, nel caso di Veeam Agent for Linux, è sufficiente un pacchetto deb per ciascuna piattaforma hardware.

Ma nel mondo dei pacchetti RPM le differenze sono grandi. Innanzitutto perché esistono due distributori completamente indipendenti, Red Hat e SUSE, per i quali la compatibilità non è assolutamente necessaria. In secondo luogo, questi distributori hanno i kit di distribuzione di quelli. supporto e sperimentazione. Non è necessaria nemmeno la compatibilità tra loro. Si è scoperto che el6, el7 ed el8 hanno i propri pacchetti. Pacchetto separato per Fedora. Pacchetti per SLES11 e 12 e uno separato per openSUSE. Il problema principale sono le dipendenze e i nomi dei pacchetti.

Problema di dipendenza

Sfortunatamente, gli stessi pacchetti spesso finiscono con nomi diversi in distribuzioni diverse. Di seguito è riportato un elenco parziale delle dipendenze dei pacchetti Veeam.

Per EL7:
Per SLES 12:

  • libblkid
  • libcc
  • libstdc++
  • ncurses-libs
  • librerie di fusibili
  • file-librerie
  • veeamsnap=3.0.2.1185
  • libblkid1
  • libgcc_s1
  • libstdc + + 6
  • libmagic1
  • libfuse2
  • veeamsnap-kmp=3.0.2.1185

Di conseguenza, l'elenco delle dipendenze è univoco per la distribuzione.

Ciò che peggiora è quando una versione aggiornata inizia a nascondersi sotto il vecchio nome del pacchetto.

Esempio:

Il pacchetto è stato aggiornato in Fedora 24 nmaledizioni dalla versione 5 alla versione 6. Il nostro prodotto è stato realizzato con la versione 5 per garantire la compatibilità con le distribuzioni precedenti. Per utilizzare la vecchia quinta versione della libreria su Fedora 5, ho dovuto utilizzare il pacchetto ncurses-compat-libs.

Di conseguenza, ci sono due pacchetti per Fedora, con dipendenze diverse.

Ancora più interessante. Dopo il successivo aggiornamento della distribuzione, il pacchetto ncurses-compat-libs con la versione 5 della libreria risulta non essere disponibile. È costoso per un distributore trascinare le vecchie librerie in una nuova versione della distribuzione. Dopo qualche tempo, il problema si è ripetuto nelle distribuzioni SUSE.

Di conseguenza, alcune distribuzioni hanno dovuto abbandonare la loro dipendenza esplicita da ncurses-libse correggi il prodotto in modo che possa funzionare con qualsiasi versione della libreria.

A proposito, nella versione 8 di Red Hat non esiste più un metapacchetto python, che si riferiva al buon vecchio python 2.7. C'è python2 и python3.

Alternativa ai gestori di pacchetti

Il problema con le dipendenze è vecchio ed è evidente da tempo. Ricorda solo l'inferno della dipendenza.
Combinare varie librerie e applicazioni in modo che funzionino tutte stabilmente e non siano in conflitto: in effetti, questo è il compito che qualsiasi distributore Linux cerca di risolvere.

Il gestore pacchetti tenta di risolvere questo problema in un modo completamente diverso. Elegante da Canonico. L'idea principale: l'applicazione viene eseguita in una sandbox isolata e protetta dal sistema principale. Se un'applicazione richiede librerie, queste vengono fornite con l'applicazione stessa.

Flatpak consente inoltre di eseguire applicazioni in una sandbox utilizzando contenitori Linux. Viene utilizzata anche l'idea della sandbox AppImage.

Queste soluzioni ti consentono di creare un pacchetto per qualsiasi distribuzione. In caso di Flatpak l'installazione e l'avvio dell'applicazione sono possibili anche all'insaputa dell'amministratore.

Il problema principale è che non tutte le applicazioni possono essere eseguite in una sandbox. Alcune persone necessitano di un accesso diretto alla piattaforma. Non sto nemmeno parlando dei moduli del kernel, che dipendono strettamente dal kernel e non rientrano nel concetto di sandbox.

Il secondo problema è che le distribuzioni popolari in ambiente aziendale di Red Hat e SUSE non contengono ancora il supporto per Snappy e Flatpak.

A questo proposito Veeam Agent for Linux non è disponibile snapcraft.io Non sopra flathub.org.

Per concludere la domanda sui gestori di pacchetti, vorrei notare che esiste un'opzione per abbandonare del tutto i gestori di pacchetti combinando file binari e uno script per installarli in un unico pacchetto.

Un tale pacchetto consente di creare un pacchetto comune per diverse distribuzioni e piattaforme, eseguire un processo di installazione interattivo, effettuando la personalizzazione necessaria. Ho riscontrato solo pacchetti di questo tipo per Linux da VMware.

Problema di aggiornamento

Linux ha molte facce: come lavorare su qualsiasi distribuzione
Anche se tutti i problemi di dipendenza vengono risolti, il programma potrebbe funzionare in modo diverso sulla stessa distribuzione. Si tratta di aggiornamenti.

Esistono 3 strategie di aggiornamento:

  • Il più semplice è non aggiornare mai. Ho configurato il server e me ne sono dimenticato. Perché aggiornare se tutto funziona? I problemi iniziano la prima volta che contatti l'assistenza. Il creatore della distribuzione supporta solo la versione aggiornata.
  • Puoi fidarti del distributore e impostare gli aggiornamenti automatici. In questo caso, è probabile che una chiamata all'assistenza venga immediatamente dopo un aggiornamento non riuscito.
  • L'opzione di aggiornamento manuale solo dopo averlo eseguito su un'infrastruttura di test è la più affidabile, ma costosa e dispendiosa in termini di tempo. Non tutti possono permetterselo.

Poiché utenti diversi utilizzano strategie di aggiornamento diverse, è necessario supportare sia l'ultima versione che tutte quelle rilasciate in precedenza. Ciò complica sia il processo di sviluppo che quello di test e aggiunge grattacapi al team di supporto.

Varietà di piattaforme hardware

Le diverse piattaforme hardware rappresentano un problema in gran parte specifico del codice nativo. Come minimo, devi raccogliere i file binari per ciascuna piattaforma supportata.

Nel progetto Veeam Agent for Linux, non possiamo ancora supportare nulla di simile a questo RISC.

Non mi soffermerò su questo problema in dettaglio. Descriverò solo i problemi principali: tipi dipendenti dalla piattaforma, come size_t, allineamento della struttura e ordine dei byte.

Collegamento statico e/o dinamico

Linux ha molte facce: come lavorare su qualsiasi distribuzione
Ma la domanda è: “Come collegarsi alle biblioteche – dinamicamente o staticamente?” vale la pena discutere.

Di norma le applicazioni C/C++ sotto Linux utilizzano il collegamento dinamico. Funziona benissimo se l'applicazione è creata appositamente per una distribuzione specifica.

Se l'attività è coprire varie distribuzioni con un file binario, è necessario concentrarsi sulla distribuzione supportata più vecchia. Per noi si tratta di Red Hat 6. Contiene gcc 4.4, che nemmeno lo standard C++11 supporta completamente.

Costruiamo il nostro progetto utilizzando gcc 6.3, che supporta completamente C++14. Naturalmente, in questo caso, su Red Hat 6 dovrete portare con voi le librerie libstdc++ e boost. Il modo più semplice è collegarli staticamente.

Ma ahimè, non tutte le biblioteche possono essere collegate staticamente.

Innanzitutto, le librerie di sistema come libfuse, libblkid è necessario collegarsi dinamicamente per garantire la loro compatibilità con il kernel e i suoi moduli.

In secondo luogo, c'è una sottigliezza con le licenze.

La licenza GPL consente sostanzialmente di collegare librerie solo con codice opensource. MIT e BSD consentono il collegamento statico e consentono l'inclusione di librerie in un progetto. Ma la LGPL non sembra contraddire il collegamento statico, ma richiede che i file necessari per il collegamento siano condivisi.

In generale, l'utilizzo del collegamento dinamico ti eviterà di dover fornire qualsiasi cosa.

Creazione di applicazioni C/C++

Per costruire applicazioni C/C++ per diverse piattaforme e distribuzioni, è sufficiente selezionare o costruire una versione adatta di gcc e utilizzare cross-compilatori per architetture specifiche e assemblare l'intero set di librerie. Questo lavoro è abbastanza fattibile, ma piuttosto problematico. E non vi è alcuna garanzia che il compilatore e le librerie selezionati forniranno una versione funzionante.

Un vantaggio evidente: l'infrastruttura è notevolmente semplificata, poiché l'intero processo di costruzione può essere completato su un'unica macchina. Inoltre, è sufficiente raccogliere un set di binari per un'architettura e puoi impacchettarli in pacchetti per diverse distribuzioni. Ecco come vengono creati i pacchetti veeam per Veeam Agent for Linux.

Invece di questa opzione, puoi semplicemente preparare una build farm, cioè più macchine da assemblare. Ciascuna di queste macchine fornirà la compilazione di applicazioni e l'assemblaggio di pacchetti per una distribuzione specifica e un'architettura specifica. In questo caso la compilazione viene effettuata utilizzando i mezzi predisposti dal distributore. Cioè, la fase di preparazione del compilatore e di selezione delle librerie viene eliminata. Inoltre, il processo di compilazione può essere facilmente parallelizzato.

C'è, tuttavia, uno svantaggio in questo approccio: per ogni distribuzione all'interno della stessa architettura, dovrai raccogliere il tuo set di file binari. Un altro svantaggio è che è necessario mantenere un numero così elevato di macchine e allocare una grande quantità di spazio su disco e RAM.

Ecco come vengono compilati i pacchetti KMOD del modulo kernel veeamsnap per le distribuzioni Red Hat.

Apri il servizio di compilazione

I colleghi di SUSE hanno cercato di implementare una via di mezzo sotto forma di un servizio speciale per la compilazione di applicazioni e l'assemblaggio di pacchetti - openbuildservice.

In sostanza, si tratta di un hypervisor che crea una macchina virtuale, installa al suo interno tutti i pacchetti necessari, compila l'applicazione e costruisce il pacchetto in questo ambiente isolato, dopodiché la macchina virtuale viene rilasciata.

Linux ha molte facce: come lavorare su qualsiasi distribuzione

Lo scheduler implementato in OpenBuildService determinerà quante macchine virtuali può avviare per una velocità ottimale di creazione dei pacchetti. Il meccanismo di firma integrato firmerà i pacchetti e li caricherà nel repository integrato. Il sistema di controllo della versione integrato salverà la cronologia delle modifiche e delle build. Tutto ciò che resta è semplicemente aggiungere le tue fonti a questo sistema. Non devi nemmeno configurare tu stesso il server; puoi usarne uno aperto.

C’è però un problema: una tale mietitrice è difficile da inserire nell’infrastruttura esistente. Ad esempio, il controllo della versione non è necessario; ne abbiamo già uno per i codici sorgente. Il nostro meccanismo di firma è diverso: utilizziamo un server speciale. Inoltre, non è necessario un repository.

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

Il vantaggio di un tale servizio è il supporto rapido per la prossima versione della distribuzione SUSE. Prima dell'annuncio ufficiale del rilascio, i pacchetti necessari per l'assemblaggio vengono pubblicati su un repository pubblico. Ne viene visualizzata una nuova nell'elenco delle distribuzioni disponibili su OpenBuildService. Selezioniamo la casella e questa verrà aggiunta al piano di costruzione. Pertanto, l'aggiunta di una nuova versione della distribuzione avviene in quasi un clic.

Nella nostra infrastruttura, utilizzando OpenBuildService, è assemblata l'intera varietà di pacchetti KMP del modulo kernel veeamsnap per le distribuzioni SUSE.

Successivamente, vorrei soffermarmi sulle questioni specifiche dei moduli del kernel.

kernel ABI

I moduli del kernel Linux sono stati storicamente distribuiti in forma sorgente. Il fatto è che i creatori del kernel non si caricano della preoccupazione di supportare un'API stabile per i moduli del kernel, e soprattutto a livello binario, denominata in seguito kABI.

Per costruire un modulo per un kernel vanilla, hai sicuramente bisogno degli header di questo particolare kernel e funzionerà solo su questo kernel.

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

Tuttavia, questa situazione non è particolarmente adatta al segmento Enterprise. I distributori di codice proprietario desiderano distribuire il prodotto come binari compilati.

Gli amministratori non desiderano mantenere gli strumenti di sviluppo sui server di produzione per motivi di sicurezza. I distributori Enterprise Linux come Red Hat e SUSE hanno deciso di poter supportare kABI stabile per i propri utenti. Il risultato sono stati i pacchetti KMOD per Red Hat e i pacchetti KMP per SUSE.

L'essenza di questa soluzione è abbastanza semplice. Per una versione specifica della distribuzione, l'API del kernel è congelata. Il distributore dichiara di utilizzare il kernel, ad esempio 3.10, e di apportare solo correzioni e miglioramenti che non influiscono sulle interfacce del kernel, e che i moduli raccolti per il primissimo kernel possono essere utilizzati per tutti quelli successivi senza ricompilazione.

Red Hat dichiara la compatibilità kABI per la distribuzione durante tutto il suo ciclo di vita. Ciò significa che il modulo assemblato per rhel 6.0 (versione novembre 2010) dovrebbe funzionare anche sulla versione 6.10 (versione giugno 2018). E questo è quasi 8 anni. Naturalmente, questo compito è piuttosto difficile.
Abbiamo registrato diversi casi in cui il modulo veeamsnap ha smesso di funzionare a causa di problemi di compatibilità con kABI.

Dopo che il modulo veeamsnap, compilato per RHEL 7.0, si è rivelato incompatibile con il kernel di RHEL 7.5, ma si è caricato ed è stato garantito il crash del server, abbiamo abbandonato del tutto l'uso della compatibilità kABI per RHEL 7.

Attualmente, il pacchetto KMOD per RHEL 7 contiene un assembly per ogni versione di rilascio e uno script che carica il modulo.

SUSE ha affrontato il compito della compatibilità kABI con maggiore attenzione. Forniscono la compatibilità kABI solo all'interno di un service pack.

Ad esempio, il rilascio di SLES 12 è avvenuto a settembre 2014. E SLES 12 SP1 era già a dicembre 2015, ovvero è passato poco più di un anno. Anche se entrambe le versioni utilizzano il kernel 3.12, sono incompatibili con kABI. Ovviamente mantenere la compatibilità kABI solo per un anno è molto più semplice. Il ciclo annuale di aggiornamento dei moduli del kernel non dovrebbe causare problemi ai creatori dei moduli.

Come risultato di questa politica SUSE, non abbiamo registrato un singolo problema con la compatibilità kABI nel nostro modulo veeamsnap. È vero, il numero di pacchetti per SUSE è quasi un ordine di grandezza maggiore.

Patch e backport

Sebbene i distributori cerchino di garantire la compatibilità kABI e la stabilità del kernel, cercano anche di migliorare le prestazioni ed eliminare i difetti di questo kernel stabile.

Allo stesso tempo, oltre al proprio "lavoro sugli errori", gli sviluppatori del kernel Linux aziendale monitorano i cambiamenti nel kernel vanilla e li trasferiscono in quello "stabile".

A volte questo porta a nuovi errori.

Nell'ultima versione di Red Hat 6 è stato commesso un errore in uno degli aggiornamenti minori. Ciò ha portato al fatto che il modulo veeamsnap avrebbe garantito il crash del sistema al rilascio dello snapshot. Confrontando i sorgenti del kernel prima e dopo l'aggiornamento, abbiamo scoperto che la colpa era del backport. Una correzione simile è stata apportata nella versione 4.19 del kernel Vanilla. È solo che questa correzione ha funzionato bene nel kernel Vanilla, ma durante il trasferimento alla versione "stabile" 2.6.32 si è verificato un problema con lo spinlock.

Certo, tutti hanno sempre degli errori, ma valeva la pena trascinare il codice dalla 4.19 alla 2.6.32, rischiando la stabilità?.. non ne sono sicuro...

La cosa peggiore è quando il marketing viene coinvolto nel tiro alla fune tra “stabilità” e “modernizzazione”. Il reparto marketing ha bisogno che il nucleo della distribuzione aggiornata sia stabile, da un lato, e allo stesso tempo abbia prestazioni migliori e abbia nuove funzionalità. Ciò porta a strani compromessi.

Quando ho provato a creare un modulo sul kernel 4.4 da SLES 12 SP3, sono rimasto sorpreso di trovare funzionalità di Vanilla 4.8 al suo interno. A mio parere, l'implementazione I/O a blocchi del kernel 4.4 di SLES 12 SP3 è più simile al kernel 4.8 rispetto alla versione precedente del kernel 4.4 stabile di SLES12 SP2. Non posso giudicare quale percentuale di codice sia stata trasferita dal kernel 4.8 a SLES 4.4 per SP3, ma non posso nemmeno chiamare lo stesso kernel stabile 4.4.

La cosa più spiacevole è che quando si scrive un modulo che funzionerebbe ugualmente bene su kernel diversi, non si può più fare affidamento sulla versione del kernel. Bisogna anche tenere conto della distribuzione. È positivo che a volte tu possa essere coinvolto in una definizione che appare insieme a nuove funzionalità, ma questa opportunità non sempre appare.

Di conseguenza, il codice viene ricoperto da strane direttive di compilazione condizionale.

Esistono anche patch che modificano l'API del kernel documentata.
Mi sono imbattuto nella distribuzione Neon di KDE 5.16 e sono rimasto molto sorpreso nel vedere che la chiamata lookup_bdev in questa versione del kernel ha cambiato l'elenco dei parametri di input.

Per metterlo insieme, ho dovuto aggiungere uno script al makefile che controlla se la funzione lookup_bdev ha un parametro maschera.

Firma dei moduli del kernel

Ma torniamo alla questione della distribuzione dei pacchetti.

Uno dei vantaggi di kABI stabile è che i moduli del kernel possono essere firmati come file binario. In questo caso, lo sviluppatore può essere sicuro che il modulo non sia stato danneggiato accidentalmente o modificato intenzionalmente. Puoi verificarlo con il comando modinfo.

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

Il problema qui è che i certificati possono essere integrati nel kernel (i distributori li usano) o devono essere scritti nella memoria non volatile EFI utilizzando un'utilità mokutil. Utilità mokutil Quando si installa un certificato, richiede il riavvio del sistema e, anche prima di caricare il kernel del sistema operativo, richiede all'amministratore di consentire il caricamento di un nuovo certificato.

Pertanto, l'aggiunta di un certificato richiede l'accesso fisico dell'amministratore al sistema. Se la macchina si trova da qualche parte nel cloud o semplicemente in una sala server remota e l'accesso avviene solo tramite la rete (ad esempio tramite ssh), sarà impossibile aggiungere un certificato.

EFI su macchine virtuali

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

Non tutti gli hypervisor supportano EFI. VMWare vSphere supporta EFI a partire dalla versione 5.
Anche Microsoft Hyper-V ha ottenuto il supporto EFI a partire da Hyper-V per Windows Server 2012R2.

Nella configurazione predefinita, tuttavia, questa funzionalità è disabilitata per le macchine Linux, il che significa che il certificato non può essere installato.

In vSphere 6.5, imposta l'opzione Secure Boot possibile solo nella vecchia versione dell'interfaccia web, che funziona tramite Flash. L'interfaccia utente Web su HTML-5 è ancora molto indietro.

Distribuzioni sperimentali

Infine, consideriamo la questione delle distribuzioni sperimentali e delle distribuzioni senza supporto ufficiale. Da un lato, tali distribuzioni difficilmente si trovano sui server di organizzazioni serie. Non esiste alcun supporto ufficiale per tali distribuzioni. Pertanto, fornisci quelli. Il prodotto non può essere supportato su tale distribuzione.

Tuttavia, tali distribuzioni diventano una piattaforma conveniente per provare nuove soluzioni sperimentali. Ad esempio, Fedora, OpenSUSE Tumbleweed o le versioni Unstable di Debian. Sono abbastanza stabili. Hanno sempre nuove versioni dei programmi e sempre un nuovo kernel. Tra un anno, questa funzionalità sperimentale potrebbe finire in un RHEL, SLES o Ubuntu aggiornato.

Quindi, se qualcosa non funziona su una distribuzione sperimentale, questo è un motivo per capire il problema e risolverlo. Devi essere preparato al fatto che questa funzionalità apparirà presto sui server di produzione degli utenti.

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

Personalmente ero interessato all'esperimento con il sistema operativo Elbrus. Dopo aver finalizzato il pacchetto veeam, il nostro prodotto è stato installato e funzionante. Ho scritto di questo esperimento su Habré in Articolo.

Bene, il supporto per le nuove distribuzioni continua. Stiamo aspettando il rilascio della versione 4.0. La beta sta per apparire, quindi tieni gli occhi aperti Cosa c'è di nuovo!

Fonte: habr.com

Aggiungi un commento