
Créer une application de sauvegarde fonctionnant sur n'importe quelle distribution est un défi. Assurer le fonctionnement de Veeam Agent for Linux sur des distributions allant de Red Hat 6 et Debian 6 à OpenSUSE 15.1 et Ubuntu 19.04 nécessite de résoudre un éventail de problèmes, surtout si l'on considère que le produit comprend un module noyau.
Cet article est basé sur les matériaux présentés lors de la conférence .
Linux n'est pas seulement l'un des systèmes d'exploitation les plus populaires. En réalité, c'est une plateforme sur laquelle on peut créer quelque chose d'unique, quelque chose qui nous appartient. Grâce à cela, Linux a de nombreuses distributions qui diffèrent par leur ensemble de composants logiciels. Et ici se pose un problème : pour qu'un produit fonctionne sur n'importe quelle distribution, il faut prendre en compte les particularités de chacune d'entre elles.
Gestionnaires de paquets. .deb vs .rpm
Commençons par le problème évident de la distribution du produit pour différentes distributions.
La méthode la plus typique de distribution des produits logiciels consiste à télécharger un paquet sur un dépôt, afin que le gestionnaire de paquets intégré au système puisse l'installer à partir de là.
Cependant, nous avons deux formats de paquets populaires : rpm et deb. Cela signifie qu'il faut supporter chacun d'eux.
Dans le monde des paquets deb, le niveau de compatibilité est incroyable. Un même paquet s'installe et fonctionne également bien sur Debian 6 et Ubuntu 19.04. Les standards du processus de construction des paquets et de travail avec eux, établis dans les anciennes distributions Debian, demeurent pertinents dans les distributions modernes comme Linux Mint et elementary OS. Ainsi, pour Veeam Agent for Linux, un seul paquet deb est suffisant pour chaque plateforme matérielle.
En revanche, dans le monde des paquets rpm, les différences sont considérables. Tout d'abord, parce qu'il existe deux distributeurs complètement indépendants, Red Hat et SUSE, pour lesquels la compatibilité n'est pas nécessaire. Deuxièmement, ces distributeurs ont des distributions avec support technique et expérimentales. Entre elles, la compatibilité n'est pas non plus nécessaire. Nous nous retrouvons donc avec des paquets distincts pour el6, el7 et el8. Un paquet séparé pour Fedora. Des paquets pour SLES11 et 12 ainsi qu'un séparé pour openSUSE. Le principal problème réside dans les dépendances et les noms des paquets.
Problème des dépendances
Malheureusement, les mêmes paquets apparaissent souvent sous des noms différents selon les distributions. Voici une liste non exhaustive des dépendances du paquet veeam.
Pour EL7 :
Pour 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
En conséquence, la liste des dépendances est unique pour la distribution.
C'est encore pire lorsque la nouvelle version se cache sous l'ancien nom du paquet.
Exemple :
Dans Fedora 24, le paquet a été mis à jour ncurses de la version 5 à la version 6. Notre produit était construit précisément avec la version 5 pour assurer la compatibilité avec les anciennes distributions. Pour pouvoir utiliser l'ancienne version 5 de la bibliothèque sur Fedora 24, il a fallu utiliser le paquet ncurses-compat-libs.
En conséquence, deux paquets apparaissent pour Fedora, avec des dépendances différentes.
C'est encore plus intéressant. Après une nouvelle mise à jour de la distribution, le paquet ncurses-compat-libs avec la version 5 de la bibliothèque devient indisponible. Pour le distributeur, il est coûteux de tirer les anciennes bibliothèques vers la nouvelle version de la distribution. Après un certain temps, le problème s'est répété dans les distributions SUSE.
En conséquence, pour certaines distributions, il a fallu renoncer à une dépendance explicite envers ncurses-libs, et ajuster le produit pour qu'il puisse fonctionner avec n'importe quelle version de la bibliothèque.
À propos, dans la 8e version de Red Hat, il n'y a plus de méta-paquet python, qui faisait référence au bon vieux python 2.7. Il y a python2 et python3.
L'alternative aux gestionnaires de paquets
Le problème des dépendances est ancien et évident depuis longtemps. Il suffit de se souvenir de l'enfer des dépendances.
Unir diverses bibliothèques et applications de manière à ce qu'elles fonctionnent toutes de manière stable et sans conflit — c'est précisément ce que tout distributeur Linux tente de résoudre.
C'est d'une manière complètement différente que le gestionnaire de paquets Snappy de Canonical tente de résoudre ce problème. L'idée principale : l'application s'exécute dans un bac à sable isolé et protégé du système principal. Si l'application a besoin de bibliothèques, elles sont fournies avec l'application elle-même.
Flatpak permet également de lancer des applications dans un bac à sable, en utilisant les conteneurs Linux. L'idée du bac à sable est également utilisée par AppImage.
Ces solutions permettent de créer un paquet unique pour toutes les distributions. Dans le cas de Flatpak l'installation et le lancement de l'application sont possibles même sans la connaissance de l'administrateur.
Le principal problème est que toutes les applications ne peuvent pas fonctionner dans un bac à sable. Certaines nécessitent un accès direct à la plateforme. Je ne parle même pas des modules du noyau, qui dépendent strictement du noyau et ne s'intègrent pas du tout dans le concept du bac à sable.
Le deuxième problème est que les distributions populaires dans le milieu des entreprises de Red Hat et SUSE ne supportent pas encore Snappy et Flatpak.
En raison de cela, Veeam Agent for Linux n'est pas disponible sur ni sur .
En conclusion de la question sur les gestionnaires de paquets, je note qu'il est possible de se passer complètement des gestionnaires de paquets, en regroupant dans un seul package des fichiers binaires et un script pour leur installation.
Un tel bundle permet de créer un package unique pour différentes distributions et plateformes, de produire un processus d'installation interactif tout en réalisant la personnalisation nécessaire. J'ai rencontré de tels packages pour Linux uniquement de la part de VMware.
Problème des mises à jour

Même si tous les problèmes de dépendances sont résolus, un programme peut fonctionner différemment sur une même distribution. C'est une question de mises à jour.
Il existe 3 stratégies de mise à jour :
- La plus simple consiste à ne jamais mettre à jour. On configure le serveur et on oublie. Pourquoi mettre à jour si tout fonctionne ? Les problèmes commencent dès le premier appel au support technique. Le créateur de la distribution ne prend en charge que la version mise à jour.
- On peut faire confiance au distributeur et configurer une mise à jour automatique. Dans ce cas, un appel au support est probable immédiatement après une mise à jour ratée.
- L'option de mise à jour manuelle uniquement après des tests sur l'infrastructure de test est la plus fiable, mais elle est coûteuse et laborieuse. Tout le monde ne peut pas se le permettre.
Étant donné que les utilisateurs appliquent différentes stratégies de mise à jour, il est également nécessaire de maintenir à la fois la version la plus récente et toutes les versions précédemment publiées. Cela complique à la fois le processus de développement et le processus de test, ajoutant des maux de tête au service d'assistance.
Diversité des plateformes matérielles
Les différentes plateformes matérielles représentent un problème en grande partie spécifique au code natif. Au minimum, il faut compiler des binaires pour chaque plateforme prise en charge.
Dans le projet Veeam Agent for Linux, nous n'avons toujours pas pu prendre en charge quoi que ce soit d'aussi RISC.
Je ne m'attarderai pas longuement sur cette question. Je soulignerai simplement les principaux problèmes : types dépendants de la plateforme, tels que size_t, alignement des structures et ordre des octets.
Liaison statique et/ou dynamique

La question « Comment se lier aux bibliothèques - dynamiquement ou statiquement ? » mérite d'être discutée.
En général, les applications C/C++ sous Linux utilisent une liaison dynamique. Cela fonctionne très bien si l'application est compilée spécifiquement pour une distribution donnée.
Si l'objectif est de couvrir divers distributions avec un seul fichier binaire, il faut se baser sur la distribution la plus ancienne prise en charge. Pour nous, cela serait Red Hat 6. Il contient gcc 4.4, qui ne supporte même pas la norme C++11. .
Nous compilons notre projet avec gcc 6.3, qui supporte entièrement C++14. Naturellement, dans ce cas, il faut apporter avec soi la bibliothèque libstdc++ et boost sur Red Hat 6. Il est le plus simple de les lier statiquement.
Mais malheureusement, il n'est pas possible de lier statiquement avec toutes les bibliothèques.
Tout d'abord, les bibliothèques système, telles que libfuse, libblkid doivent être liées dynamiquement, afin d'assurer leur compatibilité avec le noyau et ses modules.
Deuxièmement, il existe une subtilité concernant les licences.
La licence GPL permet en principe de lier les bibliothèques uniquement avec du code opensource. MIT et BSD autorisent la liaison statique et permettent d'inclure des bibliothèques dans le projet. Par contre, LGPL ne semble pas s'opposer à la liaison statique, mais exige que les fichiers nécessaires à la liaison soient mis à disposition du public.
En général, l'utilisation de la liaison dynamique évitera d'avoir à fournir quoi que ce soit.
Compilation d'applications C/C++
Pour compiler des applications C/C++ pour différentes plateformes et distributions, il suffit de sélectionner ou de compiler gcc dans une version appropriée et d'utiliser des compilateurs croisés pour des architectures spécifiques, de rassembler l'ensemble des bibliothèques. Cela peut être réalisé, mais c'est plutôt compliqué. Et il n'y a aucune garantie que le compilateur et les bibliothèques choisis fourniront une version fonctionnelle.
L'avantage évident : l'infrastructure est considérablement simplifiée, puisque l'ensemble du processus de compilation peut être effectué sur une seule machine. De plus, il suffit de compiler un ensemble de fichiers binaires pour une architecture et on peut les empaqueter dans des paquets pour différentes distributions. C'est ainsi que les paquets veeam pour Veeam Agent for Linux sont construits.
En revanche, il est possible de préparer simplement une ferme de construction, c'est-à-dire plusieurs machines dédiées à la compilation. Chaque machine sera responsable de la compilation de l'application et de la création du package pour une distribution spécifique et une architecture précise. Dans ce cas, la compilation se fait avec les outils fournis par le distributeur. Ainsi, l'étape de préparation du compilateur et de sélection des bibliothèques est éliminée. De plus, le processus de construction peut être facilement parallélisé.
Cependant, ce type d'approche a un inconvénient : pour chaque distribution au sein d'une même architecture, il faudra assembler un ensemble de fichiers binaires distinct. Un autre inconvénient est qu'il faut gérer ce grand nombre de machines, ce qui nécessite une quantité importante d'espace disque et de mémoire vive.
C'est ainsi que les paquets KMOD du module noyau veeamsnap sont construits pour les distributions Red Hat.
Open Build Service
Les collègues de SUSE ont essayé de trouver un juste milieu avec un service spécial pour la compilation d'applications et la création de paquets — .
Il s'agit essentiellement d'un hyperviseur qui crée une machine virtuelle, installe tous les paquets nécessaires, effectue la compilation de l'application et assemble le package dans cet environnement isolé, après quoi la machine virtuelle est libérée.

Le planificateur implémenté dans OpenBuildService déterminera lui-même combien de machines virtuelles il peut lancer pour optimiser la vitesse de création des paquets. Le mécanisme de signature intégré signera automatiquement les paquets et les déposera dans le dépôt intégré. Le système de contrôle de version intégré sauvegardera l'historique des modifications et des constructions. Il suffit d'ajouter ses propres sources à ce système. Il n'est même pas nécessaire de configurer un serveur soi-même ; on peut utiliser un service ouvert.
Cependant, il y a un problème : un tel combine s'intègre difficilement dans l'infrastructure existante. Par exemple, le contrôle de version n'est pas nécessaire, car nous avons déjà le nôtre pour les sources. Le mécanisme de signature diffère : un serveur spécial est utilisé. Le dépôt n'est pas non plus nécessaire.
De plus, le support d'autres distributions — par exemple, Red Hat — est plutôt limité, ce qui est assez compréhensible.
L'un des avantages de ce service est le support rapide de la dernière version de la distribution SUSE. Avant l'annonce officielle de la sortie, les paquets nécessaires à la compilation sont mis à disposition dans un référentiel public. Un nouveau distributeur apparaît dans la liste des distributions disponibles sur OpenBuildService. Nous cochons la case, et il est ajouté au plan de compilation. Ainsi, l'ajout d'une nouvelle version de la distribution se fait presque en un clic.
Dans notre infrastructure utilisant OpenBuildService, toute la diversité des paquets KMP du module noyau veeamsnap pour les distributions SUSE est compilée.
Je voudrais maintenant aborder des questions spécifiques liées aux modules du noyau.
ABI du noyau
Les modules du noyau Linux ont historiquement été distribués sous forme de code source. En effet, les créateurs du noyau ne se soucient pas de la prise en charge d'une API stable pour les modules du noyau, et encore moins à un niveau binaire, que l'on appelle kABI.
Pour compiler un module pour un noyau vanilla, il faut obligatoirement les en-têtes de ce noyau, et il ne fonctionnera que sur ce noyau.
DKMS permet d'automatiser le processus de compilation des modules lors de la mise à jour du noyau. En conséquence, les utilisateurs du référentiel Debian (et de ses nombreux dérivés) utilisent des modules du noyau soit provenant du référentiel du distributeur, soit compilés à partir des sources à l'aide de DKMS.
Cependant, cette situation ne satisfait pas vraiment le segment des entreprises. Les distributeurs de code propriétaire souhaitent distribuer leur produit sous forme de binaires compilés.
Les administrateurs ne souhaitent pas maintenir des outils de développement sur les serveurs de production pour des raisons de sécurité. Les distributeurs d'Enterprise Linux, tels que Red Hat et SUSE, ont décidé qu'ils pourraient supporter une kABI stable pour leurs utilisateurs. En conséquence, des paquets KMOD pour Red Hat et des paquets KMP pour SUSE sont apparus.
Le principe de cette solution est assez simple. Pour une version spécifique de la distribution, l'API du noyau est gelée. Le distributeur annonce qu'il utilise exactement le noyau, par exemple, 3.10, et n'apporte que des corrections et des améliorations qui n'affectent en rien les interfaces du noyau, et les modules compilés pour le tout premier noyau peuvent être utilisés pour tous les suivants sans recompilation.
Red Hat annonce la compatibilité kABI pour sa distribution tout au long de son cycle de vie. Cela signifie qu'un module compilé pour RHEL 6.0 (sortie de novembre 2010) doit également fonctionner sur la version 6.10 (sortie de juin 2018). Cela représente presque 8 ans. Naturellement, cette tâche est assez complexe.
Nous avons constaté plusieurs cas où, en raison de problèmes de compatibilité kABI, le module veeamsnap cessait de fonctionner.
Après que le module veeamsnap, compilé pour RHEL 7.0, se soit révélé incompatible avec le noyau de RHEL 7.5, tout en se chargeant et garantissant de faire planter le serveur, nous avons abandonné l'utilisation de la compatibilité kABI pour RHEL 7 dans son intégralité.
Actuellement, le paquet KMOD pour RHEL 7 contient une compilation pour chaque version de release et un script qui assure le chargement du module.
SUSE a abordé la question de la compatibilité kABI avec plus de prudence. Ils assurent la compatibilité kABI uniquement au sein d'un même service pack.
Par exemple, la sortie de SLES 12 a eu lieu en septembre 2014. Et SLES 12 SP1 est sorti en décembre 2015, soit un peu plus d'un an plus tard. Bien que les deux versions utilisent le noyau 3.12, elles ne sont pas compatibles kABI. Il est évident que maintenir la compatibilité kABI pendant seulement un an est beaucoup plus simple. Un cycle de mise à jour annuel du module de noyau ne devrait pas poser de problèmes pour les créateurs de modules.
En raison de cette politique de SUSE, nous n'avons constaté aucun problème de compatibilité kABI avec notre module veeamsnap. Cependant, le nombre de paquets pour SUSE est presque dix fois plus élevé.
Correctifs et backports
Bien que les distributeurs s'efforcent d'assurer la compatibilité kABI et la stabilité du noyau, ils cherchent également à améliorer les performances et à corriger les défauts de ce noyau stable.
En outre, en plus de leur propre « travail sur les erreurs », les développeurs du noyau de Linux Enterprise suivent les modifications dans le noyau vanilla et les portent dans leur version « stable ».
Parfois, cela conduit à de nouveaux .
Dans la dernière version de Red Hat 6, une erreur a été introduite dans l'une des mises à jour mineures. Cela provoquait le fait que le module veeamsnap faisait systématiquement planter le système lors de la libération d'un instantané. En comparant les sources du noyau avant et après la mise à jour, nous avons découvert que cela était dû à un backport. Un correctif similaire avait été appliqué dans le noyau vanilla version 4.19. Cependant, dans le noyau vanilla, ce correctif fonctionnait correctement, alors que lors de son transfert dans le « stable » 2.6.32, un problème de blocage s'est produit.
Bien sûr, des erreurs peuvent survenir à tout le monde, mais était-il judicieux de transférer le code de 4.19 vers 2.6.32, en risquant la stabilité ?... Je ne suis pas sûr...
Le pire, c'est quand le marketing s'implique dans le tir à la corde entre "stabilité" "modernisation". Le département marketing a besoin que le noyau de la distribution mise à jour soit stable d'une part, tout en étant plus performant et doté de nouvelles fonctionnalités d'autre part. Cela conduit à des compromis étranges.
Lorsque j'ai essayé de compiler un module avec le noyau 4.4 de SLES 12 SP3, j'ai été surpris de trouver des fonctionnalités issues du noyau vanilla 4.8. À mon avis, l'implémentation du bloc d'entrée/sortie du noyau 4.4 de SLES 12 SP3 ressemble plus à celle du noyau 4.8 qu'à la version précédente stable du noyau 4.4 de SLES12 SP2. Je ne me prononcerai pas sur le pourcentage de code transféré du noyau 4.8 vers le 4.4 de SLES pour SP3, mais je n'arrive pas à appeler le noyau le même stable 4.4.
Le plus désagréable, c'est qu'en écrivant un module qui doit fonctionner de manière uniforme sur différents noyaux, on ne peut plus se fier à la version du noyau. Il faut aussi tenir compte de la distribution. Heureusement, on peut parfois se baser sur un define qui apparaît avec une nouvelle fonctionnalité, mais cette possibilité n'est pas toujours présente.
En conséquence, le code devient chargé de directives de compilation conditionnelle étranges.
Il existe également des patches qui modifient l'API documentée du noyau.
Je suis tombé sur la distribution 5.16 et j'ai été très surpris de voir que l'appel à lookup_bdev dans cette version du noyau a modifié la liste des paramètres d'entrée.
Pour compiler, j'ai dû ajouter dans le makefile un script qui vérifie s'il y a un paramètre mask pour la fonction lookup_bdev.
Signature des modules du noyau
Revenons à la question de la distribution des paquets.
Un des avantages du kABI stable est que les modules du noyau en tant que fichiers binaires peuvent être signés. Dans ce cas, le développeur peut être sûr que le module n'a pas été accidentellement corrompu ou intentionnellement modifié. On peut vérifier cela avec la commande modinfo.
Les distributions Red Hat et SUSE permettent de vérifier la signature d'un module et de le charger uniquement si le certificat correspondant est enregistré dans le système. Le certificat est une clé publique avec laquelle le module est signé. Nous le distribuons sous forme de paquet séparé.
Le problème ici est que les certificats peuvent être soit intégrés dans le noyau (utilisés par les distributeurs), soit doivent être écrits dans la mémoire non volatile EFI à l'aide d'un utilitaire. mokutilindique que mokutil lors de l'installation du certificat, il demande de redémarrer le système et, avant même le chargement du noyau du système d'exploitation, propose à l'administrateur d'autoriser le chargement du nouveau certificat.
Ainsi, l'ajout d'un certificat nécessite un accès physique de l'administrateur au système. Si la machine est hébergée quelque part dans le cloud ou dans un serveur distant avec un accès uniquement via le réseau (par exemple, par ssh), il sera impossible d'ajouter le certificat.
EFI sur les machines virtuelles
Bien que l'EFI soit maintenant largement supporté par presque tous les fabricants de cartes mères, lors de l'installation du système, l'administrateur peut ne pas penser à la nécessité de l'EFI, et celui-ci peut être désactivé.
Tous les hyperviseurs ne supportent pas l'EFI. VMWare vSphere prend en charge l'EFI depuis la version 5.
Microsoft Hyper-V a également acquis le support de l'EFI, à partir de Hyper-V pour Windows Server 2012R2.
Cependant, dans la configuration par défaut, cette fonctionnalité est désactivée pour les machines Linux, ce qui signifie qu'il est impossible d'installer le certificat.
Dans vSphere 6.5, il est possible de définir l'option Secure Boot uniquement dans l'ancienne version de l'interface web, qui fonctionne via Flash. L'interface Web en HTML-5 est encore très en retard.
Distributions expérimentales
Enfin, examinons la question des distributions expérimentales et des distributions sans support officiel. D'une part, ces distributions ne se rencontrent guère sur les serveurs d'organisations sérieuses. Ces distributions n'ont pas de support officiel. Par conséquent, il n'est pas possible d'assurer le support technique du produit sur une telle distribution.
Cependant, ces distributions deviennent une plateforme pratique pour essayer de nouvelles solutions expérimentales. Par exemple, Fedora, OpenSUSE Tumbleweed ou les versions instables de Debian. Elles sont assez stables. Elles contiennent toujours de nouvelles versions de logiciels et toujours un nouveau noyau. Dans un an, cette fonctionnalité expérimentale pourrait apparaître dans une version mise à jour de RHEL, SLES ou Ubuntu.
Ainsi, si quelque chose ne fonctionne pas sur une distribution expérimentale, c'est une occasion de comprendre le problème et de le résoudre. Il faut être prêt à ce que cette fonctionnalité apparaisse bientôt sur les serveurs de production des utilisateurs.
Vous pouvez consulter la liste actuelle des distributions officiellement prises en charge pour la version 3.0. Mais la liste réelle des distributions sur lesquelles notre produit peut fonctionner est bien plus étendue.
Personnellement, j'étais intéressé par l'expérience avec le système d'exploitation « Elbrus ». Après des travaux supplémentaires sur le paquet veeam, notre produit a été installé et a fonctionné. J'ai écrit à ce sujet sur Habr dans .
La prise en charge de nouvelles distributions se poursuit. Nous attendons la sortie de la version 4.0. Une version bêta devrait bientôt être disponible, alors restez à l'écoute pour des !
Source : habr.com
