Greboca  

Suport technique et veille technologique

Aujourd’hui, les grandes entreprises et administrations publiques hésitent entre continuer à utiliser des logiciels propriétaires ou basculer vers les Logiciels Libres. Pourtant, la plupart des logiciels libres sont capables de bien traiter les données issues des logiciels propriétaire, et parfois avec une meilleur compatibilité.

C’est alors la barrière de la prise en main qui fait peur, et pourtant...

Les logiciels libres

L’aspect « Logiciel Libre » permet une évolution rapide et une plus grande participation des utilisateurs. Les aides et tutoriels foisonnent sur Internet ou sont directement inclus dans le logiciel lui-même.

Enfin, les concepteurs sont plus proches des utilisateurs, ce qui rend les logiciels libres plus agréable à utiliser et conviviaux.

Grâce à la disponibilité des logiciels libres, vous trouverez facilement des services de support techniques et la licence n’est plus un frein à l’utilisation de ces logiciels par votre personnel.

Notre support technique concerne essentiellement les logiciels libres, que ce soit sous forme de services ponctuels ou de tutoriels.

DLFP - Dépêches  -  MirageOS - un micro OS (unikernel) en OCaml

 -  Décembre 2021 - 

MirageOS est un outil permettant de créer un unikernel (un système d’exploitation) pouvant faire office de micro-service comme un site Internet, un service SMTP ou encore un service DNS. L’objectif de MirageOS est de proposer une solution modulaire afin que l’utilisateur puisse créer son propre système selon ce qu’il souhaite vraiment. La modularité et l’approche clean-state de MirageOS permet de délester le système final d’éléments superficiels à priori. De ce fait, MirageOS est capable de produire un système d’exploitation complet comme un simple site internet ne pesant au final que ~16 Mo.

L’approche de MirageOS est de reconstruire tous les éléments de votre application finale en OCaml (en partant de votre API REST à la pile TCP/IP). Les logiques d’abstraction et la modularité d’OCaml sont les bases de MirageOS afin de s’abstraire de tout ce qui est à proprement parler lié au système (les syscalls) et de pouvoir interchanger une implémentation avec une autre sans changer le reste de l’application. Par ces mécanismes-là, MirageOS a la possibilité de produire un simple exécutable ou de produire un système complet capable d’être virtualisé avec Xen ou KVM.

Dans cette dépêche, nous allons voir ce qu’est concrètement MirageOS et expliquer comment l’utiliser.

Sommaire

Un peu d’OCaml

Avant de parler de MirageOS, nous avons besoin d’une petite introduction à OCaml. OCaml est un langage né en 1996 et maintenu par l’Inria venant de la famille des langages ML (contraction de Meta Language : un langage de programmation généraliste fonctionnel). Il propose un système de types ainsi qu’une gestion automatique de la mémoire. Nous allons ici nous intéresser aux points qui ont dirigé MirageOS vers ce langage.

Un système de modules

OCaml a un système de modules, qui est régi par une règle : un fichier OCaml, un module ou unité de compilation. Un module est une implémentation d’un certain type de données ainsi que des fonctions permettant de traiter/manipuler ce type de données. On peut par exemple parler du module String qui implémente les chaînes de caractères ainsi que les fonctions comme find_character/trim/ etc.

La particularité d’OCaml réside dans l’idée qu’une implémentation peut être décrite par une signature/interface, le fichier *.mli. Ce dernier décrit ce qui doit être montré aux autres modules de son implémentation associée. Il est courant en OCaml d’abstraire/cacher le type de données et de décrire uniquement les fonctions associées au type. Il est même commun d’utiliser une signature/interface à plusieurs implémentations tel que :

module type ORDERED = sig
  type t

  val compare : t -> t -> int
end

Ici, on décrit une interface qui expose compare. Cette dernière peut être utilisée avec type t = string (et utiliser memcmp) ou un entier type t = int (et utiliser la soustraction).

Foncteur

Grâce à ce mécanisme de modules, on a la possibilité de produire un module/une implémentation à partir d’un autre module décrit par une interface tel que Ordered. Ainsi, un simple dictionnaire associant une clé avec une valeur peut se spécialiser selon la clé tel que :

module Make (S : ORDERED) : sig
  type 'a t

  val empty : 'a t
  val add : S.t -> 'a -> 'a t -> 'a t
end

Ce principe d’abstraction en OCaml est largement utilisé par la communauté, mais on le retrouve à une autre échelle en ce qui concerne MirageOS.

Fonctoriser les syscalls

Le principe de MirageOS et de son écosystème est de ne pas dépendre d’un appel système tel que ceux offerts par le noyau Linux. L’objectif est de s’abstraire des syscalls et d’injecter leurs implémentations à la production du système d’exploitation. De ce fait, on est en capacité autant d’introduire les syscalls usuels proposés par le noyau Linux comme ceux d’un micro-noyau pouvant être virtualisé avec Xen ou KVM.

L’idée est d’être en capacité de produire la logique applicative (votre service comme un site-web) qui est complétement abstrait de la logique du système - la stack TCP/IP, la couche de cryptographie, le système de fichiers. De cette manière, il devient possible de compiler votre application comme un exécutable ou comme un système entier qui peut être virtualisé via Xen ou KVM ou encore un système pouvant fonctionner sur des puces ESP32.

L’objectif est que le côté applicatif ne devrait pas changer.

Gestion de la mémoire

Depuis la version 3.9 (octobre 2020), seul le mode PVH(VM) est pris en charge, la partie bas niveau permettant le boot est assurée par Solo5 et la gestion de la mémoire est passée à la version de Doug Lea pour malloc.

L’éco-système de MirageOS

Ainsi, tout l’éco-système de MirageOS se fonde sur un principe d’abstraction de ce qui peut être considéré comme le système d’exploitation. Au-delà de ça, les composants du système sont eux-mêmes abstraits à l’aide d’interfaces comme ORDERED. Ainsi, MirageOS se définit plus comme un outil permettant de faire la glue entre plusieurs composants à l’aide d’interfaces.

Par ce biais, il devient simple par exemple d’interchanger l’implémentation d’un type de données de l’un à l’autre sans changer le reste de la logique du système. C’est, concrètement, ce qui se passe lorsqu’on injecte la stack TCP/IP du système hôte lorsqu’on veut produire un simple exécutable avec MirageOS ou qu’on utilise une implémentation de la stack TCP/IP en OCaml: mirage-tcpip.

Bien entendu, cette approche requiert que ces implémentations existent ! Et c’est le principal travail de l’équipe de MirageOS : implémenter des formats/protocoles/logiques qui peuvent être utilisés avec MirageOS. Au travers de ce travail d’abstraction, ces différentes implémentations peuvent être utilisées en dehors de MirageOS. Ainsi, la plupart des projets de MirageOS sont utilisés par la communauté dans d’autres contextes que celui de MirageOS comme :

  • Windows
  • Mac OSX
  • votre navigateur web grâce à js_of_ocaml

Quelques super-stars

Dans ces projets qui sont utilisés par d’autres personnes en dehors de MirageOS, nous avons :

  • irmin
  • mirage-tcpip
  • cohttp
  • ocaml-tls
  • ocaml-dns

Irmin

L’idée d’un système de fichiers n’est pas garantie par MirageOS et, même si nous avons essayé d’implémenter certains formats, l’équipe MirageOS a décidé de concentrer ses efforts dans l’implémentation d’un Key-Value store. Irmin est une abstraction de ce que devrait être une telle base de données. Mais, à la différence des systèmes tels que LMDB ou encore Git, Irmin n’offre qu’une abstraction commune. Ensuite, c’est à l’utilisateur de choisir son implémentation.

MirageOS utilise aujourd’hui Irmin avec une implémentation de Git en OCaml. Par ce dernier, un unikernel peut obtenir une base de données clé-valeur interne qui peut se synchroniser au boot avec un dépôt Git. Sur ce dernier, il peut se synchroniser ou il peut le mettre à jour. On parle alors de système de base de données persistant – même si le système s’éteint, il peut reprendre l’état dans lequel la base de données était juste avant de s’éteindre.

Irmin est actuellement utilisé par la crypto-monnaie Tezos afin de manipuler la block-chain.

Une autre utilisation concrète d’Irmin avec MirageOS est un système d’exploitation qui fait office de serveur DNS primaire dont le fichier zone est stocké dans un dépôt Git. Ainsi, l’unikernel se synchronise avec ce dépôt (avec le protocol Git - comme un git clone), il peut le modifier (comme git push) et l’utilisateur peut tout autant modifier aussi et demander ensuite à l’unikernel de se resynchroniser (comme git pull).

Dans le dernier cas, l’utilisateur peut décrire sa politique de merge (comment résoudre un conflit s’il y en a un) avec Irmin. Cela permet d’assurer la persistance des données en dehors de l’unikernel lui-même.

mirage-tcpip

Du fait que MirageOS est un système d’exploitation complet, l’équipe de MirageOS a finalement implémenté la stack TCP/IP au travers du projet mirage-tcpip. Au-delà de l’intérêt technique de ré-implementer une stack TCP/IP, cette dernière est industriellement utilisée par Docker.

Ce projet permet d’introduire un concept fondateur de MirageOS. L’objectif de l’outil mirage est de produire, au mieux, un système complet capable d’être virtualisé sur KVM ou Xen, mais il permet aussi de produire un simple exécutable UNIX comme nous aurions l’habitude d’avoir. Le point crucial ici est la capacité de mirage à orchestrer (indépendamment de l’application) les stacks selon la cible.

Pour ce qui est de la production d’un simple exécutable, mirage va injecter la stack TCP/IP du système hôte. Pour ce qui est de KVM ou Xen, il va tout simplement injecter mirage-tcpip (qui n’est fait qu’en OCaml). L’idée est de n’utiliser, du point de vue de l’application, qu’une interface (en l’occurrence mirage-stack) nous permettant de séparer la logique de l’application des autres fondements de notre système.

cohttp

Bien entendu, en tant que premier exemple réel d’un unikernel, il nous faut aussi une implémentation du protocole HTTP 1.1 : cohttp. Pour l’exemple, le site officiel de MirageOS est un unikernel utilisant cohttp. Mais ce projet, comme la plupart des projets MirageOS, dépasse l’écosystème et fait partie intégrante du plus large écosystème d’OCaml.

Puisque le développement d’une bibliothèque OCaml pour MirageOS se fait toujours en abstraction du système, spécialiser le cœur pour un système comme Linux ou Windows devient plus facile. La qualité la plus reconnue des projets Mirage est leur capacité à pouvoir être utilisés sur pratiquement tous les systèmes. Bien entendu, cette qualité est fortement liée à OCaml aussi qui propose un runtime s’exécutant nativement sur une multitude de plateformes.

ocaml-tls

La conception d’un système entier nécessite aussi de disposer de primitives de cryptographie, qui sont usuellement disponibles avec OpenSSL (ou l’un de ses forks). Il n’est, pour autant, pas aussi simple de ré-intégrer un code C existant (dépendant généralement des syscalls POSIX) dans MirageOS qui n’a, pour le coup, rien de toutes ces primitives.

Un effort colossal a donc été fait pour ré-implémenter les primitives de cryptographie essentielles afin de pouvoir ré-implémenter le protocole TLS, nécessaire pour servir un site internet accessible en HTTPS.

Là aussi, ocaml-tls est un projet phare limitant le prérequis d’instructions assembleur tout en proposant une bibliothèque avec des performances raisonnables. Encore une fois, il n’a absolument aucune notion de ce que peut être un socket ou de tout ce qui peut être POSIX-compliant.

ocaml-dns

Enfin, le domaine mirage.io est géré par un serveur primaire DNS qui est aussi un unikernel. Plusieurs services DNS tel qu’un résolveur et un service s’occupant du challenge DNS de let's encrypt sont disponibles grâce à ocaml-dns. Ce projet s’inscrit dans l’ambition de proposer des micro-services : un système d’exploitation pour un service spécifique.

Vous lancer dans l’écriture d’un unikernel

Des exemples prêts à compiler sont disponibles sur le github dont le classique hello world entièrement reproduit ici :

open Lwt.Infix

module Hello (Time : Mirage_time.S) = struct

  let start _time =

    let rec loop = function
      | 0 -> Lwt.return_unit
      | n ->
        Logs.info (fun f -> f "hello");
        Time.sleep_ns (Duration.of_sec 1) >>= fun () ->
        loop (n-1)
    in
    loop 4

end

Ce « noyau » se contente d’écrire quatre fois hello en 4 secondes et se termine. Si vous voulez expérimenter chez vous, vous devez mettre en place un environnement propice à la compilation et à l’exécution, par exemple avec les commandes suivantes (à adapter en fonction de votre distribution) :

sudo dnf install opam && \
opam init && opam update -yu && \
opam install mirage && eval $(opam env) && \
git clone https://github.com/mirage/mirage-skeleton && \
cd mirage-skeleton/tutorial/hello && \
mirage configure -t unix && make depend && make && \
./hello

(Dans cet exemple on produit un binaire exécutable mais en utilisant l’option de configuration mirage configure -t xen on peut par exemple produire un noyau utilisable avec l’hyperviseur Xen.)

Abstraction avec les functors

Comme vous pouvez le constater, un functor Time est utilisé pour générer la fonction start (qui est comme le main en C). C’est un module qui respect la signature Mirage_time.S. Grâce à ce module, nous pouvons utiliser la fonction sleep_ns. Son implémentation dépend bien entendu de la cible de votre MirageOS:
- pour UNIX, comme dans cette exemple, nous allons utiliser Unix.sleep
- pour Solo5 (KVM ou Xen), nous allons utiliser une fonction spécifique disponible dans mirage-solo5

Ce choix d’implémentation est fait par l’outil mirage, lorsque vous lancez: mirage configure. Dans ce cas, on prend l’implémentation par défaut proposée par mirage mais l’utilisateur peut très bien choisir son implémentation (tant qu'elle respecte la signature Mirage_time.S).

Autres aspects

Il y aurait encore beaucoup à dire, car cet article ne peut être exhaustif !
Vous êtes invités à le compléter dans vos commentaires.

Commentaires : voir le flux Atom ouvrir dans le navigateur

par Dinosaure, Snark, Ysabeau, palainp, theojouedubanjo, bobble bubble, BAud, Pierre Jarillon, palm123, Xavier Claude, nokomprendo, dourouc05, tisaac, Nicolas Casanova

DLFP - Dépêches

LinuxFr.org

Entretien avec GValiente à propos de Butano

 -  16 avril - 

GValiente développe un SDK pour créer des jeux pour la console Game Boy Advance : Butano.Cet entretien revient sur son parcours et les raisons (...)


Nouveautés d'avril 2024 de la communauté Scenari

 -  11 avril - 

Scenari est un ensemble de logiciels open source dédiés à la production collaborative, publication et diffusion de documents multi-support. Vous (...)


Annuaire de projets libres (mais pas de logiciels)

 -  9 avril - 

Les communs sont une source énorme de partage !S’il est plutôt facile dans le monde francophone de trouver des ressources logicielles (Merci (...)


Les enchères en temps réel, un danger pour la vie privée mais aussi pour la sécurité européenne

 -  7 avril - 

Les enchères en temps réel, ou Real-Time Bidding (RTB), sont une technologie publicitaire omniprésente sur les sites web et applications mobiles (...)


XZ et liblzma: Faille de sécurité volontairement introduite depuis au moins deux mois

 -  31 mars - 

Andres Freund, un développeur Postgres, s’est rendu compte dans les derniers jours que xz et liblzma ont été corrompus par l’un des mainteneurs du (...)