Aller au contenu

Blog ·

Réinstaller un conteneur, c’est le remplacer

Un conteneur n’a ni disque de modèle ni cloud-init : le réinstaller revient à le détruire puis à le reconstruire. Cela change ce que vous devez au client.

Par Provibr

Réinstaller une machine virtuelle sur Proxmox est presque ennuyeux. L’arrêter, la supprimer, cloner le modèle sur le même identifiant, laisser cloud-init faire le reste. Le modèle porte un disque et le disque porte un système d’exploitation : « réinstaller » veut en réalité dire « faire une nouvelle copie d’une chose connue comme bonne ».

Un conteneur Linux n’a ni l’un ni l’autre. Il est décompressé depuis une archive tar, et tout ce qu’il est ensuite, les cœurs, la mémoire, le swap, la taille du système de fichiers racine, l’interface réseau, le résolveur, le nom d’hôte, vit dans un fichier de configuration sur l’hyperviseur et nulle part ailleurs. Il n’y a pas d’image à cloner par-dessus. Une réinstallation de conteneur est donc une destruction suivie d’une création, et une fois que l’on a dit cela à voix haute, il en découle une série d’obligations qu’une réinstallation de VM n’a jamais.

Voici comment nous l’avons construite, et la partie qui a pris le plus de temps n’était pas la destruction.

Deux phases, parce que la plateforme ne sait pas ce qui est monté

Le flux est scindé. La première phase ne fait que lire, et ne modifie rien : l’agent récupère la configuration du conteneur et la renvoie. La seconde phase arrête, détruit, crée et démarre, en utilisant ce qu’a lu la première phase comme condition préalable.

L’alternative évidente serait une phase unique, avec un aperçu construit à partir de ce que la plateforme sait déjà. Cela n’est pas disponible ici. L’inspection du matériel n’est pas implémentée pour les conteneurs, parce que l’analyseur qu’elle utiliserait lit des clés de configuration QEMU : il n’existe donc nulle part dans notre base de données d’instantané des points de montage d’un conteneur. Un aperçu qui étiquette chaque point de montage « unknown » n’est pas un aperçu, c’est une clause de non-responsabilité.

Cinq mots pour ce qui arrive aux données

Chaque point de montage de la configuration reçoit exactement une étiquette, et l’étiquette porte sur les données, pas sur le montage.

ÉtiquetteSignification
erasedle volume est détruit avec le conteneur ; le système de fichiers racine, toujours
excludedle volume appartient à un autre invité, Proxmox le saute donc et rien n’est touché
externally_managedun bind mount, un chemin de l’hôte ; le répertoire survit, le montage ne revient pas
unknownla ligne n’a pas pu être lue, et c’est la seule étiquette qui bloque toute l’opération
preservedles données restent et le montage revient

La dernière ligne figure dans la liste et n’est jamais produite. Conserver un volume à travers une destruction suppose de le détacher, de supprimer le conteneur sans ramasser les disques non référencés, puis de le rattacher ensuite : trois étapes dont celle du milieu, en cas d’échec, laisse un volume que plus personne ne possède. C’est un travail à part, avec sa propre boucle de nettoyage. La valeur existe dans le vocabulaire pour qu’une version ultérieure n’ait pas à la classer discrètement sous un autre nom, une fonction déclare qu’elle n’est pas encore proposée, et un test affirme que le classificateur ne la renvoie jamais.

La distinction importante est celle de la première phrase. Un point de montage peut voir ses données survivre sans pour autant revenir, parce que l’appel de création que nous envoyons ne définit jamais que le système de fichiers racine. Tout autre montage disparaît du nouveau conteneur, quoi qu’il soit arrivé à ses octets. Prendre cela à l’envers produit la phrase la plus dangereuse de toute cette fonctionnalité : « preserved » à côté d’un montage qui n’existera plus ensuite.

La propriété se trouve dans l’identifiant de volume

Proxmox écrit un volume sous l’une de deux formes, et toutes les deux disent quel invité le possède. local-lvm:vm-5001-disk-0 et local:5001/vm-5001-disk-0.raw appartiennent tous deux au conteneur 5001. Un bind mount n’est ni l’un ni l’autre : c’est un chemin absolu, et c’est la barre oblique initiale que Proxmox lui-même teste.

Cette analyse décide entre excluded et erased. Un point de montage présent dans la configuration de ce conteneur mais pointant vers le volume d’un autre invité est sauté par la destruction, parce que Proxmox vérifie la propriété : ces données ne sont donc pas effacées. Sans la comparaison, l’aperçu dirait « erased » à propos de données qui restent. Quelqu’un restaurerait ensuite une sauvegarde dont il n’avait pas besoin, ce qui est un désastre plus petit que l’inverse, mais un désastre que nous aurions causé.

L’aperçu est une condition préalable, pas une image

Ce que le client a vu repart avec la commande d’application sous la forme d’une liste d’accusés de réception, et l’agent lit la configuration une nouvelle fois, juste avant de détruire. Si elle diffère, un point de montage ajouté, un volume différent sous la même clé, il refuse.

Entre l’aperçu et le clic, il y a un être humain, et pendant ce temps un administrateur peut attacher un disque dans l’interface de Proxmox. « Les bind mounts et les points de montage gérés par l’hôte ne font pas automatiquement partie d’une réinstallation » n’est vrai que si quelque chose le fait respecter.

Chaque accusé de réception porte une clé et un volume, et délibérément pas l’étiquette. L’étiquette est le jugement de la plateforme et n’a rien à décider à l’autre bout ; si elle était remise à l’agent dans ce qu’il compare, il y aurait un branchement qui choisirait dessus, et les règles vivraient alors à deux endroits. L’étiquette est tout de même consignée, à côté des accusés de réception dans l’enregistrement de la commande et dans la ligne d’intention, parce que c’est le versant audit, et là elle doit dire exactement ce qui a été montré au client.

Uniquement à la hausse, comparé en octets, affiché en gibioctets

Une réinstallation ne peut pas réduire le système de fichiers racine. La comparaison se fait sur les octets et l’écran arrondit à l’entier supérieur en gibioctets.

Arrondir vers le haut est la direction conservatrice, et le contre-exemple dit pourquoi. Arrondissez 8,5 Gio vers le bas en « 8 Gio » à l’écran, acceptez sur cette base une demande de 8 Gio, et un demi-gibioctet disparaît que personne n’avait demandé à perdre. Mesuré contre un Proxmox reconstruit, un conteneur annonçant 16 Gio là où le plan demandait 8 a été refusé.

Il y a un piège un étage plus bas. Proxmox écrit les tailles sous la forme 8G, 512M ou sous celle d’un nombre nu, et un nombre nu est en kibioctets. Lu comme des octets, 8388608 fait 8 Mio ; lu correctement, il fait 8 Gio. Un analyseur qui se trompe ici ne lève pas d’erreur, il approuve simplement toutes les réductions qu’on lui présentera.

Neuf raisons de refuser

Avant que quoi que ce soit d’irréversible ne se produise, il existe neuf blocages nommés : le conteneur a disparu, protection=1 est activé, Proxmox détient un verrou sur l’invité, une de nos propres tâches de sauvegarde ou de restauration est en cours, un point de restauration protégé existe, le système de fichiers racine serait réduit, sa taille actuelle n’a pas pu être lue, un point de montage n’a pas pu être classé, ou une réinstallation est déjà en route.

Chacun est une valeur typée avec sa propre phrase traduite en neuf langues, si bien qu’une raison absente de la liste ne peut pas en obtenir une. Ils sont vérifiés deux fois de notre côté, une fois quand l’aperçu est dessiné et une nouvelle fois quand le bouton de confirmation est pressé, et une troisième fois par l’agent contre la configuration qu’il lit lui-même. Trois fermetures, parce que c’est l’étape irréversible du chapitre.

Le mot de passe root est demandé lors de la seconde phase, pas de la première, et voyage comme donnée transitoire au lieu d’être stocké. Un secret que l’on saisit juste avant l’étape irréversible ne passe pas une demi-heure posé dans un onglet de navigateur.

« needs reconcile » est la troisième issue, et elle garde le verrou

Si quelque chose échoue entre la destruction et la création, l’exécution ne devient pas « failed ». Elle devient « needs reconcile », en portant l’identifiant du conteneur, l’étape sur laquelle elle est morte, et si le conteneur existait encore au moment où l’agent a abandonné. Ce dernier champ peut valoir null, et null n’est pas false : « je n’ai pas pu le déterminer » ne doit pas se lire comme « l’identifiant est libre ».

Le caractère destructeur d’un échec est dérivé de la position de l’étape dans la liste ordonnée des étapes, à partir de la destruction. C’est pour cela que les étapes sont une liste et non un ensemble de chaînes éparses : la question « quelque chose d’irréversible s’était-il déjà produit » reçoit sa réponse à un seul endroit.

Une exécution dans cet état garde fermé le verrou sur le serveur. Lancer un second remplacement sur un identifiant dont personne ne sait s’il porte un conteneur est précisément ce que cette conception interdit, et c’est un humain qui doit clore le dossier.

Le perdant d’un compare-and-swap doit relire

Il n’y a pas de processus de fond qui met ces lignes à jour. L’état avance quand quelqu’un le lit : la lecture regarde la commande à laquelle l’intention est rattachée et fait avancer la ligne, avec un compare-and-swap sur un compteur de génération, si bien que deux lecteurs simultanés effectuent ensemble chaque transition exactement une fois.

Cela avait été conçu. Ce qui ne l’avait pas été, et ce que le test navigateur a attrapé, c’est ce que doit renvoyer le perdant de cet échange.

Le panneau interroge une fois par seconde après une demande d’aperçu, et cette interrogation effectue la même transition. Si le client charge la page dans cette même seconde, l’interrogation gagne l’échange, la page lui rend sa propre ligne, désormais périmée, et l’écran continue de dire « en attente de l’agent » alors que l’aperçu est déjà dans la base de données. Une navigation, une requête, et un écran montrant un état qui n’existait plus. Seul un second rechargement corrigeait cela.

Le correctif tient en une phrase et mérite d’être intériorisé : perdre un compare-and-swap signifie, par construction, que quelqu’un d’autre vient de faire exactement la transition que vous vouliez. La bonne réponse est donc sa ligne à lui, et cela coûte un select. Renvoyer la vôtre est ce qui vient intuitivement, et c’est toujours faux.

Ce que nous retiendrions

  1. Si la chose que vous remplacez n’a pas d’image, appelez cela un remplacement. Le mot change ce que vous devez au client avant même de commencer.
  2. Étiquetez les données et le montage séparément. Ce sont deux questions, et la combinaison d’une réponse vraie à l’une et d’une réponse fausse à l’autre est la pire sortie que le système puisse produire.
  3. Faites revenir l’aperçu comme une condition, relisez à l’autre bout, et refusez à la moindre différence. Un humain a cliqué entre les deux.
  4. Arrondissez de façon conservatrice quand une erreur d’arrondi ne peut que détruire.
  5. Donnez à une opération destructrice à moitié faite son propre état, gardez le verrou fermé, et ne la laissez jamais tomber ni dans le succès ni dans l’échec.
  6. Relisez après un compare-and-swap perdu, toujours.

La prise en charge des conteneurs sur Proxmox est déployée par étapes, et la réinstallation arrive tard dans cet ordre, pour les raisons ci-dessus. Si vous êtes plus tôt dans cette séquence, les compromis entre les deux types d’invités sont dans VM ou conteneur sur Proxmox, et la raison pour laquelle un appel d’API réussi n’est pas un travail terminé est dans pourquoi un 200 de Proxmox n’est pas un résultat.

Issu du travail Proxmox sur Provibr.

Commencez avec un hôte et un agent

Créez un compte, installez l’agent avec une seule commande et connectez votre premier hôte Proxmox. La période d’essai dure 14 jours et ne demande aucune coordonnée de paiement.