Bonjour à tous ! Comme toujours, le week-end est pour moi un moment de réflexion et d’expérimentation. Cela fait déjà quelques week-ends que je me penche sur les unikernels. J’ai essayé de comprendre leur fonctionnement pour voir si cela vaut vraiment le coup ou non et, aujourd’hui, je vais vous faire un petit retour d’expérience.

Qu’est-ce qu’un unikernel ?

Avant de répondre à cette question, je vous invite à lire mon article sur les micro-VMs, dans lequel j’explique ce qui existait auparavant : les VMs classiques, les conteneurs, puis les micro-VMs. Je ne vais donc pas m’attarder là-dessus ici.

Je vais tout de même vous rappeler brièvement ce qu’est une micro-VM. En gros, il s’agit d’une machine virtuelle ultra-légère : le VMM ne simule que les ressources dont j’ai réellement besoin. Par exemple, si je n’ai pas besoin de carte réseau, je n’en simule pas. Le support des périphériques est également réduit. Résultat : une machine virtuelle bien plus légère qu’une VM classique et, par conséquent, beaucoup plus rapide à démarrer.

Mais les micro-vms ça reste quand meme des Vm’s, vous avez toujours un système d’exploitation (OS) complet qui tourne dedans, avec son kernel généraliste. Et ça, ça veut dire un “overhead” : plein de fonctions, de pilotes, de services présents dans ce kernel et cet OS que votre application n’utilisera jamais, mais qui sont là, qui prennent de la place et consomment des ressources que votre application aurait pu utiliser. C’est un peu comme avoir une énorme boîte à outils de plombier juste pour planter un clou.

C’est là que des chercheurs (et maintenant pas mal de monde !) se sont dit : et si on créait un système qui ne contiendrait que mon application et strictement les briques systèmes (les fonctions de l’OS, comme la gestion du réseau, de la mémoire…) dont elle a réellement besoin ? L’idée, c’est de virer tout le superflu du système d’exploitation traditionnel pour ne garder que l’essentiel.

Comment on fait ça ? Eh bien, au lieu d’installer une application sur un OS existant, on va construire une image très spéciale. On prend le code de notre application, et on le compile directement avec les bibliothèques systèmes nécessaires. Imaginez que vous construisez une voiture : au lieu de prendre un châssis standard et d’y ajouter votre moteur, vous construisez un véhicule unique où le moteur et seulement les pièces de châssis indispensables sont fusionnés dès le départ.

alt text

Le résultat, c’est l’unikernel : une image unique, auto-suffisante. Elle contient à la fois votre code applicatif et les fonctions systèmes indispensables qui faisaient avant partie du gros OS/Kernel généraliste. Il n’y a plus vraiment de séparation nette entre l’application et l’“OS” en dessous, parce qu’il n’y a plus d’OS complet “en dessous” au sens où on l’entend habituellement !

L’image présentée un peu plus haut illustre bien ça : on zappe les couches séparées traditionnelles (Application qui tourne sur un OS, qui lui-même utilise un Kernel) pour avoir cette entité unique “Application + ses dépendances systèmes minimales” qui cause directement avec l’hyperviseur.

Donc en résumé, l’idée c’est vraiment que l’application et les morceaux d’OS essentiels qu’elle utilise ne fassent plus qu’UN.

Comment ça marche ?

Si vous avez bien suivi jusqu’à là, votre tête est sûrement pleine de questions. Comment mon système peut-il fonctionner sans un OS complet ? Comment je fais pour gérer le réseau, le stockage, la mémoire sans ce bon vieux kernel Linux ou Windows ? Et surtout, comment on fabrique ce “machin” ?

Accrochez-vous, c’est là que ça devient vraiment intéressant et différent de ce qu’on connaît ! La clé, ce n’est pas d’installer une application sur un OS allégé, mais de construire un système minimaliste taillé sur mesure pour l’application !!

Le “Build”

Pour faire ça, on ne lance pas juste un gcc ou un go build classique sur notre code et c’est tout. Non, on utilise des outils et des frameworks spécialisés dédiés aux unikernels. Vous avez peut-être entendu parler de projets comme MirageOS, Unikraft, IncludeOS, ou Nanos.

Ces outils sont la baguette magique. Ils fournissent les fonctions de base qu’on trouve normalement dans un OS (gérer le réseau, la mémoire, les fichiers…) non pas comme un gros bloc monolithique (le kernel), mais sous forme de bibliothèques (des “libs”, comme celles que vous ajoutez à votre projet pour faire du JSON ou des appels HTTP !). On parle parfois de “Library OS”.

C’est qui est vraiment pas mal, c’est que lors de la compilation (le “build”) de votre unikernel :

  1. Vous dites à l’outil : “Mon application a besoin de parler en TCP/IP et d’accéder à des données embarquées”.
  2. L’outil va chercher uniquement les bibliothèques systèmes correspondantes (une lib pour TCP/IP minimaliste, une lib pour accéder aux données mises dans l’image).
  3. Il prend ensuite votre code applicatif.
  4. Et il lie (“linke”) tout ça ensemble : votre code + les bibliothèques systèmes choisies.

Le résultat ? Une image unique et autonome qui contient tout ce qu’il faut pour tourner, et rien de plus. Tout ce qui n’est pas explicitement nécessaire (le pilote pour l’USB dont vous n’avez pas besoin, le support multi-utilisateurs, le shell…) n’est tout simplement pas inclus dans l’image finale !

Et concrètement, pour le Système de Fichiers et le Réseau ?

Maintenant qu’on a compris le principe de la construction par bibliothèques, voyons comment ça s’applique :

Système de fichiers (FS) : Dans beaucoup de cas simples (servir une page web statique, une API simple), l’unikernel n’a même pas besoin d’une bibliothèque de système de fichiers complexe. Les données nécessaires (la page HTML, la config) sont souvent directemment mises dans l’image lors de la compilation. L’application y accède comme si c’était en mémoire, souvent en lecture seule.

Mais attention : Si votre application doit écrire des fichiers (logs, données…), ce n’est pas interdit ! Il suffit de le dire à l’outil de build, qui inclura alors la bibliothèque nécessaire pour parler à un système de fichiers (qui peut être un simple disque virtuel exposé par l’hyperviseur). C’est juste qu’on ne l’inclut pas par défaut si ce n’est pas requis.

Réseau : C’est exactement la même idée. Oubliez la pile réseau complète d’un OS classique avec ses milliers d’options. Ici, on lie une bibliothèque réseau minimaliste (une implémentation TCP/IP souvent très optimisée) qui fournit juste les fonctions dont l’application a besoin (écouter sur un port, envoyer des données…). Cette bibliothèque est conçue pour communiquer directement avec la carte réseau virtuelle fournie par l’hyperviseur (le VMM, comme Firecracker, QEMU, etc.), en général via une interface standardisée comme virtio-net. Moins de couches, moins de détours, potentiellement plus rapide !

La philosophie : Minimalisme à tous les étages

Vous commencez à voir le schéma ? C’est la même logique pour tout le reste :

  • Gestion de la mémoire
  • Gestion du temps CPU (“scheduling”)

Le principe fondamental est toujours :

  1. De quoi mon application a-t-elle strictement besoin ?
  2. On prend seulement les bibliothèques correspondantes.
  3. On compile tout ça ensemble en une image unique.

C’est cette approche radicale qui permet d’obtenir des systèmes sans utilisateurs multiples, sans shell par défaut, sans la notion de processus multiples telle qu’on la connaît sous Linux/Windows. L’application devient le système.

Et maintenant qu’on a vu comment ça marche et ce que ça implique comme architecture interne, on peut commencer à parler des conséquences… notamment les avantages ! (Et oui, il y a aussi quelques inconvénients, on y viendra).

Conclusion : Unikernels, on y va ou pas ?

Bon, on a fait le tour : les unikernels, c’est l’idée de fusionner votre application avec juste le strict minimum de l’OS, le tout compilé en une seule image super optimisée. On a vu que ça se fait avec des outils spéciaux et une approche “bibliothèque OS”.

Alors, le verdict rapide ? C’est un peu les deux faces d’une même pièce :

Le bon côté : Une sécurité potentiellement béton (surface d’attaque minuscule !), une légèreté et une vitesse de démarrage imbattables. Quand on a besoin de ça, c’est difficile de faire mieux.

Le mauvais côté: C’est plus complexe à fabriquer et à débugger qu’un bon vieux conteneur Docker. L’outillage est plus jeune, demande un certain effort et tout ne tourne pas dessus “out-of-the-box”.

En bref : Non, les unikernels ne vont pas remplacer toutes nos VMs et conteneurs demain matin. C’est trop spécifique pour ça aujourd’hui. MAIS, c’est un outil extrêmement puissant pour des niches bien précises : sécurité absolue, performance extrême, edge computing, fonctions serverless ultra-rapides…

C’est une technologie à garder sérieusement dans un coin de sa tête pour les cas où les approches classiques montrent leurs limites. Perso, je trouve ça fascinant et je vais continuer à expérimenter ! Peut etre je vous ferais une vraie demo dans un futur article !