URL: https://linuxfr.org/news/mirageos-un-micro-os-unikernel-en-ocaml Title: MirageOS - un micro OS (unikernel) en OCaml Authors: Dinosaure Snark, Ysabeau đŸ§¶, palainp, theojouedubanjo, bobble bubble, BAud, Pierre Jarillon, palm123, claudex, nokomprendo, dourouc05, tisaac et Nicolas Casanova Date: 2020ćčŽ08月25æ—„T13:41:21+02:00 License: CC By-SA Tags: mirageos et unikernel Score: 74 [MirageOS](http://mirage.io/) est un outil permettant de crĂ©er un [unikernel](https://fr.m.wikipedia.org/wiki/Unikernel#:~:text=Les%20unikernels%20sont%20des%20images,des%20syst%C3%A8mes%20d'exploitation%20biblioth%C3%A8ques.) (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](https://ocaml.org/) (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](https://xenproject.org) ou [KVM](https://www.linux-kvm.org). Dans cette dĂ©pĂȘche, nous allons voir ce qu’est concrĂštement MirageOS et expliquer comment l’utiliser. ---- [MirageOS](https://mirage.io/) [OCaml](https://ocaml.org/) ---- # 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](https://fr.wikipedia.org/wiki/ML_(langage)) (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 : ```ocaml 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 : ```ocaml 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](https://mirage.io/blog/2018-esp32-booting). L’objectif est que le cĂŽtĂ© applicatif ne devrait pas changer. ## Gestion de la mĂ©moire Depuis la version [3.9](https://github.com/ocaml/opam-repository/pull/17485) (octobre 2020), seul le mode [PVH(VM)](https://wiki.xenproject.org/wiki/PV_on_HVM) est pris en charge, la partie bas niveau permettant le boot est assurĂ©e par [Solo5](https://github.com/solo5/solo5) et la gestion de la mĂ©moire est passĂ©e Ă  la version de [Doug Lea pour malloc](https://github.com/mirage/ocaml-freestanding/blob/master/nolibc/dlmalloc.i). # 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](https://tezos.com/) 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`](https://en.wikipedia.org/wiki/Zone_file) 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](https://github.com/mirage/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](https://www.docker.com/blog/docker-unikernels-open-source/). 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](https://github.com/mirage/mirage-stack/blob/master/src/mirage_stack.ml)) 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](https://github.com/mirage/ocaml-cohttp). Pour l’exemple, le site officiel de [MirageOS](https://mirage.io/) 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](https://github.com/mirage/mirage-skeleton) 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`](https://mirage.github.io/mirage-time/mirage-time/Mirage_time/module-type-S/index.html). 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.

AltStyle ă«ă‚ˆăŁăŠć€‰æ›ă•ă‚ŒăŸăƒšăƒŒă‚ž (->ă‚ȘăƒȘă‚žăƒŠăƒ«) /