DDelonix RuntimeGuide du contributeur

Fondamentaux

Initiation au cloud native

Avant de lire : Fondations Linux (namespaces, cgroups v2, descripteurs de fichier) et IaaS et cloud native (de quoi le moteur est responsable).

Le moteur est une couche fine et soigneuse au-dessus de fonctionnalités du noyau Linux et d’une poignée de spécifications ouvertes. Cette page vous donne juste assez de chaque concept pour lire le code, et dit où il vit dans ce dépôt. Après elle, vous pourrez prendre n’importe quel mécanisme — un namespace, une limite de cgroup, une couche d’image, une chaîne de pare-feu, le démarrage d’une VM, l’apply d’un manifeste — et nommer le fichier et le symbole qui l’implémente. Pour aller plus loin, suivez les liens officiels — ils valent mieux que n’importe quel résumé ici.

Les concepts sont enseignés une seule fois dans ce manuel. Les primitives du noyau (namespaces, user namespaces, cgroups v2) sont enseignées en pratique dans Fondations Linux, donc les sections 4.1 et 4.2 ne font que les récapituler et les relier au code. Ce que chaque standard ouvert exige, et jusqu’où le moteur y est conforme, se trouve dans Standards cloud native, plus loin dans le parcours. Les chemins nomment des crates (crates/<couche>/<crate>) ; les couches sont expliquées dans Architecture, et pour l’instant un chemin dit simplement où se trouve le code.

Chaque section comporte trois parties : le concept, Dans Delonix (fichiers et symboles que vous pouvez chercher avec grep), et Pour aller plus loin. Les chemins sont relatifs à la racine du dépôt.

Pour vous orienter dans l’écosystème plus large, le CNCF Landscape et le CNCF Glossary sont de bonnes cartes. Les comparaisons avec runc, crun, containerd ou Podman n’apparaissent que là où elles aident à expliquer un choix de conception.


4.1 Namespaces Linux et fonctionnement rootless#

Récapitulatif. Un container est un processus démarré dans un nouvel ensemble de namespaces (mount, PID, network, IPC, UTS, cgroup, user). Le user namespace est ce qui le rend rootless : à l’intérieur, le processus est uid 0 sur les ressources que ce namespace possède ; à l’extérieur, c’est un utilisateur ordinaire. Un utilisateur non privilégié ne peut mapper que son propre uid ; une plage nécessite newuidmap/newgidmap et /etc/subuid. Les deux sont enseignés, avec des commandes à taper, dans Fondations Linux — Namespaces et User namespaces et mappage d’uid. Certaines distributions restreignent en plus les user namespaces non privilégiés via AppArmor — voir Environnement pour les conséquences pratiques.

Dans Delonix

  • fn spawn dans crates/adapters/delonix-linux/src/lib.rs construit les CloneFlags (CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWNET, CLONE_NEWUSER, …) et appelle nix::sched::clone. Le partage d’IPC/UTS entre membres d’un pod est géré par setns dans container_init.
  • write_userns_maps dans le même fichier écrit les mappages depuis le parent : un mappage à un seul uid (0 <euid> 1) en rootless, ou une plage de subuid via newuidmap/newgidmap quand have_subid_helpers() indique qu’ils sont disponibles. USERNS_UID_BASE/USERNS_RANGE définissent la plage utilisée en mode root.
  • setup_rootfs monte la racine du container et appelle pivot_root ; container_init est le code qui s’exécute dans les nouveaux namespaces avant execvp.
  • Les opérations rootless sur des fichiers appartenant à des subuids mappés ré-exécutent le binaire dans un user namespace mappé : reexec_mapped, reexec_mapped_hold, remove_tree_mapped.

Pour aller plus loin : namespaces(7), user_namespaces(7), newuidmap(1), subuid(5), pivot_root(2).


4.2 cgroups v2 et délégation#

Récapitulatif. cgroup v2 est un arbre unique à /sys/fs/cgroup dont les fichiers (memory.max, cpu.max, pids.max, memory.events) limitent et comptabilisent un groupe de processus. Un utilisateur non privilégié ne peut écrire que dans une sous-arborescence que systemd lui a déléguée, et un shell dans une scope de session SSH se trouve généralement en dehors — si bien que des limites peuvent, en silence, ne pas s’y appliquer. L’arbre, la règle « pas de processus internes » et la délégation sont enseignés en pratique dans Fondations Linux — cgroups v2 ; le contrat de délégation en tant que standard, et la conformité du moteur, se trouvent dans Standards cloud native — 13.15. Le moteur ne prend en charge que v2.

Dans Delonix

  • Le mode root place les containers sous delonix_compute::DELONIX_SLICE (/sys/fs/cgroup/delonix.slice).
  • Le mode rootless trouve le cgroup de service de l’utilisateur et crée des feuilles sous <user@uid.service>/dlx-containers — voir user_service_base et try_delegated_base dans crates/adapters/delonix-linux/src/lib.rs. cgroup_limits_apply répond à « les limites s’appliqueront-elles sur cet hôte ? » sans démarrer de container.
  • La décision de conception sur le niveau intermédiaire est l’ ADR-0015 ; la façon dont le CRI suit la hiérarchie de cgroups du kubelet est l’ADR-0038, avec le parent du kubelet validé par KubeCgroupParent::parse dans crates/contexts/delonix-compute/src/record.rs.

Pour aller plus loin : Noyau Linux — Control Group v2, systemd — Control Group APIs and Delegation, cgroups(7).


4.3 Capabilities, seccomp, AppArmor, chemins masqués#

Le pouvoir de root se divise en capabilities (CAP_NET_ADMIN, CAP_SYS_ADMIN, …). Un container conserve un petit ensemble par défaut et abandonne le reste. seccomp installe un filtre BPF qui autorise ou refuse des appels système, réduisant la surface d’attaque du noyau. AppArmor (et SELinux sur d’autres distributions) sont des Linux Security Modules qui confinent un processus par profil. Enfin, les runtimes masquent des chemins sensibles de /proc et /sys (montent quelque chose de vide par-dessus) et rendent d’autres lecture seule, parce que ces fichiers révèlent des informations sur l’hôte ou permettent de le contrôler.

Un point subtil que le code documente : clone3 passe ses flags via un pointeur qu’un filtre seccomp ne peut pas inspecter, si bien qu’un filtre qui bloque clone(CLONE_NEWUSER) doit aussi faire échouer clone3 avec ENOSYS pour forcer la libc à revenir au clone filtrable.

Dans Delonix

Pour aller plus loin : capabilities(7), noyau — Seccomp BPF, Documentation AppArmor, OCI runtime spec — Linux config (les champs maskedPaths/readonlyPaths/seccomp que d’autres runtimes consomment).


4.4 Images OCI, stockage adressé par contenu et overlayfs#

L’Open Container Initiative publie trois spécifications :

  • la image spec — une image est un manifeste (JSON) qui pointe vers une config et une liste ordonnée de layers (des tarballs), et éventuellement un index pointant vers un manifeste par plateforme ;
  • la distribution spec — l’API HTTP que servent les registres (/v2/<name>/manifests/<ref>, /v2/<name>/blobs/<digest>, authentification par jeton) ;
  • la runtime spec — comment un runtime tel que runc ou crun reçoit l’instruction d’exécuter un bundle de système de fichiers.

Tout est adressé par contenu : un blob est nommé par le digest SHA-256 de ses octets, si bien qu’un client vérifie ce qu’il a téléchargé en le hachant. Un pull par digest (name@sha256:…) n’est une garantie que si le manifeste lui-même est vérifié par rapport à ce digest, en plus de chaque blob par rapport au manifeste.

À l’exécution, les couches sont empilées avec overlayfs : des lowerdir en lecture seule, un upperdir inscriptible où les changements sont copiés, et un workdir. Beaucoup de containers peuvent partager les mêmes couches inférieures.

Dans Delonix

  • Client de registre (distribution spec) : crates/adapters/delonix-oci/src/registry.rs — fonctions pull_from_registry*, les types de média ACCEPT_MANIFEST, et verify_manifest_digest. Les types viennent du crate oci-spec.
  • Store de blobs adressé par contenu : Cas dans crates/adapters/delonix-oci/src/cas.rs.
  • Écriture d’une archive au format OCI image layout : write_oci_archive dans save.rs.
  • Préparation de l’overlay : ImageStore::prepare_overlay dans overlay.rs écrit un marqueur overlay-lowers (LOWERS_FILE) ; le montage lui-même se produit à l’intérieur des namespaces user et mount du container dans mount_overlay_if_marked (crates/adapters/delonix-linux/src/lib.rs), via la nouvelle API de montage — voir ADR-0016 et ADR-0037.
  • Le moteur exécute lui-même les containers plutôt que de confier un bundle de runtime OCI à runc/crun.

Pour aller plus loin : OCI image spec, OCI distribution spec, OCI runtime spec, noyau — Overlay Filesystem.


4.5 Réseau des containers#

Briques de base du réseau Linux :

  • un network namespace a ses propres interfaces, routes et pare-feu ;
  • une paire veth est un câble virtuel avec une extrémité dans chaque namespace ;
  • une bridge est un commutateur virtuel reliant plusieurs extrémités veth ;
  • nftables est le filtre de paquets et moteur NAT du noyau ; DNAT réécrit une destination (comment un port publié atteint un container), et conntrack suit les flux pour que le trafic de réponse d’une connexion autorisée passe (ct state established,related) ;
  • slirp4netns donne une connectivité sortante à un network namespace non privilégié en émulant une pile TCP/IP en espace utilisateur, et redirige des ports de l’hôte vers celui-ci ;
  • VXLAN transporte des trames L2 sur UDP entre hôtes, et WireGuard chiffre un tunnel.

CNI (Container Network Interface) est une spécification où un runtime exécute des binaires de plugin (bridge, host-local, portmap, …) avec des commandes ADD/DEL et une configuration JSON depuis /etc/cni/net.d. Les runtimes Kubernetes l’utilisent pour le réseau des pods.

Dans Delonix

  • Le réseau rootless ne peut pas créer d’interfaces sur l’hôte, donc le moteur maintient un network namespace holder de longue durée : un processus pin minimal possède les namespaces, et un processus control redémarrable sert un socket Unix. Voir start_pin, start_control et ensure_up dans crates/adapters/delonix-sdn/src/infra.rs.
  • Attacher un workload : attach_container (IPAM + commande de contrôle) et do_attach (veth vers la bridge, à l’intérieur du holder). Les noms de bridge viennent de bridge_name, dans le crate sans dépendances crates/foundation/delonix-net-rules/src/lib.rs.
  • Sortie et redirection de ports : slirp_attach et slirp_add_hostfwd dans crates/adapters/delonix-sdn/src/lib.rs (qui démarrent slirp4netns) ; la publication à l’intérieur du holder dans publish_port/do_publish (infra.rs).
  • Pare-feu : table ip dlxing avec les base chains fwguard, fwdeny, fwcont et la verdict map fwmap (FWMAP), générée dans infra.rs (do_firewall, apply_firewall_all, ns_set_join pour les sets d’isolement de namespace).
  • DNS interne (nom standard <name>.<namespace>.svc.delonix.internal, l’ancien <name>.<namespace>.delonix.internal répond toujours ; service_fqdn, parse_internal_name) : dns_server_main, handle_dns, dns_resolve_for, dns_resolve_multi_for dans infra.rs.
  • Réseaux overlay : set_vxlan (infra.rs) et les assistants WireGuard dans crates/adapters/delonix-sdn/src/wg.rs, orchestrés par realize_overlay dans bins/delonix-runtime-bin/src/cmd/network.rs.
  • CNI : crates/adapters/delonix-sdn/src/cni.rs — add, del, readiness, attach_named_netns. L’usage en rootless est opt-in (enabled_conf vérifie DELONIX_CNI=1) ; le chemin CRI en root utilise la chaîne CNI du nœud (root_cni_readiness dans crates/interfaces/delonix-cri/src/runtime_svc.rs).
  • Décisions de topologie : ADR-0013, ADR-0014.

Pour aller plus loin : network_namespaces(7), veth(4), wiki nftables, slirp4netns, noyau — VXLAN, WireGuard, CNI et sa spécification.


4.6 Kubernetes : CRI, kubelet, kubeadm et kind#

Le kubelet est l’agent de nœud de Kubernetes. Il n’exécute pas lui-même les containers ; il parle à un runtime de containers via la Container Runtime Interface, une API gRPC (RuntimeService, ImageService) servie sur un socket Unix local. Le kubelet crée une pod sandbox (RunPodSandbox) puis les containers à l’intérieur. Il a aussi un réglage de cgroup driver (systemd ou cgroupfs) qui doit correspondre à la façon dont le runtime gère les cgroups, sinon les cgroups du pod et ceux du container divergent.

kubeadm amorce un cluster sur des machines existantes (kubeadm init, kubeadm join). kind exécute des nœuds Kubernetes sous forme de containers construits à partir de l’image kindest/node.

Dans Delonix

  • crates/interfaces/delonix-cri est un serveur CRI runtime.v1. Le protobuf est proto/api.proto à l’intérieur de ce crate, compilé par build.rs avec tonic-build. Le binaire est src/bin/delonix-cri.rs.
  • Le cgroup driver rapporté au kubelet : engine_cgroup_driver dans runtime_svc.rs ; son commentaire de doc explique pourquoi la réponse est celle-là, et ce qui devrait changer pour que ce soit l’autre.
  • Un aller-retour sur du gRPC réel est testé dans crates/interfaces/delonix-cri/tests/grpc_status.rs.
  • Commandes d’amorçage de cluster : kubeadm via SSH dans bins/delonix-runtime-bin/src/cmd/cluster.rs (avec kubeadm_config.rs, etcd.rs, lb.rs), et des clusters locaux façon kind dans kindmode.rs.

Pour aller plus loin : Kubernetes — Container Runtime Interface, dépôt cri-api, Configurer un cgroup driver, kubeadm, kind.


4.7 Virtualisation : KVM, virtio, Cloud Hypervisor, libvirt, cloud-init#

KVM est l’hyperviseur du noyau, exposé comme /dev/kvm ; un VMM en espace utilisateur (QEMU, Cloud Hypervisor) l’utilise pour exécuter des invités. virtio est la famille de périphériques paravirtuels (disque, réseau, partage de système de fichiers 9p) que les invités utilisent pour des E/S efficaces. Cloud Hypervisor est un VMM en Rust centré sur les workloads cloud, capable de s’exécuter sans privilège avec accès à /dev/kvm ; il démarre les invités via un firmware (une build UEFI EDK2 ou rust-hypervisor-firmware) ou directement depuis une image de noyau. libvirt gère des domaines QEMU/KVM décrits en XML, via virsh et libvirtd.

Les cloud images sont génériques ; la configuration par instance (hostname, clés SSH, utilisateurs, réseau) vient de cloud-init, qui lit une datasource. La datasource NoCloud est un petit ISO étiqueté cidata contenant user-data, meta-data et éventuellement network-config.

Dans Delonix

Pour aller plus loin : noyau — KVM, spécification virtio (OASIS), Cloud Hypervisor et sa documentation, libvirt, cloud-init NoCloud.


4.8 Réconciliation déclarative#

Kubernetes a popularisé un modèle où les utilisateurs soumettent un état désiré sous forme d’objets typés (apiVersion, kind, metadata, spec), et des contrôleurs le comparent répétitivement à l’état réel et agissent pour converger. kubectl apply ajoute un diff à trois voies : il stocke la dernière configuration appliquée sur l’objet, ce qui permet de distinguer « vous avez retiré ce champ de votre fichier » (le rétablir) de « quelqu’un a défini ce champ à la main » (le laisser tranquille).

Le principe, et ce qu’il vous demande quand vous ajoutez un Kind, se trouvent dans IaaS et cloud native — Déclaratif et convergent.

Dans Delonix

  • Le moteur a ses propres Kinds dans des groupes d’API (delonix api-resources les liste). Les faits sur chaque Kind (domaine, s’il converge, teardown, namespacing) vivent dans une seule table : KindFacts dans crates/contexts/delonix-stack/src/kinds.rs.
  • Le planificateur est pur : plan(desired, actual, stack) dans crates/contexts/delonix-stack/src/reconcile.rs. Le commentaire de module contient la table de vérité à trois voies, et le dernier spec appliqué est stocké sur la ressource elle-même sous l’annotation LAST_APPLIED (delonix.io/last-applied) — il n’y a pas de fichier d’état séparé.
  • La propriété est une étiquette sur la ressource ; l’historique de révisions se trouve dans revision.rs (ADR-0019).
  • Les manifestes sont analysés dans bins/delonix-runtime-bin/src/cmd/manifest.rs ; stack plan/apply vivent dans cmd/stack.rs.
  • Aucune boucle de contrôleur ne s’exécute en arrière-plan : la réconciliation se produit quand une commande s’exécute (daemonless). Le réconciliateur de pull proposé conserve cette propriété en étant un timer systemd qui invoque le même apply, et non un processus résident (ADR-0021, état Proposed).

Pour aller plus loin : Kubernetes — Objects, Controllers, Gestion déclarative avec kubectl apply.


4.9 Observabilité et l’interface MCP#

OpenTelemetry est un standard CNCF pour les traces, métriques et logs, exportés via OTLP vers un collecteur. Prometheus récupère des métriques depuis un endpoint HTTP /metrics dans un format d’exposition texte. Le Model Context Protocol est un protocole ouvert qui permet à des clients d’IA de découvrir et d’appeler des outils exposés par un serveur, généralement via stdio.

Dans Delonix

Pour aller plus loin : Documentation OpenTelemetry, Spécification OTLP, Prometheus — formats d’exposition, Model Context Protocol.


4.10 Daemonless, en un paragraphe#

containerd et le Docker Engine gardent un daemon résident qui possède l’état des containers ; Podman a montré qu’un runtime peut à la place être une commande qui se termine, avec des processus auxiliaires par container et systemd pour tout ce qui doit persister. Delonix suit le second modèle : la CLI fait le travail et se termine, l’état ce sont des fichiers sous la racine d’état protégés par flock (voir Initiation à Rust §3.8), un processus superviseur existe par container détaché, le holder réseau n’existe que tant que quelque chose en a besoin, et la persistance au démarrage passe par des units systemd (bins/delonix-runtime-bin/src/cmd/boot.rs). Les conséquences — bonnes et mauvaises — sont discutées dans Architecture et System Design Interview. Le principe lui-même, et la règle qu’un nouveau daemon exige un ADR, se trouvent dans IaaS et cloud native — Daemonless.