solutions comme LXC, LXD ou Docker (limité à un seul processus) permettent cela. En dédiant des cartes réseaux à chaque conteneur, il n’y a aucun impact sur les performances.
Un autre contournement de l’OS consiste à utiliser des API spécifiques pour éviter ces copies vers les zones mémoires applicatives. Cela est possible dans certaines conditions, lorsque le paquet du réseau peut être directement copié sur disque, où l’inverse. L’idée est d’indiquer la source et la destination, et laisser le kernel s’occuper de transférer les données, sans que l’application ne puisse voir le contenu des zones mémoires.
Les données du disque étant cachées dans le kernel, cela permet d’exploiter astucieusement le cache kernel, pour éviter les copies en espace utilisateur.
Par exemple, le logiciel Kafka (kafka.apache.org) utilise stratégiquement cette approche. Le code est intégralement conçu pour exploiter autant que possible du Zero copy. Ce composant se charge de gérer des fils de messages binaires. Il gère des clusters et de gros volumes de données avec des performances foudroyantes.
Notez qu’en architecture ARM (Android, iPhone, etc.) il est possible d’utiliser le DMA pour un transfert direct entre périphériques, sans passer par la mémoire !
Mais cette approche n’est pas possible s’il est nécessaire de consulter les données.
Une autre approche pour éviter les copies, de réduire le code dans l’OS et mieux contrôler les flux réseaux, consiste à proposer une nouvelle stack IP s’exécutant en espace utilisateur. Pour cela, il faut réserver des zones mémoires dans l’OS à des adresses fixes, et manipuler le driver des cartes réseaux pour lui indiquer où déposer les paquets manipulés sur le réseau.
Le code applicatif est alors capable de s’appuyer directement sur ces tampons, et de réagir dès l’arrivée des paquets. Avec un peu d’astuce, il n’est pas nécessaire de reconstruire un flux pour les sockets avec les contenus des différents paquets. Un accès directement à chaque portion est plus efficace. C’est bien entendu plus compliqué à coder.
Par exemple, l’API DPDK est utilisée par la base de données Scylla DB (www.scylladb.com) pour optimiser la couche réseau si besoin. Une pile IP simplifiée est alors utilisée en espace utilisateur en lieu et place de la pile de l’OS.
Plus radical encore, il est possible de supprimer complètement l’OS. Au même titre que Docker propose un modèle d’exécution basé sur un seul processus, la solution OSv (osv.io) propose de lier physiquement le code de l’application avec le code d’un OS simplifié.
L’idée est de s’appuyer sur la glibc pour en proposer une autre implémentation. Au lieu de passer par une gateway pour chaque appel système, la solution effectue un link classique directement avec la librairie système. L’application ne possède plus d’espace mémoire séparé du kernel. Elle fait intégralement partie du kernel. La couche réseau est également directement liée au code applicatif.
Des outils simples permettent de créer des images OSv avec vos applications. Il existe également de nombreuses images pour les serveurs les plus courus.
Notez qu’un utilitaire permet une conversion Docker vers OSv.
Pour ne pas avoir à gérer les différents drivers matériels, et proposer des images génériques, OSv s’appuie sur les API des hyperviseurs pour s’occuper de la colle avec le physique. Lancer un composant applicatif dans l’hyperviseur c’est lancer une instance de OSv linké avec l’application.
Dans ce schéma, il n’y a pas de SSH disponible ou autre processus. Les appels systèmes fork() et exec() ne sont pas disponibles. Il n’existe que des threads. C’est pour cela qu’une API REST de supervision est proposée. Elle permet la consultation de tous les indicateurs, de gérer les fichiers, etc.
Des JVM peuvent tourner dans ce modèle, afin de faire exécuter toutes les applications Java. JMX sert alors à exposer les indicateurs de production. Les gains de performances peuvent atteindre 20% à 400% suivant les cas.
L’étape ultime est de se passer de l’hyperviseur et d’installer une stack logicielle directement en connexion avec le physique. Ce sont les UniKernel.
Le projet RumpKernel propose de construire un OS sur mesure. Des paramètres lors de la construction de l’OS permettent d’ajouter au coeur de l’OS : une pile IP, un gestionnaire de fichier, plus ou moins d’utilitaires Linux, etc. L’idée est de partir du minimum et de construire un OS sur mesure, en relation direct avec l’application. Ainsi, il n’y a que le nécessaire et rien d’autre.
Il faut alors produire une image dédiée à chaque cible matérielle ou pour une cible virtuelle.
Chaque étape réduit la souplesse de l’OS, au bénéfice des performances. Il est en effet parfois possible de brancher un remote debugger mais impossible d’ajouter des sondes d’analyses, de connecter une console, etc. Il est très difficile de faire une analyse post-mortem avec ces architectures.
Les OS des VM sont maintenant souvent immuables. Il faut provisionner des instances de VM à la demande, sans les modifier. Il n’est plus question de maintenir un serveur mais de le remplacer. En cas de problème on supprime la VM et on en crée une nouvelle vierge et immuable. L’essentiel est de pouvoir construire une instance à partir de rien et de l’ajouter dynamiquement au cluster. Les approches de réplications des bases de données NoSQL se chargent d’initialiser les données dans la nouvelle instance si nécessaire.
La souplesse apportée par un OS est indispensable en développement, mais n’est plus nécessaire en production. Il est possible de s’en passer à condition d’avoir des remontées d’indicateurs (JMX par exemple pour une JVM ou d’autres consoles WEB comme le propose OSv à fin de remplacer iostat ou d’autres outils). Les solutions d’UniKernel sont attendues sur ces points. Il est indispensable d’avoir une vue fine de la production. Mais plus besoin des nombreux outils conçus pour un OS.
La technologie Jitsu (Just-in-Time Summoning of Unikernels) propose d’instancier une VM en 20ms. Il est possible de monter une instance le temps de chaque requête HTTP !
Les développeurs doivent intégrer la création des images lors des builds, afin de tester les applications dans un environnement ISO production. Vingt millisecondes de plus pour lancer un serveur est parfaitement acceptable dans la chaîne de développement.
Les architectures à base de micro-services peuvent bénéficier de cette souplesse en provisionnant les VM suivant la charge réelle.
La simplification de la production des images OS+Application est la clé de la généralisation de ces modèles. Les migrations des scripts contribueront également à leurs diffusions. Par exemple, convertir un script Docker vers OSv, RumpKernel ou MirageOS permettra de vulgariser ces modèles.
Les approches de contenérisation sont complémentaires aux approches UniKernels. Un scénario classique consiste à déployer les bases de données SQL avec LXC ou Docker. Pour les Workers ou les serveurs d’applications, les UniKernels permettent une élasticité quasi immédiate. Certains serveurs resteront probablement dans l’ancien modèle, pour faciliter quelques tâches de maintenance.
Pourquoi utiliser ces approches ? Pour améliorer les performances et la sécurité tout en réduisant l’OPEX (dépenses d'exploitation) sur plusieurs axes : Nombre de serveurs (meilleure exploitation des ressources disponibles), énergie (green IT), réduction de la maintenance de la production (Serveur immuable).
Nous pensons qu’avec la multiplication des serveurs répondant à l’explosion du nombre de requêtes, il est pertinent de s'intéresser à ces approches. Indéniablement, les UniKernel vont jouer un rôle de plus en plus important dans les infrastructures réseaux de demain. Il s’agit de la prochaine étape de l’isolation des applications.
Philippe PRADOS et l'équipe "Réactive"