Garambrogne 2.0http://blog.garambrogne.net/2024-03-08T09:04:00+01:00Campagne internationale pour la sécurité informatique (1/3)2024-03-08T09:04:00+01:002024-03-08T09:04:00+01:00Mathieu Lecarmetag:blog.garambrogne.net,2024-03-08:/la-maison-blanche-veut-securiser-Internet-1.html<p>La Maison-Blanche demande un audit sur la sécurité des logiciels open source. Partie 1/3 : sécuriser la chaine d’approvisionement.</p><h1>Campagne internationale pour la sécurité informatique</h1>
<p>Il y a quelques jours, <a href="https://www.whitehouse.gov/wp-content/uploads/2024/02/Final-ONCD-Technical-Report.pdf">la Maison-Blanche demande un audit sur la sécurité de ses logiciels</a>, tout en reconnaissant l’utilisation massive (et indispensable) de l’écosystème open source pour les services de l’État.</p>
<p>Le sujet de l’audit est large (et très technique), et a pour objectif d’améliorer drastiquement la sécurité des logiciels.
Les pistes sont bien connues, mais là, c’est écrit noir sur blanc par la Maison-Blanche.</p>
<ul>
<li>Sécurisation des fournisseurs de paquets (logiciels et bibliothèques)</li>
<li>Mis en place d’outils pour quantifier la qualité et la sécurité du code</li>
<li>Remplacement des logiciels codé avec des langages sans gestion stricte de la mémoire (memory safe).</li>
</ul>
<p><a href="https://github.blog/category/security/">Github fournit des outils sur ce thème</a> depuis quelque temps, aucune surprises, mais entendre le président des États-Unis dauber sur le C/C++, c’est une première.
Oui, le document de la Maison-Blanche cite le nom de Biden à tour de bras, j’imagine que c’est l’usage dans ce pays, mais hors <span class="caps">US</span>, on a l’impression que c’est une attaque personnelle.</p>
<p>Le document est plein d’intentions, on attend le résultat de l’audit pour avoir des éléments concrets, mais il contient quand même plein de liens bien plus concrets comme <a href="https://www.cisa.gov/sites/default/files/2023-09/CISA-Open-Source-Software-Security-Roadmap-508c.pdf">une roadmap sur la sécurité open source</a></p>
<p>Comme le sujet est touffu, et plein d’acronymes, le billet sera coupé en trois, en suivant le découpage de la Maison-Blanche :</p>
<ul>
<li>Sécuriser la chaine d’approvisionnement (ce billet)</li>
<li>Quantifier la qualité du code</li>
<li>Protéger l’accès à la mémoire</li>
</ul>
<h2>Dépendances logicielles</h2>
<p>Une application est composée de code pris sur l’étagère, sous forme de source ou de paquets (gros binaire, paquet système, image Docker…) ou de code développé maison qui va utiliser des bibliothèques existantes.</p>
<p>Pour ne pas réinventer la roue (et la maintenir), une application va dépendre de paquets (au sens large) gérés par des tiers.</p>
<p>C’est un des aspects principaux de l’open source.</p>
<p><a href="https://xkcd.com/2347/"><img alt="Dependency by XKCD" class="image-process-article-image" src="derivatives/article-image/dependency_2x.png"/></a></p>
<p>Le sujet est pris en main par la <strong>Linux Foundation</strong>, via l’<a href="https://openssf.org/">Opensource Security Foundation</a>, via <a href="https://alpha-omega.dev/">Alpha-Omega</a> fondé par les gros du cloud (Amazon, Google et Microsoft) pour supporter les outils essentiels de l’open source, du début à la fin (la ref ⍺ - Ω, vous l’avez?).</p>
<p>Les cascades de fondations et de comités, avec pour l’instant des zones floues ou redondantes, laissent présager moult réunions et visioconférences.
Mais trêve de mauvaise langue, ils ont déjà produit des choses concrètes : code, données et financement de projets existants.
L’adoubement de la fondation Linux devrait calmer la fascination de la dispersion et des projets qui n’aboutissent jamais.</p>
<h2>Chaine d’approvisionnement</h2>
<p>Gérer la dépendance à des produits tiers se joue sur la durée, il peut se passer plein de choses durant ce voyage.</p>
<h3>Failles officielles</h3>
<p>Découvrir une chouette faille de sécurité est un titre de gloire, surtout si ça permet de faire des choses créatives et imprévues.</p>
<p>Pour éviter les doublons et pour que tout le monde parle de la même chose, l’usage est d’enregistrer sa vulnérabilité auprès de <span class="caps">CVE</span>, <a href="https://cve.mitre.org/">Common Vulnerabilities and Exposures</a> géré par l’organisme Mitre, soutenu par le département de la sécurité intérieure des Etats-Unis.
<span class="caps">CVE</span> est le standard de fait, mais sa gestion est problématique, cf <a href="https://daniel.haxx.se/blog/2024/02/21/disputed-not-rejected/">Curl conteste certains de ses CVEs</a>.
L’<span class="caps">API</span> de <span class="caps">CVE</span> est peu pratique, et ses données, très peu formatées, sont dures à exploiter.</p>
<p>L’OpenSSF propose un nouveau format, développé conjointement avec la communauté open source : l’<a href="https://osv.dev/"><span class="caps">OSV</span></a> (pour Open Source Vulnerability).</p>
<p>Les paquets sont identifiés par un écosystème (le dépôt officiel), un nom et une version, ou directement par un commit hash.</p>
<p><span class="caps">OSV</span> <a href="https://google.github.io/osv.dev/data/">agrège de multiples sources</a>.</p>
<p><span class="caps">OSV</span> fournit une <span class="caps">API</span> <span class="caps">REST</span> et un cli (en go) qui sait lire la <a href="https://google.github.io/osv-scanner/supported-languages-and-lockfiles/">liste de dépendances gelées de différents formats</a> ainsi que les listes de paquets installés de Alpine ou Debian, les images Docker basées sur Debian.</p>
<p>Tout l’outillage d’<span class="caps">OSV</span> est lié à <span class="caps">GCE</span>, avec des services pénibles comme AppEngine, ou l’utilisation de <span class="caps">GCS</span> (du S3, quoi) comme source.
La documentation ressemble à un publireportage <span class="caps">GCE</span>, ce qui est dommage.
D’un autre côté, proposer un schéma de table, agréger les failles, lire des listes de paquets n’est pas un challenge technique, mais surtout un travail politique pour convaincre et devenir la référence.</p>
<h4>Paquets malhonnêtes</h4>
<p>Au-delà des failles (involontaires), il existe aussi des paquets volontairement dangereux qui <a href="https://github.com/ossf/package-analysis/blob/main/docs/case_studies.md">peuvent faire par exemple ce genre de choses</a>.</p>
<p>Le projet <a href="https://github.com/ossf/package-analysis">package-analysis</a> effectue l’analyse dynamique de paquet, en le lançant dans un environnement isolé ( <a href="https://gvisor.dev/">gVisor, un conteneur paranoïaque</a>, pour observer les fichiers qu’il tripote, les adresses où il se connecte.</p>
<p>Les paquets scélérats, compromis, ou avec des noms trompeurs (aka typosquatting) sont répertoriés par le projet <a href="https://github.com/ossf/malicious-packages">malicious-packages</a> pour créer des <span class="caps">OSV</span>.</p>
<h3>Paquets de confiance</h3>
<p>Il arrive qu’un paquet légitime soit corrompu durant son cycle de vie.</p>
<p>L’attaque peut être effectuée à différents niveaux.</p>
<ul>
<li>Des patchs malicieux peuvent être proposés, prétendant améliorer un point alors qu’il en fragilise un autre. <a href="https://www.theverge.com/2021/4/30/22410164/linux-kernel-university-of-minnesota-banned-open-source">Un étudiant a réussi à faire passer un patch malicieux dans le kernel Linux avant de s’en vanter. Sa fac, l’université du Minnesota, s’est fait bannir avec lui</a>.</li>
<li>En truandant l’authentification d’un mainteneur, il est possible de publier une version scélérate d’un paquet sur un dépôt.</li>
<li>Via l’intégration continue, il est possible de modifier ou de publier des bêtises.</li>
<li>Il est tentant de modifier le cache d’un dépôt, bien que les signatures soit censées empêcher ce genre d’attaque.</li>
</ul>
<h4>Dépôts pour bibliothèques de langage</h4>
<p>Les langages disposent de bibliothèques de code disponibles via des dépôts spécifiques.</p>
<p>La sensibilisation à la sécurité pour la publication de bibliothèque a été bien trop tardive.
Même si la possibilité de signer un paquet est arrivé tôt, sans prosélytisme, ça a eut du mal à prendre, et il faut une chaine de confiance, de bout en bout, pour que ce soit utilisable.</p>
<h5>Python Pypi</h5>
<p>Dans leur liste de diffusion, <a href="https://mail.python.org/pipermail/distutils-sig/2016-May/028933.html">les paqueteurs Python évoquent le drame du faible taux de signature en 2016</a>, de toute façons, seul le format binaire, <a href="https://packaging.python.org/en/latest/specifications/binary-distribution-format/#signed-wheel-files">les whl, gèrent les signatures</a>.</p>
<p>Python a des spécifications pour l’empaquetage, <a href="https://www.pypa.io/en/latest/">Pypa</a>, un hangar qui progresse bien, <a href="https://warehouse.pypa.io/">Warehouse</a>, mais côté client, c’est très confus.</p>
<p>Il y a plusieurs formats pour définir les dépendances, rien de bien précis pour le verrouillage, que n’utilise pas l’outil par défaut, <em>pip</em>.
Les challengers, <a href="https://python-poetry.org/">poetry</a> et <a href="https://github.com/frostming/unearth">unearth</a> manquent d’ambitions, alors que <a href="https://github.com/astral-sh/uv">uv</a> rêve de la perfection de rust.</p>
<p>Plutôt que de batailler avec des clés de développeurs, Python se dirige vers la notion de <a href="https://docs.pypi.org/trusted-publishers/">diffuseur de confiance</a>.
On indique à Pypi qu’un build précis d’une <span class="caps">CI</span> (projet, branche…) est de confiance.
La <span class="caps">CI</span> va publier vers Pypi l’artéfact, avec le token <span class="caps">OIDC</span> contenant les détails du build, si le token est valide, et si les détails correspondent à ce qui a été paramétré, Pypi accepte l’artéfact.</p>
<p>Même si cette approche semble simple, fonctionne pour une personne ou une <span class="caps">CI</span>, elle ne survit pas à la compromission du serveur Pypi, personne ne pourra savoir quels paquets ont été bidouillés.</p>
<p>Il faut que les paquets aient une signature auditable, qui soit faite en dehors du serveur Pypi.</p>
<h5>Golang</h5>
<p>Golang ne prévoit rien pour signer, il délègue toute la confiance au dépôt de source distant (Github, quoi).
Par contre, <a href="https://go.dev/ref/mod#go-sum-files">Golang maintient une base de hash pour les différentes versions d’un paquet, avec un journal transparent</a>, ce qui permet d’utiliser un miroir pour télécharger un paquet.</p>
<p>Les applications Golang sont livrées sous forme binaire, que vous pouvez signer de manière classique.</p>
<h5>Ruby Gem</h5>
<p><a href="https://guides.rubygems.org/security/">Ruby documente l’utilisation de certificat asymétrique avec ses gems</a> mais avoue ne pas savoir comment gérer une chaine de confiance, et qu’ils en discutent.</p>
<p>Par contre, <a href="https://guides.rubygems.org/trusted-publishing/adding-a-publisher/">une gem peut faire confiance à une source <span class="caps">OIDC</span></a> avec l’exemple d’une action de la <span class="caps">CI</span> Github (et ils citent la documentation pypi pour comprendre le fonctionnement).</p>
<p>Même fragilité que Pypi en cas de compromission du serveur.</p>
<h5>Node npm</h5>
<p>Longtemps mauvais élève, <a href="https://docs.npmjs.com/generating-provenance-statements">Npm vante maintenant la notion de provenance avec Sigstore</a> et propose de <a href="https://docs.npmjs.com/verifying-registry-signatures">vérifier la signature du dépôt utilisé</a> : <code>npm audit signatures</code>.</p>
<p>En signant les paquets en dehors du dépôt et en utilisant un journal public, ce que permet l’utilisation de <a href="https://www.sigstore.dev/">Sigstore</a>, le dépôt npm est moins fragile que pypi et rubygem.</p>
<p>Les concurrents de Nodejs ne sont pas très ambitieux sur la sécurité:</p>
<ul>
<li><a href="https://docs.deno.com/runtime/manual/basics/modules/integrity_checking">Deno se contente d’écrire les hashs des dépendances dans un fichier </a>.</li>
<li><a href="https://bun.sh/docs/install/registries">Bun utilise le registre de npm, sans rien ajouter</a>.</li>
</ul>
<h5>Rust crates</h5>
<p>Pour l’instant, les paquets publiés sur crate.io ne sont pas signés, mais il y a en préparation <a href="https://github.com/trustification/rust-rfcs/blob/sigstore-rfc/text/0000-sigstore-integration.md">une <span class="caps">RFC</span> pour intégrer Sigstore</a>.
La <a href="https://doc.rust-lang.org/cargo/guide/continuous-integration.html">documentation parle d’intégration continue</a>, mais pas de déploiement continu.
Pour l’instant, crate.io authentifie les utilisateurs en OAuth2 avec Github, et permet la création de token pour publier, on a donc un lien entre un crate publié et un compte Github, mais c’est un peu peu.</p>
<h5>Paquets Linux</h5>
<p>La gestion de paquets des distributions Linux est mature depuis bien longtemps, et toutes gèrent les signatures (sauf Slackware).</p>
<p>La plupart des distributions utilisent gpg pour signer les paquets (sauf Alpine qui se distinguent avec de la clé openssl nue).</p>
<p><a href="https://archlinux.org/master-keys/">ArchLinux prend la peine d’établir un vraie rituel de signature</a> :</p>
<ul>
<li>5 clés maitres ont le pouvoir signer les clés des mainteneurs.</li>
<li>5 personnes détiennent une clé maitre ainsi que la clé de résiliation d’une autre clé maitre.</li>
<li>Une clé de mainteneur est légitime pour signer un paquet si elle est signée par au moins 3 clés maitres.</li>
</ul>
<p>Les autres distributions se contentent de fournir un trousseau avec des personnes de confiance.</p>
<p>Même si les distributions Linux disposent d’une <span class="caps">PKI</span> pour signer leurs paquets, et empaquetent une partie des bibliothèques de langage, les paquets Linux ne sont pas utilisable pour développer des applications métiers.</p>
<ul>
<li>Le cycle de vie d’un paquet Linux est lié à une version de la distribution, seuls les mises à jour de sécurité seront rétro-appliquées (backportées, quoi), les bugs fonctionnels resteront en l’état, et certaines distributions se permettent des patchs qui ne seront pas supportés par le mainteneur de la bibliothèque.</li>
<li>Une bibliothèque emballée dans un paquet Linux sera disponible dans une unique version majeure, utilisé par tous les paquets qui en ont besoin.</li>
<li>Même si les langages compilés modernes créent des binaires statiques, les paquets Linux doivent être reproductibles, et donc les bibliothèques des sources doivent être empaquetées. Cette règle empêche d’avoir des paquets officiels pour des applications agressives sur les versions de bibliothèques, comme Mongodb, Clickhouse ou même Nodejs.</li>
<li>Le vendoring, largement utilisé dans le développement contemporain, en gérant de manière isolée (et redondante) les dépendances logicielles est un tabou des distributions Linux. Il empêche la rationalisation et le travail de correction des mainteneurs.</li>
<li>Les formats de paquets historiques sont vieillissants, confus et la somme d’un empilement d’outils. Leur cout est négligeable par rapport à la valeur de la cohérence d’une distribution, mais hors de prix pour des applications métiers.</li>
<li>Les distributions Linux récentes sont tentées par la livraison continue, <em>rolling release</em>, sans jamais figer de version. Cette approche favorise la fraicheur des paquets, mais ne garanti plus la constance du comportement des applications ni même la possibilité d’avoir des mises à jour espacées dans le temps. Conçu pour les postes de travail, ce genre de distribution impose des stratégies de mises à jour rigoureuses sur un serveur, comme l’immutabilité</li>
</ul>
<h5>Paquets Linux universels</h5>
<p>Fournir des paquets Linux pour un éditeur est une tannée.</p>
<p>En ciblant des serveurs, il est imaginable d’imposer des distributions majeures comme Redhat, Debian ou Ubuntu, mais pour des applications grand public, il y aura trop de variations possibles.
S’ajoutent à ça les soucis de dépendances à des versions précises de bibliothèques.</p>
<p>Pour l’utilisateur, installer une application non disponible dans sa distribution, sans forcément avoir accès aux sources n’est pas une très bonne idée d’un point de vue sécurité.
Un paquet Linux classique s’installe avec les droits de super utilisateur et fait bien ce qu’il veut.</p>
<p>Depuis l’avènement des conteneurs, Linux sait correctement isoler des applications, même si l’intégration à un bureau ne fait pas partie des objectifs d’un Docker (même si <a href="https://blog.jessfraz.com/post/docker-containers-on-the-desktop/">Jessfraz a relevé le challenge</a>).</p>
<p>Freedesktop a effectué un travail de titan pour normaliser les points d’entrées d’un bureau Linux, permettant une intégration élégante d’applications disparates.
Il faut donc que les applications, bien que bordées, puissent profiter de Freedesktop.</p>
<h6>Flatpack</h6>
<p><a href="https://flatpak.org/">Flatpak</a> se positionne comme solution universelle de déploiement d’application sur Linux.</p>
<p>Il s’appuie sur <a href="https://github.com/containers/bubblewrap">Bubble Wrap</a> pour l’isolation (namespace et seccomp), cgroup étant géré par systemd.</p>
<p>Plutôt que de bricoler un chroot obèse comme image de base, <a href="https://docs.flatpak.org/en/latest/available-runtimes.html">FlatPack propose des runtimes</a> qui seront mutualisés entre les différents paquets (et mis à jour indépendamment de l’application, en s’engageant à garder stable l’<span class="caps">API</span>).</p>
<p>Le dédoublonnage, et le versionnage des fichiers sont gérés par <em>ostree</em>.</p>
<p>Une <a href="https://docs.flatpak.org/en/latest/sandbox-permissions-reference.html">liste compréhensible de droits</a> permet de choisir ce que peut faire une application.</p>
<p>Pour les mises à jour, un outil permet de le faire en ligne de commande, ou un plugin permet à Flatpack de s’incruster dans le gestionnaire de paquets de Gnome.</p>
<h6>Snap</h6>
<p><a href="https://snapcraft.io/">Snap</a> voit plus loin que le bureau Linux (il a été conçu pour l’<span class="caps">IOT</span>), mais son choix d’utiliser Apparmor brime la famille de distributions Linux qui activent SELinux automatiquement : Redhat (qui pousse Flatpack, le concurrent).
SELinux et Apparmor se branchent au même <a href="https://en.wikipedia.org/wiki/Linux_Security_Modules">endroit dans le noyau Linux</a> : ils sont exclusifs.</p>
<p>Les images de bases, appelées <em>core</em> sont basées sur Ubuntu.</p>
<p>Quand Ubuntu a décidé d’utiliser snap quand on installe un navigateur web avec un paquet <em>deb</em>, ça a un peu fait grincer des dents.
Ce choix permet d’avoir les navigateurs dans leur version courante, dans différentes versions d’Ubuntu, même les très stables <span class="caps">LTS</span> (support à long terme).
Je ne souhaite à personne de backporter les patchs de sécurités dans les bases de code tentaculaires des navigateurs webs.</p>
<p>Un Snap utilise SquashFS (un système de fichier compressé) comme format de paquet.</p>
<p>Snap se met à jour automatiquement, avec tout le confort moderne : livraison par delta, modification atomique, retour arrière possible.</p>
<p>Les <a href="https://snapcraft.io/docs/supported-interfaces">interfaces</a> permettent de réclamer des droits avec le système, et même d’en créer de nouveaux pour que les snaps discutent entre eux.</p>
<p>La documentation ne l’évoque que partiellement, mais Snap utilise des <a href="https://ubuntu.com/core/docs/reference/assertions">signatures à tous les étages</a>, avec des chaines de confiances entre le publieur, le dépôt, le paquet.</p>
<h5>Distributions Linux immuables</h5>
<p>Les distributions Linux peuvent être considérées comme un unique paquet, immuable, avec des stratégies de mises à jour atomiques, et la possibilité de rétro-déploiement (rollback), on parle alors de <a href="https://github.com/castrojo/awesome-immutable">distributions immuables</a>.</p>
<p>Ne laissant aucun choix d’installation de paquets, il n’y a pas de multiples combinaisons à tester pour valider une nouvelle version.</p>
<p>Il est possible de composer une distribution immuable à partir de paquets Linux (et de profiter des patchs, des mises à jour et de la cohérence entre eux).</p>
<p>Ces distributions peuvent avoir des stratégies de mises à jour agressive, ayant la garantie de pouvoir revenir à un état stable en cas d’incident de mises à jour ou de régression.
Le déploiement des mises à jour est différentiel (pour limiter le temps de téléchargement), et surtout signé, pour avoir un confort et une sécurité comparable à un <span class="caps">OS</span> classique.
Démarrer sur une partition en lecture seule améliore grandement la sécurité de l’<span class="caps">OS</span>, il sera impossible de corrompre cette couche (les couches supérieures devront elles, faire attention).</p>
<p>Les <span class="caps">OS</span> immuables utilisent souvent un système de paquet d’applications neutre, comme FlatPack ou des conteneurs, ne fournissant que des services de bases.</p>
<p>Le pionnier du Linux immuable, CoreOS a été racheté par RedHat qui propose maintenant des variantes de RedHat/Fedora immuables (et forcément spécialisé) et fournit <a href="https://ostreedev.github.io/ostree/">OStree</a> comme outil de base pour créer des arborescences de fichiers immuables.
Au moment du rachat, Coreos a été forké en <a href="https://www.flatcar.org/">FlatCar</a>.</p>
<h5>Images conteneurs</h5>
<p>Les conteneurs, popularisés par Docker, permettent le déploiement d’applications indépendamment de l’<span class="caps">OS</span> hôte qui va principalement fournir un noyau Linux (et donc des ressources matérielles), et contrôler son utilisation.</p>
<p>Les conteneurs sont isolés les uns des autres (namespace, cgroup, seccomp…), et si un conteneur est compromis ce sera compliqué pour atteindre les autres conteneurs.</p>
<p>Les conteneurs permettent d’avoir le même environnement sur le poste du développeur, dans l’intégration continue et en pré-production puis production.</p>
<p>Les conteneurs adorent les signatures, et savent les utiliser de bout en bout.</p>
<p>Une image conteneur va embarquer tous les fichiers nécessaires pour lancer une application.
Les arborescences de fichiers sont composés de couches, seule la plus haute permet l’écriture, permettant ainsi la mutualisation des couches (en lecture seule, donc) permettant un gain de place certain.</p>
<p>La plupart des images sont construites avec des paquets système, avant d’attaquer la couche applicative qui utilisera des paquets logiciels.
Un conteneur ne doit faire qu’une seule chose, et donc ne devrait pas nécessiter trop de paquets.</p>
<p>Chaque conteneur pourra contenir ses propres versions de paquets, et un serveur pourra contenir moult conteneurs, multipliant ainsi les failles possibles et les besoins de mises à jour.
Il existe de multiples outils pour découvrir les systèmes de paquets utilisés, puis de confronter leurs versions à des bases de failles, comme <em>osv-scanner</em> ou d’autres services payants.</p>
<p>Dans une vision naïve, l’image de base contiendra le contenu complet d’une installation Linux; une copie de tous les fichiers, on sait jamais.
120Mo pour une Debian 12.</p>
<p>Ensuite, en activant quelques options (comme ne jamais déployer la documentation) et en dégraissant la liste des paquets d’une installation de base, on arrive aux distributions dites <em>slim</em>.
75Mo pour une Debian 12 Slim.</p>
<p>Google propose de faire mieux avec <a href="https://github.com/GoogleContainerTools/distroless">Distroless</a>, toujours avec une Debian.
2 Mo pour une version <em>static</em> qui n’utilise que 3 paquets, même pas de <em>libc</em>, juste de quoi lancer une application statique.
La version <em>base</em> pèse 20Mo, avec une <em>libc</em> et <em>openssl</em>, 15Mo sans <em>openssl</em>.
Des variantes spécifiques à des runtimes sont aussi fourni : python, node et java.</p>
<p>Au-delà de l’exploit, ce minimalisme drastique améliore la sécurité de l’image en limitant la surface d’attaque, mais surtout va limiter les faux positifs dans les analyses de paquets.</p>
<p>Les conteneurs sont issus des travaux de Google pour son Borg, qui a fourni au noyau Linux les fonctions de base pour isoler les processus, ce qui a permis la création de Docker qui a assumé l’évangélisation, puis de lancer Kubernetes (le Borg pour le reste du monde).
Kubernetes a sorti de la boucle Docker en créant l’Open Container Initiative et en normalisant la couche juste en dessous de Docker : <a href="https://containerd.io/">Containerd</a>.</p>
<p>RedHat en a profité du coup de mou de Docker pour venger cet affront innovation en créant <a href="https://podman.io/">Podman</a> qui utilise poliment les normes existantes et recrée tout l’écosystème de Docker :</p>
<ul>
<li>Podman est basé sur <a href="https://cri-o.io/">cri-o</a> l’outil de bas niveau utilisé par Containerd</li>
<li>Podman utilise les images <span class="caps">OCI</span></li>
<li>Podman n’utilise pas de démon lui préférant systemd</li>
<li>Podman croit très fort aux <a href="https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md">conteneurs démarré sans droit d’admin (axa rootless)</a>, même si tout ne fonctionne de manière optimal pour l’instant</li>
</ul>
<h3>Notaire</h3>
<p>Pour signer des commits de code ou des paquets, il faut utiliser un système de clé privée/public, avec une chaine de confiance permettant de diffuser les clés publiques signées.</p>
<h4><span class="caps">GPG</span></h4>
<p><a href="https://gnupg.org/">GnuPG</a> tente depuis longtemps de démocratiser la cryptographie asymétrique.
Le concept de chaine de confiance signé entre pairs n’a jamais fonctionné comme il faut.
L’écosystème est bancal, mal maintenu :</p>
<ul>
<li>le serveur de clé est un bricolage</li>
<li>la révocation d’une clé est une tannée</li>
<li>le format de clé est confus, personne ne sait quel type de clé il utilise et si la taille de la clé est suffisante.</li>
<li>le vaillant mainteneur est seul, avec des micro-contributions d’autres personnes</li>
</ul>
<p><a href="https://keybase.io/">Keybase</a> a fait un gros travail de pédagogie en fournissant des outils agréables à utiliser.
Keybase se fiche de la chaine de confiance pour crédibiliser une clé, mais propose de la lier à différente présence sur Internet, avec un système de challenge (compte de réseaux sociaux, site web personnel, porte monnaie de crypto…).
Keybase a été racheté par Zoom qui avait besoin de se payer une réputation suite à différents incidents, puis laissé en l’état, en stagnation.</p>
<p>Debian utilise gpg et met à disposition <a href="https://keyring.debian.org/">son trousseau</a>, pour avoir un moyen simple de vérifier une signature, avec une politique de cooptation pour les nouveaux arrivants.</p>
<p>Git permet de signer avec gpg un commit ou un tag, et le kernel Linux, l’utilisateur initial de git, publie aussi <a href="https://korg.docs.kernel.org/pgpkeys.html">son trousseau</a>.</p>
<p>Je pense que l’on peut <a href="https://trends.google.fr/trends/explore?date=all&q=%2Fg%2F11c5wlscyg,%2Fm%2F09p9v">en 2024 affirmer que <span class="caps">GPG</span> n’a pas tenu ses promesses</a>, surtout son idée de chaine de confiance.</p>
<h4>The Update Framework</h4>
<p>Le <span class="caps">CNCF</span> (Cloud Native Computing Foundation) a proposé une norme pour la mise à jour de logiciels : <a href="https://theupdateframework.io/"><span class="caps">TUF</span> (The Update Framework)</a>.
Les travaux initiaux sont basés sur le système de mises à jour de <a href="https://www.torproject.org/"><span class="caps">TOR</span></a>, et ont commencé en 2009.</p>
<p><span class="caps">TUF</span> propose un framework pour sécuriser le procédure de mise à jour de paquets, en se focalisant sur les métadatas, sans empiéter sur la gestion de paquets.</p>
<p><span class="caps">TUF</span> utilise une autorité de certification, avec la clé privée qui est hors ligne, la clé publique est fourni avec le client.
Tous les efforts sont focalisés sur diminuer les dégâts en cas de compromissions de clés, pour ça <span class="caps">TUF</span> utilise une délégation de confiance avec des rôles précis, et des certificats de courtes durées.</p>
<p><span class="caps">TUF</span> a beaucoup plus à l’industrie automobile qui l’utilise via le projet <a href="https://uptane.org/">uptane</a>.</p>
<p>Docker a implémenté <span class="caps">TUF</span> avec <a href="https://github.com/notaryproject/notary">Notary</a>, avant de le sortir de la marque Docker.
Notary utilise une très classique <span class="caps">PKI</span> pour signer/vérifier, mais surtout lie aux images une <a href="https://github.com/notaryproject/specifications/blob/main/specs/trust-store-trust-policy.md#trust-policy">police de confiance</a>, un ensemble de règles disant qui peut signer où, avec quelle autorité de certification.</p>
<p>Python proscranise depuis 10 ans avec sa <a href="https://peps.python.org/pep-0480/"><span class="caps">PEP</span>-0480</a> pour savoir si ils vont faire du <span class="caps">TUF</span>.
Pourtant, l’implémentation de référence est en Python.</p>
<h4>Sigstore</h4>
<p>En s’appuyant sur les concepts de <span class="caps">TUF</span>, <a href="https://www.sigstore.dev/">Sigstore</a> propose un système de signature sans clé (keyless signing, mais le terme enthousiaste cache une clé à courte vie), pour tout l’écosystème open source, en espérant une adoption massive et une sécurité accrue, comme l’a fait <a href="https://letsencrypt.org/">Let’s Encrypt</a> pour le <span class="caps">TLS</span>.</p>
<p>Sigstore est directement sous l’égide la Linux Foundation, sans passer par le <span class="caps">CNCF</span> (qui dépend de la Linux Foundation aussi).</p>
<p>Pour afficher son côté neutre, les clés d’autorités sont des clés matérielles, détenues par 5 experts, venus de l’industrie et de l’université, pour une durée d’à peu près 18 mois, avec renouvèlement des signatures tous les 4 mois.</p>
<p>Le principe de fonctionnement de Sigstore est décrit dans une publication scientifique <a href="https://dl.acm.org/doi/pdf/10.1145/3548606.3560596">Sigstore: Logiciel signé pour tout le monde</a>, signé par deux devs de chez <a href="https://www.chainguard.dev/">Chainguard</a> et un chercheur de l’université de Purdue.
L’un des développeurs publie des trucs sur <span class="caps">TOR</span>, assez à cheval sur la sécurité, les chaines de confiance et les mises à jours nickels.</p>
<p>Chainguard, startup de la supply chain, a été fondé par des gens qui ont bossé chez Microsoft, Google, dans l’écosystème Kubernetes, avec <a href="https://knative.dev">KNative</a> par exemple.
Il est donc logique que l’on retrouve du Github (qui appartient à Microsoft) et du Google à tous les étages de Sigstore.</p>
<h5>Deux services isolés</h5>
<p>Sigstore s’appuie sur deux services :</p>
<ul>
<li><a href="https://github.com/sigstore/fulcio">Fulcio</a>, la <span class="caps">PKI</span> qui va signer la clé temporaire utilisée pour signer le paquet, après que l’utilisateur se soit authentifié avec <a href="https://fr.wikipedia.org/wiki/OpenID_Connect">OpenID Connect</a> (chez Github ou Google par exemple), en promettant d’utiliser de l’authentification à deux facteurs (<span class="caps">OIDC</span> ne précise pas l’utilisation de <span class="caps">2FA</span> dans son token).</li>
<li><a href="https://github.com/sigstore/rekor">Rekor</a>, va enregistrer les étapes de la signature d’un paquet dans un journal public qui n’efface rien (reprenant la logique de publicité des autorités de certification de <a href="https://certificate.transparency.dev/">Certificate Transparency</a>.</li>
</ul>
<h5><span class="caps">OIDC</span></h5>
<p>sigstore.dev utilise <a href="https://dexidp.io/">Dex</a> pour l’authentification <span class="caps">OIDC</span>, avec Github, Google et Azur comme cibles, ce qui permet de choisir depuis une page web sans avoir à préciser un <span class="caps">OIDC</span> tiers lors de la signature.</p>
<h5>Clients</h5>
<p>La discussion avec Sigstore se fait en <span class="caps">REST</span>, avec les outils de <span class="caps">SSL</span>.
Vous pouvez reproduire la danse de Sigstore en bricolant sur un coin de table avec <code>curl</code>, <code>openssl</code>, <code>jq</code> et <code>base64</code>.</p>
<p>Sigstore fournit un client de référence, <a href="https://github.com/sigstore/cosign">cosign</a> écrit en go.
Sa bibliothèque Python, <a href="https://github.com/sigstore/sigstore-python">sigstore-python</a>, propose aussi un cli (capable de <a href="https://github.com/di/id#supported-environments">deviner le token <span class="caps">OIDC</span> dans une <span class="caps">CI</span></a>).</p>
<p>Github (même s’il ne fait que de l’Oauth2 et pas de l’<span class="caps">OIDC</span>) et <a href="https://docs.gitlab.com/ee/ci/yaml/signing_examples.html">Gitlab</a> fournissent les outils pour utiliser Sigstore (vérification et signature) dans l’intégration continue, dans le but d’atteindre le niveau 3 de la certification <a href="https://slsa.dev/"><span class="caps">SLSA</span></a>.</p>
<p>La doc de Sigstore évoque même Jenkins, il n’y a donc pas d’inquiétudes sur la possibilité d’intégrer Sigstore à n’importe quelle <span class="caps">CI</span>.</p>
<h5>Validation</h5>
<p>La procédure de validation d’un paquet est tout à fait classique, avec du json, du base64 et des certificats x509.</p>
<ul>
<li>Validation de la clé publique :</li>
<li>signature par l’autorité de certification</li>
<li>émetteur crédible</li>
<li>source <span class="caps">OIDC</span> crédible</li>
<li>auteur crédible (un mail ou un build depuis une <span class="caps">CI</span>)</li>
<li>l’horodatage signé est dans la période de validité du certificat</li>
<li>Vérification que la création de la clé apparait comme il faut dans le journal Rekor</li>
<li>Calcul du hash de l’artéfact et validation de sa signature avec cette clé publique éphémère</li>
</ul>
<h5>Signature</h5>
<ul>
<li>Création d’une paire de clé publique/privée</li>
<li>Création d’un certificat X509 avec des attributs confirmables par le token <span class="caps">OIDC</span></li>
<li>Le certificat contient un challenge (aléatoire) et la signature effectuée avec la clé privée.</li>
<li>Récupération d’un token <span class="caps">OIDC</span>, via sa <span class="caps">CI</span>, ou via Fulcio qui est une application OAuth2</li>
<li>Envoie du certificat éphémère public et du token <span class="caps">OIDC</span> à Fulsio, qui vérifie la signature du token, puis <a href="https://github.com/sigstore/fulcio/blob/main/docs/oid-info.md">la cohérence des champs entre le certificat et le token</a>. Fulcio raconte tout ça à Rekor, renvoie le certificat signé pour 10 minutes</li>
<li>Calcul du hash de l’artéfact et signature avec la clé privée</li>
<li>Création d’un <a href="https://en.wikipedia.org/wiki/Trusted_timestamping">horodatage signée</a> selon le <a href="https://www.rfc-editor.org/rfc/rfc3161"><span class="caps">RFC3161</span></a> avec un service tiers comme <a href="https://freetsa.org">freeTSA</a> ou <a href="https://github.com/sigstore/timestamp-authority">Sigstore Timestamp Authority</a></li>
<li>Envoi de la signature, du hash et du certificat sur Rekor</li>
<li>Envoi de l’horodatage signé sur Rekor</li>
<li>Création d’un bundle, un json avec des blobs en base64 à fournir avec l’artéfact</li>
</ul>
<p>Le certificat pourra être utilisé plusieurs fois pendant sa durée de vie.
La clé privée sera détruite, sans jamais avoir touché le disque dur.</p>
<p>Fulcio préconise l’utilisation de gestionnaire de clés (matériel, services <span class="caps">KMS</span> des clouds, <a href="https://www.hashicorp.com/products/vault">Vault d’Hashicorp</a>) et en dernier recours, d’une clé privée sur un fichier.</p>
<p>Pour héberger un service <span class="caps">TSA</span>, il faut prévoir du hardware pour avoir une horloge rigoureuse, comme un <span class="caps">GPS</span>.</p>
<h5>Paquets</h5>
<p>Sigstore fournit des outils spécifiques aux gestionnaires de paquets suivant :</p>
<ul>
<li><em>cosign</em> sait publier la signature d’une image de conteneur sur un dépôt <a href="https://opencontainers.org/"><span class="caps">OCI</span></a> (comme Docker)</li>
<li><a href="https://github.com/sigstore/sigstore-go">Golang</a></li>
<li><a href="https://github.com/sigstore/sigstore-python">Python</a></li>
<li><a href="https://github.com/sigstore/sigstore-rs">Rust</a> (instable, en version 0.8 pour l’instant)</li>
<li><a href="https://github.com/sigstore/sigstore-java">Java</a></li>
<li><a href="https://github.com/sigstore/sigstore-js">Javascript</a></li>
</ul>
<p>Il est possible de signer un commit git via Sigstore, avec <a href="https://github.com/sigstore/gitsign">gitsign</a>, qui utilise pour ça <span class="caps">GPG</span> avec des documents au format x509. Oui, git sait utiliser une <span class="caps">PKI</span> comme tout le monde.</p>
<p>Pour les paquets, plus que le nom de la personne qui a signé, ou le numéro de série du build, c’est la notion de <a href="https://slsa.dev/spec/v1.0/provenance">provenance</a> qui importe.</p>
<p>Dans ce fouillis de cascade de normes, il ne faut non plus oublier <a href="https://in-toto.io/">in-toto</a> (sour l’égide de la Linux Foundation), pour spécifier les méta-datas d’un paquet.</p>
<h4>OpenPubKey</h4>
<p>Un autre consortium, composé de <a href="https://www.bastionzero.com/">BastionZero</a> et de Docker, propose leur propre norme pour signer des clés publiques après une authentification <span class="caps">OIDC</span> : <a href="https://github.com/openpubkey/openpubkey">OpenPubKey</a>.</p>
<p>OpenPubKey est aussi un projet de la Linux Foundation, et il est utilisé par le registre public de Docker.</p>
<p>Ils ont publié leur <a href="https://eprint.iacr.org/2023/296">white paper</a> expliquant leur démarche.</p>
<p>L’idée d’OpenPubkey est de créer une paire de clés publique/privée et un défi (un nombre aléatoire), de le signer avec la clé privé.
La clé publique, le défi, la signature soit utilisé comme <code>nonce</code> pour demander un jeton <span class="caps">OIDC</span>.
Normalement, un <code>nonce</code> est un nombre aléatoire utilisé lors de l’échange OAuth2, en utilisant le triplet défi-clé-signature, avec un défi de taille suffisante, ça ne remettra pas en cause l’entropie lors de l’échange.</p>
<p>Pour un jeton <span class="caps">OIDC</span> émis lors d’une <span class="caps">CI</span>, il n’y a pas de <code>nonce</code>, et ce sera donc <code>aud</code> qui sera détourné pour faire signer sa clé par <span class="caps">OIDC</span>.</p>
<p>Le jeton ne permettra pas de faire grand chose (son scope est “openid profile email”) et sera rapidement périmé.
Lors de la vérification de la clé publique, le champs <code>exp</code> du token ne sera pas pris en compte (il est périmé comme jeton <span class="caps">OIDC</span>, mais pas comme preuve), et il sera possible d’utiliser la date d’émission (<code>iat</code>) comme date fiable d’émission de la preuve.</p>
<p>Le jeton peut être vérifié à partir de la clé publique du service <span class="caps">OIDC</span>, mais il y aura une rotation (entre 15 jours et 3 mois pour les gros services <span class="caps">OIDC</span>), et le papier évoque l’utilisation d’un journal de signature, pour avoir une conservation sur un temps long.</p>
<p><a href="https://blog.sigstore.dev/openpubkey-and-sigstore/">Sigstore cause de OpenPubKey dans son blog</a>, reconnait la simplicité de leur approche, mais doute fort de la possibilité de vérifier une clé sur un temps long, et hurle sur l’idée de laisser trainer un token <span class="caps">JWT</span> n’importe où (oui, ce n’est clairement pas orthodoxe).</p>
<h3>Mises à jour des dépendances</h3>
<p>Il est important d’avoir une politique fluide de mises à jour de paquets, et de faire des déploiements d’un service sans nouvelles fonctionnalités/corrections, juste pour des mises à jour.</p>
<p>Pour la chasse aux paquets troués, tâche ô combien rébarbatives, il faut un robot et une bonne couverture de tests.
Tous les gestionnaires de paquets ont une <span class="caps">API</span> distante et un cli pour faire ça, mais des outils comme le <a href="https://github.com/dependabot/dependabot-core">dependabot</a> de Github, bien que non libre, a le bon gout de fluidifier les mises à jour, en créant une demande de fusion (pull request), qui va déclencher l’intégration continue, et donc les tests.</p>
<p>Il ne faut pas que cette tâche dépende de la disponibilité ou de l’humeur d’un développeur, c’est un travail de robot.</p>
<h2>Sécuriser la chaine d’approvisionnement</h2>
<p>Pour ce point, il faut des gestionnaires de paquets en bon état, de l’authentification forte pour les contributeurs, une <span class="caps">PKI</span> pour signer un peu partout, la possibilité d’auditer les signatures et de l’automatisation pour les montées en version.</p>
<p>Tout ça existe.</p>Comment vendre une base de données open source2023-10-02T09:20:00+02:002023-10-02T09:20:00+02:00Mathieu Lecarmetag:blog.garambrogne.net,2023-10-02:/comment-vendre-une-base-de-donnees-open-source.html<p>Influxdb vient de sortir la version 3 de sa base de données (orientée time-series), avec une petite astuce sur le modèle de licence, pour continuer à faire de l’open-source sans se faire piller, comme certain de ses prédécesseurs.</p><h1>Comment vendre une base de données open source</h1>
<p>Influxdata vient de sortir la version 3 d’Influxdb sa base de données “temporelles”, avec une petite astuce sur le modèle de licence, pour continuer à faire de l’open-source sans se faire piller, comme certain de ses prédécesseurs.</p>
<p>Mais commençons par le commencement.</p>
<p><img alt="Lac d'Aiguebelette" class="image-process-article-image" src="derivatives/article-image/comment-vendre-le-lac-d-aiguebelette.jpg"/></p>
<h2>L’histoire ancienne</h2>
<h3>Postgresql</h3>
<p>1996 <span class="caps">MIT</span></p>
<p>Ingres, est né en 1982 comme projet universitaire à Berkley, pour travailler sur la notion de base de données relationnelle. Le leader de l’équipe Ingres va créer une entreprise, Ingres, puis finalement revenir à la fac et créer un deuxième produit, Post-Ingres, aka Postgres.
Bascule vers le <span class="caps">SQL</span> en 1994, changement de noms en PostgreSQL en 96, début de la présence sur Internet avec la version 6, peu de temps après Mysql finalement. A part le faux départ Ingres, Postgresql a toujours été open source (<span class="caps">MIT</span>, donc team <span class="caps">BSD</span>) et maintenu par sa communauté.</p>
<p>Postgres a un écosystème complet, <a href="https://github.com/dhamaniasad/awesome-postgres">awesome</a> comme on dit, qui va de la haute disponibilité avec <a href="https://github.com/zalando/patroni">Patroni</a> de Zalando (en <span class="caps">MIT</span>), à de la sauvegarde continue avec <a href="https://pgbarman.org/">Barman</a>
de <a href="http://www.2ndquadrant.com/">2ndquadrant</a> (en <span class="caps">GPL</span>-3).</p>
<p>Personne n’est arrivé à accaparer Postgresql, fidèle à sa licence <span class="caps">MIT</span>, il permet l’apparition d’entreprise proposant des services complexes (et open source), en parallèle avec des outils communautaires.</p>
<h3>Mysql</h3>
<p>1995 <span class="caps">GPL</span></p>
<p>Mysql a été crée comme un clone libre du maintenant oublié <strong>msql</strong>, par Mysql <span class="caps">AB</span>, comme le récite Wikipedia et toutes les pages qui le cite.
Pas de trace d’une version antérieur à la version 3, il est donc facile d’imaginer qu’il y a eut deux versions privées, avant de créer l’entreprise éponyme, puis de commencer la conquête d’Internet en 1995, qui est une bonne date pour commencer un truc sur Internet : c’est l’arrivée du grand public sur le grand réseau mondial.</p>
<p>Mysql, est suffisamment souple pour propulser tout le web, du simple <span class="caps">LAMP</span> (Linux/Apache/Mysql/<span class="caps">PHP</span>) aux premiers GAFAs (Facebook, Flickr, Twitter, Wikipedia, Youtube, plus tard Github …).</p>
<p>Le bizness model ne suit pas, rachat par Sun, qui frustre le vendeur qui fork en MariaDB, et patatra, Oracle rachète un Sun moribond, et met la main sur ce qui reste moralement son concurrent.</p>
<p>Les gros utilisateurs bricolent de la réplication à échelle titanesque :</p>
<ul>
<li><a href="https://github.blog/2016-08-01-gh-ost-github-s-online-migration-tool-for-mysql/">Github parle de son <strong>gh-ost</strong></a> qui deviendra un produit (très confidentiel) sous le nom d’<a href="https://code.openark.org/blog/">openark</a></li>
<li><a href="https://blog.twitter.com/engineering/en_us/a/2010/introducing-gizzard-a-framework-for-creating-distributed-datastores">Twitter parle de son <strong>Gizzard</strong></a> qui a commencé par répliqué du Mysql avant de passer à autre chose. Mysql qui est toujours utilisé d’après leur billet récapitulatif de 2017 <a href="https://blog.twitter.com/engineering/en_us/topics/infrastructure/2017/the-infrastructure-behind-twitter-scale">Scale</a></li>
<li><a href="https://engineering.fb.com/?s=mysql">Facebook parle régulièrement de Mysql</a> et fournit <a href="http://myrocks.io/">myrocks</a>, un fork de Mysql 5.6, sous licence <span class="caps">GPL</span>-2, qui utilise son moteur de stockage, rocksdb (successeur du leveldb de Google). <a href="https://docs.percona.com/percona-server/8.0/myrocks/install.html">Percona propose une version qui tourne comme module pour Mysql 8.0</a></li>
<li><a href="https://vitess.io/">Vitess</a> est le proxy utilisé par Youtube pour scaler du Mysql. Ce proxy a mis beaucoup de temps pour causer mysql avec les clients (et enfin devenir un vrai proxy).</li>
</ul>
<p>L’entreprise <a href="https://en.wikipedia.org/wiki/Percona">Percona</a> est la référence de fait pour Mysql, hors Oracle, et proposent des outils, de l’infogérence et surtout du consulting.</p>
<p>Il ne faut pas se mentir, le rachat de Mysql par Oracle en 2010 (via Sun en 2008), a cassé l’ambiance. Mariadb permet de rester droit dans ses bottes face au grand méchant Oracle, mais c’est tout, le fork d’arrière garde n’a pas les épaules pour proposer des innovations, ou même toucher quoi que ce soit.</p>
<p>Mysql a une chouette histoire, mais cette histoire est figé dans le marbre, et cette stabilité depuis toujours, pour toujours est ce que recherche ses utilisateurs.
À part pour créer un Wordpress de plus, qui donc choisi Mysql pour un nouveau projet après 2020?</p>
<h3>Sqlite</h3>
<p>2000 Public Domain</p>
<p><a href="https://en.wikipedia.org/wiki/SQLite">Sqlite</a> est une base de données relationnelles, avec tout ce qu’il faut de <span class="caps">SQL</span>, mais sans réseau, sans accès concurrents.
Longtemps cantonné au rôle de base de données pour crétin qui ne savent pas installer une vraie base de données, Sqlite est maintenant la référence pour stocker des données complexes de manière pérenne, largement utilisé dans les téléphones portables.</p>
<h2>L’ère du 2.0</h2>
<p>Le Web a démarré en s’appuyant sur des outils existants, avant de commencer à créer des choses originales. Github devient rapidement incontournable et les utilisateurs distribuent des ⭐️ comme un gastronome de chez Michelin. </p>
<h3>Mongodb</h3>
<p>2007 24k⭐️ <span class="caps">SSPL</span></p>
<p>Mongodb arrive en fanfare, pour sauver le web du méchant <span class="caps">SQL</span> et du modèle relationnel.</p>
<p>Bruyant et malhonnête (écriture sans sync pour aller plus vite, surconsommation de disques durs avant l’arrivé de Tiger…), il s’est ensuite calmé, et à forcé les autres bases de données à évoluer pour avoir du typage plus complexe (listes, tableaux…) ou plus simple (hstore, json…), plus proche des ORMs, par ce que ne nous mentons pas, de la base de données sans modèle, ça n’existe pas, le modèle est soit explicite, soit tacites. Mongo</p>
<p>Mongodb sera la première victime de la cloudification, et de manière prévisible, ça fera beaucoup de bruits.</p>
<h3>Rethinkdb</h3>
<p>2009 26k⭐️ Apache 2</p>
<p>En 2009, <a href="https://github.com/rethinkdb/rethinkdb">Rethinkdb</a> trouve que Mongodb est un petit bras, et qu’il est possible d’exposer un <span class="caps">AST</span> dans le client pour devenir une requête dans la db. Création d’une boite avec le core dev, licence Apache 2.</p>
<p>Ça ne prends pas, mais 26k étoiles sur Github.</p>
<p>Pivot sur l’aspect temps réel (des événements pour les clients restent synchronisés), ça ne prends pas non plus.</p>
<p>Gros coup de frein en 2015.</p>
<p>Le core-dev tente un reboot base sur FondationDB, <a href="https://github.com/srh/refound">Refound</a>, qui cartonne avec 11 étoiles sur Github.</p>
<h3>Arangodb, ex Advocadodb</h3>
<p>2012 13k⭐️ Apache 2</p>
<p>En 2012, encore dans la hype de Mongodb qui est un petit bras, <a href="https://github.com/arangodb/arangodb">Arangodb</a> propose en plus du modèle orienté document, un modèle orienté graphe, et même du clef/valeur.
En plus des requêtes en javascript comme Mongo, il y a un Query Language.</p>
<p>Gros coup de frein en 2017, mais un score honorable avec 13 milles étoiles Github.</p>
<h3>Redis</h3>
<p>2009 62k⭐️</p>
<p>Redis débute sa vie comme moteur de cache, du clef/valeur non relationnel avec des types complexes et les opérations adéquates pour les manipuler. Redis méprise les disques durs, tout doit tenir en <span class="caps">RAM</span>, mais bon, les tailles des barrettes ont grandis avec lui, et il y a quand même de la persistance (instantanés et journalisation) pour repartir directement cas de reboot. Il gère la réplication et la haute dispo.</p>
<p>Il n’échappera pas à la cloudification.</p>
<h3>Elasticsearch</h3>
<p>2010 65k⭐️</p>
<p>Elasticsearch commence par rendre accessible le trop théorique Lucene, outil de recherche full text, pour finalement devenir une base de données orientées colonnes, agréable à utiliser en cluster.</p>
<p>Elasticsearch a ensuite su pivoter, pour devenir un stockeur de logs, puis de time series, et de se diversifier avec tout un écosystème un peu confus avec des morceaux de libre mélangé avec des morceaux propriétaires, mais open-source.</p>
<p>Ça ne l’empêchera pas de se faire cloudifier aussi.</p>
<h3>Clickhouse</h3>
<p>2009 31k⭐️ Apache</p>
<p>Profitant de la hype des bases orientés colonnes, fort pratique pour analyser le comportement de ses clients, Yandex, le Yahoo des russes, libère son <a href="https://clickhouse.yandex/">Clickhouse</a> sous licence Apache.
Yandex permet d’entasser trop de données, de jeter du <span class="caps">SQL</span> dessus, et d’obtenir des résultats rapidement sans passer par la case map-reduce du monde Hadoop.</p>
<p>ClickHouse est maintenant une boîte à San Francisco et Amsterdam, lève des fonds, et de chouettes projets commencent à l’utiliser (comme <a href="https://github.com/PostHog/posthog">PostHog</a>). C’est la phase “lune de miel”, tout est merveilleux, il faut devenir un standard de fait, puis on parlera bizness à ce moment là.</p>
<p>De manière très classique, Clickhouse se gère facilement sur une seul instance, bare metal ou conteneur, par contre, pour le cluster, il y a <span class="caps">LE</span> chois technique de l’équipe, avec un gros zookeeper, et comme ça piaille, ils ont fourni une <span class="caps">API</span> pour utiliser autre chose, mais comme ils n’utiliseront pas les alternatives en prod, bah, ce sera en l’état, à la communauté de bosser. Des outils tiers commencent à apparaitre, comme <a href="https://github.com/PostHog/HouseWatch">HouseWatch</a></p>
<h3>Leveldb</h3>
<p>2011 34k⭐️ New <span class="caps">BSD</span></p>
<p>Google explique sa stratégie d’écriture sans modification (mais en consolidant périodiquement), et l’importance d’avoir des clefs ordonnées dans une base clef/valeur. Héritière de <a href="https://research.google/pubs/pub27898/">BigTable</a>, <a href="https://github.com/google/leveldb">leveldb</a> est une réécriture complète, par ce que BigTable s’appuie sur des bibliothèques qui n’ont pas vocation à être libérées.</p>
<p>C’est du simple clef/valeur ordonnées, avec des itérations efficaces sur les clefs (d’où l’intérêt de les garder ordonnées), et des écritures par lot.</p>
<p>Pas de produit, pas de service.</p>
<h3>Rocksdb</h3>
<p>2012 26k⭐️ Apache 2</p>
<p>Facebook aime bien le concept de Leveldb et fork en <a href="https://github.com/facebook/rocksdb/">Rocksdb</a> pour d’aller plus loin sur le tuning spécifique au <span class="caps">SSD</span> (qui était nouveau à cette époque), d’exposer les journaux d’écritures pour avoir de la réplication, et de l’utiliser comme couche basse de persistance, un peu partout, comme Mysql par exemple avec Myrocks, ou simplement du cache.</p>
<h3>Foundationdb</h3>
<p>2017 13k⭐️ Apache 2</p>
<p>Apple reprend le concept d’une base clefs/valeurs ordonnées avec des écritures par lots, mais se focalise sur la notion de réplication cohérente au sein d’un cluster et
sort discrètement <a href="https://github.com/apple/foundationdb">Foundationdb</a>.
Comme “fondation”, elle se positionne comme outil de base niveau, sans protocole réseau spécifié, mais en fournissant le client adéquat dans différents langages (C, Ruby, Python, Java, Golang).
Orienté performance, gros volume et fiabilité, <a href="https://apple.github.io/foundationdb/data-modeling.html">Foundationdb demande à l’utilisateur de faire des efforts</a>, en dénormalisant son modèle, en utilisant une serialisation qui ne casse pas l’ordre des clefs, et en recommandant de ne pas dépasser le ko pour les données, au pire 10k si on sait que l’on ne pas écrire/itérer trop souvent dans cette table, et la limite dure est 100ko.</p>
<p>Foundationdb se positionne clairement comme outil sur lequel construire des services (et potentiellement facturer), ce qu’Apple fait en interne.</p>
<p><a href="https://deno.com/kv">Deno <span class="caps">KV</span></a> utilise Fundationdb, mais reste une arme secrète pour l’offre d’hébergement de Deno, les offres plus ou moins serverless basées sur javascript sont en pleine ébullition.</p>
<h3>Les héritiers de Postgresql</h3>
<p>Postgresql avec sa communauté maintient un logiciel vivant, et sa licence incite la création d’entreprise, qui en retour financeront (ou dédieront des développeurs) le projet.</p>
<h4>Citusdb</h4>
<p>2016 9k⭐️ <span class="caps">AGPL</span></p>
<p><a href="https://www.citusdata.com/">Citusdb</a> fournit une <em>extension</em>, pour distribuer les données et les calculs dans un cluster, du stockage orienté colonnes (compressé).</p>
<p>La licence Affero brime les offres d’hébergement, et Citus réserve l’offre en Cloud à Azure dans son offre <a href="https://learn.microsoft.com/fr-fr/azure/cosmos-db/distributed-relational">Cosmos-db</a></p>
<h4>Timescaledb</h4>
<p>2018 16k⭐️ Apache 2</p>
<p><a href="https://github.com/timescale/timescaledb">TimescaleDB</a> propose une <em>extension</em>, pour gérer les timeseries, avec du sharding basé sur la date de l’événement, du stockage orienté colonne (compressé). La version autohébergement est sous licence Apache, et ils gardent pour eux la version Cloud.</p>
<h4>Neon</h4>
<p>2021 10k⭐️ Apache 2</p>
<p><a href="https://github.com/neondatabase/neon">Neon</a> se positionne comme un Aurora open source, en séparant le calcul du stockage. <a href="https://neon.tech/blog/architecture-decisions-in-neon">Le stockage s’appuie sur le <span class="caps">WAL</span> (Write Ahead Log) répliqué, pour créer des blocs immuables régulièrement consolidé (du <span class="caps">LSM</span>), qui seront à chaud sur un <span class="caps">SSD</span>, à froid sur un S3</a>. Le sharding est une possibilité, mais pas la priorité immédiate.</p>
<p>L’idée étant d’avoir du serverless (du <span class="caps">SQL</span> over <span class="caps">HTTP</span>/Websocket), avec la possibilité d’éteindre les noeuds de calcul, sans remettre en cause la persistance, et de pouvoir dimensionner le calcul et le stockage indépendement.
le site envoie du rêve en parlant de stockage sans fond, mais aussi de cold start qui se chronomètre en seconde, ce service n’est pas universel, ni magique.</p>
<p>Pour l’instant, il faut patcher les sources de Postgres (les modifications ont vocations à remonter) compiler et déployer. Neon n’a pas trop à craindre de la concurrence de son offre <span class="caps">SAAS</span>.</p>
<h4>Alloydb</h4>
<p>2022 propriétaire</p>
<p>Google fork Postgresql pour créer <a href="https://dbdb.io/db/alloydb">Alloydb</a>, avec une architecture avec des nodes calcul/stockage, ressemblant à Neon, mais avec plus de violence : le stockage est orienté colonnes, la cible est donc de gros volumes de données sans modification, avec des reqêtes complexes.</p>
<p>Hors documentation utilisateur, Google communique peu sur les rouages de ce produit, qui doit recycler des outils existants, des éléments de <a href="https://en.wikipedia.org/wiki/Spanner_(database)">Spanner</a>, crée en 2012, avec un moteur <span class="caps">SQL</span> en 2017, l’année de l’offre <span class="caps">SAAS</span> public.</p>
<p>Alloydb semble être la réponse définitive à tous les utilisateurs de Spanner qui ralent sur la compatibilité Postgresql, Spanner ne gérant que le protocole.</p>
<h3>Les héritiers de Sqlite</h3>
<h4>Rqlite</h4>
<p>2014 14k⭐️ <span class="caps">MIT</span></p>
<p>[Rqlite] se présente comme une base de donnée relationnelle distribuée, utilisant sqlite comme moteur de stockage. Il permet d’avoir des transactions, de la cohérence écriture puis lecture (la bagarre avec la réplication asynchrone), tout en restant simple et léger.</p>
<p>Projet communautaire sans offre d’une entreprise.</p>
<h4>Litestream</h4>
<p>2020 9k⭐️ Apache 2</p>
<p>En 2020, <a href="https://litestream.io/">Litestream</a> prouve qu’il est efficace de répliquer les modifications d’un sqlite vers quelque chose de pérenne, comme un S3, et de proposer des services simples.</p>
<p>Pas d’entreprise ni d’hébergement, c’est un projet “because we can”.</p>
<h4>Litefs</h4>
<p>2022 3k⭐️ Apache 2</p>
<p>Superfly utilise <a href="https://github.com/superfly/litefs">LiteFS</a> la même réplication des journaux que Litestream, pour répliquer massivement et de manière asynchrone les écritures,
suivant un motif primaire/secondaire avec un consensus Raft (du Consul).
LiteFS est une couche de persistance pour l’offre serverless de Superfly, adapté pour des cas avec peu d’écriture concurrentes, et beaucoup de lecture qui bénéficieront de faible latence.
Il se positionne comme plus performant que Rqlite, et plus qu’un simple backup comme Litestream.</p>
<p>Litefs est la vitrine technologique (en bêta) d’une offre Cloud volontairement sobre, pour se différencier des gros du Cloud. </p>
<h3>Parquet</h3>
<p>2012 2k⭐️ Apache 2</p>
<p>La fondation Apache adore rationaliser les écosystèmes éparpillés, et fournir des fondations saines sur lesquelles construire de s choses plus grande.</p>
<p>Inspiré par le <a href="https://research.google/pubs/pub36632/">Dremel white paper</a> de Google, conçu pour Hadoop (et <span class="caps">HDFS</span>),
<a href="https://parquet.apache.org/">Parquet</a> est un format de stockage orienté colonne : les écritures se font par lot, on réécrit pour modifier, on peut lire que les colonnes de son choix.
Le format est compact, compressé, optimisé pour la lecture et les performances.
Imaginé pour remplir du <span class="caps">HDFS</span> et être lu par du java, il s’avère tout à fait apte à remplir du S3 et à être écrit/lu par tout les langages gérés par Arrow.
Toutes les audaces sont permises, on peut écrire avec un logiciel, et lire avec un autre, utilisant une technologie différente, comme vous le faisiez avec du <span class="caps">CSV</span> ou du <span class="caps">JSON</span>.</p>
<h4>Arrow</h4>
<p>2016 12k⭐️ Apache 2
<a href="https://arrow.apache.org/">Arrow</a> est la bibliothèque officielle pour lire et écrire du parquet, pour exposer des colonnes en <span class="caps">RAM</span> avec les primitives pour faire de l’<span class="caps">IPC</span> (discussion entre process) et du <span class="caps">RPC</span> (pareil, entre deux machines), avec tout ce qu’il faut de zéro copie et de binding dans moult langages (dont <a href="https://arrow.apache.org/docs/python/numpy.html">Numpy</a>).</p>
<h4>Datafusion</h4>
<p>2016 4k⭐️ Apache 2
<a href="https://arrow.apache.org/datafusion">Datafusion</a> est un moteur <span class="caps">SQL</span> utilisant Arrow, qui peut utiliser du Parquet, mais aussi du <span class="caps">JSON</span>, du <span class="caps">CSV</span> et d’autres formats.
Les réponses sont du Arrow, et donc lisible en natif dans de multiples langages.</p>
<h4>Ballista</h4>
<p>2016 1k⭐️ Apache 2
<a href="https://arrow.apache.org/ballista">Ballista</a> permet de faire des requêtes avec Datafusion, mais en distribué.</p>
<h4>Flight</h4>
<p>2019 Apache2</p>
<p><a href="https://arrow.apache.org/blog/2019/10/13/introducing-arrow-flight/">Flight</a>, basé sur grpc, implémente le début d’un protocole réseau :</p>
<ul>
<li>poignées de mains (handshake en <span class="caps">VO</span>)</li>
<li>discussion direct entre noeuds avec un ticket pour amorcer une discussion authentifiée</li>
<li>découverte de méta data</li>
<li>authentification</li>
<li>chiffrage</li>
<li>middleware</li>
<li>tracing (avec OpenTracing)</li>
<li>du <span class="caps">RDMA</span> est prévu pour plus tard (les machines discutent de <span class="caps">RAM</span> à <span class="caps">RAM</span>, sans passer par le <span class="caps">CPU</span>)</li>
</ul>
<h4>FlightSQL</h4>
<p><a href="https://arrow.apache.org/blog/2022/02/16/introducing-arrow-flight-sql/">FlightSQL</a> normalise le protocole réseau comme le faisait <span class="caps">ODBC</span> et <span class="caps">JDBC</span>, en s’appuyant sur Flight.</p>
<p>Il existe <a href="https://arrow.apache.org/flight-sql-postgresql/current/">une extension pour causer à Postgresql en FlightSQL</a></p>
<h3>Les autres bases de données</h3>
<p>J’oublie volontairement les bases de données dédiées au big data (coucou <a href="https://www.scylladb.com/">Scylladb</a> ou <a href="https://github.com/cockroachdb/cockroach">Cockroachdb</a>), tout ce qui est orienté graphe, et les bases trop exotiques (ou qui sont passé sous mon radar).</p>
<p>Le projet <a href="https://dbdb.io/">database of databases</a> tente l’exhaustivité, ce qui est assez fascinant.</p>
<h2>La rationalisation par le nuage</h2>
<p>L’innovation suit un chemin très classique : un démarrage plein de créativité, une rationalisation, et finalement une <a href="https://en.wikipedia.org/wiki/Commoditization">banalisation</a>.</p>
<p>Les hébergeurs ont commencé par proposer la triplette machine virtuelle/stockage/réseau.</p>
<p>En y collant une <span class="caps">API</span>, c’est devenu le Cloud.</p>
<p>En étant suffisamment gros, vous avez du stock de machines, et donc la promesse de pouvoir ajouter rapidement de la ressource pour suivre un pic d’usage.</p>
<p>Ensuite, sont arrivés les offres de services, parfois des services open source existant, comme Mysql ou Postgresql, parfois des services maisons que la concurrence n’a pas.</p>
<p><span class="caps">GNU</span> sent l’embrouille et crée en 2007 l’<a href="https://en.wikipedia.org/wiki/GNU_Affero_General_Public_License"><span class="caps">AGPL</span></a>, comme du <span class="caps">GPL</span>, mais avec l’obligation de fournir les sources (sous la même licence) des patchs utilisés par un hébergeur. </p>
<p>S3, services spécifiques à <span class="caps">AWS</span> est devenu un standard de fait, cloné par tous les autres clouds.</p>
<p>Héberger des services open-source reste fairplay, c’est même le but de la famille <span class="caps">BSD</span>.
Il y a juste une distorsion de concurrence avec la force de frappe commercial, les économies d’échelles.
On peut ajouter à ça l’effet winner take all, le premier rafle tout, et l’assurance vie que propose ces demi dieux de l’informatique : ok, le projet n’a pas tenu la charge, mais qui aurait pu faire mieux que le glorieux Amazon/Google/Microsoft?! Fatalitas!! Ce n’est pas de ma faute.</p>
<p>Deuxième étape, une fois l’entreprise construite à partir de briques open-source (qui est aussi fait pour ça, pas de soucis), on peut passer à l’étape “moissonner le blé de l’open-source” : on prends un produit open-source, avec sa base utilisateur, sa réputation, ses threads de questions/réponses sur StackOverflow, et on l’intègre proprement à son offre Cloud : on peut commencer à vidanger l’offre “as a service” de l’éditeur, vers son offre Cloud.</p>
<p>Si on a un peu de temps, on peut créer un élément spécifique à un logiciel open-source, un élément taillé sur mesure pour son architecture logiciel, sur un thème un peu casse-gueule, comme la persistance, redondance ou performance.</p>
<p>Le plus connu est <a href="https://aws.amazon.com/rds/aurora/">Aurora</a>, un moteur de stockage distribué, sur lequel on peut poser un tout à fait classique Mysql ou un Postgresql.</p>
<p>Là, la cible est facile, il n’y a pas d’entreprises à piller (Oracle ne compte pas trop sur la vente de licence Mysql).</p>
<p>Quand <span class="caps">AWS</span> a proposé une offre intégré avec Mongodb, ça a un peu plus dérangé l’éditeur qui a riposté en changeant de licence pour <a href="https://en.wikipedia.org/wiki/Server_Side_Public_License"><span class="caps">SSPL</span></a>, propriétaire.
<span class="caps">AWS</span> a simplement crée un clone de Mongodb, <a href="https://en.wikipedia.org/wiki/Amazon_DocumentDB">Documentdb</a>, propriétaire, avec une compatibilité sur le protocole.</p>
<p><span class="caps">AWS</span> lance une offre intégré pour Elasticsearch, qui bascule en <span class="caps">SSPL</span> et une partie de son code passe en “open source lecture seule”. <span class="caps">AWS</span> fork pour créer <a href="https://github.com/opensearch-project/OpenSearch">OpenSearch</a> qui reste sous Licence Apache 2.</p>
<p>Redis anticipe et bascule en free-core, le Redis “comme d’habitude” garde sa licence permissive, <a href="https://redis.com/blog/redis-license-bsd-will-remain-bsd/">mais toutes les nouveautés, sous forme de modules, n’ont pas le droit d’être vendu par un hébergeur</a>.</p>
<h2>Le cas Influxdb</h2>
<p>En 2013 le monde est prêt pour avoir une base de données temporelles open source : le Cloud permet d’avoir des services distribués, d’adapter les ressources à la charge, bref, on passe de la question binaire “le site est il disponible?” à la finesse de “quelles sont les performances pour quelle consommation”.</p>
<p>Tellement prêt que Soundcloud crée en 2012, <a href="https://github.com/prometheus/prometheus">Prometheus</a> basé sur le Borgmon de Google. Ciblant le cloud, Prometheus est découpé en plusieurs services et suit le choix de Google : c’est la base de données qui va chercher les valeurs sur les services (un préfixe en <span class="caps">HTTP</span>) en attendant que tout le monde cause le prom, il faut déployer des sidekicks, comme blackbox, qui font les entremetteurs pour aller chercher les métriques.</p>
<h3>Influxdb 1</h3>
<p>Financé par Y-Combinator, Influxdb apparait en 2013, avec une approche minimaliste : un seul binaire à déployer, des agents poussent des données, avec une large compatibilité avec les agents déjà déployés.</p>
<p>Un faux <span class="caps">SQL</span> permet de faire des requêtes et des agrégations. Premier péché : personne ne doit créer un presque <span class="caps">SQL</span>, c’est extrêmement frustrant, autant créer un vrai <span class="caps">DSL</span> sans comparaison possibles, ou avoir du vrai <span class="caps">SQL</span>.</p>
<p>2015, changement de nom pour pour Influxdata, et créer une suite logicielle, avec <a href="https://github.com/influxdata/telegraf">Telegraf</a>, un agent permettant d’envoyer des données depuis de multiples sources. </p>
<p>Coté interface utilisateur, Kibana, apparu en 2012 et dédié à Elasticsearch, fork en Grafana pour devenir multi bases, en 2013.</p>
<p>La citation d’Influxdb apparait dans un <a href="https://github.com/grafana/grafana/commit/e3f56f2645683fd3345ce8e7800bc45411b0a981">commit pour la première fois en février 2014</a>. Prometheus est cité dans un <a href="https://github.com/grafana/grafana/commit/bf98cfeadcc8a2bc546d6ebb8af70ffbd15a22d6">commit pour la première fois en septembre 2015</a></p>
<p>Influxdata a donc une stack complète pour lire et écrire des métriques.</p>
<p>Influxdata se concentre rapidement sur son bizness model, propose un <a href="https://github.com/influxdata/influxdb-relay">relais</a> permettant de faire la réplication master/master (avec des données horodatées, immuables, on doit être dans le cas de réplication le plus simple), puis oups, l’interromps, ça brime son offre as a service. Des forks seront maintenus un temps.</p>
<p>Influxdb utilise une astuce pour avoir une base de code unique qui permet de fournir un monolithe open source, et de générer une version en microservices, spécifiques à l’offre commerciale, avec du protobuf et du raft.</p>
<p>La foule a choisi Prometheus qui a une communauté (bientôt cannibalisé par Grafana).
Influxdb se repositionne comme timeseries pour le Edge, avec une offre on premise (les données restent chez vous), en plus du cloud, qui supporte un accès intermittent à Internet.</p>
<h3>Influxdb 2</h3>
<p>Il faut un second souffle.</p>
<p>Personne n’utilise directement l’influxql, il est tellement plus simple de cliquer partout depuis l’<span class="caps">UI</span> de Grafana.</p>
<p>Pour sa version 2, Influxdb sort un vrai <span class="caps">DSL</span> : <a href="https://www.influxdata.com/products/flux/">Flux</a>, et tord le bras de utilisateur pour qu’ils lâchent influxql, qui n’est plus géré dans la version libre, il faut une licence pour ça.
Flux a une syntaxe élégante, basé sur la notion de pipe, les <code>|></code> que l’on voit apparaitre dans les langages récents pour chainer les fonctions, sans avoir des tempêtes de parenthèses.</p>
<p>Le machine learning, qui deviendra l’intelligence artificiel, commence à se généraliser, Numpy a envie de manger de la métrique, pour faire de la prévision, ou même la très casse gueule recherche d’incohérences.</p>
<p>Le protocole réseau des requêtes réponds en <span class="caps">JSON</span>, comme tout bon <span class="caps">REST</span>, mais c’est loin d’être optimal pour envoyer des myriades de float.</p>
<p>Flux ne remplacera jamais Numpy ou même R, faire des requêtes dans deux langages en même temps est une punition, avec le risque de filtrer les données coté client (et non du coté de la base de données, ajoutant la latence de la connexion) et d’autre sous optimisation.</p>
<h3>Influxdb 3</h3>
<p>Nouveau pivot, Influxdb conserve Golang pour les couches hautes, mais bascule sur Rust pour la couche de persistance/requêtes, et crée <a href="https://github.com/influxdata/influxdb_iox">IOx, prononcez eye-ox, oeil de boeuf</a>.
IOx s’appuie sur Parquet, qui fait sauter les contraintes de cardinalité qui peuvent mordre très fort et Datafusion qui permet d’avoir du vrai <span class="caps">SQL</span> avec des possibilité de jointures sur des bases de données relationnel externes.</p>
<p>influxql et Flux sont dépréciés, on peut parler de faux départ pour cette technologie pourtant élégante.</p>
<p>Avec le stockage en Parquet, techniquement, si vous avez envie de lire les données avec Clickhouse ou n’importe quel <span class="caps">OLAP</span> sympathique qui cause le Parquet, faites vous plaisir.</p>
<p>IOx est déployé dans <a href="https://www.influxdata.com/blog/the-plan-for-influxdb-3-0-open-source/">Influxdb 3.0 annoncé la semaine dernière</a>.</p>
<p>En normalisant(sous traitant?) les couches basses, Influxdb supprime deux drames, et se concentre sur son coeur de métier.</p>
<p>Nouvelle version, nouvelles licences.</p>
<p>Edge, un Open-core, monolithique, libre, sans compactage.</p>
<p>Community, gratuit, comme Edge, mais permet des requêtes sur des temps long, et du compactage.</p>
<p>Influxdata se réserve la partie cluster :</p>
<ul>
<li>Serverless, proche du mutualisé d’antan, où l’on paye à l’usage.</li>
<li>Dédié pour envoyer du gros débit de manière prévisible.</li>
</ul>
<p>Si vous avez des besoins de edge, vous n’avez qu’à synchroniser vos fichiers Parquet sur un S3 (ou un de ses clones), qui assurera la redondance.
Les fichiers sont horodaté, ce sera facile d’effacer les vieilles données, ou de faire de la consolidation, pour perdre en granularité en échange de place.</p>
<h2>Conclusion</h2>
<p>Construire une base de données à partir de rien, tout seul est utopiste.
Il faut mutualiser l’effort de développement et la construction de la base utilisateur, et sans se faire piller tout de suite.
La couche de persistance, et donc le risque de perdre des données, est terrifiante.
Pas mal de base de données innovantes (Mongodb, Influxdb, Prometheus…) ont débuté sur un moteur de stockage bricolé sur un coin de table avant de pivoter (ou racheter) sur quelques choses de plus crédibles.</p>
<p>Les efforts pour mutualiser les couches basses (stockage, requête, réplication…) du Big Data sont resté dans l’écosystème Java, avec un ticket d’entrée trop haut, golang s’est coincé tout seul avec un cout trop élevé pour faire des aller-retours avec du C qui le force à paraphraser en pure Go. Cockroach l’explique dans son billet expliquant <a href="https://www.cockroachlabs.com/blog/pebble-rocksdb-kv-store/">la bascule de Rocksb vers Pebble</a>.</p>
<p>Les tentatives de recycler le Innodb de Mysql n’ont pas abouti, mais on a vu apparaître des simples base de données clef/valeur (local comme Rocksdb, distribué comme Fundationdb) qui sont des bases saines pour construire une application.</p>
<p>Rust est la bonne réponse à Java pour fournir des outils juste au dessus de la couche de persistance, sans tirer tout un écosystème (et ses guerres de communauté).</p>
<p>Postgres, avec sa licence permissive et sa modularité reste incontournable pour ajouter des approches différentes (colonnes, géographie, graphes, vecteurs…) mais en gardant tout le reste (<span class="caps">SQL</span>, protocole réseau, client, réplication…).
Pas mal de base de données récentes causent le dialecte Postgres pour profiter des clients (et <span class="caps">UI</span>) existants.</p>
<p>Distribuer une base de données sur plusieurs machines est compliqué, mais profiter d’une reprise sur l’incident qui arrive au milieu de la nuit est quand même chouette.</p>
<p>Les white papers sont disponibles, les briques logicielles libres existent, mais le travail pour fournir un service distribué est difficilement mutualisable et monétisable, surtout quand on se fait piller.</p>
<p>Utiliser des chouettes jouets sans pouvoir ouvrir le capot est frustrant, en plus d’être moins efficace et moins pérenne.
Les gros du Cloud, comme tout le monde, ont besoin d’utiliser et produire de l’open source :
<a href="https://fr.wikipedia.org/wiki/Des_nains_sur_des_%C3%A9paules_de_g%C3%A9ants">Nanos gigantum umeris insidentes</a></p>Ubus roi2023-08-31T16:23:00+02:002023-08-31T16:23:00+02:00Mathieu Lecarmetag:blog.garambrogne.net,2023-08-31:/ubus-roi.html<p><a href="https://openwrt.org/docs/techref/ubus">ubus</a> est le microbus système développé par OpenWRT. Pourquoi yet another bus?</p><p><a href="https://openwrt.org/docs/techref/ubus">ubus</a> est le microbus système développé par OpenWRT.
u étant la version <span class="caps">ASCII</span> de µ.</p>
<p>Pourquoi yet another bus?</p>
<p><img alt="De par ma chandelle verte!" class="image-process-article-image" src="derivatives/article-image/ubus.jpg"/></p>
<h2>OpenWRT</h2>
<p><a href="https://openwrt.org/">OpenWRT</a>
est une distribution Linux ciblant l’embarqué : des appareils avec peu de mémoire,
un stockage lent et maigrichon, et un processeur minimaliste.
L’appareil fera peu de chose, mais bien, sans bouffer des gigawatts.</p>
<p>OpenWRT est conçu sur la notion de contrainte, faisant passer <a href="https://www.alpinelinux.org/">Alpine</a> pour une distrib de débauché.</p>
<p>OpenWRT est surtout connu pour son utilisation dans les routeurs wifi, il est même possible d’en acheter avec directement un openWRT dessus, sans avoir besoin de le flasher.</p>
<h3>Composabilité</h3>
<p>OpenWRT s’appuie sur la <a href="https://en.wikipedia.org/wiki/Unix_philosophy">philosophie <span class="caps">UNIX</span></a> : un ensemble de services qui ne font qu’une seul chose, mais correctement.
Cette approche théorique est élégante dans un shell, où, avec quelques commandes et autant de pipes, on obtient à peu prés ce que l’on veut.
Cette composabilité favorise l’émergence de logiciels largement utilisé pour mériter des audits et des <a href="https://cve.mitre.org/">CVEs</a>,
et suffisamment de contributions pour avoir des corrections et des évolutions (en théorie).</p>
<p>Les services en ligne perdent les atouts du shell et de sa composabilité.
Le shell ne prévoit rien pour la notion d’événement de plus, dans ce glorieux passé,
à part des cliques sur un clavier, un reboot de la machine, il ne se passait pas grand chose.
Non, le cron n’est pas une réponse acceptable, et encore moins l’immonde polling.</p>
<p>Pour que des services puissent discuter entre eux, il faut passer par un bus.
<span class="caps">UNIX</span> fournit gentiment des sockets <span class="caps">UNIX</span> et même des files d’attentes (le très peu connu <code>/dev/mqueue</code>),
mais ces outils ont été conçu à l’ère des processeurs mono-coeurs, et fournissent que des <span class="caps">IPC</span> (communications inter processus) pas de la gestion d’événements.</p>
<h3><span class="caps">UI</span> centralisé</h3>
<p>OpenWRT est entièrement configurable en ligne de commande via un accés <span class="caps">SSH</span>, mais sa vraie force est de proposer une interface web, maintenant écrite en lua : LuCI.
LuCI a permis de tirer une (petite) dépendance dans cet univers de minimalisme : <a href="https://www.lua.org/">Lua</a>, la pépite du “code as config”.</p>
<h2>D-bus</h2>
<p>Le besoin de coordination et d’évenement a très vite était ressenti pour les desktops (le fameux “est-ce que Linux est raidi pour le dekstop?”).
Mais <a href="https://kde.org"><span class="caps">KDE</span></a>, le premier bureau pas tout moche, avait un soucis de licence (avec la licence de <a href="https://www.qt.io/">Qt</a>, soyons précis),
ce qui a conduit l’apparition de <a href="https://www.gnome.org/">Gnome</a> (basé sur <a href="https://www.gtk.org/">gtk</a>, concurrent de Qt, crée pour <a href="https://www.gimp.org/">Gimp</a>),
les deux se sont tirés la bourre, et ont fait le maximum de choix différents, pour, euh pourquoi? pour se différencier.
Chacun a bien sur choisi un bus différent.</p>
<p><a href="https://www.freedesktop.org">Freedesktop</a> a été crée pour arrêter le massacre, et établir consensus puis des normes.
Freedesktop a fournit <a href="https://en.wikipedia.org/wiki/D-Bus">D-bus</a>, débuté en 2002, avec une <span class="caps">API</span> stable en 2006.
Fortement inspiré de <span class="caps">DCOP</span>, d-bus a rapidement été intégré dans <span class="caps">KDE</span>, Gnome a un peu plus trainé la patte.
Mais c’esst de l’histoire ancienne, d-bus va maintenant au delà du desktop
et gère maintenant le réseau avec <a href="https://en.wikipedia.org/wiki/NetworkManager">NetworkManager</a>
le bluetooth avec <a href="http://www.bluez.org/">bluez</a>,
mais surtout le boot avec <a href="https://systemd.io/">systemd</a>.</p>
<p>Systemd qui tire d-bus a fait hurler sur le coup, mais la concurrence était tellement inférieur
et une norme contestable (mais utilisé) pour une couche basse a tellement plus de valeur que de vaines divergences.</p>
<p>D-bus a gagné la guerre du bus desktop, mais n’a jamais trop pris sur les serveurs, même si il reste un prérequis système.</p>
<p>Les applications métiers se concentrent sur les couches au dessus, avec des services maintenant distribués,
utilisant soit de la découverte/configuration (<a href="https://zookeeper.apache.org/">zookeeper</a>, <a href="https://etcd.io/">etcd</a>, <a href="https://www.consul.io/">consul</a>)
soit du gros bus avec maintenant de la persistance/reprises d’événement (<a href="https://kafka.apache.org/">Kafka</a>,
<a href="https://nats.io/">nats</a>, <a href="https://redis.io/docs/data-types/streams/">Redis <span class="caps">STREAM</span></a>…).</p>
<p>Trop gros, trop complexe, D-bus n’a aucun intérêt pour des applications cotés serveurs.</p>
<p>Les bus prévus pour les applications distribués sont tout aussi sur-dimensionnés pour de l’embarqué.</p>
<h2>Ubus</h2>
<p>OpenWRT fonctionne sur des petits Linux, bien incapable de gérer D-bus.</p>
<p><a href="https://openwrt.org/docs/techref/ubus">Ubus</a> (µbus ?) est un logiciel embarqué, cousu main en C, premier commit en 2010, avec une chaine de dépendance minimaliste, utilisant les libs maison pour mutualiser des fonctions pour grapiller de la place.
On parle de libs qui se pèsent en kilo.</p>
<p>Pas de <span class="caps">README</span> (la doc, c’est pour les faibles), une arborescence plate, <code>cmake</code> et un compilateur C sont les seul prérequis pour compiler.</p>
<p>Ubus s’appuie sur <a href="https://openwrt.org/docs/techref/libubox">ubox</a>, la bibliothèque fourre-tout de openwrt, et <a href="https://github.com/json-c/json-c">json-c</a> via un wrapper maison.</p>
<p>Pour ne pas se laisser distraire par des fonctionnalités exotiques, qui mettrait en péril le minimalisme du projet, mais sans pour autant bloquer les évolutions, lua est bindé à l’application.</p>
<p>Une architecture classique et de bon gout : core en C minimaliste, modules en lua.</p>
<p>Le principal usage de lua est un module http, qui expose ubus en <span class="caps">REST</span> avec l’incontournable <span class="caps">JSON</span>, qui permet de lui causer avec <code>curl</code>
et même d’exposer ubus au reste du monde (dans le <span class="caps">LAN</span>, hein, foufou, mais pas trop), avec de l’authentification et des ACLs.</p>
<p>Le module http pour ubus est bien sur prévu pour s’intégrer à LuCI, l’<span class="caps">UI</span> web d’OpenWRT.</p>
<p>Pour parler le ubus (à <code>ubusd</code>, donc), il n’y a que 3 possibilités :</p>
<ul>
<li>l’outil en ligne de commande : <code>ubus</code></li>
<li>la lib officielle, le protocole n’est pas spécifié, il existe juste une implémentation</li>
<li>l’<span class="caps">API</span> <span class="caps">REST</span>, avec le classique <code>curl</code> ou whatever qui cause <span class="caps">HTTP</span>/1.1 et <span class="caps">JSON</span></li>
</ul>
<p>Ubus est très lié à l’écosystème d’OpenWRT, les sources ne compilent pas sur une simple Debian (et encore moins sur un Mac).
OpenWRT cible une multitude d’architectures exotiques, et pour ça utilise une chaine de compilation (cross compilation, en l’occurence),
disponible via Docker, par ce qu’on est quand même en 2023 : <a href="https://github.com/openwrt/docker">openwrt/docker</a>.</p>
<p>Une image “bac à sable” est fourni, pour avoir un shell dans un environnement OpenWRT, mais sans les services lancés (pas de <code>ubusd</code>, donc), l’idée est de pouvoir simplement tester un paquet.</p>
<p>Le plus simple pour jeter un oeil reste de passer par un Vagrant avec <a href="https://github.com/vladimir-babichev/vagrant-openwrt-box">vagran-openwrt-box</a>.</p>
<h2>Le bus roi</h2>
<p>Pour pouvoir composer une application à base de services, le plus simple est de passer par un bus métier.</p>
<p>Un peu fatigué par les zélotes de Kafka (ou pire, des services spécifiques des gros du Cloud), ubus apporte une touche de fraicheur, avec une approche petit boutiste, bien loin des orgies grand boutiennes.</p>
<h3>OpenWRT, Linux comme avant</h3>
<p>Utiliser OpenWRT donne l’impression de voyager dans le passé, d’effleurer l’age d’or d’<span class="caps">UNIX</span>, avec son C omniprésent et des contraintes matériels énormes.</p>
<p>OpenWRT est minimaliste et traditionaliste, sans pour autant être complaisant et réactionnaire (chose assez facile à trouver dans le monde des firewalls) :
il utilise un kernel récent (un 5.10),
ne démarre pas les services depuis un dossier rempli de shell (il utilise son <a href="https://openwrt.org/docs/techref/procd">procd</a>),
utilise les possibilités d’isolations qui font la joie de Docker (namespace, capabilities, cgroup2, seccomp, bpf…).</p>
<p>Minimaliste, mais contemporain.</p>
<h3>Aller plus loin</h3>
<p>Il existe des <a href="https://openwrt.org/docs/guide-user/installation/openwrt-as-stock-firmware">boitiers avec OpenWRT installé dessus</a>,
coutant quelques dizaines d’euros, pour pouvoir jouer et profiter tout de suite de l’écosystème d’OpenWRT.</p>
<p>Si vous aimez l’absurde et les jeux de mots bancals sur <a href="https://fr.wikipedia.org/wiki/Ubu_roi">père Ubu</a> allez jeter un oeil <a href="https://www.youtube.com/watch?v=_vuzhkEkNSQ">l’étonnante adaptation filmé par Jean Christophe Averty</a>.</p>Libre, as a service2020-04-05T15:20:00+02:002020-04-05T15:20:00+02:00Mathieu Lecarmetag:blog.garambrogne.net,2020-04-05:/libre-as-a-service.html<p>Il faut degoogliser Internet, ok. Avec du logiciel libre, ok. Libre comme le discours, pas la bière, ok.</p><p>Il faut degoogliser Internet, ok. Avec du logiciel libre, ok. Libre comme le discours, pas la bière, ok.</p>
<h2>Brassage domestique</h2>
<p>Je dois pouvoir micro héberger, sur un gros mutu <span class="caps">PHP</span>, sans compétences particulières, euh non, en fait.</p>
<p>Il est indispensable de garder le contrôle de ses données, et de décentraliser Internet. C’est une démarche politique et technique. Les deux ensembles.
Une bouse technique, mais correcte politiquement, c’est un échec.</p>
<h2>Parlons pognon</h2>
<p>Vous voulez déposer une application sur Internet, et que des gens puissent l’utiliser. Ça va avoir un cout : en temps, en matos, en sous.</p>
<p>Le cout du développement, si c’est un logiciel libre, installé tel quel, sans modification, bah il est gratos, comme dans bière gratos.</p>
<p>Ensuite, il faut un serveur, et un branchement vers Internet. Avec les ordis de la taille d’une paume, et une <span class="caps">ADSL</span> à prix agressif, le cout d’un hébergement à la maison va être faible. Les offres entrées de gammes de nos hébergeurs nationaux (ou plus loin) sont elles aussi accessibles.</p>
<p>Concrètement, pour un service simple et peu sollicité, un hébergement peu cher aura des performances correctes.</p>
<p>Jusqu’ici tout va bien, le cout du développement et de l’hébergement matériel sont raisonnables.</p>
<p>Mais c’est que maintenant que l’histoire commence.</p>
<h2>Notre temps</h2>
<p>Vient maintenant la notion de maintenance.</p>
<p>Le développement, c’est ton temps, l’hébergement, c’est le temps des autres.</p>
<p>Le service va avoir des incidents, qu’ils soient matériels ou logiciels.
De simples bugs, sur lesquels on peut s’assoir, ou des failles de sécurité, qu’il faut patcher rapidement avant que ça dégénère. Pour certains services, comme du <span class="caps">SMTP</span>, un incident, ça veut dire un bannissement, et donc un arrêt partiel du service. Un service compromis qui va miner du bitcoin en mettant à genoux le serveur, ça veut dire aussi un arrêt du service.</p>
<p>Un disque dur qui boude, un serveur qui se coince, ça veut dire restaurer un backup, et donc avoir fait un backup.</p>
<p>À un moment, vos utilisateurs vont utiliser votre service, sinon, il ne sert pas à grand-chose, et donc le secouer un peu, puis beaucoup.
Ce n’est pas grave, vous centralisez les erreurs, enregistrez les métriques et faites un joli tableau de bord.
Une première passe pour trouver des usages abusifs, puis finalement, ils vont atteindre de vraies limites, et ça va ramer sévère.</p>
<p>Là, l’hébergement de ce service va commencer à être pénible. Le cout du dev est gratos, le cout du matériel est symbolique, le cout de la maintenance explose.
Que faire? Passer en force et surdimensionner les serveurs, mais ça veut dire tout réinstaller (bah oui, sans provisionning, sans conteneur, ça a un cout sympathique), et ça transfert un cout en temps vers un cout en sous.
Avec plus de matos, ou du matos plus gros, ça va pouvoir secouer plus fort, avec plus d’utilisateurs à mécontenter, et de nouveaux drames rendus possibles par le franchissement de la première vague de drames.</p>
<h3>Cramage</h3>
<p>À cette étape, il est possible de cramer l’admin, qui va se trouver bien seul, avec plein de gens pour râler. Si c’est pour du support utilisateur, un forum et des grunts peuvent décharger une partie de la charge. Pour les serveurs qui pètent, bah, c’est compliqué.</p>
<p>Pour ne pas cramer un humain, une seule solution en avoir plusieurs, une équipe en fait. Pour rentabiliser, il leur faut donc plus d’utilisateurs et donc une instance plus grosse. Il sera humainement possible de gérer plus de matos et donc céder à la facilité de continuer de passer en force</p>
<p>Ce n’est pas sale de payer des gens. Que ce soit en micropaiement ou salarié par une structure associative. Ils mangent, payent un loyer et s’habillent de teeshirt trop cher à cause des dessins qu’il ya dessus.</p>
<h3>Automatisation ou l’anti-luddisme</h3>
<p>À cette étape-là, infra as code n’est plus une option.</p>
<p>Par contre, empiler du matos, juste pour gérer des pics d’utilisation, ça va devenir hors de prix, et c’est même probable que le code ne va pas gérer la répartition sur plusieurs machines. L’impasse va vite arriver.</p>
<p>En parallèle, un autre problème va arriver : la complexité des services.
On est passé de l’ère des sites webs à celui des applications, et tout ne rentre pas dans l’http de l’an 2000, pour avoir du tchat, de la visioconférence, oui, ça va passer par les navigateurs webs, mais via websocket, webrtc et autres streamings. Cette complexité va faire exploser les couts de développement, et la complexité côté serveur. Cette fois-ci, Varnish ne pourra plus sauver votre développement de bourrin.</p>
<p>Les utilisateurs ont rapidement pris gout aux interfaces léchées des applications web des GAFAs. Essayez de faire passer un utilisateur non militant de Gmail à <a href="https://squirrelmail.org/">Squirrel mail</a>, juste pour voir.
Pour développer un service de qualité, il faut des compétences complémentaires (<span class="caps">UX</span>, graphisme, support, traduction, développement…) et tous ces métiers ne se payent pas tous de gloire, leur métier ne leur laisse pas forcément autant de temps et de thunes en rab, pour le consacrer au logiciel libre.</p>
<p>Le matos a changé d’échelle avec le remplacement des disques à plateaux qui se trainaient lamentablement par le <span class="caps">SSD</span>, et même avant qu’on se rende compte du changement par du NVMe. En face, de la fibre ou de la grosse 4G. Le matos est prêt à tout engloutir, ce ne sera ni le stockage, ni la bande passante qui vont ralentir la soif de pixels.</p>
<p>Donc, voilà, des hordes d’utilisateurs exigeants, avec des gros appétits, sont là pour apprécier vos services. Serez-vous à la hauteur?</p>
<h2>Un nouvel espoir</h2>
<p>Pour avoir une chance de réussir à proposer des services sympathiques en ligne, alternatifs ou pas, il faut d’abord mettre de l’ordre dans tout ce fatras.</p>
<h3>Protocoles</h3>
<p>L’idée de base est de ne pas avoir de monopole, il faut donc avoir des systèmes décentralisés, des machins qui causent entre eux pour profiter de l’effet d’échelle. Un réseau social fonctionne par ce qu’il y a déjà plein de gens dessus. Pour ça, il faut des protocoles, une locomotive pour tirer plein de monde, et des implémentations adaptés à des cas moins courants.</p>
<p><a href="https://fr.wikipedia.org/wiki/BitTorrent">Bittorrent</a> a parfaitement réussi ça, et <a href="https://joinmastodon.org/">Mastodon</a> peut réussir si <a href="https://pleroma.social/">Pleroma</a> arrive à se démocratiser. <a href="https://joinpeertube.org/fr/">Peertube</a> est fonctionnel, en déclinant le concept du pair à pair à la vidéo. Il faut voir ce que donnera <a href="https://ipfs.io/"><span class="caps">IPFS</span></a>.</p>
<p>Le mail est <span class="caps">LE</span> premier service distribué largement utilisé, mais le spam et la stagnation des outils libres qui ont permis l’apparition de gmail ont eu un effet dramatique. <span class="caps">DKIM</span> et <span class="caps">SPIF</span> sont maintenant un prérequis, mais l’arbitraire des gros serveurs est maintenant une menace permanente. L’incapacité du courriel à garantir la confidentialité des échanges, confirmée par l’échec de <a href="https://gnupg.org/"><span class="caps">GPG</span></a>, ont reléguer le mail au rôle de renouveleur de mots de passe.</p>
<p>Il faut des protocoles bien spécifiés pour être interopérables, et des implémentations qui scalent petit, et grand.</p>
<p>Étonnement, Gitlab et <a href="https://gitea.io/">Gittea</a> via git comme protocole permettent ça. Reste à normaliser la notion de fork et pull request.</p>
<p>De la distribution de service sans syndication efficace, bah, c’est juste de la dispersion.</p>
<h3>De bons outils</h3>
<p>Avant, on avait des disques qui ramaient, une pénurie de développeurs web, et des navigateurs pourrites. Donc pas de dev front. Il y eut ensuite l’épisode Flash + <span class="caps">IE6</span>, pour enfin aboutir à la modernité : <span class="caps">HTML5</span> et les spécifications EcmaScript, la déferlante des smartphones, les disques qui vont trop vite et les gros tuyaux.</p>
<p>Pour les devs, on a maintenant des langages matures, des bouquins avec des gravures d’animaux dessus, des frameworks, des workflows pour gérer le code, et de plus en plus d’analyses statiques.</p>
<p>Il faut un écosystème logiciel suffisamment responsable pour avoir des bibliothèques maintenues et pas systématiquement trouées.</p>
<h3>Qualité</h3>
<p>Donc, pour proposer des services de qualité en ligne, il faut des technologies suffisamment répandues pour avoir un stock de développeurs, et une chance de reprise si le mainteneur a un pépin. J’ai fait des patchs <a href="https://www.erlang.org/">Erlang</a>, par vice, mais je suis moyen chaud pour <span class="caps">RTFM</span> suffisamment pour proposer des patchs <a href="https://www.haskell.org/">Haskell</a>, <a href="https://www.ponylang.io/">Pony</a> ou autre <a href="https://crystal-lang.org/">Crystal</a>.</p>
<h3>Qui garde les gardiens</h3>
<p>Un serveur vautré a une qualité de zéro, niveau expérience utilisateur. Il faut avoir un système pour prévenir quand ça se vautre, avec tout ce qu’il faut de contexte pour pouvoir comprendre rapidement l’incident, puis le corriger (dites bonjour à <a href="https://sentry.io/">Sentry</a>), et que ce surveillant soit lui même plus fiable que le système qu’il surveille. Pour ça, il n’y a pas 36 solutions, il faut de la redondance, et là, ça commence à gripper pour l’auto hébergement. Pour l’instant il n’y a rien comme outils simples pour avoir une coopérative de monitoring, regroupant quelques auto-hébergeur.
Une fois que vous vous serez mangé un incident au moment où vous avez le plus envie de faire toute autre chose, bah, vous commencerez à vous renseigner sur la redondance et la tolérance de panne. Je ne parle pas de l’inévitable <span class="caps">RAID</span> de disques, mais des outils comme <a href="https://www.consul.io/">Consul</a> pour coordonner une petite grappe de machines et relancer des services sur les machines survivantes, vous laissant le temps d’organiser la réparation ou le changement d’une pièce. Je parle juste de coordination de services, l’omniprésent Kubernetes est largement au-delà du scope du petit hébergement.</p>
<h3>Efficience</h3>
<p>Concrètement, ce qui coince, maintenant, c’est la facture énergétique, et l’utilisation du <span class="caps">CPU</span>. La prévisibilité de son utilisation, pour être précis.</p>
<p>C’est difficile de dimensionner un serveur. Techniquement le premier palier va être gros, la machine sera surdimensionnée, tout comme l’est votre poste de développement. Avec peu d’utilisateurs, si marche sur votre laptop, ça marchera sur le serveur. Par contre, avec plein d’utilisateurs, le matériel va commencer à être utilisé comme il faut.
On va avoir un budget <span class="caps">CPU</span>/<span class="caps">RAM</span>/Disque par utilisateur. La notion de hit, avec les gens qui prennent le temps de lire, ça saute avec les applications et les flots <span class="caps">HTTP</span>. Pour avoir de la visibilité, il faut que ce budget ressource soit prévisible : si vous avez quelques workers avec 512Mo de <span class="caps">RAM</span> et un timeout de 30 secondes, ça va gérer les actions violentes sur le serveur, mais au détriment du nombre d’actions en parallèle.
En permettant que quelques-uns puissent tout prendre, bah ça va brimer l’ensemble du groupe. Pour offrir une qualité correcte à l’ensemble, il faut des quotas raisonnables pour tous, et mettre dans des files d’attente toutes les actions gourmandes : elles seront effectuées, mais sans se marcher dessus, et en prenant le temps qu’il faut. Rigoler en voyant le load d’un serveur, ça se faisait avant l’an 2000, plus en 2020.
L’asynchrone devient la règle, pour lisser les accès parallèles et assumer les différentes latences.</p>
<h3>Simple ou simpliste, complet ou complexe ?</h3>
<p>La tolérance du langage pour faciliter son apprentissage se paye cash au niveau maintenance et sécurité. L’analyse statique, que ce soit dans son éditeur ou la <span class="caps">CI</span> devient un prérequis. Typescript est venu ajouter la rigueur du typage fort à l’historiquement bordélique Javascript. Même Python a maintenant des annotations de type.</p>
<p>La facilité de déploiement est un argument ambigu. Pouvoir poser un machin en <span class="caps">PHP</span> dans un bout d’arborescence apache n’est pas un déploiement facile. Les gros mutus sont d’une autre époque, en fait. Proposer un déploiement simple sans drames de dépendances est l’objectif. La réponse ultime est bien sur le conteneur (le concept, hein, je ne parle pas d’implémentation). Les paquets systèmes ont failli pour déployer des services, mais ils gardent toute leur pertinence comme briques de base pour composer un service.
La norme existe : <a href="https://cnab.io/"><span class="caps">CNAB</span></a>, reste à peaufiner les implémentations.</p>
<p>La facilité de mises à jour avec un rollback facile est un prérequis. Si on déploie, on met à jour. Sinon, ce sera du site statique.</p>
<h3>Visibilité</h3>
<p>La visibilité, que ce soit les incidents ou les mesures (ce que ça fournit niveau métier, ce que ça mange), doit être systématique. <a href="https://fr.wikipedia.org/wiki/Htop">htop</a> et <code>grep /var/log</code> ne sont plus suffisants, hein. Sentry, même avec sa nouvelle licence est votre ami.</p>
<p>Arrêtez de tanner les gens avec des comptes et des mots de passe qui seront soit tout le temps le même, ou oubliées. Mozilla a lâché l’affaire avec son <span class="caps">SSO</span>, ce qui est frustrant, mais <span class="caps">JWT</span> ou OAuth sont maintenant bien diffusé. Par contre, il faut un retour en grâce d’OpenID, pour ne pas tout appuyer sur l’OAuth d’un des GAFAMs, ce qui serait un drame.</p>
<h3>L’usine nouvelle</h3>
<p>Arrêtez la comparaison industrie/artisanat pour l’informatique. Un service installé à la main, sur un serveur installé avec un <span class="caps">CD</span>, ce n’est pas comparable avec du pain ou un meuble d’artisan. La valeur de l’installation est dans la rigueur, les choix techniques, la connaissance de l’ensemble pour prendre des choix éclairés, pas dans la sueur et la vélocité des commandes tapée sur un clavier <a href="http://www.typematrix.com/">typematrix</a>. Faites votre levain, et provisionnez vos serveurs. L’installation c’est le prélude, l’histoire, c’est la maintenance et les mises à jour de nouvelles fonctions.</p>
<p>Gmail n’a pas gagné par ce qu’il était meilleur, mais par ce que les offres du logiciel libre ont échoué.</p>Déployer des microservices avec des dépendances2018-12-01T14:09:00+01:002018-12-01T14:09:00+01:00Mathieu Lecarmetag:blog.garambrogne.net,2018-12-01:/microservices-avec-dependances.html<p>Les microservices permettent de tout découper en petites entitées autonomes, mais comment garder une cohérence lors des multiples déploiements?</p><h1>Microservices</h1>
<p>Le principe de bases des microservices est simple : une application est composée d’un ensemble de services, qui peuvent discuter entre eux.</p>
<p>La règle de base étant que ces microservices puissent avoir un cycle de vie (et de déploiement) autonome.</p>
<p>Si vous devez déployer plusieurs microservices d’un coup, bravo, vous avez un monolithe distribué.</p>
<h1>La confusion entre microservice et conteneur</h1>
<p>Attention, il n’y a absolument pas de ratio un pour un entre microservice et conteneur. Un service peut être rendu par plusieurs conteneurs, regroupé ou non en pod.
L’exemple typique étant le site web et son traitement asynchrone (rails+sidekiq, flask+celery …). Les serveurs de bases de données peuvent être mutualisés, tant qu’on ne touche pas à la sacrosainte isolation des bases.</p>
<h1>Versionning</h1>
<p>Un service peut consommer un autre service.
Les différents microservices sont versionnés, que ce soit avec un hash git tout moche, ou un joli semver.</p>
<p>Même s’il est possible d’appeler un service par son nom, il est quand même sage d’avoir un graphe des différents services : qui consomme qui.
C’est l’approche des <code>links</code> de Docker-compose, Kubernetes préférant l’anonymat des <code>Services</code>.</p>
<p>Les services sont des boites, la version du service doit rester privée, ce qui est public (et concerne les autres services), c’est la version de son <span class="caps">API</span>.</p>
<p>Du coup, chaque service peut réclamer un autre service avec une version d’<span class="caps">API</span>.</p>
<p>Un peu comme de la gestion de bibliothèque, quoi, on retombe sur le pattern bien maitrisé de gem/yarn/dep/pip et compagnie.</p>
<p>Pour chaque service, on peut déterminer sa version d’<span class="caps">API</span>, et la liste des services avec les versions minimum attendues. Ces informations peuvent être rangées dans des étiquettes des images (des labels docker, quoi), ce qui permet de retrouver simplement l’information sur un environnement live.</p>
<p>La procédure de déploiement peut débuter par un preflight : on vérifie que le service que l’on souhaite déployer va trouver les services consommés dans les versions attendues.</p>
<p>Intégrer le preflight, à titre informatif, dans son cycle d’intégration continue, est une bonne idée : “attention, là maintenant, votre service n’a pas les prérequis pour être déployé en prod, il va peut être falloir discuter avec l’autre équipe”.</p>
<p>D’un autre côté, présenter une <span class="caps">API</span>, mais sans la déployer, ce n’est pas super fairplay non plus, ça sent le refactoring qui a du mal à aboutir.</p>
<h1>Déployer à blanc</h1>
<p>Pour limiter les embrouilles de chronologie de déploiement pour des histoires de dépendances, chose que l’approche micro services promettait d’éviter, il ne faut pas hésiter à déployer des services à blanc, en déployant des bouts d’<span class="caps">API</span> qui ne seront consommés que par des tests fonctionnels. On déploie d’abord un serveur sans clients, puis dans un second temps les clients.</p>
<p>Le service est déployé, testé, un éventuel test de charge pour évaluer son comportement en charge, ou même déployé en fantôme, avec des clients qui l’utilisent, mais sans le dire, soit en doublon d’un service qu’il souhaite remplacer, soit headless, sans que les utilisateurs le voie. Facebook avait fait ce genre de chose pour son tchat.</p>
<p>Lorsque l’on déploie un service, on se réserve la possibilité de faire un rollback en cas de drame. Par contre, attention, quand on déploie une nouvelle <span class="caps">API</span>, qu’on la valide incorrectement, puis en déployant le client qui va la consommer, on se rend compte d’un problème : c’est le drame, le seul rollback possible est une cascade de rollback, ce que l’on souhaiter éviter à tout prix.</p>
<p>Pour les services très fortement sollicités, le déploiement se fera de manière progressive, en gardant les yeux rivés sur les performances et les taux d’erreurs. Le déploiement progressif rend physiquement impossible, ou presque le déploiement par lot. Ça tombe bien, c’est ce que l’on souhaite éviter.</p>
<h1>Étendre le concept</h1>
<p>Cette notion de version d’<span class="caps">API</span> peut être étendue aux modèles de données.
La modification devra se faire en deux étapes, la première va ajouter des colonnes, sans casser le code actuellement déployé. Ensuite, on pourra déployer le code utilisant le nouveau modèle, et enfin, déploiement des modifications qui vont enlever des colonnes.</p>
<p>Modifier des colonnes va être un peu plus compliqué. Soit le modèle logiciel (l’<span class="caps">ORM</span>, à priori) va assurer la transition en calculant les modifications à la volée, puis en tache de fond, de gros batchs assurent une transition complète, sans subir un phénomène de longue traine. Mongodb se prête bien à ce genre d’astuce.</p>
<p>La migration de modèle est un sujet complet, complémentaire du déploiement.
Allez jeter un oeil à <a href="https://github.com/github/gh-ost">gh-ost</a> pour avoir une étendu du sujet.</p>
<h1>Déprécier</h1>
<p>Effacer des trucs, ça veut dire prendre le risque de le regretter plus tard.
À grande échelle, on passe du regret à l’angoisse.
Du coup, il est tellement plus confortable d’entasser à l’infini, de se proposer des <span class="caps">API</span> avec une compatibilité ascendante depuis le premier jour.
Dans le même esprit, on ne rajoute pas de contrainte sur des données stockées (dans l’approche de stockage massif à la <a href="http://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html"><span class="caps">HDFS</span></a>).
C’est ce que propose <a href="https://developers.google.com/protocol-buffers/">Protobuff</a> et <a href="https://thrift.apache.org/">Thrift</a>.</p>
<p>Il est quand même possible de connaitre les versions de clients déployés (en interne), et d’avoir des stats sur les clients externes. Avec ces infos, on peut déprécier l’<span class="caps">API</span>, forcer la main pour mettre à jour les clients, puis retirer l’ancienne <span class="caps">API</span>. Bazarder du code mort, ça fait toujours plaisir, et ça évitera d’avoir des surprises plus tard. Mettre à disposition un proxy qui fera le passe plat entre différentes versions de l’<span class="caps">API</span> peut être un palliatif, mais une coupe franche est nettement plus simple.</p>
<h1>Automatiser</h1>
<p>Il existe des grammaires pour les différentes <span class="caps">API</span> distantes. <a href="https://www.openapis.org/">OpenAPI</a> (ex-swagger), <a href="https://grpc.io/">grpc</a> et tant d’autres sont basé sur cette approche.
Ce contrat doit être le seul lien entre les différents services, le sujet de discussion entre les différentes équipes. Ce contrat va permettre de générer du code, ou le valider, selon les cultures logicielles.</p>
<p>Avec du code bien rangé, il est possible de lancer automatiquement des tests sur les différents clients quand on modifie le serveur. Trivial pour des tests fonctionnels avec des services conteneurisés, envisageable pour des bibliothèques. C’est là que débute le troll sur le <a href="https://en.wikipedia.org/wiki/Monorepo">monorepo</a>, mais un sujet orthogonal au déploiement de microservices.</p>Kubernetes est la réponse, c’est quoi déjà la question ?2018-06-20T19:23:00+02:002018-06-20T19:23:00+02:00Mathieu Lecarmetag:blog.garambrogne.net,2018-06-20:/kubernetes_est_la_reponse_c_est_quoi_deja_la_question.html<p>Drôle d’ambiance autour de Kubernetes, en ce moment. Le Docker bashing à toujours la cote : des gens avec des avis tranchés, qui ont réussi avec arrogance, là où <span class="caps">LXC</span> pataugeait depuis toujours, pour finalement aboutir à une norme et des spécifications. Mais, en même temps, Kubernetes, qui est prêt pour la prod, lui, est encensé, attendu comme le messie.</p><p>Drôle d’ambiance autour de Kubernetes, en ce moment. Le Docker bashing à toujours la cote : des gens avec des avis tranchés, qui ont réussi avec arrogance, là où <span class="caps">LXC</span> pataugeait depuis toujours, pour finalement aboutir à une norme et des spécifications.
Mais, en même temps, Kubernetes, qui est prêt pour la prod, lui, est encensé, attendu comme le messie. Ça donne des <a href="https://twitter.com/amicel/status/1009326802106552320">tweets de ce genre</a>.</p>
<h2>Services</h2>
<p>Commençons par le commencement, votre application est orientée service : c’est l’assemblage de différents services.
Un truc comme django/celery/postgresql/redis ou même php-fpm/mariadb/memcache/varnish/nginx ou que sais-je dans ce gout-là.</p>
<p>Si votre application est un gros binaire qui contient tout, un peu comme la boite contenant le mouton du Petit Prince, félicitation, vous faites du <a href="https://fr.wikipedia.org/wiki/Zope">Zope</a>, vous n’avez pas à vous enquiquiner avec ces histoires de Kubernetes.</p>
<h2>Microservices</h2>
<p>La question n’est pas là, utiliser des services est déjà suffisant, et ce sera encore plus fun avec des microservices.</p>
<h2>Conteneur</h2>
<p>Docker quoi, on peut tourner autour du pot, mais même avec la normalisation de l’<a href="https://www.opencontainers.org/"><span class="caps">OCI</span></a>, Docker est pour l’instant le standard de fait. Le travail d’intégration sur les postes de travail, sur Windows/Mac/Linux, est trop énorme (et trop pénible) pour être paraphrasé par un concurrent.</p>
<p>Un conteneur, c’est juste un format de livraison d’un service standardisé, immuable, et la possibilité de le lancer avec des paramètres, de l’isolation et des contraintes matérielles (<span class="caps">RAM</span>, <span class="caps">CPU</span>, <span class="caps">IO</span>).</p>
<p>Son immuabilité renforce et facilite la mise en place de tests fonctionnels et autres étapes de qualification.</p>
<p>La construction déterministe, et le résultat déterminé facilitent le déploiement, sur un ou plusieurs serveurs, et le retour en arrière en cas de drame.</p>
<p>Les conteneurs donnent aux développeurs la responsabilité de la création du conteneur, il choisit les paquets et applications nécessaires. Ces choix sont décrits de manière simple et lisible, qui seront versionnés, deux points qui facilitent l’analyse statique et les audits.</p>
<p>L’étape de créations des images et les tests/analyses qui suivent doivent être construits depuis un service d’intégration continue.</p>
<p>La recette de build automatique, les versions d’outils explicites, et les différentes astuces pour faciliter le développement diminuent drastiquement le ticket d’entrée pour un nouveau développeur sur un projet. Avec une <span class="caps">VM</span>, on comptait un jour, avec des conteneurs, une heure.</p>
<h2>Composition</h2>
<p>Docker-compose est le standard de fait pour décrire la composition de services, hey wouais, l’<span class="caps">OCI</span> n’a encore rien dit là-dessus.</p>
<p>Tout comme le <code>Dockerfile</code> décrit de manière non ambigüe la création d’une image, <code>docker-compose.yml</code> va décrire l’assemblage de différents services, avec les paramètres et les liens entre eux.</p>
<p>L’outil <code>docker-compose</code> va gérer tranquillement tout plein de détails pénibles, comme la création d’un réseau isolé, les dépendances entres services, les logs, et même un peu de scale.
Il est bon pour mettre à jour le service que l’on vient de mettre à jour et de relancer les services qui l’utilisent, sans forcément tout relancer.</p>
<p>Techniquement, le fichier <code>docker-compose.yml</code> expose à peu près tous les choix que peut faire un développeur pour décrire son application. Bon, Swarm a un peu cochonné le format, mais ça reste anecdotique. Pour ce qui manque, les <em>labels</em> permettent de décrire les intentions sans trop de difficultés ou de circonvolutions.</p>
<p>Le format du fichier <code>docker-compose.yml</code> permet de générer des configurations vers d’autres outils :
- <a href="http://kompose.io/">Kompose</a> pour Kubernetes
- <a href="https://github.com/paypal/dce-go">dce-go</a> pour <a href="http://mesos.apache.org/">Mesos</a>
- Bon, <a href="https://www.nomadproject.io/">Nomad</a> est tellement peu opiniated qu’il faudra faire vos choix et votre moulinette.</p>
<h2>Supervision</h2>
<p>Le service <code>dockerd</code> (<code>containerd</code> dans les faits) est aussi un superviseur, il va suivre et commander le cycle de vie des différents conteneurs.</p>
<p><code>dockerd</code> empiète un peu sur <code>systemd</code>, et les deux projets aiment ce jeter des cailloux dessus.</p>
<p>Le monde se divise en deux catégories, enfin, le monde, les services : systèmes et applicatif. Les applications s’appuyant sur les services systèmes (en dessous), pour être exposées à des utilisateurs (au-dessus).</p>
<p>Systemd, ça gère la couche système, et seul l’utilisateur root peut le manipuler.</p>
<p>La première cible de Docker est la couche applicative, et n’importe qui avec le bon certificat <span class="caps">SSL</span> ou le bon groupe peut l’utiliser. Ça ne veut pas pour autant dire que c’est une super idée d’avoir des utilisateurs qui tripote des dockers en prod depuis la console.</p>
<p>Il est tout à fait légitime d’avoir un service lancé par <code>systemd</code> qui gère des conteneurs systèmes .C’est comme ça qu’est implémenté Kubernetes, d’ailleurs, avec son <code>kubelet</code>, et <a href="https://blog.mobyproject.org/containerd-namespaces-for-docker-kubernetes-and-beyond-d6c43f565084">les namespaces de <code>containerd</code> vont pouvoir encore mieux isoler ces groupes distincts de conteneurs</a>.</p>
<p>Par contre, vouloir orchestrer un ensemble de services formant une application avec un ensemble de fichiers <code>`.service</code> systemd est une drôle d’idée. Les conteneurs sont des fils des <code>docker-containerd-shim</code> qui vont cafter au démon <code>containerd</code> l’état du conteneur, excluant complètement <code>systemd</code> de la boucle, qui serait bien incapable de superviser quoi que ce soit.</p>
<h2>Instrumentation</h2>
<p>Docker va gérer des services, bien rangés dans des cgroups qui maintiennent des métriques, et branche <span class="caps">STDOUT</span> vers une sortie quelconque, un fichier ou un service de logging.
<a href="https://12factor.net/">12 factors</a> prétends que ça suffit, mais c’est juste le strict minimum au niveau observabilité.</p>
<p>Il faut passer rapidement à des trucs plus sérieux, comme des rapports d’erreurs avec Sentry, des logs structurés (au format fluentd), des mesures métiers avec les exports prometheus pour les technos asynchrones, et statsd pour les autres.</p>
<h2>Routage</h2>
<p>C’est bien, d’avoir plein de conteneurs, mais vous allez exposer tout ça, dans la plupart des cas avec une seule <span class="caps">IP</span> et un seul port, 443 à priori (pour bien faire, cette <span class="caps">IP</span> sera flottante, et vous la collerez à une machine virtuelle en vie).</p>
<p>Il va donc falloir un peu de coordination entre les conteneurs et des règles de routages (et de la répartition de charge).</p>
<h2>Persistance</h2>
<p>C’est bien d’avoir des conteneurs “quelque part”, qui bougent en fonction des vagues. Mais cette mobilité n’est possible qu’avec des conteneurs sans état, état qui va bien devoir être quelque part. Pour ça, il faut une solution de stockage distribué, en mode blob, à la S3 (ou <a href="https://www.minio.io/">Minio</a>), ou en mode block, à la iSCSI. Donc, du <a href="https://ceph.com/">Ceph</a> ou une offre <span class="caps">SAAS</span> spécifique et propriétaire que votre fournisseur de nuage vous proposera gentiment.</p>
<h2>Orchestration</h2>
<p>Pour gérer un ensemble de conteneurs sur plusieurs machines, il faut de l’orchestration. L’orchestrateur a la charge de décider comment les conteneurs vont être répartis sur les différentes machines, que faire quand un noeud disparait ou quand un nouveau apparait.</p>
<p>L’orchestrateur va s’appuyer sur service clef/valeur distribué (comme <a href="https://coreos.com/etcd/">etcd</a>) pour mettre à disposition de tout le monde tout ce qui est paramétrage et maintenir une carte de l’existant.</p>
<p>L’orchestrateur va réagir à un ensemble d’évènements, et en réponse à des commandes, déclencher des évènements (et des actions) comme les stratégies de mises à jour des conteneurs, ou la mythique promesse du Cloud : lancer des <span class="caps">VM</span> pour empiler encore plus de conteneurs pour accompagner un pic de charge, puis les éteindre tranquillement une fois la vague passée.</p>
<p>Voilà, là, vous êtes à l’étape Kubernetes, et pour ça, vous avez pieusement suivi toutes les étapes précédentes, car vous avez besoin de plusieurs serveurs pour héberger votre application.</p>
<p>La vieille promesse de l’élastique du nuage n’est possible qu’avec de l’orchestration, et donc des conteneurs, de l’intégration continue, de l’observabilité, une séparation nette entre l’expression de l’intention (sous la responsabilité des devs) et les choix de son implémentation (sous la responsabilité des ops).</p>Google a très envie de manger tout l’hébergement Internet2018-05-08T17:30:00+02:002018-05-08T17:30:00+02:00Mathieu Lecarmetag:blog.garambrogne.net,2018-05-08:/google-veut-manger-Internet.html<p>Google part conquérir l’hébergement Internet et veut éradiquer les <span class="caps">DSI</span> avec toute la finesse d’un tapis de bombes.</p><p>Le marché de l’hébergement Cloud arrive à maturité. OpenStack a réussi à éradiquer tous les pouilleux, la place est libérée pour les trois gros, Amazon, Google et Azure, qui vont pouvoir s’étriper tranquilement.</p>
<p>Le marché du cloud a été découvert par Amazon pour répondre à ses propres besoins.
Amazon s’est contentée d’ouvrir son outillage interne, très rapidement organisé en unités autonomes, chacune étant cliente les unes les autres. L’offre d’<span class="caps">AWS</span> est un mélange d’outils standards (machines virtuelles, disques distants…) et de services spécifiques dont certains sont maintenant des standards de fait (S3) d’autres non (dynamodb), pour ensuite proposer des services de plus haut niveau, comme les bases infogérées ou les <span class="caps">CDN</span>.</p>
<p>Cette offre a parfaitement accompagné la première vague du Web et corresponds tout à fait aux besoins des gros sites américains, qui ont besoin de grosses croissances pour arriver rapidement en position abusivement dominante à l’échelle de l’Occident, pour commencer à être bénéficiaire. Ces sites croissent comme des crabes, chaque changement de carapace correspondant à un tour de table, et il faut devenir bien plus gros. Le fameux “ça scale” tant vanté par le Cloud.</p>
<p>Google a bien vu grandir Amazon, et à toujours eut une technologie plus performante, plus intégrée, plus tout, en fait. Une partie des technos d’Amazon sont issus des white papers de Google. Sauf que Google, à la différence d’Amazon a une approche très cathédrale, très intégrée, très peu ouverte, en fait. Google a zappé l’étape des machines virtuelles en passant directement aux conteneurs (les CPUs n’étaient pas près quand la question s’est posée) bien plus efficaces pour densifier, et tout à fait compatible avec un partage non agressif, coordonné des ressources.</p>
<p>Google, une fois ses services bien installés, a su faire des efforts pour avoir des clients qui consomment ses services (Android, Chrome), mais a été longtemps une grosse quiche pour ses offres d’hébergement. Google semble tout simplement inadapté pour reprendre une idée qui n’est pas la sienne. Un peu comme les humiliations de Google+ ou pire, <a href="https://fr.wikipedia.org/wiki/Google_Wave">Google Wave</a>.</p>
<p>Par contre, leurs services sont bons : gmail, calendar, docs. Ils sont surtout encore meilleurs pour bosser en groupe, et remplacent des outils forts pénibles et corporate, comme la suite Microsoft, ou pire encore, <span class="caps">IBM</span> Lotus. Outre leur ergonomie agréable, ces outils éradiquent les <a href="https://fr.wikipedia.org/wiki/Bastard_Operator_From_Hell"><span class="caps">BOFH</span></a>, craints par tous, direction comme utilisateurs. Avec ces offres orientés pro, Google à mis un pied dans l’entreprise.</p>
<p>Donc, les services sont bons, les clients sont bons, que reste-t-il à récupérer dans la chaine de valeur? Les serveurs, pas les siens qui sont optimums depuis une grosse décennie, mais ceux des autres.</p>
<p>Première vague, Google App Engine. Google reprends son approche interne, les applications sont contraintes, mais bénéficient d’un ensemble de services de très bonne qualité (comme la BigTable). Sauf que non. Les utilisateurs n’ont que faire de contraintes les obligeants à réinventer la roue alors qu’ils existent tant de trucs open source permettant de sortir trop tôt un gros tas de boue mal fagoté. Les services proposés sont effectivement mythiques et permette de scaler plus haut que sa carte bleue, mais le ticket d’entrées (technique) est rédhibitoire, tout ça pour un truc captif aux performances fluctuantes.</p>
<p>Entre temps apparait Docker, qui réassemble de manière cohérente les bouts de kernel que Google à écrit pour sa propre solution de conteneur, <a href="https://en.wikipedia.org/wiki/Lmctfy"><span class="caps">LMCTFY</span></a>. La première appropriation du logiciel libre des conteneurs, <span class="caps">LXC</span>, n’était guère plus qu’un gros <span class="caps">POC</span> myope. Docker apporte la notion de services composables, immuables et paramétrable, et, arme secrète, Docker fonctionne très bien sur les ordinateurs des développeurs (Linux, Mac et Windows à égalité), et c’est en fait le premier critère pour l’adoption d’une nouvelle technologie.</p>
<p>Google manque terriblement d’empathie, ils sont capables d’inventer des tas de concepts, mais sont incapables de se mettre au niveau des gros ploucs de développeurs. Ou des gros ploucs de pipelettes des réseaux sociaux, d’ailleurs.</p>
<p>Ils ont du coup mise en place une nouvelle approche, un peu comme leurs withes papers qui deviennent des outils utilisables par le reste du monde. Ils prennent une de leur technologie interne, qui à eut le temps de bien maturer, et ne conservent que les concepts ou les parties bas niveau (comme les patchs kernels) pour les confier à des gens extérieurs, des gens bizarres avec des sentiments. De fait, ils exfiltrent eux même leurs technologies, en petits bouts autonomes. Ils ont réussi ça avec golang, qui a une vraie communauté, et qui a d’ailleurs servi de base à Docker, et à toute une génération des services de bas niveau. Cela dit en passant, un des gros échec d’OpenStack est python, tout à fait inadapté à ce genre de tache, mais bon à l’époque, les autres langages disponibles (java, C++) auraient aussi été un drame.</p>
<p>Donc, pour attaquer l’hébergement, il faut d’abord un socle, et faire des concessions aux ploucs. C’est l’offre Google Cloud, qui expose une couche super standard : de la machine virtuelle avec <span class="caps">KVM</span> (avec comme effet de bord de faire chier <span class="caps">AMZ</span> qui a choisi l’autre virtualisation, Xen, qui n’est pas dans le kernel vanilla). Réel effort de la part de Google qui n’utilise pas de virtualisation en interne.
Ensuite les trucs de base, comme le réseau, les disques distants, du stockage objet compatible S3, du routage, du <span class="caps">SQL</span> infogéré, quelques services qui envoient du rêve (à base de big data et de machine learning).</p>
<p>Voilà, ça, c’est le ticket d’entrée pour commencer à discuter. Pour avoir des utilisateurs et donc du débug, Google utilise la technique super classique, une offre massive de crédits à des startoups ambitieuses, des incitations à la création de sociétés de services qui ne font que du Google Cloud, des espèces de franchises, en fait.
Pour montrer leur humanité, Google met en place un système astucieux de conseil sur le dimensionnement des services pour que l’on crame moins de sous. Un peu comme une cafétéria qui conseillerait de ne pas remplir son plateau pour pas que ça finisse à la poubelle, en fait. Payer ce que l’on utilise, ça semble bête, mais ça le différencie de <span class="caps">AWS</span>, qui eux ont pour motto “la carte bleu comme seul limite”.</p>
<p>Voilà, Google a aussi son offre Cloud crédible, mais comment aller plus loin? la guerre des prix avec <span class="caps">AWS</span> est un peu vaine, et ce genre de blague finit souvent en victoire à la <a href="https://fr.wikipedia.org/wiki/Victoire_%C3%A0_la_Pyrrhus">Pyrrhus</a>. Non, la cible n’est pas d’aller tout de suite vers le <a href="https://fr.wikipedia.org/wiki/Co%C3%BBt_marginal">coût marginal</a> et la notion de <a href="https://fr.wikipedia.org/wiki/Commodit%C3%A9">commodité</a> (pas les <span class="caps">WC</span>, le barbarisme traduisant “commodity”). La cible est d’aller là où il y a des sous, d’aller s’occuper des <span class="caps">DSI</span> et de l’hébergement de leurs serveurs.</p>
<p>Pour ça, Google est en train de déployer la tactique finaude du tapis de bombe, le carpet bombing des années 1940.</p>
<p>Première vagues, les services, gmail et Google Docs, les utilisateurs veulent du Google et trouvent ringard les services existants.</p>
<p>Deuxième vague, l’hébergement classique. Techniquement, la notion de <span class="caps">VM</span> n’est qu’une transition, c’est compliqué, dur à dimensionner, dur à maintenir au fil du temps. Comme solution plus adéquate, il y a bien sûr les conteneurs, standard maintenant officialisé par l’<a href="https://www.opencontainers.org/"><span class="caps">OCI</span></a> et installé sur tous les postes de développeurs. Docker est mature coté dévelopement, mais pas encore coté hébergement, pour ça, il faut de l’orchestration, et il y a maintenant Kubernetes, offre exfiltrée à partir d’une techno maison de Google, <a href="https://ai.google/research/pubs/pub43438">Borg</a>, qui devient tout aussi standard et officiel que les conteneurs de l’<span class="caps">OCI</span>, mais dans un autre consortium, le <a href="https://www.cncf.io/"><span class="caps">CNCF</span></a>, avec ses amis Envoy (pour le routage) et Prometheus (pour les mesures).</p>
<p>Kubernetes est open source, bien spécifié, bien maintenu, plein de vie. Pourquoi se méfier de son coté captif? Tout simplement par ce qu’il est tout aussi complexe à héberger qu’un <a href="https://ceph.com/">Ceph</a> dont tout le monde vante l’open source sans pour autant le déployer. Kubernetes va éradiquer l’offre Docker Swarm qui n’a jamais convaincu grand monde, et débarquer par défaut sur tout les postes de dev avec le très confortable <a href="https://kubernetes.io/docs/getting-started-guides/minikube/">minikube</a>. Kubernetes a besoin de s’appuyer sur des services de persistances (mode bloques et objets), bien pénible à héberger. En partant de bare metal, il faut donc assumer un Ceph en plus de Kubernetes comme ticket d’entrée.</p>
<p>C’est de plus en plus visible : la notion d’open source fusionne avec la notion de standard et de portabilité. L’hébergement de Kubernetes, même si on garde le choix entre 3 mammouths est de fait propriétaire, tout en gardant le développement open source. L’open source se fait commodifier la gueule, en fait, mais avec le sourire. L’open source est utilisé pour faire chier les autres, Microsoft peut en témoigner.</p>
<p>Une bonne vague d’hardware inaccessible, pour continuer à distancer la concurrence (gpu haut de gamme , <a href="https://cloud.google.com/tpu/">tpu</a>) toujours basée sur des frameworks bien libres comme <a href="https://www.tensorflow.org/">Tensorflow</a>.</p>
<p>La prochaine vague, déjà bien amorcée, est le serverless, un buzzword pour imposer un framework n’exposant que du code métier, facilitant de fait l’hébergement (et basé sur des conteneurs encore plus densifiés).</p>
<p>Une vague de métrologie, avec <a href="https://opencensus.io/">OpenCensus</a> qui va permettre d’unifier les mesures privées (les services fournit par Google), avec les mesures publics, dans le code déployé.</p>
<p>La vague suivante va être la sécurité avec <a href="https://cloudplatform.googleblog.com/2018/05/Open-sourcing-gVisor-a-sandboxed-container-runtime.html">gVisor</a> pour les conteneurs, et <a href="https://asylo.dev/">Asylo</a> pour les enclaves.</p>
<p>La tactique est des plus classiques. On abuse de l’exclusivité tant qu’on peut, tout en démolissant la marge de la génération précédente de technologie, tout en maintenant un cout d’entrée énorme, pour exclure les nouveaux entrants. Ça permet à la fois de réduire ses propres coûts tout en asséchant la concurrence. On a ainsi vu récemment passer la libération des frameworks de machine learning, en mode terre brulée (Google, Nokia, Microsoft…), pour finalement se concentrer sur la vente d’hébergement avec du matériel spécifique (les fameux <span class="caps">TPU</span>), ou “as a service” pour les utilisateurs aux compétences limités : <a href="https://cloud.google.com/automl/">AutoML</a>.</p>
<p>À l’époque de la ruée vers l’or, le meilleur moyen de devenir riche était de vendre des pelles (avec l’exclusivité), pas de creuser. L’offre Cloud de Google nous vends des pelles.</p>La tokenisation et les index inversés au service de la lecture des logs2018-03-07T19:23:00+01:002018-03-07T19:23:00+01:00Mathieu Lecarmetag:blog.garambrogne.net,2018-03-07:/tokenisation-lecture-logs.html<p>Les logs ne servent pas qu’à remplir les disques, mais dans tous les cas, ce n’est pas aux humains de lire tout ça.</p><p>Les logs servent à remplir les disques (souvent) et à déboguer (parfois), mais pas que.
Logstash a démontré que l’on pouvait entasser des logs dans un moteur de recherche pour faciliter les analyses postmortem d’incidents. On peut aussi compter des trucs et des machins pour faire de jolis graphes, voir même, en lâchant des sous, avoir des alertes avec <a href="https://www.elastic.co/fr/products/x-pack/alerting">alerting</a>. Elasticsearch propose aussi la notion de <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-percolate-query.html">percolate</a> avec ses recherches à l’envers.
Les notions existent et sont même déjà implémentées (en Java).</p>
<p>Voici une présentation pédagogique, avec une implémentation toute légère en golang, pour chercher des motifs dans des flots de logs : <a href="https://github.com/athoune/yangtze">Yangtze 长江</a>.</p>
<h2>Concept</h2>
<p>L’idée de base est d’enregistrer des requêtes, et de leur soumettre des lignes de log. C’est la percolation d’Elasticsearch.</p>
<p>Pour ne pas tout bouffer la <span class="caps">RAM</span> et le <span class="caps">CPU</span>, les expressions régulières ne seront surtout pas utilisées de manière systématique.</p>
<p>Même si <a href="https://www.elastic.co/guide/en/logstash/current/plugins-filters-grok.html">Grok</a> permet d’écrire des regex concises et compréhensibles, même si <a href="https://github.com/google/re2"><span class="caps">RE2</span></a> limite le bazar des très classiques <span class="caps">PCRE</span>, les regexps restent un marteau qui scalent mal. C’est vrai, Piwik (enfin, <a href="https://matomo.org/">Matomo</a>) l’a prouvé avec son <a href="https://github.com/matomo-org/device-detector">parser de user-agent</a>.</p>
<p>Indexer une expression régulière oblige à la déplier, et c’est rapidement pénible à faire (du code ruby existe et fonctionne plutôt bien), pour un résultat peu concluant.</p>
<p>L’idée est de découper le motif en jetons (en tokens, en mots, quoi), et de les utiliser pour indexer les requêtes. En découpant les lignes de logs en jeton, on peut retrouver les requêtes correspondantes, et ne tester le motif que sur les lignes qui ont les jetons nécessaires.</p>
<h2>Implémentation</h2>
<h3>Motifs</h3>
<p>Plutôt que d’aller se coincer dans du vrai regex, je pars sur un sous-ensemble minimaliste :</p>
<ul>
<li><code>.</code> un jeton</li>
<li><code>?</code> zéro ou un jeton</li>
<li><code>...</code> plusieurs jetons</li>
</ul>
<h3>Tokenisation</h3>
<p>La tokenization est tout aussi brutale, les jetons sont constitués de <a href="https://golang.org/pkg/unicode/#IsLetter">lettres</a>, de <a href="https://golang.org/pkg/unicode/#IsDigit">chiffres</a>, de <code>_</code> et de <code>-</code>. Tout le reste, ponctuation, parenthèses, espaces… est considéré comme des séparateurs.</p>
<div class="highlight"><pre><span></span><code><span class="n">Je</span><span class="w"> </span><span class="o">[</span><span class="n">mange</span><span class="o">]</span><span class="w"> </span><span class="n">des</span><span class="w"> </span><span class="n">carottes</span><span class="p">.</span>
</code></pre></div>
<p>Va donner :</p>
<ul>
<li>Je</li>
<li>mange</li>
<li>des</li>
<li>carottes</li>
</ul>
<p>Les tokens qui sont indexés (ceux des requêtes, pas ceux des lignes) sont rangés dans un tableau, et un identifiant (un simple compteur) leur est attribué. Par convention, les identifiants commencent à 1, 0 est réservé pour gérer le rien, un token qui n’est pas indexé.</p>
<p>Avec la tokenisation et les identifiants, on peut transformer une ligne en une suite de nombres.</p>
<div class="highlight"><pre><span></span><code>"a ... c" => [1, 0, 2] // un motif
"a a b c" => [1, 1, 0, 2] // une ligne
</code></pre></div>
<h3>Bitset</h3>
<p>Les <a href="https://godoc.org/github.com/willf/bitset">bitsets</a> sont de longues suites de booléens, leur rang correspond à leur valeur. Par convention, le 0 est toujours <code>false</code>. Avec un bitset, on ne peut savoir que si un jeton est présent ou non, on perd sa position et son nombre d’occurrences. Par contre, il est facile d’effectuer des opérations booléennes, des opérations sur des <a href="https://fr.wikipedia.org/wiki/Ensemble_(informatique)">ensembles</a>, ou compter le nombre de bits allumés (on parle alors de cardinalité).</p>
<div class="highlight"><pre><span></span><code>"a ... c" => ◻︎◼︎◼︎
"c . a" => ◻︎◼︎◼︎
"a a" => ◻︎◼︎◻︎
"a a b c" => ◻︎◼︎◼︎
</code></pre></div>
<p>Une ligne avec une cardinalité nulle ne matchera jamais, première simplification.</p>
<p>Si le bitset d’une requête n’est pas inclus dans le bitstet de la ligne, on sait que ça ne matchera pas.</p>
<div class="highlight"><pre><span></span><code>set("a ... c") ⊂ set("a a b c")
set("c . a") ⊂ set("a a b c")
</code></pre></div>
<p>Si ces deux premières conditions sont passées, on peut tester le motif (en travaillant sur les suites de nombres).</p>
<div class="highlight"><pre><span></span><code>pattern("a ... c").match("a a b c") == True
pattern("c . a").match("a a b c") == False
</code></pre></div>
<p>Pour limiter le travail, les requêtes sont indexées par jetons. Pour l’instant, le jeton de la ligne utilisé pour tirer les requêtes potentielles est le premier. Pour bien faire, il faudrait trier les jetons par occurrence, et choisir le moins courant (et donc le plus discriminant).</p>
<h2>Performance</h2>
<p>Les performances de <a href="https://golang.org/pkg/regexp/">regexp</a> de golang, basé sur <span class="caps">RE2</span>, sont très bonnes. Tokenizer une ligne, vérifier si le motif correspond est plus que de tester une expression régulière. Mais ça tombe bien, ce n’est pas le cas d’usage, on tokenize une fois, exclue puis test le motif plein de fois.
Obtenir l’identifiant d’un jeton est super rapide, travailler avec les bitsets et vérifier les motifs sont aussi super rapide, bien plus qu’une regexp. Le cout de la tokenization (qui gère de manière orthodoxe l’<span class="caps">UTF8</span>) est très rapidement amorti quand il s’agit de tester une brouette de motifs.</p>
<p>Préparer une ligne est 4 fois plus long que de lancer une regexp, mais tester un motif est presque 10 fois plus rapide.</p>
<p>Bon, un benchmark est toujours un peu raide et rien ne remplace un vrai usage.</p>
<h2>Usage</h2>
<p>Avoir de jolis tests unitaires et des benchmarks, c’est bien, avoir du code utilisable, c’est mieux. Le premier usage sera de surveiller des flots de logs (tail, <a href="https://www.freedesktop.org/software/systemd/man/systemd-journald.service.html">journald</a>, <a href="https://www.fluentd.org/">fluentd</a>…) pour soit lever des erreurs qualifiées (<a href="https://sentry.io/">sentry</a>, <a href="https://prometheus.io/docs/alerting/alertmanager/">alertmanager</a>, <a href="http://alerta.io/">alerta</a>), soit compter des trucs qui finiront dans du timeseries. Des actions à la <a href="https://github.com/fail2ban/fail2ban">fail2ban</a> sont potentielement envisageable.</p>RPC2018-01-22T22:16:00+01:002018-01-22T22:16:00+01:00Mathieu Lecarmetag:blog.garambrogne.net,2018-01-22:/rpc.html<p>Les <span class="caps">RPC</span> sont les héraults de l’informatique maintenant distribué.</p><h1><span class="caps">RPC</span></h1>
<p>Les <span class="caps">RPC</span> existent depuis longtemps, ils ont leurs heures de gloire (<a href="https://fr.wikipedia.org/wiki/Common_Object_Request_Broker_Architecture"><span class="caps">CORBA</span></a>, <a href="https://fr.wikipedia.org/wiki/SOAP"><span class="caps">SOAP</span></a>), et de déchéance (<span class="caps">CORBA</span>, <span class="caps">SOAP</span>). Ils reviennent sur le devant de la scène avec l’invasion de Javascript au dépens des templates cotés serveurs, et surtout des micro services.</p>
<p>Techniquement, un <span class="caps">RPC</span>, c’est : une sérialisation, un protocole, une couche transport.
Certains <span class="caps">RPC</span> proposant différentes sérialisations (une binaire, une texte), et même différents transports.</p>
<h2>Sérialisation</h2>
<p>La réponse magique est <a href="https://fr.wikipedia.org/wiki/JavaScript_Object_Notation"><span class="caps">JSON</span></a>, ce qui est un progrès par rapport à la réponse magique <a href="https://fr.wikipedia.org/wiki/Extensible_Markup_Language"><span class="caps">XML</span></a>, d’il y a 20 ans. Mais <a href="http://seriot.ch/parsing_json.php">le <span class="caps">JSON</span> est ambigu, chaque langage et même chaque bibliothèque va gérer de manière différente chaque point mal spécifié</a>. Il ne gère ni différencie pas les entiers des flottants, ni les nombres en 64 bits, le binaire doit passer par du base64 et donc prendre plein de place.</p>
<p><a href="https://fr.wikipedia.org/wiki/BSON"><span class="caps">BSON</span></a> corrige le <span class="caps">JSON</span> en ajoutant de nouveaux types et un format binaire, mais il reste très lié à <a href="https://www.mongodb.com/">Mongodb</a>.</p>
<p><a href="https://msgpack.org/">MessagePack</a> fait peu ou prou la même chose, mais de manière neutre, sans être lié à un produit.</p>
<p>On reste dans la famille des sérialisations auto descriptive, qui permet techniquement de faire un peu n’importe quoi de chaque coté d’un <span class="caps">RPC</span>.</p>
<p>Cette approche prend beaucoup de place, avec Mongodb, il n’est pas rare que les clefs prennent plus de place que les contenus.</p>
<p><a href="https://avro.apache.org/docs/current/">Avro</a> propose une approche intermédiaire. On envoie la grammaire, en <span class="caps">JSON</span>, puis le contenu en binaire, bien compacte.</p>
<p>La mode est maintenant à la sérialisation avec grammaire. Cette grammaire étant neutre, elle peut être utilisée par différents langages pour effectuer les sérialisations/deserialisation. Ces grammaires sont incrémentales, il est possible d’ajouter des informations, sans casser la compatibilité avec l’existant.</p>
<p>Petite collision de date, Google avait déjà son <a href="https://developers.google.com/protocol-buffers/">protobuff</a> (qu’il utilisait bien au-delà des <span class="caps">RPC</span>) quand Facebook a publié spécification et implémentation de référence pour son <a href="https://thrift.apache.org/">Thrift</a>, fort similaire. Protobuff a été libéré après Thrift.</p>
<p>Pour aller un cran plus loin dans la recherche de performance, il existe des sérialisations qui ne sérialisent pas, et permettent le zéro-copie, ce qui est fort pratique pour les <span class="caps">RPC</span> locaux, directement en mémoire, sans réseau. <a href="https://arrow.apache.org/">Apache Arraow</a> et <a href="https://capnproto.org/">Cap’n Proto</a> font ce genre de chose.</p>
<h2>Protocole</h2>
<p>La première étape est la notion de question du client au serveur, et la réponse correspondante, dans l’autre sens.</p>
<h3><span class="caps">REST</span></h3>
<p><a href="https://fr.wikipedia.org/wiki/Representational_state_transfer"><span class="caps">REST</span></a> n’est tout simplement pas un protocole <span class="caps">RPC</span>. C’est un standard largement diffusé, le meilleur ami de <a href="https://curl.haxx.se/">curl</a>, mais ce n’est pas un <span class="caps">RPC</span>.</p>
<p><span class="caps">REST</span> permet d’exposer rapidement des données hiérarchisées, pas plus, et <a href="http://graphql.org/">GraphQL</a> (quand il sera sec) est bien parti pour lui ravir cette niche.</p>
<p>Des formalisations comme <a href="https://swagger.io/">Swagger</a> (maintenant <a href="https://www.openapis.org/">OpenAPI</a>) sont clairement les bienvenus,
et même indispensable, mais ne suffisent pas à définir de bout en bout les échanges.
Du code spécifique côté client et serveur reste indispensable, et fragilise l’ensemble.</p>
<p><a href="https://www.elastic.co/fr/products/elasticsearch">ElasticSearch</a> reste le parfait exemple des limites de <span class="caps">REST</span> :
le protocole/sérialisation ne brime pas les performances, par contre,
les fonctions spécifiques comme les batchs, la pagination, la gestion du cluster,
rendent indispensable l’utilisation des <a href="https://www.elastic.co/guide/en/elasticsearch/client/index.html">drivers officiels</a>.</p>
<p><a href="https://www.docker.com/">Docker</a> a le même souci, avec son truandage pour gérer les flots <span class="caps">TTY</span>, et ils sont en train de <a href="https://github.com/containerd/containerd/tree/master/api/">tout re-ranger en grpc</a> avec <a href="https://containerd.io/">Containerd</a>, pour faciliter les échanges de serveur à serveur.</p>
<h3>Multiplexage</h3>
<p>Les connexions ne sont pas illimitées, que ce soit des clients web vers un serveur, ou même en interne, avec le nombre de connexions simultanées que peuvent ouvrir les technologies asynchrones. Le multiplexage s’impose assez vite. Les requêtes ont des identifiants, et les réponses arrivent dans l’ordre de leur résolution.</p>
<p><a href="https://xmpp.org/extensions/"><span class="caps">XMPP</span></a> a formalisé cette approche avec ses stanzas, tout comme <a href="http://www.jsonrpc.org/specification"><span class="caps">JSON</span>-<span class="caps">RPC</span></a>.</p>
<h3>Flot</h3>
<p>Les <span class="caps">RPC</span> sont confrontés aux mêmes problèmes que les Big Data, où il faut choisir entre le gros débit des traitements par lots (<a href="https://hadoop.apache.org/">Hadoop</a>), et les faibles latences des flots (<a href="https://spark.apache.org/">Spark</a>, <a href="http://storm.apache.org/">Storm</a>).</p>
<p>La gestion de flot facilite le multiplexage, limite la taille des buffers, et permet de commencer tôt le traitement du message.</p>
<p><span class="caps">HTTP</span>/1 propose une gestion de flot descendant, avec <a href="https://fr.wikipedia.org/wiki/Server-sent_events">EventSource</a>, mais rien en flot montant, il faut alors dégainer <a href="https://fr.wikipedia.org/wiki/WebSocket">Websocket</a>. Hum, est-ce bien raisonnable d’utiliser WebSocket pour de la communication de serveur à serveur?</p>
<p><a href="https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol/2"><span class="caps">HTTP</span>/2</a> gère le multiplexage, et permet la gestion de flot montant et descendant.</p>
<p><a href="https://twitter.github.io/finagle/">Finagle</a>, de Twitter, propose un <span class="caps">RPC</span> complet basé sur Thrift, mais surtout explique bien la notion de multiplexage, de streaming et de méta information. Finagle a publié beaucoup de lecture pédagogique indispensable, mais il est fortement lié à <a href="http://www.scala-lang.org/">Scala</a>, et même à Twitter, en fait.</p>
<h2>Méta information</h2>
<p>Les RPCs ont besoin d’informations complètement différentes de ce qui sera utilisé par les fonctions exposées. La notion de format, de trace, d’authentification, de chiffrage et de compression par exemple. Toutes les belles choses que l’on est habitué à utiliser avec <span class="caps">HTTP</span>, en fait.</p>
<h2>Parralélisation</h2>
<p>On est habitué aux contraintes d’<span class="caps">HTTP</span> qui a l’habitude d’imposer des temps de réponse courts, et de se faire couper en cas de “timeout”. Les <span class="caps">RPC</span> n’ont pas les mêmes contraintes sur les temps de réponse. Les langages synchrones sont alors brimés par la difficulté de paralléliser des taches de durée très variables. L’implémentation de <span class="caps">GRPC</span> en python, par exemple, travaille avec un pool de 10 connexions, ce qui n’est pas farfelu si on garde la règle magique de 2 workers par coeur de processeurs. En augmentant le pool, on prend le risque d’avoir des traitements avec peu d’attentes <span class="caps">IO</span> et de faire exploser le load.</p>
<p>De toute façon, la notion de file d’attente devra être géré, soit par le langage qui va accepter plein de connections, soit via un service spécifique, comme <a href="https://redis.io">Redis</a>, <a href="https://www.rabbitmq.com/">RabbitMQ</a>, <a href="https://nsq.io/">nsq</a>, <a href="https://kafka.apache.org/">kafka</a>…. Les services de file d’attente permettent de faire de jolies choses, souvent pénibles à reproduire dans son code, comme la répartition de charge ou la reprise de traitement en cas de vautrage. Google a une sainte horreur des serveurs de file d’attentes, déjà qu’il n’aiment pas les proxy, car il se focalise sur la course à la latence. Twitter semble les suivre dans cette approche.</p>
<h2>Débogabilité</h2>
<p>Déboguer un <span class="caps">RPC</span> bancal peut rapidement devenir un enfer.</p>
<h3>Traces</h3>
<p>Il est indispensable d’utiliser une gestion d’erreur centralisée (à la <a href="https://sentry.io">Sentry</a>), des métriques (<a href="https://github.com/etsy/statsd">statsd</a> ou des compteurs <a href="https://prometheus.io/">Prometheus</a>), et rapidement des logs unifiés, des traces en fait, comme le proposent <a href="http://opentracing.io/">OpenTracing</a> (<a href="https://zipkin.io/">Zipkin</a>, <a href="https://github.com/jaegertracing/jaeger">Jaeger</a>, <a href="https://github.com/sourcegraph/appdash">Appdash</a>…).</p>
<h3>Ligne de commande</h3>
<p>Il est difficile de se passer du confort de <code>curl</code> + <span class="caps">JSON</span>, tous les <span class="caps">RPC</span> de la galaxie proposent un équivalent, avec plus ou moins de complétion et de retour d’erreurs.</p>
<h3>Sniffer</h3>
<p>Encore plus culte que <code>curl</code>, il existe <a href="https://fr.wikipedia.org/wiki/Tcpdump">tcpdump</a>, pour filtrer et voir passer un flot sur le réseau.
<a href="https://github.com/elastic/beats/tree/master/packetbeat">PacketBeat</a> propose de jolies bases pour construire un sniffeur.</p>
<p>La généralisation de <span class="caps">TLS</span>, même sur les réseaux privés rends ça rapidement pénible.</p>
<p>L’approche sniffing est du débogage en boite noir, il est tellement plus simple d’être dans la place, et de profiter de l’approche boite blanche, comme Google le préconise :
<a href="https://github.com/google/protobuf/issues/3303#issuecomment-313236016">ticket grpc et tecpdump</a>.</p>
<p>Finagle confirme cette approche avec <a href="https://twitter.github.io/twitter-server/index.html">Twitter-Server</a>,
mais comme Finagle n’impose pas <span class="caps">TLS</span>, il est possible de tricher : <a href="https://github.com/pinterest/thrift-tools">thrift-tools</a>.</p>
<p>Rapidement, les <span class="caps">RPC</span> vont être distribués (sur plusieurs serveurs), il est assez illusoire de penser pouvoir attraper l’ensemble des flots pour ensuite isoler celui qui nous intéresse.
Bricoler son Mysql/Postgres/Mongodb ou autres services tiers pour avoir du log spécifique n’est pas la meilleur des idées, dans ce cas, le pcap est légitime, mais pour du code qui nous appartiens, avec la possibilité d’utiliser des middlewares ou pires des proxys, ce serait dommage de ne pas en profiter.</p>
<h2>Transport</h2>
<p>Les rpc ne sont pas forcément fascinés par la couche transport, mais la plupart s’épanouissent dans <span class="caps">HTTP</span>, la garantie de pouvoir circuler sur internet sans drame, et sans faire crier les firewalls corporates. Il existe des transports plus spécialisés, comme <a href="http://zeromq.org/">zeromq</a>, <a href="https://nats.io/">nats</a> ou même plus exotique comme <a href="https://fr.wikipedia.org/wiki/Streaming_text_oriented_message_protocol"><span class="caps">STOMP</span></a>.</p>
<p>Hum, peut-on considérer un broker comme une couche transport?
Dans ce cas, Kafka et <a href="https://fr.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol"><span class="caps">AMQP</span></a> en font partis.</p>
<p><span class="caps">HTTP</span>/2 permet de profiter de l’héritage <span class="caps">HTTP</span>/1 (Internet, proxy…) mais le protocole est bien récent, et les bibliothèques pour le gérer ne sont pas bien répandues.
C’est mature pour des serveurs webs (nginx, traefik…), pour des clients (Chrome, Firefox…), mais pour du code, golang écrase un peu tout.</p>
<h2>Ceinture et bretelle</h2>
<p>Rien n’interdit d’exposer une fonction avec différentes approches.</p>
<p>Les usages sont trop divers pour qu’une seule réponse universelle puisse être envisagée.
Les interfaces utilisateurs sont maintenant en <span class="caps">HTML5</span> avec beaucoup de Javascript, et proposent des interfaces utilisateurs réactives. Il est donc indispensable d’utiliser <span class="caps">HTTP</span> pour les <span class="caps">UI</span> web. L’outillage <span class="caps">HTTP2</span> en javascript est pour l’instant introuvable, il faut donc basculer sur les plus classiques <span class="caps">HTTP</span>/1.1 et ses extensions, comme <span class="caps">SSE</span> et surtout Websocket.</p>
<p>Pour les appels internes, <span class="caps">HTTP</span>/1 est un peu court des pattes et va poser des problèmes avec les parallélisations massives que permettent les technologies asynchrones.</p>
<p>L’approche la plus saine me parait de surtout se concentrer sur l’utilisation de grammaire neutre, qui permettent de générer (ou d’introspecter pour les langages qui n’aiment pas le code écrit par des robots), les clients et serveurs, et même de générer plusieurs protocoles pour une seule fonction exposée.</p>
<p><a href="https://blog.twitch.tv/twirp-a-sweet-new-rpc-framework-for-go-5f2febbf35f">Twirp</a> propose cette approche après s’être cassé les dents sur du grpc, qui reste pourtant la référence de ce genre d’approche.</p>
<p>Grpc fait des choix plutôt autoritaires pour présenter ce que Google estime être l’état de l’art.
Personnellement, aucun de ces choix ne me frustre, c’est plutôt le manque de maturité des bibliothèques qui m’inquiètent. Les fonctions avancées de la bibliothèque golang n’a pas de numéro de release, avec une documentation légère.
La bibliothèque Python embarque un gros machin en C pour gérer toute la partie <span class="caps">HTTP</span>/2, avec un pool de workers. Je suppose que les autres langages sont dans le même état. Par contre, je n’ai aucune inquiétude sur la pérennité de cette technologie et sur le fait que les bibliothèques vont évolués, et surtout que tous les langages vont avoir l’outillage requis pour gérer comme il faut <span class="caps">HTTP</span>/2/</p>
<p>Il existe une passerelle Grpc/<span class="caps">REST</span>,
<a href="https://github.com/grpc-ecosystem/grpc-gateway">Grpc-gateway</a>
qui permet d’exposer une fonction, en utilisant la grammaire grpc, sous deux formes, du <span class="caps">REST</span> avec la documentation Swagger, et du Grpc.</p>
<p>La couche <span class="caps">HTTP</span>/2 n’est pas forcément la réponse universelle, pour certains projets discrets, utilisant un grand nombre de connexions, le surcout en <span class="caps">RAM</span> peut être pénalisant. Le projet a fait le choix d’utiliser un protocole différent (du <span class="caps">TCP</span> sans stream), et de générer le code avec les grammaires <span class="caps">GRPC</span>.</p>
<h2>Bref</h2>
<p>L’informatique est maintenant distribué, que ce soit sur des coeurs de processeurs, ou des serveurs. Les <span class="caps">RPC</span> sont maintenant indispensable (ne serait-ce que par la traction des <span class="caps">UI</span> complexes en Javascript), mais le domaine n’est pas encore stabilisé.</p>
<p>En l’état, la seul recommendation sans risque est d’utiliser des grammaires pour déclarer ses APIs, et d’instrumenter son code (log, erreurs, métriques).</p>Persister les conteneurs2016-11-28T09:34:00+01:002016-11-28T09:34:00+01:00Mathieu Lecarmetag:blog.garambrogne.net,2016-11-28:/persister-les-conteneurs.html<p>Les conteneurs permettent d’isoler les ressources utilisées par des services, et avec un peu de discipline, offrent la promesse d’utiliser au mieux un groupe de machines. Mais comment assurer de la persistence dans cet environnement mobile?</p><h1>Isoler</h1>
<p>Les conteneurs permettent d’isoler les ressources utilisées par des services, et avec un peu de discipline, offrent la promesse d’utiliser au mieux un groupe de machines.</p>
<h1>Distribuer</h1>
<p>Distribuer des services est maintenant un classique, pour ne pas dire un prérequis.
Par contre, il faut ensuite assumer; surdimensionner pour assurer quelques pics devient rapidement un problème de place, mais surtout de puissance électrique.</p>
<p>La distribution permet d’avoir plus de ressources à disposition, mais aussi d’assurer plus de résilience, en proposant plusieurs instances de chaque service, capable de prendre le relai en cas d’incident, ou tout simplement de démarrer un nouveau service sans états, pour remplacer un autre défaillant.</p>
<h2>L’élastique dans le nuage</h2>
<p>Le cloud nous promet cette élasticité, mais la taille efficace d’une machine virtuelle est bien trop grosse pour un découpage propre, alors que le conteneur a lui, la taille et la mobilité requise.</p>
<p>Les ressources nécessaires fournies par la machine aux services sont le classique triplet : processeur, mémoire, stockage.
Triplet que sait bien border un conteneur. En faisant attention à avoir du code immuable et versionné, il est possible d’avoir plusieurs instances d’un même conteneur, sur une ou plusieurs machines.
Mobilité et ubiquité que sait bien gérer un conteneur.</p>
<h2>Orchestrer l’utilisation des ressources</h2>
<p>Il est possible de gérer un ensemble de conteneurs, avec un nombre d’instances fluctuant.</p>
<p>Pouvoir optimiser cette fluctuation permet de dépasser le taux ridicule d’utilisation de la plupart des datacenters : 15%.
Une gestion efficace des ressources est l’arme secrète de Google, Borg. Enfin, l’une des armes secrètes.</p>
<p>Le principe est simple : on met à disposition un ensemble de ressources, on commande ensuite des ressources, et un planning est établi. Des outils sont mis à disposition permettant la coordination ou la découverte de services, qui, couplés à des proxyies, permet la mise à disposition publique des services.</p>
<p>Google a publié, sur le tard (une dizaine d’année après sa mise en place), leur approche avec de la gestion de cluster avec <a href="http://research.google.com/pubs/pub43438.html">Borg</a>. Ils insistent beaucoup sur la différence entre les tâches longues (qui ne doivent pas s’arrêter), et les taches éphémères (sensibles aux latences).</p>
<p><a href="http://mesos.apache.org/">Mesos</a> a été une des premières implémentations libres de planification de ressources, mais il vise gros, et propose de généraliser ce que proposait le <span class="caps">YARN</span> de Hadoop. Si vous ne comptez pas vos ressources en rack ou en datacenter, il faut chercher autre chose.</p>
<p>Des choses comme <a href="https://www.nomadproject.io/">Nomad</a> (le sage), <a href="https://www.docker.com/products/docker-swarm">Swarm</a> (le tonitruant), ou <a href="http://kubernetes.io/">Kubernetes</a> (le conquérant).</p>
<p>Ces outils sont utilisables sur une poignée de serveur, et en se basant sur des serveurs dédiés, ou des clouds frustres, il est possible d’être bien moins cher que des clouds toutes options.</p>
<h1>Persister</h1>
<p>La partie pénible dans l’usage des clusters est la persistance, distribuer le travail n’est finalement pas si complexe : le loadbalancing naïf est la première étape, avant de passer à de la parallélisation et au fameux map-reduce.</p>
<p>Le stockage dans un environnement distribué est un problème de cohérence, <a href="https://gist.github.com/jboner/2841832">latence</a>, et de débit.</p>
<p>Il n’existe pas de réponse absolue, mais un ensemble de choix et de tuning.
Le <a href="https://en.wikipedia.org/wiki/CAP_theorem">théorème du <span class="caps">CAP</span></a> résume cet ensemble de contraintes, et l’obligation d’en choisir deux parmi les trois lettres <em>Consistency</em>, <em>Availabiliy</em> et <em>Partition tolerance</em>.</p>
<h3>Le cas du conteneur</h3>
<p>Docker (et rkt) proposent gentiment d’emballer une application, pour pouvoir la versionner, et la déployer simplement. Une généralisation du <span class="caps">WAR</span> de Java, quoi. Le prérequis est d’avoir une application en lecture seule. Il est ensuite possible de monter des volumes au sein des conteneurs, pour proposer des dossiers en lecture/écriture. Sauf que ces volumes montés n’apportent ni l’ubiquité, ni la distribution, et seront liés à un serveur. Un conteneur permet d’abstraire et de généraliser plein de choses, mais pas la persistance. Pour un système distribué, c’est ballot.</p>
<h2>Qui est le responsable du stockage?</h2>
<p>Première décision, la persistance est-elle de la responsabilité du système d’exploitation l’application ?</p>
<h3>Disques distants</h3>
<p>Le moyen le plus simple pour déporter le stockage est d’utiliser un disque distant.
Ce disque pourra être démonté puis remonté sur un autre serveur, il est mobile, mais n’existera qu’en un seul exemplaire. Pour peu que le serveur propose des instantanés, il sera possible d’avoir du <span class="caps">COW</span>, et de monter des réplicats du disque sur d’autres noeuds.</p>
<p>Tous les clouds proposent un service de ce genre, avec de l’iSCSI par exemple.
Openstack fournit une <a href="https://wiki.openstack.org/wiki/CinderSupportMatrix">liste indigeste des disques qu’il peut utiliser en mode bloc</a>.
Il est possible de redonder ce genre de disque, côté client, avec un simple <span class="caps">RAID</span>, ou côté serveur, avec de la magie, si c’est un <span class="caps">SAN</span>.</p>
<p>Il n’existe pas grand-chose en libre comme serveur iSCSI. Il existe le vénérable <span class="caps">NBD</span>, et le plus récent <a href="https://bitbucket.org/hirofuchi/xnbd/wiki/Home">xNBD</a>, prévu initialement pour du netboot. Coreos bosse sur <a href="https://coreos.com/blog/torus-distributed-storage-by-coreos.html">Torus</a>, un serveur <span class="caps">NBD</span> distribué, utilisable dans quelques années?.</p>
<p>Le Graal étant le <a href="http://docs.ceph.com/docs/jewel/rbd/rbd/">rdb</a> de Ceph (qui a rejoint le giron de RedHat), montable en <span class="caps">NBD</span> ou en Fuse. <span class="caps">OVH</span> propose maintenant du <a href="https://www.ovh.com/fr/cloud/cloud-disk-array/">Ceph <em>as a service</em></a>.</p>
<p>Pour les aventuriers, il existe des systèmes de fichiers distribués(<a href="https://oss.oracle.com/projects/ocfs2/"><span class="caps">OCFS2</span></a> d’Oracle, <a href="https://en.wikipedia.org/wiki/GFS2"><span class="caps">GFS2</span></a> de Red Hat… ), permettant l’ubiquité d’un même disque monté sur plusieurs serveurs. En dehors du calcul scientifique, on trouve peu d’exemples d’usage de cette approche. Pour garantir la cohérence des données, un serveur de verrou est utilisé, garantissant des problèmes de performance pour les écritures concourantes.</p>
<p>Il existe des abstractions exposant une <span class="caps">API</span> unique vers diverses implémentations, comme <a href="https://clusterhq.com/flocker/introduction/">Flocker</a> ou <a href="https://wiki.openstack.org/wiki/Cinder">Cinder</a>.</p>
<h3>Fichiers distants</h3>
<p>Le partage en mode fichier permet un accès à distance, mais aussi multiple, à un ensemble de fichiers.
<span class="caps">NFS</span> et <a href="https://docs.microsoft.com/fr-fr/azure/storage/storage-how-to-use-files-linux">Samba, que seul Azure préconise</a> en sont les exemples les plus classiques.</p>
<p>Le partage de fichier est fort pratique, mais, pour assurer des performances correctes, certains raccourcis sont pris : une partie des engagements requis pour un système de fichier <span class="caps">UNIX</span> ne sont plus assumés.</p>
<p>Toutes les bases de données dédient une page de la documentation pour promettre d’atroces souffrances à qui osera mélanger <span class="caps">NFS</span> et base de donnée : <a href="https://www.postgresql.org/docs/9.4/static/creating-cluster.html">Postgres et <span class="caps">NFS</span></a>, <a href="https://docs.datastax.com/en/cassandra/1.2/cassandra/architecture/architecturePlanningAntiPatterns_c.html">Cassandra et <span class="caps">NFS</span></a></p>
<p>L’architecture de <span class="caps">NFS</span> commence à dater, et ce n’est pas un, mais un ensemble de services qui sont utilisés, pas forcément prévu pour un environnement hostile comme l’est le cloud.
<span class="caps">NFS</span> ne prévoit rien pour la distribution du stockage.</p>
<p>Les <a href="https://www.ovh.com/fr/nas/">serveurs <span class="caps">NAS</span> haute dispo, comme celui proposé par <span class="caps">OVH</span></a>, sont souvent des baies de disques connectés en optique avec deux serveurs, l’un <em>ou</em> l’autre serveur pourra utiliser la baie, du <span class="caps">RAID</span> assurera la redondance des disques, une double alimentation, la redondance d’énergie, mais en cas d’incident sur la baie, bah, pas de bol, vous avez perdu.</p>
<p>Des implémentations propriétaires, comme l’<a href="https://aws.amazon.com/fr/efs/"><span class="caps">EFS</span></a> d’Amazon permettent d’avoir de la redondance et de la distribution.</p>
<p>Pour les acharnés du partage en mode fichier, il existe le tant redouté <a href="https://www.gluster.org/">GlusterFS</a>, récemment racheté par RedHat. La prochaine version majeur, <a href="https://github.com/gluster/glusterd2">GlusterFS 4</a> sera d’ailleurs en Go et utilisera Etcd.
On voit son nom apparaitre de temps en temps, comme l’<a href="https://docs.acquia.com/cloud/arch">offre un poil daté d’Acquia</a>.</p>
<p>L’incontournable <a href="http://ceph.com/ceph-storage/file-system/">Ceph propose aussi un partage en mode fichier</a>, mais cette option semble un peu boudé, c’est pour le mode bloc qu’il est réputé. <a href="https://about.gitlab.com/2016/11/10/why-choose-bare-metal/">Gitlab l’utilise pour son offre <span class="caps">SAAS</span></a> mais ils ont souffert de la non-homogénéité des <span class="caps">IO</span> des clouds publiques, et sont passé à du <em>bare metal</em>.</p>
<h3>Objets distants</h3>
<p>Assurer un stockage distribué, aussi à l’aise en lecture qu’en écriture,
sans corrompre les données, fut une des premières questions que Google dut résoudre, et surtout la première révélation des secrets de Google.
Le <a href="http://static.googleusercontent.com/media/research.google.com/fr//archive/gfs-sosp2003.pdf">white paper sur le <em>Google File System</em></a>
fut publié en 2003. Il servit de base à la création du <a href="https://en.wikipedia.org/wiki/Apache_Hadoop#HDFS"><span class="caps">HDFS</span></a> du projet Hadoop.</p>
<p>Le principe est simple, les fichiers sont immuables, mais versionnés. Il n’est pas possible de modifier un fichier existant, on va en créer une nouvelle version, et par défaut, on lira la dernière version disponible.</p>
<p>Le premier service d’<span class="caps">AWS</span>, au-delà de la simple <span class="caps">VM</span>, fut un service de persistance distribué, S3. Le <a href="http://s3.amazonaws.com/AllThingsDistributed/sosp/amazon-dynamo-sosp2007.pdf">Dynamo paper</a> expliquant la notion d’anneau virtuelle pour une architecture sans maître avec de la redondance, fut publié en 2007.</p>
<p>Pour S3, les fichiers sont immuables, versionnés, redondés, accessibles directement en <span class="caps">HTTP</span>.</p>
<p>Aucun cloud digne de ce nom n’ose sortir sans proposer un clone plus ou moins compatible de S3.</p>
<p>Tous les frameworks webs décents sont maintenant capables de gérer les uploads sur des stockage objets, avec des bibliothèques comme <a href="https://github.com/carrierwaveuploader/carrierwave">CarrierWave</a> ou <a href="https://github.com/thoughtbot/paperclip">paperclip</a>.</p>
<p>Toutes les bases de données savent (ou devraient savoir) maintenant faire des snapshots dans des stockages à la S3.</p>
<h4>Swift</h4>
<p>Le clone de référence est <a href="http://docs.openstack.org/developer/swift/">Swift</a> d’OpenStack.
Son architecture est extrêmement simple, à la limite du naïf, en python très classique.
Ils ont une excellente réputation sur la qualité de leur <span class="caps">API</span>, et la fidélité avec l’<span class="caps">API</span> du S3 d’<span class="caps">AWS</span>.</p>
<h4>Riak-S2</h4>
<p>Il existe aussi le moins connu <a href="http://fr.basho.com/products/riak-s2/">Riak-S2</a>, une surcouche au Riak de Basho, libéré il y a quelque temps.
C’est une architecture Erlang contemporaine, au code particulièrement pédagogique et étonnamment lisible, compte tenu de la complexité du bestiau.</p>
<h4>LeoFS</h4>
<p>Pour les fans d’Erlang, il y a aussi <a href="http://leo-project.net/">LeoFS</a>. Par contre, je ne sais pas trop qui l’utilise en dehors de Rakuten, leur sponsor.</p>
<h4>Minio</h4>
<p>En ce moment est en train d’émerger <a href="https://www.minio.io/">Minio</a>, il propose une belle <span class="caps">UI</span> Web, mais surtout un serveur, en Go, ce qui change du Java et de l’Erlang.</p>
<p>Son accueil enthousiaste vient du fait qu’il scale bien à un. Ce serveur est utilisable sur un seul serveur, et son image Docker juste marche.</p>
<p>Il gère tellement bien un seul serveur que la version distribuée n’est pas encore finie.</p>
<p>Par contre, il est capable de gérer une grappe de disque, et d’assurer de la redondance avec le <a href="https://fr.wikipedia.org/wiki/Code_de_Reed-Solomon">code de Reed-Solomon</a>, utilisé par le système <span class="caps">RAID</span> pour les disques, et <a href="https://www.backblaze.com/blog/reed-solomon/">vulgarisé dans un article par Backblaze</a>, le plus grandiose des entasseurs de disques durs. En gérant eux même la parité, plutôt que la délégué à du <span class="caps">RAID</span> comme tout le monde, ils peuvent réparer objet par objet, et pas par volume.</p>
<h4><span class="caps">RADOS</span> Gateway, de Ceph</h4>
<p>Le dernier, et pas des moindres, est <a href="http://ceph.com/ceph-storage/object-storage/"><span class="caps">RADOS</span> Gateway</a> de Ceph.
Ceph met à disposition sa solution de stockage <span class="caps">RADOS</span>, sous les trois approches : blocs/fichiers/objets.</p>
<h3>Base de données</h3>
<p>Le stockage objet met un pied dans le plat. Il casse la compatibilité du système de fichier <span class="caps">UNIX</span>, pour proposer quelque chose de plus adapté aux environnements distribués.
Du coup, l’application continue d’écrire sur un disque local (les essais de stockage sans <span class="caps">FS</span> d’Oracle ou Mysql ont fait un flop), mais vont se charger de la distribution avec réplication, répartition, cohérence, réparation…</p>
<p>Avoir une application distribuée, correctement implémentée est difficile.
Le projet <a href="http://jepsen.io/analyses.html">Jepsen</a> propose un framework d’analyse et publie ses analyses de différentes versions d’applications.
Le résultat des analyses est souvent “call me maybe”, et il y a ensuite des corrections de l’application autopsiée.</p>
<h3>Base de données relationnelle</h3>
<p>Les bases de données relationnelles sont toutes capables de gérer de la réplication, en utilisant le très classique modèle maître-esclave. Postgresql a quand même attendu la version 9 pour proposer ça en natif, sans passer par un produit tiers.</p>
<p>Pour répartir les données sur plusieurs machines, il faut passer par du sharding, ce qui va poser des problèmes pour faire des jointures sur des données dispersées. Le sharding est traditionnellement applicatif, et fonctionne mieux sur des données bien isolées, comme des utilisateurs, par exemple.</p>
<p><a href="https://www.citusdata.com/product/community">Citusdata</a> propose d’améliorer Postgresql en lui mettant à disposition :</p>
<ul>
<li>un <a href="https://github.com/citusdata/cstore_fdw">stockage orienté colonne</a></li>
<li>du sharding avec <a href="https://github.com/citusdata/citus">Citus</a></li>
<li>un <a href="https://docs.citusdata.com/en/v6.0/performance/query_processing.html">moteur de requêtes distribuées</a></li>
<li>des approximations statistiques pour des calculs massifs</li>
</ul>
<p>Les conteneurs adorent les applications sans états, indifférenciées. Ce qui est l’opposé des bases de données relationnelles.</p>
<h4>Multi-maître</h4>
<p>Pour ne pas avoir à gérer la différence maitre/esclave, et la complexité de la promotion de maître à esclave, puis de créer un nouvel esclave, il est possible d’utiliser de la réplication multi maître.</p>
<p>Postgres sait maintenant faire ça avec <a href="https://2ndquadrant.com/fr/resources/bdr/"><span class="caps">BDR</span></a>.</p>
<p>Mysql propose des outils similaires, comme <a href="http://galeracluster.com/">Galera</a> et <a href="http://www.proxysql.com/">proxysql</a>, ou des trucs plus emballé comme <a href="https://www.percona.com/software/mysql-database/percona-xtradb-cluster">XtraDB Cluster</a> de Percona.</p>
<p>Ces outils sont sympathiques, mais rien de prévu pour pour pouvoir redimensionner simplement.</p>
<p>Pourtant, des solutions propriétaires existent, comme <a href="https://en.wikipedia.org/wiki/Aurora_database_analytics_engine">Aurora</a> d’Amazon, basé sur Mysql, avec de gros efforts pour conserver la compatibilité et disponible dans leur offre <a href="https://aws.amazon.com/fr/rds/aurora/details/"><span class="caps">RDS</span></a>, ou le mystérieux <a href="http://research.google.com/pubs/pub41344.html">F1</a> de Google, reservé à AdWords.</p>
<p>Le modèle relationnel n’est juste pas prévu pour fonctionner de manière distribuée, mais de gros efforts sont faits pour, par contre, les applications vont devoir faire des compromis pour s’adapter. En posant un Wordpress sur un Cloud, on ne pourra pas s’attendre à de miracle.</p>
<p>Je fait bien la différence entre le modèle relationnel et le <span class="caps">SQL</span>, le langage, qui lui peut parfaitement être distribué.
Des outils comme <a href="https://drill.apache.org/">Drill</a> de la fondation Apache, <a href="https://prestodb.io/">Presto</a> de Facebook ou même <a href="http://spark.apache.org/sql/">Spark <span class="caps">SQL</span></a>.</p>
<h3>Base de données NoSQL</h3>
<p>Les bases NoSQL ont été inventées pour remettre en cause le dogme relationnel.</p>
<p>Bon, elles ont ensuite fournis baucoup d’efforts pour s’en rapprocher,
le <span class="caps">SQL</span> restant une <em>lingua franca</em> pour explorer des données, et pouvoir se brancher dessus en <span class="caps">JDBC</span>/<span class="caps">ODBC</span> est toujours appréciable.</p>
<p>Le NoSQL a aussi été conçu pour pouvoir profiter d’un hébergement distribué.</p>
<p>Le monde du NoSQL est tout récent, Darwin n’a pas eu le temps de faire le tri pour trouver ses champions. Voici une sélection arbitraire de bases de données, qui fonctionnent à des tailles raisonnables. La partie élection, pour avoir une archi sans maître, peut être plus ou moins laborieuse.</p>
<p>La première chose que va sacrifier une base NoSQL est la notion de transaction.</p>
<p><a href="https://www.mongodb.com/community">Mongodb</a> est la plus visible des bases NoSQL, et avant la version 3 et le stockage Tiger, est plutôt pénible à administrer. Le pattern de distribution est classique, un système d’élection pour avoir un master, et un proxy qui va assurer le routage vers le bon serveur. La distribution du travail se fait avec du classique map-reduce.</p>
<p><a href="http://cassandra.apache.org/">Cassandra</a> est une base de données orientée colonnes, avec un presque <span class="caps">SQL</span>, le <a href="http://cassandra.apache.org/doc/latest/cql/index.html"><span class="caps">CQL</span></a>, sans jointures ni sous-requêtes, mais avec du map-reduce. Créé par <a href="https://code.facebook.com/posts/160967930761450/cassandra-a-structured-storage-system-on-a-p2p-network/">Facebook pour manger du log</a>, il fut ensuite confié à la fondation Apache, pour passer à autre chose : Presto et Hbase, et <a href="https://www.quora.com/Why-did-Facebook-develop-Puma-pTail-instead-of-using-existing-ones-like-Flume">une autre stack pour avaler leurs logs</a>. Cassandra, même s’il est civilisé, reste une base conçue et dimensionnée pour du big data.</p>
<p><a href="http://www.scylladb.com/">Scylladb</a> est un clone C++, agressif et ambitieux de Cassandra, mettant un point d’honneur à saturer des réseaux 10Go, le kernel, les CPUs et des disques <a href="https://en.wikipedia.org/wiki/NVM_Express">NVMe</a> tout en conservant la compatibilité avec Cassandra.</p>
<p><a href="https://www.elastic.co/fr/products/elasticsearch">Elasticsearch</a> est une base orientée documents, distribués, avec des beaux index et une interface <span class="caps">REST</span>.
Initialement conçu pour la recherche full text, il a évolué vers les calculs d’agrégations, et poursuit vers le machage de logs. Elasticsearch est parfaitement à l’aise avec les principes de distribution et de réplication, tout en restant crédible à partir de 2 noeuds.</p>
<h3>Le stockage se distribue bien, se réplique bien, mais se bouge mal</h3>
<p>Les containers sont tout à fait capables d’assurer la persistance, locale ou sur du partage en mode blocs.
Par contre, les orchestrateurs ont bien envie de rééquilibrer le cluster en déplaçant les containers dans tous les sens. C’est tout à fait légitime pour des containers sans états, un contre sens pour du stockage.</p>
<p>Il faut alors revenir aux vieux principes décrits par Borg et Hadoop : les données sont fixes, les process peuvent bouger.</p>
<p>Les orchestrateurs sont maintenant capables de faire attention à ne pas déplacer les données, pour pouvoir profiter pleinement des performances locales.</p>
<p>Le stockage local est le plus performant, le mode bloc offre une simplification et un confort appréciable, le mode objet permet une pérennité incomparable et un scaling sans trop de limites, le mode fichier n’a plus sa place dans un environnement web.</p>Le phénomène Docker expliqué aux adorateurs de la Terre plate2016-10-17T09:20:00+02:002016-10-17T09:20:00+02:00Mathieu Lecarmetag:blog.garambrogne.net,2016-10-17:/le-phenomene-docker-explique-aux-adorateurs-de-la-terre-plate.html<p>Si vous aimez jouer au buzzword bingo, vous avez bien remarqué que Docker est un mot compte triple. Vous allez l’adorer, le détester, ou adorer le détester. Comment un machin si récent peu déclencher autant d’engouement ? Pourquoi les gros du cloud (abrégé en <span class="caps">GDC</span>) se prosternent devant lui, ou monte une équipe pour lui casser les genoux et lui rappeler sa place?</p><p>Si vous aimez jouer au buzzword bingo, vous avez bien remarqué que Docker est un mot compte triple. Vous allez l’adorer, le détester, ou adorer le détester. Comment un machin si récent peu déclencher autant d’engouement ? Pourquoi les gros du cloud (abrégé en <span class="caps">GDC</span>) se prosternent devant lui, ou monte une équipe pour lui casser les genoux et lui rappeler sa place?</p>
<h1><span class="caps">TL</span>;<span class="caps">DR</span></h1>
<p>Une architecture web est composée d’un ensemble de services, ensemble que l’on doit pouvoir utiliser à différents endroits, du poste de dev, à l’intégration continue, et finalement les serveurs de prod, en un ou plusieurs exemplaires.</p>
<h1>S’abstraire du matériel</h1>
<p>Avant de râler que s’était mieux avant, mieux vaut le définir, ce avant, histoire de parler de la même chose.</p>
<h2>Les machines virtuelles</h2>
<p>Les machines virtuelles n’ont jamais été qu’un mensonge. Qemu propose vraiment de la virtualisation, lui. Il isole complètement l’<span class="caps">OS</span> invité de la machine, et ce, à la vitesse d’une tortue qui a trop mangé de poutine.</p>
<p>Tous les outils utilisables de virtualisation (<a href="http://www.linux-kvm.org"><span class="caps">KVM</span></a>, <a href="https://www.xenproject.org">Xen</a>, <a href="https://www.virtualbox.org">Virtualbox</a>, <a href="https://developer.apple.com/reference/hypervisor">hypervisor</a>… ) sont en fait de la paravirtualisation, le kernel invité est adapté à ce que fournit son hôte, et s’appuie sur des fonctions spécifiques du processeur. Il est d’ailleurs impossible de virtualiser de la virtualisation en mélangeant des technos.</p>
<p>Le vrai mensonge n’est pas là, mais dans le “Rien n’a changé, tout est comme avant, un serveur virtuel s’utilise comme un serveur physique, c’est kifkif”.</p>
<p>Même si l’accès à la <span class="caps">RAM</span> sera dédié, le processeur sera partagé ou alors gâché avec de l’épinglage (du pinning, quoi). Dans tous les cas, les cartes réseau et disques durs seront partagées. La tragique conséquence est le classique phénomène du voisin bruyant.</p>
<p>De faux drivers permettent d’accéder de manière isolée à des fonctions du matériel, comme l’horloge ou le générateur d’aléatoire. Mais les vrais paranoïaques n’ont pas de voisins (allez lire la doc de <a href="https://securedrop.org/">SecureDrop</a>).</p>
<p>Autre partage pénible, les IPs, que l’on doit soit router (niveau 4 ou 7) caché derrière un <span class="caps">LAN</span>, soit gaspiller de l’IPv4, bientôt plus rare qu’un panda en liberté.</p>
<p>Mais au fait, pourquoi virtualiser ?</p>
<p>La réponse est simple, on virtualise pour isoler.</p>
<p>Isolation qui permet de</p>
<ul>
<li>limiter la portée des bêtises (erreurs ou agressions)</li>
<li>mélanger des versions d’<span class="caps">OS</span> et d’avoir des cycles de mises à jour indépendants, voir même utiliser différents <span class="caps">OS</span>, pour les plus créatifs</li>
<li>installer des logiciels ou des bibliothèques dans des versions non compatibles</li>
<li>ne pas mélanger les pommes et les oranges, aka co-tenant</li>
<li>garantir une répartition plutôt équitable des ressources</li>
</ul>
<p>Tout ça permet donc d’entasser plein de choses sur un même serveur, on parle alors de densification.</p>
<p>Ces entités isolées, abstraites de la couche matérielle peuvent être manipulées :</p>
<ul>
<li>redimensionnement (<span class="caps">RAM</span>, <span class="caps">CPU</span>, Disques)</li>
<li>déplacement d’une machine à l’autre, pour rééquilibrer, ou mettre à jour le matériel</li>
<li>snapshot et restauration</li>
<li>Jouer à jour/nuit avec des cycles de vie courts, pour faire des tests, des benchmarks, des essais</li>
<li>Ajouter de la puissance pour tenir la charge d’un évènement, comme un passage à la télévision</li>
<li>Lancer des lots violents de calculs, de manière ponctuelle</li>
</ul>
<h2>Les limites de la virtualisation des machines</h2>
<h3>Les machines des développeurs</h3>
<p>Il est complexe d’amener les machines virtuelles sur les postes des développeurs, et super pénible de gérer une grappe de serveur. Ça reste lent, ça bouffe des tonnes de <span class="caps">RAM</span>, le partage de fichier pour une édition local est mou des genoux, le watch de fichier ne marchera pas forcément.
C’est ballot, tous les <span class="caps">OS</span> proposent maintenant de la virtualisation de série, bhyve dépote sur Mac, mais, étonnement je n’ai jamais vu de dév Linux utiliser <span class="caps">KVM</span>.</p>
<p>Rebelote pour faire des tests fonctionnels ou même unitaires utilisant un service (comme une base de données), avec en prime des surprises comme
ah tiens, Xen ne veut pas de Virtualbox.</p>
<p><a href="https://www.vagrantup.com">Vagrant</a> est un excellent outil pour systématiser la mise en place de machines virtuelles. De toute façon, il est ridicule de travailler directement avec Virtualbox (ou l’un de ses concurrents), et même criminel si l’on travaille à plusieurs sur un même projet. Entendons-nous bien, je suis un grand fan de Vagrant, mais il est au top, il n’y aura rien après, Otto, son successeur a été sabordé.</p>
<p>Vouloir simplifier la stack du développeur en ne travaillant qu’avec des services directement installés en local a rapidement montré ses limites. Le temps de setup est énorme, la bascule d’un projet à l’autre est souvent la garantie d’écraser des données, et la flemme du commun va empêcher d’utiliser des outils qui sauvent le monde comme Redis ou Elasticsearch. De toute façon, qui utilise le même <span class="caps">OS</span> que le serveur cible sur sa machine locale? Une Debian stable en poste client?</p>
<h3>La taille des tranches de virtualisation</h3>
<p>La virtualisation fonctionne bien, et est très largement utilisée, mais elle a un ticket d’entrée, avec moins de 2 coeurs et 2 Go de <span class="caps">RAM</span>, ce n’est plus une <span class="caps">VM</span>, mais un jouet (<a href="https://aws.amazon.com/fr/ec2/instance-types/"><span class="caps">AWS</span> appelle ça T2, pour flatter</a>). Dédier une machine pour des services peu gourmands, reviens vite hors de prix, sans compter la place disque occupé par l’<span class="caps">OS</span> (et l’application) sur la machine physique si l’on n’a pas de système de <span class="caps">COW</span>, qui reste complexe à mettre en place.</p>
<p>Du coup, on se rabat sur le classique entassage sur une même machine, mais avec quelle garantie d’isolation (sécurité et ressource)?</p>
<p>Même si la haute dispo est heureusement réservée à des applications très spécifiques (brrrr, des gros mots comme <a href="http://linux-ha.org/">HAlinux</a>, drdb, heartbeat, pacemaker…), il est maintenant classique d’avoir des applications distribuées. Une base de données répliquée en master/slave est courante, un Elasticsearch est malheureux tout seul. Pour pouvoir espérer tenir un pic de charge, il faut tout simplement que l’application métier puisse être déployée sur plusieurs serveurs.</p>
<h1>La notion de services</h1>
<p>Les architectures actuelles sont de plus en plus riches. L’indétrônable <span class="caps">LAMP</span>, qui a permis à Wordpress de conquérir le monde est maintenant détrôné.</p>
<p>On garde le classique service de persistance (la base de données, quoi), le service applicatif, puis un service pour stocker des fichiers (<span class="caps">FS</span> local, ~~GlusterFS~~, un S3 like), mais aussi un service de mémoire partagé (Memcache, Redis…) pour gérer le cache, les sessions, les évènements. Pour travailler avec des timeout décents, on va utiliser un service de taches asynchrones (~~cron avec wget~~, Sidekiq/Celery/Beanstalk…). De la recherche full text avec ~~Solr~~ Elasticsearch. Un machin pour gérer les websockets par ce que son langage chéri est bien incapable de gérer de l’asynchrone, Un petit Logstash pour manger les logs, un Sentry pour les erreurs, un Statsd pour les compteurs de performances, un Cuttlefish pour les mails transactionnels, un Pootle pour les traductions… Et là, je ne parle que de services Open Source que l’on peut déployer, et pas des services en ligne, les fameux <span class="caps">SAAS</span>.</p>
<h1>La décrépitude du système</h1>
<blockquote>
<p>Server rots</p>
<p>— Ori</p>
</blockquote>
<p>L’entropie mange le monde, c’est comme ça, et c’est prouvé par <a href="https://fr.wikipedia.org/wiki/Ian_Malcolm_(Jurassic_Park)">Pr Ian Malcom</a> dans Jurassic Park : ce n’est qu’une question de temps, mais ça va partir en sucette.</p>
<p>Au fil du temps, son petit serveur d’amour, nommé en suivant un champ lexical spécifique, installé avec amour et inspiration, va commencer à sortir du champ de rationalité, il va continuer de fonctionner, bien sûr, mais il y aura des changements subtils, des innovations, des corrections sauvages, mais, promis, temporaires. Et au bout de suffisamment de temps, il va devenir un <em>snow flake</em>, unique et inreproductible comme un flocon de neige. Plus on entasse de choses, plus on bataille pour prolonger une version d’<span class="caps">OS</span> dépassé, plus le décollage sera rapide.</p>
<p>La seule solution est d’avoir un système immuable, construit de manière reproductible avec une recette, en précisant les quelques dossiers capables d’écrire, pour assurer la persistance.</p>
<h1>Et Docker dans tout ça ?</h1>
<p>L’isolation par la virtualisation est couteuse, et il n’y a aucun intérêt à avoir un système complet pour accueillir un service.</p>
<p>Il faut revenir à quelque chose de simple, de très <span class="caps">UNIX</span> en fait : demander au kernel d’isoler des process.</p>
<p>Chroot existe depuis toujours, et cgroups a été initialement conçu par Google il y a maintenant 10 ans.</p>
<p>Rajoutez à ça un système de fichiers en oignon, comme <span class="caps">AUFS</span>, pour mutualiser la place occupé sur le disque dur et avoir des mises à jour tranche par tranche.</p>
<p>Pour répondre à la ribambelle de questions que je viens d’énumérer, la conteneurisation apporte une réponse différente (et potentiellement complémentaire) à la virtualisation.</p>
<h2><span class="caps">LXC</span></h2>
<p>Petit incident de parcours, ces éléments ont été utilisés pour construire <a href="https://linuxcontainers.org"><span class="caps">LXC</span></a> (wouaaais) le chantre du <em>Fat Container</em> (Oooooohhhh). Plutôt que de travailler avec des process, comme tout le monde, <span class="caps">LXC</span> a trouvé très malin de singer un système complet, avec un init et une grappe de process, vous savez, “comme avant”. Pour parfaire le tout, <span class="caps">LXC</span> est livré avec une stack réseau en <span class="caps">DIY</span> (démerde-toi, en <span class="caps">VF</span>). Ubuntu a bien essayé de sauver les meubles avec son <a href="https://www.ubuntu.com/cloud/lxd"><span class="caps">LXD</span></a>, mais non. La simulation d’<span class="caps">OS</span> que propose <span class="caps">LXC</span> est très différente d’un <span class="caps">OS</span> même virtualisé. Faire des tests fonctionnels dans <span class="caps">LXC</span> en espérant que ça ressemblera à la cible, non <span class="caps">LXC</span>, est la garantie d’un drame. De toute façon, <span class="caps">LXC</span>, avant <span class="caps">LXD</span> est trop pénible à mettre en place pour du dev/test. Et <span class="caps">LXD</span>, ça veut dire Ubuntu, et du coup, grosse flemme de lui laisser une seconde chance. Surtout que <span class="caps">LXD</span> ne remet pas en cause le fat container, et la concurrence, elle, fonctionne.</p>
<p>Docker, qui avant d’être un projet libre, a utilisé <span class="caps">TOUTES</span> les technos de contenurisaton disponible sous Linux, une à une. Au moment de sa libération, Docker était une surcouche à <span class="caps">LXC</span>. En utilisant dans un vrai cadre ces outils (avec des tonnes de clients et des brouettes de devs dédiés), Docker a vraiment utilisé <span class="caps">LXC</span>, qui n’était qu’un gadget un peu reloud. Ca a permis de trouver plein d’horreurs dans le code du kernel, et les corrections qui ont suivis ont abouti au mythique 3.11, la première version offrant de la conteneurisation décente. <span class="caps">LXC</span> a été jeté au profit de bibliothèques maisons, en Go, qui utilise des fonctions avancées du kernel.</p>
<h2>Docker</h2>
<p>Donc, première révélation aux haters : Docker n’existe pas. Docker se contente d’utiliser des fonctions du kernel, sans le patcher, et d’autres outils bas niveau pour proposer une solution cohérente de conteneur.
D’autres outils proposent leurs propres solutions de conteneurisation en utilisant les mêmes briques de bases.</p>
<p>De toute façon, Docker n’existe pas, ce n’est qu’une <span class="caps">API</span> basée sur <em>runc</em>, l’implémentation officielle de l’Open Container Initiative (crée grâce à la saine polémique lancée par <a href="https://coreos.com/rkt/">Rkt</a>).</p>
<p>Donc, pour ceux qui ne suivent pas, Docker, c’est un process lancé dans un <em>chroot</em> avec le kernel isole ou partage un maximum de choses.</p>
<h3>La conquête</h3>
<p>Docker a choisi une stratégie bottom top. Sa première cible a été les développeurs, les postes clients. Je ne sais pas si c’était leur stratégie, mais clairement, leurs premières victimes ont été des développeurs.</p>
<p>Docker propose une api <span class="caps">CLI</span> élégante, une <span class="caps">API</span> <span class="caps">REST</span> logique, et surtout une très bonne documentation, des évangélistes de prestiges, comme <a href="https://jpetazzo.github.io">Petazzoni</a> ou <a href="https://blog.jessfraz.com">Jess Frazelle</a> (qui bosse maintenant chez Mesos).</p>
<h3>Immuable</h3>
<p>Une image n’a pas vocation à conserver ses données modifiées. Pour ça, il y a les <em>volumes</em>, des dossiers en <span class="caps">RW</span> qui existe hors du container.</p>
<p>Les images sont définies par couches, et si l’on suit la tendance actuelle, la première couche sera une debian stable, commune à toutes les images.</p>
<p>Il est possible d’avoir plusieurs instances d’une même image dans sa grappe de machine.</p>
<p>Les réglages spécifiques à une instance doivent passer par des variables d’environnements, et non les classiques fichiers de conf.</p>
<p>Tous ces points (une image immuable, configurée depuis l’extérieur, avec quelques dossiers explicites qui assureront un stockage non volatile), permettent de conserver une même image pour les tests d’intégration, préprod, et enfin la production, limitant au maximum le drame des tests qui passent dans un environnement, mais pas dans un autre.</p>
<h3>La recette Dockerfile</h3>
<p>Docker permet un système simple de recette, Dockerfile, pour construire ses propres images, et le Hub pour aller piocher dans un catalogue. Bon, on y trouve tout et n’importe quoi dans ce hub et il est sage de se contenter des repos officiels. Ces images publiques sont fort pratiques pour du dev, plus polémique pour de la prod.</p>
<p>On ne met pas à jour une image, on la reconstruit. Voilà, contre l’entropie, la reconstruction.</p>
<p>L’approche en pelures d’oignons du filesystem permet de profiter d’un cache tacite. Pratique, mais pas suffisant, il y a actuellement du travail qui est fait pour améliorer le cache, sans affaiblir la sécurité.</p>
<p>Dockerfile est la solution par défaut pour construire une image, mais il est possible de se débrouiller autrement.</p>
<h3>Le multi <span class="caps">OS</span></h3>
<p>Pour achever les dernières poches de résistance, Docker, qui fonctionnait déjà très bien sur Mac dans un Virtualbox (merci l’archi client/serveur) a développé la première appli connue utilisant la toute fraiche <span class="caps">API</span> de virtualisation de MacOS (basé sur un produit FreeBSD). Même effort coté Windows, mais c’est un monde que je ne fréquente pas.</p>
<h3>La composition de Docker-Compose</h3>
<p>Docker a clairement dit que le <em>fat container</em> était une mauvaise idée (voir le troll de <a href="http://phusion.github.io/baseimage-docker/">Phusion</a>), et vante l’approche “service par conteneur”. Pour une application complète, il faut donc plusieurs conteneurs, configurés, décrits simplement dans une recette. C’est ce que propose Docker-Compose.</p>
<h3>Docker à distance</h3>
<p>Docker est basé sur une architecture client/serveur, qui utilise une socket <span class="caps">UNIX</span>, ou une authentification par certificats. Il est donc possible de laisser son docker client causait avec un docker daemon, distant.</p>
<p>C’est ce que propose <a href="https://www.docker.com/products/docker-machine">Docker-machine</a>, qui permet d’utiliser de la virtualisation ou du Cloud.</p>
<p>Tous les gros du Cloud proposent une offre spécifique à Docker, qui, en proposant une abstraction pour distribuer des services, met à mal le côté captif des différents Cloud.</p>
<h3>Docker en cluster</h3>
<p>Une fois son application bien rangée en petit conteneur, il est tentant de la distribué sur plusieurs serveurs, voir même d’avoir quelque chose de mouvant, que ce soit pour accompagner une montée en charge, ou pour survivre à une panne d’un des serveurs.</p>
<p>C’est ici que ce positionne <a href="https://www.docker.com/products/docker-swarm">Docker-Swarm</a>, mais aussi <a href="http://kubernetes.io">Kubernetes</a>, <a href="https://www.nomadproject.io">Nomad</a>, <a href="http://mesos.apache.org">Mesos</a>.</p>
<p>Docker-swarm propose d’utiliser la même <span class="caps">API</span> pour gérer un ou plusieurs serveurs, et de laisser un algo se débrouiller pour répartir les services et élire des <em>masters</em> des services, si besoin.</p>
<p>Avec sa dernière release, Docker a mis le bazar en allant s’attaquer aux offres d’hébergements distribués. Par ce que bon, humilier <span class="caps">LXC</span> ou Vagrant, tout le monde s’en fout un peu, par contre, s’attaquer à de vrais produits qui génèrent de vrais sous, comme Kubernetes, c’est une autre histoire. Surtout que cette version 1.11 semble avoir été sorti un peu vite, sans trop se soucier de réutiliser des choses existantes.</p>
<h1>Ce qui marche avec Docker</h1>
<p>Docker, sur un poste de dev, c’est un nouveau dans une équipe qui peut commencer à bosser dans 1h, avec Vagrant, il fallait compter 1 jour.</p>
<p>Pour l’intégration continue, Docker est pour l’instant sans concurrence, surtout que l’on n’a pas à simuler ce que va utiliser le dev, mais on va prendre la même configuration.</p>
<p>Docker est un fan de <a href="https://12factor.net/">12 Factor</a>, il permet de déclarer simplement comment lancer un service, et se chargera des logs et de son cycle de vie.</p>
<p>Pour compiler, et encore plus avec des outils trop récents (Golang, je pense à toi), ou des trucs qui veulent installer des tonnes de paquets en *-dev, Docker est magique, on monte le dossier courant comme volume de travail, et paf, ce qui se fait là bas, atterrit ici.</p>
<p>Pour les instances à la demande, comme ce que permettent les <a href="http://jupyter.org/">notebook de Jupyter</a>, il est tentant d’utiliser des images contenant tout le bazar (les scientifiques n’ont pas la même notion de packaging que les développeurs).</p>
<p>Pour installer une application composée de plusieurs services (tout ce qui est à peine plus complexe que phpmyadmin, quoi), la mettre à jour, puis la bazarder, Docker a peu de concurrents.</p>
<h1>Ce qui n’est pas sec</h1>
<p>Docker a décidé de se friter avec Systemd, c’est une querelle de personnes, mais aussi une concurrence. Mais bon, j’en cause dans un autre billet: <a href="https://blog.garambrogne.net/quis-custodiet-ipsos-custodes.html">Quis custodiet ipsos custodes?</a>.</p>
<p>Par grosse flemme, Docker a décidé de ne pas gérer de compatibilité ascendante entre son client et son serveur. Quand on est sur la machine où se trouve le daemon, ce n’est pas grave, c’est le même paquet, à distance, ou pour les APIs tiers, c’est une autre histoire.</p>
<p>Pour la gestion de plusieurs machines, c’est actuellement la bataille, avec Google qui a envie de mettre en valeur son propre Cloud qui permet d’avoir le Kubernetes le plus beau du monde. Nomad et Mesos visent les très gros trucs, et Docker ne veut rien lâcher de sa conquête de l’Univers, et pour cela, il est prêt à aller trop vite.</p>
<p>Les APIs peuvent changer brutalement et potentiellement tout défoncer les produits tiers.</p>
<h1>Ce qui est de la mauvaise foi</h1>
<h2>Docker c’est pourri par ce qu’on ne peut pas mettre à jour openssl</h2>
<p>Une image est immuable, et ne dois pas être mise à jour sur place. Il faut donc une nouvelle image de l’application. Avec le système de couches, il suffit de repartir d’une image système à jour, et de redéployer.</p>
<p>Le souci est le même pour tout ce qui est compilation (hors bibliothèque partagé), et donc Java, Scala, Golang… Même drame pour les frameworks.</p>
<p>La réponse à ça est le baking.</p>
<p>De manière générale, il est assez criminel de demander à un dev de pousser une image depuis son poste de travail. Il est nettement plus sage d’utiliser de l’intégration continue, et donc d’avoir un combo build+test automatisé, pour redéclencher la création d’une nouvelle image.</p>
<p>De toute façon, un apt-get upgrade ne marche qu’un temps (sur deux versions d’<span class="caps">OS</span>, grosso modo), et même les langages (<span class="caps">PHP</span>, je te vois!) ne sont pas maintenus jusqu’à la fin des temps.</p>
<p>Avec les architectures distribuées, vous allez vous retrouver avec un service frontal qui va assurer le proxy/routage des services. Le classique Nginx/HAproxy/<a href="https://traefik.io">Traefik</a>/<a href="https://caddyserver.com/">Caddy</a>/… qui va bien, et qui va affronter Internet, faisant rempart de son corps devant l’application.
Ce proxy <span class="caps">HTTP</span> pourra être mis à jour, indépendamment du cycle de vie de l’application.</p>
<p>La mise à jour d’openSSL sera nécessaire pour dans la grande majorité des cas à une utilisation client, pas serveur. Mais en parlant de ça, savez-vous si l’application va accepter des réglages débiles (comme le fit Ruby), ne pas vérifier la concordance entre le nom du serveur, et le nom du certificat (comme le fit Python), si le pinning et la révocation sont gérés?</p>
<h2>Je ne veux pas sur mon infra une image qui sort du cul d’Internet</h2>
<p>Clairement, c’est une bête idée de lancer un binaire sans un minimum de crédibilité. Coup de bol, une image polie doit être fournie avec son Dockerfile, et donc, de fait, ses sources permettant de la construire.</p>
<p>Ce problème est de plus en plus générique. Qui a envie de recompiler Elasticsearch après avoir relu le code, ou même MongoDB?</p>
<p>De toute façon, c’est celui qui construit qui répare. Un dev peut très bien bosser avec une image “postgres:9.4” sur son poste de travail, mais rien ne vous oblige à la déployer. Son service a besoin d’un service Postgres dans la version 9.4, bah, coïncidence, c’est la version packagé Debian, et vous avez tuné avec amour sa conf et sa réplication. Si vous êtes de bonne humeur, vous pouvez même construire votre image Pg, basée sur “debian:stable”, avec les réglages que verront l’utilisateur. Image qui servira en dev, et pour le <span class="caps">CI</span>.</p>
<h2>Docker, ce n’est pas secure</h2>
<p>Oui, clairement, la surface d’attaque entre la virtualisation et la conteneurisation n’est pas comparable, ni leur maturité.</p>
<p>Par contre, il y a un souci pour la virtualisation qui va protéger l’hôte, et les autres <span class="caps">VM</span>, laissant libre ce qui se passe au sein de la <span class="caps">VM</span>. Visualisez un alien qui se faufile dans le véhicule blindé, ok, il aura du mal à sortir, mais pour les passagers, ce sera une autre affaire.</p>
<p>La taille minimale des VMs oblige à y déployer plusieurs services, la contamination de l’un d’entre eux pourra mettre en danger les autres.</p>
<p>Il y a deux réponses à ça.</p>
<p>En plus de l’isolation permise par Cgroups et les Namespaces, Docker permet d’utiliser simplement l’arsenal classique de Linux : Apparmor/SELinux, les capabilities et <a href="https://github.com/seccomp">seccomp</a>.</p>
<p>Apparmor est déployé par défaut sur Ubuntu (et est plaisant à utiliser), mais Seccomp est peu utilisé en dehors de Chromium. Docker est un évangéliste de ces technos soit ignorés, soit méprisés.</p>
<p>Seconde approche, mise en évidence par <a href="http://rancher.com/">Rancher</a> et CoreOS, utiliser la tactique de ceinture et bretelles, en mettant des containers dans des machines virtuelles (grâce à la délégation de droit que permet <span class="caps">KVM</span>). L’idée n’est pas d’avoir du un pour un, mais d’isoler les services critiques ou au moins de les séparer.</p>
<p>De toute façon, vous n’êtes pas tenu de faire du co-tenant sur vos serveurs.</p>
<h2>Docker, ça tourne en root</h2>
<p>Oui, c’est un poil reloud. Par contre, le root dans les images, c’est juste une histoire de flemme, et d’ailleurs Kubernetes refuse maintenant les images sans utilisateur dédié.</p>
<p>Pour l’hôte, c’est une limitation de Linux, qui bosse sur ce sujet, et une partie des outils sont déjà possibles sans privilège. Docker piaffe d’impatience d’avoir son outil qui puisse fonctionner sans root.</p>
<p>Juste pour le plaisir de dire du mal : ping a besoin du compte root, lui aussi.</p>
<h2>Docker c’est pourri, je ne peux pas me connecter en <span class="caps">SSH</span> pour déboguer mon service</h2>
<p>Ni pour y faire un apt-get upgrade, on a compris.</p>
<p>Un conteneur utilise les namespaces pour isoler son process hôte, mais il est tout à fait possible, lors de la création d’un conteneur d’utiliser les mêmes namespaces qu’un conteneur existant. On parle de side kick container (Robin qui vient déboguer Batman, quoi). Un conteneur peut ainsi stracer un autre.</p>
<p>Je vous invite à <span class="caps">RTFM</span> <a href="http://man7.org/linux/man-pages/man1/nsenter.1.html">nsenter</a>.</p>
<p>Je parle bien de conteneur en général, rien de tout ça est spécifique à Docker.</p>
<h2>Docker, c’est pourri, ça veut scaler mes services en les clonants, faisant fi des threads</h2>
<p>Non, ça, c’est une connerie des 12 factor. Heroku a la flemme de gérer les possibilités de scaling des différents serveurs webs, du coup, hop, la vérité c’est eux, ne vous occupez de rien, on n’a qu’à prendre le plus petit commun.
Donc, dans la vraie vie, Docker sait passer les messages <span class="caps">UNIX</span> au process, et il se fiche de savoir si votre application fait du fork en <span class="caps">COW</span> (comme peut le faire Gunicorn), ou des threads. Rien de vous empeche d’avoir une grappe de process/thread web dans votre container, et quand vous aurez besoin de scaler, d’instancier une (ou plusieurs) grappe, un peu plus loin.</p>
<p>12 factor, qui ont le bon gout d’être pratique et lisible, reste quand même de la propagande post mortem d’Heroku.</p>
<h2>Docker, c’est pourri, je ne peux pas sécuriser l’accés à mes services en utilisant des sockets <span class="caps">UNIX</span></h2>
<p>Bah, déjà, quand je mets mon Mysql d’un côté, et mon Wordpress de l’autre, je ne peux plus utiliser de sockets <span class="caps">UNIX</span>, pour gérer les droits. Pour ceux qui ne sont pas attentifs, mais les solutions de types Heroku sont encore pire, il n’y a pas de notion de <span class="caps">VLAN</span> privé, et du coup, tous les services non moisis proposent de l’authentification, même Memcached sait le faire, c’est dire.</p>
<p>Dans les gros boulets ouverts aux quatre vents, personnellement, je vois Statsd (avec son protocole <span class="caps">UDP</span>) et Elasticsearch, pour son gossip de cluster.
Mais Docker ne vous a pas oublié, il propose un <a href="https://docs.docker.com/engine/userguide/networking/#/user-defined-networks">système de réseau extensible</a>, pour créer des sous réseaux permettant de regrouper vos différents services. Pour le reste du monde, ça s’appelle <a href="https://en.wikipedia.org/wiki/Software-defined_networking">Software Defined Network</a>, et il existe plein d’implémentation libre. Le réseau virtuel est d’ailleurs actuellement ce qui fait la qualité d’un service Cloud (<span class="caps">OVH</span>, je te regarde).</p>
<h2>Docker, c’est pourri, ça utilise Alpine qui utilise musl que c’est compatible avec rien</h2>
<p>Une application à besoin d’un contexte : une arborescence Linux, et un kernel pour accéder au matériel.</p>
<p>Une Debian nue doit faire dans les 125Mo, pourquoi ne pas utiliser quelque chose de plus minimaliste, surtout avec les technos qui ont des traditions d’autarcisme (<span class="caps">JVM</span>, Erlang, Golang…)?
Alpine Linux est une distribution qui a une approche “embarqué”, basée sur busybox et musl comme libC. 5Mo tout mouillé.</p>
<p>C’est rigolo à utiliser, jusqu’à ce qu’on essaye de compiler un truc non packagé, et que l’on tombe sur du code spécifique glibc.
Une image Debian embarque clairement beaucoup trop de bazars pour lancer un seul service bien bordé. Mais bon, ça à le bon gout de marcher, on a la garantie qualité Debian™, et avec le système de couches, l’image de base sera commune à la plupart des images.</p>
<p>Pour l’instant, les images de base sont basées sur Debian, et c’est un choix excellent, ça limite au maximum les surprises de la couche basse, et permet de se concentrer sur le côté trop à jour de l’application.</p>
<p>Docker aime explorer des voies, mais bon, si Alpine vous chagrine, vous pouvez toujours tester Slackware, comme avant.</p>
<p>Dans tous les cas, rien ne vous oblige à utiliser une image que ne vous sentez pas, les Dockerfile sont tellement simples à hacker, ne vous privez pas.</p>
<h2>Docker, c’est pourri pour de la prod</h2>
<p>Il y a beaucoup de concurrents pour le concours de “pourri pour la prod”, hein.
Docker avance couche par couche.
L’étape prod distribué, par ce que prod sur un serveur, ce n’est pas non plus super complexe, est son chantier actuel.
Docker Swarm est arrivé avec La release 1.12, et a fait hurler pas mal de personnes. Mais bon, on change d’échelle niveau complexité, et Raft, c’est quand même un gros morceaux. Allez demander aux devs de Etcd ou Consul.</p>
<p>Swarm a quand même l’audace de s’attaquer à Kubernetes, la version libre de l’outil interne de Saint Google, quand même.</p>
<blockquote>
<p>Kubernetes builds upon 15 years of experience of running production workloads at Google, combined with best-of-breed ideas and practices from the community.</p>
</blockquote>
<p>Voici l’argumentaire “take that, Batman” de Google pour ses conccurents.</p>
<p>Enfin bon, avoir un avis définitif sur un tel outil, sorti fin juillet, c’est un poil hatif. Il est possible d’utiliser Docker sans Swarm, promis!</p>
<h2>Docker, c’est pourri</h2>
<p>Imprimez des teeshirts et des stickers, c’est efficace avec les slogans.</p>
<p>Docker est opiniated, avance vite, et il fait beaucoup de bruit.</p>
<p>On peut lui reprocher plein de choses (avec des arguments), mais s’il n’y a qu’un seul argument à retenir pour le défendre :
Docker est un produit opensource qui explore, code, explique et corrige. Et ça, que vous soyez utilisateur ou non de leur produit, c’est déjà super important.</p>
<p>La conteneurisation fait déjà partie de l’informatique moderne, il va falloir s’y faire.</p>GraphQL, repenser le modèle de données2016-09-14T19:07:00+02:002016-09-14T19:07:00+02:00Mathieu Lecarmetag:blog.garambrogne.net,2016-09-14:/graphql-repenser-le-modele-de-donnees.html<p>GraphQL est une rafraichissante invention de Facebook permettant de manipuler un modèle de donnée en passant par un petit tuyau. Ciblant clairement les applications smartphones (webs ou natives), GraphQL remet à plat tout un tas d’habitudes et de cargo cult.</p><h1>L’écosystème de React</h1>
<p>Caché derrière son blockbuster <a href="https://facebook.github.io/react/">React</a> (accompagné de <a href="https://facebook.github.io/relay/">Relay</a>), <a href="http://graphql.org/">GraphQL</a> est une rafraichissante invention de Facebook permettant de manipuler un modèle de donnée en passant par un petit tuyau. Ciblant clairement les applications smartphones (webs ou natives), GraphQL remet à plat tout un tas d’habitudes et de cargo cult. D’abord utilisé comme arme secrète, il est maintenant libéré, spécifié avec une implémentation de référence (en nodejs) et d’autres implémentations (se basant sur des frameworks largement utilisés).</p>
<p>React fait le choix de franchement basculer un maximum d’intelligence du serveur vers le client, donnant sciemment le pouvoir à JavaScript, le vrai, celui dans le navigateur web.</p>
<h1>Les échanges clients/serveurs</h1>
<p>Malgré ses velléités d’autonomies, le <span class="caps">HTML5</span> reste massivement utilisé comme une plaisante interface utilisateur connectée en <span class="caps">HTTP</span> à un serveur, qui va garder la main sur les données, et coordonner les interactions des multiples clients.</p>
<h2><span class="caps">REST</span></h2>
<p>Exposer une <span class="caps">API</span> <span class="caps">REST</span> est l’approche la plus neutre et la plus ouverte pour mettre à disposition des données et des fonctions via le protocole <span class="caps">HTTP</span>.</p>
<p>Par contre, implémenter avec ses petites mains un client à partir d’une <span class="caps">API</span> <span class="caps">REST</span> est chronophage, fragile, pénible à faire évoluer, douloureux à tester.</p>
<h2>Modèle</h2>
<p>Les données, côté client sont manipulées sous forme d’objets, mais il faut voir comment faire correspondre le modèle local avec le modèle distant.</p>
<p>Le choix d’utiliser le même langage, entre le client et le serveur, permet de partager le modèle. Il est possible de choisir JavaScript, comme le fait <a href="https://www.meteor.com/">Meteor</a>, ou pire, de choisir un langage tiers, qui sera compilé en JavaScript, client et serveur, comme <a href="http://opalang.org/"><span class="caps">OPA</span></a>.</p>
<p>Une variante de cette approche est de garder son langage de prédilection pour le serveur et de ne générer que la partie cliente, comme le presque oublié <a href="http://www.gwtproject.org/"><span class="caps">GWT</span></a>.</p>
<p>Plutôt que de partir d’un langage spécifique pour définir son modèle, il est plus sage de passer par une grammaire agnostique pour définir modèles et fonctions qui sera moulinée dans différents langages.
C’est que propose <a href="http://swagger.io/">Swagger</a> (renommé il y a peu <a href="https://openapis.org/">OpenAPI</a>, surcouche à <span class="caps">REST</span>. Il existe aussi <a href="http://raml.org/"><span class="caps">RAML</span></a>, qui semble avoir moins de soutiens.</p>
<p>Il existe aussi des grammaires neutres, non <span class="caps">REST</span> mais pouvant utiliser <span class="caps">HTTP</span> comme couche transport, comme Thrift et Protobuff avec son <a href="http://www.grpc.io/">grpc</a> qui vient de sortir en version 1.0, mais lié à la toute nouvelle version 2 d’<span class="caps">HTTP</span>.</p>
<p>Toutes ces technologies permettent d’accéder à des données en clef/valeur, ou à des fonctions. Il faut donc implémenter une fonction côté serveur, qui sera utilisé côté client, avec du coup un aller-retour demandant des compétences distinctes.</p>
<h2>Réseau et latences</h2>
<p>Le premier ennemi des réseaux mobiles est la latence, bien avant le débit. Pour limiter les temps de latence, il faut limiter le nombre de requêtes. Pour pallier aux problèmes de débit, il ne faut rapatrier que le nécessaire pour afficher la réponse. Google, avec <span class="caps">HTTP</span>/2 (version normalisée de son <span class="caps">SPDY</span>), propose de multiplexer la connexion, sur un canal bidirectionnel qui reste ouvert, permettant ainsi d’envoyer les données sous une forme permettant de les utiliser au fur et à mesure.</p>
<h1>GraphQL</h1>
<p>Pour optimiser la communication réseau, Facebook se contente de reprendre la tactique du <span class="caps">SQL</span> : utiliser un langage concis permettant de sélectionner ce que l’on souhaite, et de préciser les objets et attributs attendus en réponse.</p>
<p>Ce langage de requête est le <em>GraphQL</em>.</p>
<p>La requête est en GraphQL, un nouveau format texte, mais la réponse est classiquement en <span class="caps">JSON</span>.</p>
<p>La ressemblance de GraphQL avec <span class="caps">SQL</span> est faible, et c’est une bonne nouvelle : il est clairement impensable de confier la rédaction de <span class="caps">SQL</span> au client web, de plus, le modèle relationnel, intimement lié à <span class="caps">SQL</span> est loin d’être un modèle universel, déjà mis à mal par les <span class="caps">ORM</span> et le cache.</p>
<p>Le GraphQL permet de rapatrier une grappe d’objets, avec un peu de dynamisme, surtout pas d’effectuer des calculs sur des regroupements ou tout autres action complexes.</p>
<h2>Modèle abstrait</h2>
<p>Le GraphQL ne sera pas traité directement par une base de données. Il travaille sur un modèle abstrait et orienté graphe. Facebook est fasciné par les graphes, presque autant que Linkedin, et ça tombe bien, c’est un modèle que notre cerveau arrive bien à appréhender, mais qu’il est difficile d’implémenter autrement que tout en <span class="caps">RAM</span>.</p>
<p>L’abstraction du modèle laisse toute liberté aux choix d’implémentations. Des outils sont fournis pour exposer les objets d’un <span class="caps">ORM</span> s’appuyant sur le classique modèle relationnel, mais rien n’empêche d’utiliser d’autres modèles, fichier par exemple (MongoDB, Couchbase, Elasticsearch…), voir même les modèles hybrides que nous propose l’intégration de <span class="caps">JSON</span> dans Postgresql et plus récemment Mysql. Des <a href="https://github.com/chentsulin/awesome-graphql">outils tiers</a> permettent de faire des serveurs GraphQL dans autre chose que du Javascript, et il faut reconnaitre que l’implémentation pour Python, <a href="https://github.com/graphql-python/graphene">Graphene</a>, a une bonne tête.</p>
<h2>Typage</h2>
<p>GraphQL utilise du typage, qui permet de systématiser la validation, mais aussi de l’introspection.</p>
<p>Le typage permet de garantir la réponse attendue, mais il est suffisement souple pour permettre une évolution du serveur sans imposer la mise à jour du client. Fort pratique pour faire évoluer un service pour une nouvelle application sans pour autant casser une ancienne application utilisant le même service.</p>
<p>Il existe un éditeur en ligne, avec une interface sympathique, <a href="https://github.com/graphql/graphiql">GraphiQL</a> (notez le i, comme interactif).</p>
<h2>Paramètres</h2>
<p>Une simple astuce permet de révolutionner ce classique usage de graphes : les attributs acceptent des arguments nommés, il est donc possible d’ajouter du dynamisme (comme une recherche) ou plus simplement du paramétrage (comme une taille d’image).</p>
<p>GraphQL ne va pas utiliser le principe de <span class="caps">REST</span>, mais se contenter d’un seul point d’entrée <span class="caps">HTTP</span>, <em>/graphql</em>, mais va profiter de la session, avec donc une connaissance de l’utilisateur.</p>
<h2>Mutations</h2>
<p>Initialement conçu pour un accès lecture seul, GraphQL permet maintenant des mutations.</p>
<h2>Requêter simplement</h2>
<p>Avec GraphQL, le serveur se contente de gérer le modèle (avec la garantie de cohérence, les droits, la persistance), avec du code métier, des middlewares, tout ce genre de bonnes choses.</p>
<p>Le client pourra librement composer à partir de ces données.</p>
<p>Par dessus, vous pouvez ajouter Relay et React, pour profiter de l’écosystème officiel. Mais vous pouvez utiliser d’autres choses, sans même vous limiter à JavaScript (il suffit de savoir lire du <span class="caps">JSON</span> sur de l’<span class="caps">HTTP</span>).</p>Quis custodiet ipsos custodes?2016-09-01T20:00:00+02:002016-09-01T20:00:00+02:00Mathieu Lecarmetag:blog.garambrogne.net,2016-09-01:/quis-custodiet-ipsos-custodes.html<p>La grande bataille de la gestion de process entre Systemd et Docker</p><blockquote>
<p><a href="https://fr.wikipedia.org/wiki/Quis_custodiet_ipsos_custodes%3F">Mais qui gardera ces gardiens ?</a></p>
</blockquote>
<p>Où il sera question de Linux, de processus, de conteneurs et de choses gravitant autour.</p>
<h2>Processus et daemon</h2>
<p>Un serveur regroupe des process, et le premier d’entre eux, celui avec le pid 1 a la responsabilité de lancer tous les autres lors du démarrage.</p>
<p>Certains sont des <a href="https://fr.wikipedia.org/wiki/Daemon_(informatique)">daemons</a>, des programmes qui tournent en tache de fond, sans interaction directe. Un daemon a la responsabilité d’abandonner ses privilèges, de changer d’utilisateur et de racine (chroot), de causer dans un journal, de noter son <span class="caps">PID</span>, et de se détacher, de que décrit si bien <a href="https://github.com/zedshaw/python-lust">Zed Shaw dans Lust</a>.</p>
<h2>Init</h2>
<p>Historiquement, le rôle d’init est dévolu à init.d, qui accuse méchamment le poids de son âge.</p>
<p>Son principe, finalement simpliste alors qu’il se prétendait simple, est que chaque service est lancé par un script shell, en répondant à quelques commandes : <code>start</code>, <code>stop</code>, <code>status</code>, <code>restart</code>, <code>reload</code>.</p>
<p>Dans les faits, les efforts de l’application sont laborieux, et la qualité de ces scripts est variable pour des applications packagés par le système. Pour des applications métiers, la qualité est tout simplement atroce. Le shell n’a tout simplement pas les commandes nécessaires, et le nébuleux <a href="http://manpages.ubuntu.com/manpages/xenial/fr/man8/start-stop-daemon.8.html">start-stop-daemon</a> est une punition à débuguer.</p>
<p>Il y a eut un peu de tuning, comme le passage de <a href="https://en.wikipedia.org/wiki/Bash_(Unix_shell)">bash</a> à <a href="https://en.wikipedia.org/wiki/Almquist_shell">dash</a> pour grappiller du temps, et des efforts de parallélisation. Rien qui n’a pu sauver un init.d finalement en phase terminale.</p>
<h2>Superviseur</h2>
<p>Des tentatives laborieuses ont été explorées, côté utilisateur, comme <a href="https://cr.yp.to/daemontools.html">daemontools</a> ou <a href="http://supervisord.org/">supervisor</a>. Ces bricolages permettent de se passer de la danse du daemon, et de se contenter de lancer directement l’application, l’emballage se chargeant du reste. Deux drames <span class="caps">UNIX</span> ont perturbé cette volonté de simplification :</p>
<ul>
<li>Pour utiliser des ports réseaux < 1024, il faut être root, par ce que.</li>
<li>Un utilisateur ne peut pas changer d’utilisateur, ça, c’est du plus facile à justifier. Mais si vous pouvez utiliser un superviseur, qui tourne avec l’utilisateur root, paf, vous pouvez lancer des commandes en son nom.</li>
</ul>
<p>L’idée du superviseur n’a jamais été traitée complètement, et ces outils inaboutis finiront par vous mordre, tôt ou tard. Supervisor, le meilleur des produits disponibles, dépend de bibliothèques mortes depuis des années, impose des rechargements par grappe, et utilise des actions bien séquentielles.</p>
<p>Pourtant, le <a href="https://devcenter.heroku.com/articles/procfile">Procfile</a>, un simple format déclaratif, est devenus un standard de fait, et est maintenant sanctifié par <a href="https://12factor.net/fr/">les 12 facteurs</a> , par le <a href="https://12factor.net/fr/processes">chapitre <span class="caps">VI</span></a> précisément. </p>
<h2>Au-delà de chroot</h2>
<p>Pour limiter la possibilité de faire des bêtises, un serveur doit avoir un utilisateur, un <a href="https://fr.wikipedia.org/wiki/Chroot">chroot</a> si on est poli, et … c’est à peu près tout, une limitation sur le nombre de fichiers ouvert?
Depuis peu, d’autres possibilités de restrictions sont apparues, comme <a href="http://wiki.apparmor.net/">Apparmor</a>/<a href="https://www.nsa.gov/what-we-do/research/selinux/">SELinux</a>, les <a href="https://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface/">cgroups</a>, les <a href="http://man7.org/linux/man-pages/man7/namespaces.7.html">namespaces</a>, <a href="https://en.wikipedia.org/wiki/Seccomp">seccomp</a>, les <a href="http://man7.org/linux/man-pages/man7/capabilities.7.html">capabilities</a> … Plein de très belles choses permettant de cloisonner proprement les applications. Techniquement, c’est à l’application de gérer tout ça, ou presque, seules les cgroups sont faciles à manipuler depuis l’extérieur.</p>
<p>Plutôt que d’espérer vainement que les applications gèrent proprement toutes ces fonctionnalités, il est nettement plus efficace de reprendre l’idée simpliste des superviseurs, et d’emballer chaque process.</p>
<p>Cette approche, qui sépare l’application de l’isolation de son contexte, permet d’auditer simplement la sécurité, et d’avoir des politiques systématiques. <a href="https://github.com/google/lmctfy/blob/master/README.md"><span class="caps">LMCTFY</span></a>, “Laisse-moi contenariser ça pour toi”, comme on disait chez Google, pionnier de cette approche.</p>
<p>Il suffit de rajouter un <a href="https://en.wikipedia.org/wiki/Union_mount">système de fichier par couche</a> pour avoir un chroot propre qui prends peu de place, et voilà, vous avez un système de conteneur.</p>
<h2>La collision avec Systemd</h2>
<p>Parallèlement avec l’apparition des outils systèmes permettant la création des containers est arrivé <a href="https://www.freedesktop.org/wiki/Software/systemd/">Systemd</a>, le nouvel init standard pour Linux.</p>
<p>Après quelques concurrents timides à init.d, Systemd a débarqué avec son gros bulldozeur, en choisissant de remettre à plat la fonction d’init, en incluant la supervision et moult fonctions liées. Avec une diplomatie brutale, peu de respect pour les dogmes, et une grande ambition, Systemd est arrivé à se faire haïr comme peu de logiciels avant. En plus, il est promu (et développé) par Red Hat.</p>
<p>Mais bon, les réécritures timides d’init.d n’avait aucune chance, <a href="http://upstart.ubuntu.com/">Upstart</a> s’étant vautré, il ne restait plus beaucoup d’alternatives. L’apparition du Cloud a forcé le destin, par ce qu’il consomme énormément d’init, et annihile le culte incompréhensible du dieu uptime. Il est indispensable d’avoir des cycles de start/stop avec les dépendances qui vont bien, rapides et prévisibles.</p>
<p>Debian a sifflé la fin de la récré en incluant systemd malgré le peu d’enthousiasme de ses mainteneurs, suivi par Ubuntu qui a ainsi acté la mort du pénible Upstart. </p>
<h2>Le choc avec Docker</h2>
<p>Systemd a débloqué plein de choses, mais il a renâclé sur la notion de conteneur, son <a href="https://www.freedesktop.org/software/systemd/man/systemd-nspawn.html">nspawn</a> étant officiellement un jouet “for debugging, testing and building”. C’est ballot.</p>
<p><a href="https://www.docker.com/">Docker</a> a démontré que les outils systèmes étaient valide pour créer des conteneurs, sans suivre la fausse bonne idée qu’est <a href="https://linuxcontainers.org/fr/"><span class="caps">LXC</span></a>.</p>
<p>Docker, grisé par son succès pense qu’il va être le prochain init, et qu’il faut une guerre ouverte avec Systemd. “<a href="https://static.lwn.net/images/2016/devconf-badge.jpg">Je dis non à tout patch spécifique à systemd</a>” voilà la signature de <a href="https://blog.jessfraz.com/">Jess Frazelle</a>, core dev charismatique et influente, sur son étiquette pour une conférence Dockercon. D’ailleurs, <a href="https://chrome.google.com/webstore/detail/systemd-butts/cdlammifaiaedopfjihjnlidjjkcngja">elle qualifie Systemd de 🍑</a></p>
<p>Forcé à faire des concessions par les applications s’utilisant (ou son concurrent, <a href="https://coreos.com/rkt/docs/latest/app-container.html">Rkt</a>), Docker a été contraint d’écrire des specs, <a href="https://www.opencontainers.org/">Open Containers</a>,plutôt que de tout défoncer à chaque release majeur. Fair-play, ils ont créé une implémentation de référence, <a href="https://runc.io/">runc</a>, qu’ils utilisent maintenant comme couche basse pour leur outil (depuis la 1.11).</p>
<p>Systemd, surtout dans sa version Debian stable (Jessie, donc) n’implémente pas toutes les possibilités d’isolation du kernel Linux, mais collabore très bien avec runc, qui est par concept peu intrusif et plus facile à mettre à jour.</p>
<h2>Le déplacement du conflit</h2>
<p>Systemd et Docker sont en fait très proche : ils permettent de manipuler des process sur un serveur.</p>
<p>La notion d’application, qui n’a jamais vraiment été autonome est maintenant remplacé par une composition de services. Il est primordial que cette composition soit la même en dev, en test, en préprod et en production.</p>
<p>Autre grande évolution conceptuelle, la notion de serveurs, qu’ils soient virtuels ou “bare metal”, est en train de fondre. Pour pouvoir prolonger la densification, utiliser toujours plus de ressources, et profiter de la fluidité du Cloud, il ne faut plus lier les process à un serveur. Une application est composée d’une arborescence de services. Il y a maintenant un pool de ressources qui seront alloués par un orchestrateur, responsable de palier au crash des serveurs, et de recâbler les services entre eux, de manière dynamique.</p>
<p>Docker est le standard de fait pour la conteneurisation. <a href="https://docs.docker.com/compose/overview/">Docker-Compose</a> est bien placé pour la description de compositions, et Docker, dans sa version 1.12 a décidé de s’attaquer à la couche suivante, avec <a href="https://docs.docker.com/engine/swarm/">Docker-Swarm</a>, ce qui a fait hurler tous les produits positionnés sur cette couche-là, la seule permettant de proposer des produits financièrement viables. Docker s’est attaqué ainsi à <a href="http://kubernetes.io/">Kubernetes</a>, du dieu Google, avec un produit mal fini.</p>
<p>Entre cet affront, le clash avec Systemd, le refus de se brimer en suivant des specs, de stabiliser son cycle de vie, des drames de harcèlement moral en interne et surtout l’importance que représente cette nouvelle étape dans le développement informatique, il va y avoir un drame.</p>
<p>Comme <a href="http://thenewstack.io/docker-fork-talk-split-now-table/">un fork</a>.</p>Golang le bouleverseur2016-01-25T22:25:00+01:002016-01-25T22:25:00+01:00Mathieu Lecarmetag:blog.garambrogne.net,2016-01-25:/golang-le-boulverseur.html<p>C’est dur de traduire le mot anglais “disruption”. Boulversement semble être le terme le plus approprié. Golang fait partie des éléments “bouleverseurs” de l’informatique.</p><h2>Évolution et disruption</h2>
<p>C’est dur de traduire le mot anglais “disruption”. Boulversement semble être le terme le plus approprié.</p>
<p>Le progrès, l’évolution, n’est absolument pas linéaire, de temps en temps, plutôt que la douce pente des améliorations, on monte une marche, d’un coup.</p>
<p>Golang fait partie des éléments “bouleverseurs” de l’informatique.</p>
<h2>Les trois couches</h2>
<p>En informatique, sur la table basse du hardware, on pose un gros gâteau multicouche : le logiciel. Tout en bas, la couche du noyau, tout en haut, la couche applicative. Au milieu, quelque chose de plus indistinct, la couche de <a href="https://fr.wikipedia.org/wiki/Middleware">middleware</a> (intergiciel selon l’Office québécois de la langue française), les bases de données et les services non-métier.</p>
<p>Dans cette couche, on peut par exemple ranger les proxy, les brokers, les compteurs de métriques, les serveurs de cache/noms/temps… Tout ce qu’il faut pour relier les différentes parties applicatives qui, ensemble, forment une application.</p>
<p>On trouve dans cette catégorie des applications vénérables, éprouvées, indispensables, mais aussi très rigides. Déjà, corriger un bug, puis le maintenir en attendant qu’il passe upstream, peu de personnes peuvent se le permettre, alors écrire un plugin pour mettre du code métier en C …</p>
<h2>Au-delà du C</h2>
<p>Le <a href="http://shop.oreilly.com/product/9781565923065.do">C est sacré</a>. Il a été créé pour inventer <span class="caps">UNIX</span>, et c’est la plus fine couche possible au-dessus de l’assembleur. Les seules limites sont donc le matériel (via le kernel), et des détails, comme la responsabilité de gérer la mémoire qui est à la charge du développeur.</p>
<p>On se retrouve donc avec les pleins pouvoirs, mais les temps de développement explosent. La vélocité (le ratio fonctionnalité/temps de développement) est un des gros problèmes du C.</p>
<p>Avec des gros processeurs (qui ne coute pas cher) et des petits temps de dev (qui coute cher), le C est hors de prix.</p>
<p>Il faut donc aller voir ailleurs : les surcouches, les langages spécialisés (mais compilés), ou les langages de scripts (mais génériques).</p>
<h2>Le scripting</h2>
<p>Les langages de scripting ont une vélocité inégalable, mais le surcout en <span class="caps">RAM</span> et <span class="caps">CPU</span> est loin d’être négligeable, tout comme sa tradition pénible de ne pas savoir utiliser correctement les threads. La réponse officielle pour les soucis de performances est un mensonge : si c’est mou, tu n’as qu’à recodé la partie qui grippe en C. Double mensonge.</p>
<p>La plupart des blocages sont dus à des problèmes d’<span class="caps">IO</span> que seul l’asynchrone peut sauver. <span class="caps">OK</span>, le <span class="caps">SSD</span> peut aider. Viennent ensuite les problèmes de <span class="caps">CPU</span> : le surcout de l’interprétation (qui peut être compensé par de la compilation Just In Time) et la parallélisation (faire bosser plusieurs coeurs de son processeur).</p>
<h3>Optimiser en C</h3>
<p>Le C permet d’utiliser très efficacement son processeur, mais la parallélisation et l’asynchrone ne seront pas offertes en cadeau.
Pour appeler simplement du C depuis un autre langage, il existe depuis longtemps des outils génériques : <a href="http://www.swig.org"><span class="caps">SWIG</span></a> ou <a href="https://en.wikipedia.org/wiki/Foreign_function_interface"><span class="caps">FFI</span></a>, une <span class="caps">API</span> C, et de la <a href="https://en.wikipedia.org/wiki/Source-to-source_compiler">transcompilation</a>, mais le mélange reste douloureux et ne facilitera pas la maintenance.</p>
<p>Dans tous les cas, imaginer qu’un bon scripteur fera du bon C est complètement farfelu.</p>
<p>Intégrer du scripting dans du code C (le contraire, donc), est faisable, mais obtenir de bonnes performances peut être complexe, il n’y a qu’à voir les performances de Postgresql avec ses procédures stockées exotiques.</p>
<p>Il existe des solutions hybrides, basées sur Numpy par exemple, qui émergent pour gérer efficacement des aller-retour entre le scripting et le C : <a href="http://blog.cloudera.com/blog/2015/07/ibis-on-impala-python-at-scale-for-data-science/">Ibis</a> et <a href="https://www.monetdb.org/blog/embedded-pythonnumpy-monetdb">MonetDB</a>. Mais ce sont des réponses super spécialisées.</p>
<h3>Lua</h3>
<p><a href="http://www.lua.org">Lua</a> a été le premier a chambouler la frontière entre C est le reste du monde. Conçu pour gérer des configurations complexes d’une application en C en utilisant un modèle de mémoire similaire pour pouvoir partager des variables, il est aussi un langage de scripting plus décent que bien d’autres.
Interprèté, simple et minimaliste, avec un <span class="caps">JIT</span> efficace, Lua est le chouchou des dév C pour embarquer du code métier.</p>
<p>Il y a en ce moment une vague de logiciels intégrant Lua pour définir le comportement métier :</p>
<ul>
<li><a href="http://torch.ch">Torch</a> l’utilise pour faire du calcul scientifique (avec la possibilité d’utiliser le <span class="caps">GPU</span>)</li>
<li>Un patch <a href="https://github.com/openresty/lua-nginx-module#readme">Nginx</a> (packagé Debian, c’est dire sa popularité) existe</li>
<li><a href="http://blog.haproxy.com/2015/03/12/haproxy-1-6-dev1-and-lua/">Haproxy</a> l’aura dans sa prochaine version stable</li>
<li><a href="http://www.haka-security.org/">Haka</a> sniff son réseau, <a href="https://nmap.org/book/nse-language.html">Nmap</a> le réseau des autres</li>
<li><a href="http://sysdig.org">Sysdig</a> surveille le système d’exploitation avec ses chisels</li>
<li><a href="http://redis.io/commands/eval">Redis</a> et ses procédures stockées. Notons la présence d’un <a href="http://redis.io/topics/ldb">dévermineur</a> spécifique</li>
<li>…</li>
</ul>
<p>Bref ça fonctionne, et bien.</p>
<p>Mais, il y a un mais. Débuguer et tester du lua embarqué peut rapidement devenir apocalyptique. Comprendre le workflow du code en C pour y intercaler ses bouts de scripts n’est pas évident, et il ne sera pas possible d’aller au-delà de ce qu’expose l’application hôte.</p>
<h3>Javascript</h3>
<p>Javascript a un indéniable côté universel et il propose maintenant de bonnes performances grâce au <span class="caps">JIT</span> et ses <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays">Arrays spécialisés</a>. Nodejs l’a définitivement rendu crédible hors du browser, mais il est dur de trouver des applications embarquant du Javascript.</p>
<ul>
<li><a href="http://electron.atom.io">Electron</a> est un framework javascript pour créer des applications pour le “desktop”, Javascript y est l’hôte, et non l’invité.</li>
<li>Nginx a du créer son propre interpréteur Javascript : <a href="https://www.nginx.com/blog/nginscript-why-our-own-javascript-implementation/">nginScript</a> car les usages prévus par V8 ou Gecko sont trop spécialisés.</li>
</ul>
<p>Comme langage autonome, Node a surpris tout le monde en démontrant l’efficacité de l’approche asynchrone et la maturité des interpréteurs Javascripts modernes. Par contre, il n’a rien réglé sur la confusion du code et la fiabilité en production. Node a effectué un débroussaillage salutaire, mais sans arriver à atteindre un niveau de sérieux suffisant. Il faut voir ce que va apporter <a href="https://nodejs.org/en/docs/es6/"><span class="caps">ES6</span></a>, mais il reste encore beaucoup de travail.</p>
<h3>Golang</h3>
<p>Conçu pour remplacer le C++ moche, Golang a, contre toutes attentes, été massivement adopté par les scripteurs (python, ruby…) lassés du code linéaire, mou et imprévisible que permet leurs langages pourtant si véloces.</p>
<p>Golang a des partis pris un peu abrupts :</p>
<ul>
<li>Golang n’invente rien, n’amène aucun nouveau concept. Pas de modèle objet complexe ni même d’exceptions. Quelqu’un sachant déjà coder ne sera pas perdu en découvrant Golang.</li>
<li>Golang est compilé. Le typage est fort, et même le style de syntaxe est imposé.</li>
<li>Golang ressemble à du scripting, la syntaxe est expressive, la compilation rapide, il n’y a pas de gestion de mémoire grâce au ramasse-miette, les bibliothèques systèmes sont généreuses.</li>
<li>La parallélisation et l’asynchrone sont prévus dés le départ.</li>
<li>Golang utilise les types et les structs du C, appeler du code C est triviale avec <a href="https://golang.org/cmd/cgo/">cgo</a>.</li>
</ul>
<p>Avec ces choix et ces contraintes, Golang permet de développer efficacement des applications à tous les niveaux du gateau logiciel :
des couches basses (comme le fameux <a href="https://www.docker.com">Docker</a>), des applications distribuées, des bases de données, du middleware, des applications métiers.</p>
<p>Golang fait sauter la distinction entre le code système et le code métier.</p>
<p>Malgré l’absence de modules dynamiques et de <a href="https://en.wikipedia.org/wiki/Generic_programming">generics</a>, Golang fournit des bibliothèques de grande qualitées :
beaucoup d’applications Golang ne sont en fait que des gros frameworks permettant de composer une application, et pas simplement la configurer.</p>
<p>Ces deux derniers points sont tout simplement une révolution.</p>
<p>Avant, les gens se contentaient de tricoter avec des outils existants, où s’arrêtait en route, faute d’outils.</p>
<p>La fin de la loi de Moore, la maturité du cloud et de la virtualisation, les exigences de résiliance, tout pousse les applications à travailler en environement distribué.
Ce type d’environnement amène avec lui son lot de complexité, qui nécessite des bibliothèques robustes, et plus seulement des services fiables.</p>
<p>Java a bien prouvé qu’il était possible de cloner les outils révélés par Google (MapReduce, BigTable, Chubby, Borg, Dremel…), mais voilà, Java, quoi.
Le ticket d’entrée est élevé, que ce soit en compétence technique et en nombre de serveurs.
C’est rentable pour du big data, qui nécessite déjà des machines par palettes entières, mais c’est rapidement impressionnant pour des applications distribuées plus classiques. Les gros fournisseurs de clouds fournissent certains des ses outils, mais comme service, en mode boite noire, la vengeance du logiciel propriétaire contre l’hégémonie de Linux.</p>
<p>Golang a montré que l’on pouvait créer ces outils de coordinations, simplement, et de manière composable, pour ensuite créer des applications distribuées.
Cette zone floue entre bibliothèque et service semble impossible en C/C++, un problème de packaging, de divergence d’approche.
C’est dommage, des applications distribuées existent, comme les bases de données
<a href="http://www.scylladb.com">ScyllaDB</a>,
<a href="http://rethinkdb.com">RethinkDB</a>,
<a href="http://www.aerospike.com">Aerospike</a> …</p>
<p>Ces applications sont prometteuses, mais sont développés en autarcie, pas moyen d’en recomposer simplement de nouvelles à partir du travail existant.</p>
<p>De son côté Golang propose des briques de base permettant d’agir sur l’ensemble de la stack.</p>
<p>Les bibliothèques de Golang permettent un accès complet aux couches basses, mais le réseau est sa grande force,
il gère différents protocoles réseau, au-delà de l’inévitable <span class="caps">HTTP</span> :
<a href="https://godoc.org/golang.org/x/net/http2">http2</a>,
<a href="https://godoc.org/golang.org/x/crypto/ssh">ssh</a>,
<a href="https://golang.org/pkg/crypto/tls/">tls</a>,
<a href="https://github.com/miekg/dns">dns</a>… ce qui permet d’agir à chacune des étapes du protocole.
Des outils complémentaires permettent de simplifier l’utilisation de protocoles classiques, comme
<a href="https://github.com/vulcand/oxy">oxy</a> pour créer des proxy http, <a href="https://github.com/hashicorp/yamux">yamux</a> pour multiplexer des connexions <span class="caps">TCP</span>.</p>
<p>D’autres bibliothèques réseaux, elles, fournissent des outils de haut niveau, comme la coordination sans maitre : <a href="https://github.com/hashicorp/raft">raft</a> (Paxos étant jugé trop complexe) ou la communication par potin (gossip) : <a href="https://github.com/hashicorp/serf">serf</a>, ou le déverrouillage par double clef : <a href="https://github.com/cloudflare/redoctober">red october</a>.</p>
<p>Par dessus sont construit des outils réseaux de haut niveau : <a href="http://vulcand.github.io">Vulcand</a>, <a href="https://coreos.com/etcd/">etcd</a>, <a href="https://www.consul.io">Consul</a>…</p>
<p>Il faut être honnête et reconnaitre que l’écosystème Java fournit des outils similaires avec <a href="https://zookeeper.apache.org">Zoo keeper</a>, <a href="http://akka.io">Akka</a>, <a href="http://netty.io">Netty</a>, <a href="https://github.com/Netflix/Hystrix">Hystrix</a>, <a href="https://github.com/Netflix/zuul">Zuul</a>…, mais on est clairement à une autre échelle, et Java est une famille envahissante.</p>
<p>Golang profite d’une bienveillante neutralité, faisant fi des guerres des langages historiques.</p>
<p>Jusqu’à présent, il y avait une séparation claire entre langages système et langage métier.
Cette frontière est fortement remise en cause avec Golang, et je suis curieux de voir ce que va pouvoir apporter <a href="https://www.rust-lang.org">Rust</a> dans cette zone intermédiaire.</p>Fighting with EventMachine2015-06-16T21:41:00+02:002015-06-16T21:41:00+02:00Mathieu Lecarmetag:blog.garambrogne.net,2015-06-16:/fighting-with-eventmachine.html<p>Refurbished post about async Ruby : event machine.</p><p>Here is one of my old post (late 2011) from <a href="http://dev.af83.com/2011/09/20/fighting-with-eventmachine.html">dev.af83.com</a> about EventMachine, when I worked with ruby. <span class="caps">TL</span>;<span class="caps">DR</span> without <a href="https://en.wikipedia.org/wiki/Futures_and_promises">futures/promises</a> or <a href="https://en.wikipedia.org/wiki/Fiber_(computer_science)">fibers</a> async languages are doomed.</p>
<blockquote>
<p>If it’s your first night with EventMachine, you have to fight.</p>
</blockquote>
<h2>EventMachine in a nutshell</h2>
<p>EventMachine is an implementation of the <a href="http://en.wikipedia.org/wiki/Reactor_pattern">reactor pattern</a>. It’s not Twisted, nor Node, nor Erlang. It follows the Ruby way, hence you have to follow the EventMachine way.</p>
<h2>The event-loop pattern is now mainstream</h2>
<p>The event-loop pattern is quite simple. The actions are no more systematically sequential. It’s not thread-based: there is only one thread for the event-loop, therefore execution is still sequential. This enforce a unique context, accessing variables is still atomic (no concurrency).</p>
<p>Classical asynchronous actions are waiting actions, such as IOs (network or disk access). This makes long-lived actions (<span class="caps">CPU</span> intensive) to jam the flow. Async actions are detached from the flow, and a callback is called when it finished.
If you don’t take care, you will have stairs of block (one for each callback) and troubles for handling errors, the now infamous <em>callback spaghetti</em>.</p>
<h2>The EventMachine way</h2>
<div class="highlight"><pre><span></span><code><span class="no">EventMachine</span><span class="o">.</span><span class="n">run</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="n">http</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">EventMachine</span><span class="o">::</span><span class="no">HttpRequest</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">"http://google.com"</span><span class="p">)</span><span class="o">.</span><span class="n">get</span>
<span class="w"> </span><span class="n">http</span><span class="o">.</span><span class="n">callback</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="nb">puts</span><span class="w"> </span><span class="s2">"got a response"</span>
<span class="w"> </span><span class="nb">puts</span><span class="w"> </span><span class="n">http</span><span class="o">.</span><span class="n">response</span>
<span class="w"> </span><span class="no">EventMachine</span><span class="o">.</span><span class="n">stop</span>
<span class="w"> </span><span class="k">end</span>
<span class="w"> </span><span class="nb">puts</span><span class="w"> </span><span class="s2">"too early"</span>
<span class="k">end</span>
</code></pre></div>
<p>The first block handles the reactor, the second one is the callback.</p>
<p>What if I want to fetch more than one url?</p>
<div class="highlight"><pre><span></span><code><span class="no">EventMachine</span><span class="o">.</span><span class="n">run</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="n">google</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">EventMachine</span><span class="o">::</span><span class="no">HttpRequest</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">"http://google.com"</span><span class="p">)</span><span class="o">.</span><span class="n">get</span>
<span class="w"> </span><span class="n">google</span><span class="o">.</span><span class="n">callback</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="nb">puts</span><span class="w"> </span><span class="n">google</span><span class="o">.</span><span class="n">response</span>
<span class="w"> </span><span class="n">yahoo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">EventMachine</span><span class="o">::</span><span class="no">HttpRequest</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">"http://yahoo.com"</span><span class="p">)</span><span class="o">.</span><span class="n">get</span>
<span class="w"> </span><span class="n">yahoo</span><span class="o">.</span><span class="n">callback</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="nb">puts</span><span class="w"> </span><span class="n">yahoo</span><span class="o">.</span><span class="n">response</span>
<span class="w"> </span><span class="no">EventMachine</span><span class="o">.</span><span class="n">stop</span>
<span class="w"> </span><span class="k">end</span>
<span class="w"> </span><span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p>As the event-loop pattern forces you to nest callbacks, you’re building an infinite staircase. It also makes actions sequential, but you may don’t want google to answer before yahoo. You also had to add one level of indentation per url.</p>
<p>A workaround is to handle it by hand:</p>
<div class="highlight"><pre><span></span><code><span class="no">EventMachine</span><span class="o">.</span><span class="n">run</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="n">urls</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sx">%w{ google.com yahoo.com}</span>
<span class="w"> </span><span class="n">finished</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">urls</span><span class="o">.</span><span class="n">length</span>
<span class="w"> </span><span class="n">responses</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">urls</span><span class="o">.</span><span class="n">map</span><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="o">|</span><span class="n">url</span><span class="o">|</span>
<span class="w"> </span><span class="n">response</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">EventMachine</span><span class="o">::</span><span class="no">HttpRequest</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">"http://</span><span class="si">#{</span><span class="n">url</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span><span class="o">.</span><span class="n">get</span>
<span class="w"> </span><span class="n">response</span><span class="o">.</span><span class="n">callback</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="n">finished</span><span class="w"> </span><span class="o">-=</span><span class="w"> </span><span class="mi">1</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">finished</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span>
<span class="w"> </span><span class="nb">puts</span><span class="w"> </span><span class="n">responses</span><span class="o">.</span><span class="n">map</span><span class="p">{</span><span class="o">|</span><span class="n">response</span><span class="o">|</span><span class="w"> </span><span class="n">response</span><span class="o">.</span><span class="n">response</span><span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="no">EventMachine</span><span class="o">.</span><span class="n">stop</span>
<span class="w"> </span><span class="k">end</span>
<span class="w"> </span><span class="k">end</span>
<span class="w"> </span><span class="n">response</span>
<span class="w"> </span><span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p>Don’t you love flashbacks in movies? This way, url fetches are parallel, and you’ve even get a callback when <em>all</em> actions are finished.</p>
<p>EventMachine provides a helper for such common patterns:</p>
<div class="highlight"><pre><span></span><code><span class="no">EventMachine</span><span class="o">.</span><span class="n">run</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="n">urls</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sx">%w{ google.com yahoo.com}</span>
<span class="w"> </span><span class="no">EventMachine</span><span class="o">::</span><span class="no">Iterator</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">urls</span><span class="p">,</span><span class="w"> </span><span class="n">urls</span><span class="o">.</span><span class="n">length</span><span class="p">)</span><span class="o">.</span><span class="n">map</span><span class="p">(</span>
<span class="w"> </span><span class="nb">proc</span><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="o">|</span><span class="n">url</span><span class="p">,</span><span class="w"> </span><span class="n">iter</span><span class="o">|</span>
<span class="w"> </span><span class="n">http</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">EventMachine</span><span class="o">::</span><span class="no">HttpRequest</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">"http://</span><span class="si">#{</span><span class="n">url</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span><span class="o">.</span><span class="n">get</span>
<span class="w"> </span><span class="n">http</span><span class="o">.</span><span class="n">callback</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">iter</span><span class="o">.</span><span class="n">return</span><span class="w"> </span><span class="n">http</span><span class="o">.</span><span class="n">response</span><span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">end</span><span class="p">,</span>
<span class="w"> </span><span class="nb">proc</span><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="o">|</span><span class="n">responses</span><span class="o">|</span>
<span class="w"> </span><span class="nb">puts</span><span class="w"> </span><span class="n">responses</span>
<span class="w"> </span><span class="no">EventMachine</span><span class="o">.</span><span class="n">stop</span>
<span class="w"> </span><span class="k">end</span>
<span class="w"> </span><span class="p">)</span>
<span class="k">end</span>
</code></pre></div>
<p>It is the official way, but <span class="caps">IMO</span> it turned out not as elegant as it could have been. The first <code>Proc</code> handles the iterations and <code>return</code> the response, the second <code>Proc</code> is the <em>finished</em> callback.</p>
<p>Em-http-request provides a specific object for doing batch jobs in a simpler form:</p>
<div class="highlight"><pre><span></span><code><span class="no">EventMachine</span><span class="o">.</span><span class="n">run</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="n">multi</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">EventMachine</span><span class="o">::</span><span class="no">MultiRequest</span><span class="o">.</span><span class="n">new</span>
<span class="w"> </span><span class="n">multi</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="ss">:google</span><span class="p">,</span><span class="w"> </span><span class="no">EventMachine</span><span class="o">::</span><span class="no">HttpRequest</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">"http://google.com"</span><span class="p">)</span><span class="o">.</span><span class="n">get</span><span class="p">)</span>
<span class="w"> </span><span class="n">multi</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="ss">:yahoo</span><span class="p">,</span><span class="w"> </span><span class="no">EventMachine</span><span class="o">::</span><span class="no">HttpRequest</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">"http://yahoo.com"</span><span class="p">)</span><span class="o">.</span><span class="n">get</span><span class="p">)</span>
<span class="w"> </span><span class="n">multi</span><span class="o">.</span><span class="n">callback</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="nb">puts</span><span class="w"> </span><span class="n">multi</span><span class="o">.</span><span class="n">responses</span><span class="o">[</span><span class="ss">:callback</span><span class="o">]</span>
<span class="w"> </span><span class="nb">puts</span><span class="w"> </span><span class="n">multi</span><span class="o">.</span><span class="n">responses</span><span class="o">[</span><span class="ss">:errback</span><span class="o">]</span>
<span class="w"> </span><span class="no">EventMachine</span><span class="o">.</span><span class="n">stop</span>
<span class="w"> </span><span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<p>Short, elegant, and specific, isn’t it? As nobody cares about the specific requests’ callbacks, they are out. The response is <em>deferrable</em> and MultiRequest collects the spreaded responses. You don’t have to handle parallel or sequential multi-actions in your own <span class="caps">API</span>. It’s really a job for EventMachine (or Synchrony)!</p>
<h3>The mysterious Deferrable behavior</h3>
<p>As seen above, an async action returns a deferrable response, which is a mysterious object bound a callback. Why not a classical <code>Proc</code>? Why not a quick and dirty <code>DefaultDeferrable</code> as you may have seen in <span class="caps">EM</span>’s doc?</p>
<p>The callback is just a trigger, it is not responsible for giving the answer back: the deferrable object <strong>is</strong> the answer.</p>
<p>But Deferrable is a complex answer, providing a state, two (actual) triggers, a success callback and a error callback; and an optional timeout.</p>
<p>In this pattern, an async function returns a response immediately (a Deferrable one), but the actual <em>value</em> of the response will be available later. Think of it as a closed box that will open itself, later on.</p>
<p>Using <span class="caps">API</span> functions with a block is just a syntactic sugar, but don’t forget to provide both a callback and an errorback, and more importantly, you must return a Deferrable.</p>
<h3>Example of an <span class="caps">API</span> based on EventMachine</h3>
<p>Let’s take a simple example: airports are required to freely give weather information (no authentication layer to handle). So here is the first weather webservice I found in Google. It’s <span class="caps">PHP</span>-based on the server side, response serialization is done the oldschool way, but it’s free indeed and <em>it just works</em>.</p>
<div class="highlight"><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">Weather</span>
<span class="w"> </span><span class="kp">include</span><span class="w"> </span><span class="no">EventMachine</span><span class="o">::</span><span class="no">Deferrable</span>
<span class="w"> </span><span class="kp">attr_reader</span><span class="w"> </span><span class="ss">:code</span><span class="p">,</span><span class="w"> </span><span class="ss">:date</span><span class="p">,</span><span class="w"> </span><span class="ss">:temp</span><span class="p">,</span><span class="w"> </span><span class="ss">:humidity</span><span class="p">,</span><span class="w"> </span><span class="ss">:wind_speed</span>
<span class="w"> </span><span class="k">def</span><span class="w"> </span><span class="n">_feed</span><span class="p">(</span><span class="n">code</span><span class="p">,</span><span class="w"> </span><span class="n">raw</span><span class="p">)</span>
<span class="w"> </span><span class="n">rough</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">raw</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">'#'</span><span class="p">)</span>
<span class="w"> </span><span class="vi">@code</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">code</span>
<span class="w"> </span><span class="vi">@date</span><span class="p">,</span><span class="w"> </span><span class="vi">@temp</span><span class="p">,</span><span class="w"> </span><span class="vi">@humidity</span><span class="p">,</span><span class="w"> </span><span class="vi">@wind_speed</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rough</span><span class="o">[</span><span class="mi">0</span><span class="o">..</span><span class="mi">3</span><span class="o">]</span>
<span class="w"> </span><span class="k">end</span>
<span class="w"> </span><span class="k">def</span><span class="w"> </span><span class="nf">to_s</span>
<span class="w"> </span><span class="s2">"<weather: code="</span"><span class="si">#{</span><span class="vi">@code</span><span class="si">}</span><span class="s2"> temp=</span><span class="si">#{</span><span class="vi">@temp</span><span class="si">}</span><span class="s2"> humidity=</span><span class="si">#{</span><span class="vi">@humidity</span><span class="si">}</span><span class="s2"> wind_speed=</span><span class="si">#{</span><span class="vi">@wind_speed</span><span class="si">}</span><span class="s2">>"</span>
<span class="w"> </span><span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span><span class="w"> </span><span class="nf">meteo</span><span class="p">(</span><span class="n">icao</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">block</span><span class="p">)</span>
<span class="w"> </span><span class="n">response</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">Weather</span><span class="o">.</span><span class="n">new</span>
<span class="w"> </span><span class="n">response</span><span class="o">.</span><span class="n">callback</span><span class="p">(</span><span class="o">&</span><span class="n">block</span><span class="p">)</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nb">block_given?</span>
<span class="w"> </span><span class="n">http</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">EventMachine</span><span class="o">::</span><span class="no">HttpRequest</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">"http://stationmeteo.meteorologic.net/metar/your-metar.php?type=mes&icao=</span><span class="si">#{</span><span class="n">icao</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span><span class="o">.</span><span class="n">get</span>
<span class="w"> </span><span class="n">http</span><span class="o">.</span><span class="n">errback</span><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="o">|</span><span class="n">error</span><span class="o">|</span>
<span class="w"> </span><span class="n">response</span><span class="o">.</span><span class="n">fail</span><span class="w"> </span><span class="ss">:system</span><span class="p">,</span><span class="w"> </span><span class="n">error</span>
<span class="w"> </span><span class="k">end</span>
<span class="w"> </span><span class="n">http</span><span class="o">.</span><span class="n">callback</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="n">response</span><span class="o">.</span><span class="n">_feed</span><span class="w"> </span><span class="n">icao</span><span class="p">,</span><span class="w"> </span><span class="n">http</span><span class="o">.</span><span class="n">response</span>
<span class="w"> </span><span class="n">response</span><span class="o">.</span><span class="n">succeed</span><span class="w"> </span><span class="n">response</span><span class="w"> </span><span class="c1">#giving itself as first argument give the choice to how handle answer</span>
<span class="w"> </span><span class="k">end</span>
<span class="w"> </span><span class="n">response</span>
<span class="k">end</span>
</weather:></span></code></pre></div>
<p>And now an example with EventMachine’s iterator:</p>
<div class="highlight"><pre><span></span><code><span class="no">EventMachine</span><span class="o">.</span><span class="n">run</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="c1"># Airport of Lyon, Paris and Marseille; US airports don't seems to work.</span>
<span class="w"> </span><span class="no">EventMachine</span><span class="o">::</span><span class="no">Iterator</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="sx">%w{LFRS LFLL LFML}</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="o">.</span><span class="n">map</span><span class="p">(</span>
<span class="w"> </span><span class="nb">proc</span><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="o">|</span><span class="n">airport</span><span class="p">,</span><span class="w"> </span><span class="n">iter</span><span class="o">|</span>
<span class="w"> </span><span class="n">meteo</span><span class="w"> </span><span class="n">airport</span><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="o">|</span><span class="n">score</span><span class="o">|</span>
<span class="w"> </span><span class="n">iter</span><span class="o">.</span><span class="n">return</span><span class="w"> </span><span class="n">score</span>
<span class="w"> </span><span class="k">end</span>
<span class="w"> </span><span class="k">end</span><span class="p">,</span>
<span class="w"> </span><span class="nb">proc</span><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="o">|</span><span class="n">scores</span><span class="o">|</span>
<span class="w"> </span><span class="nb">puts</span><span class="w"> </span><span class="n">scores</span>
<span class="w"> </span><span class="no">EM</span><span class="o">.</span><span class="n">stop</span>
<span class="w"> </span><span class="k">end</span>
<span class="w"> </span><span class="p">)</span>
<span class="k">end</span>
</code></pre></div>
<h3>Fiber, or the hidden-threaded way</h3>
<p>It can be funny to fight with callback in daemon project, at least for some time. But it may come a time when you just yearn for sequential programming. Still you want to use this cool framework wich uses EventMachine to handle lots of parallels connections. How would you do then?</p>
<p>You could strive to parrallelize a few requests, but most of the time, you just want to be able to describe your needs in a sequential fashion, <em>everywhere</em>.</p>
<p>Fiber, shipped with Ruby 1.9, is the answer. A Fiber waits for the response, for you, pausing the execution in the middle of a flat code chunk using <em>#yield</em> and resuming execution at any time with <em>#resume</em>. We are using Synchrony here:</p>
<div class="highlight"><pre><span></span><code><span class="k">alias</span><span class="w"> </span><span class="ss">:ameteo</span><span class="w"> </span><span class="ss">:meteo</span>
<span class="k">def</span><span class="w"> </span><span class="nf">meteo</span><span class="p">(</span><span class="n">icao</span><span class="p">)</span>
<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">Fiber</span><span class="o">.</span><span class="n">current</span>
<span class="w"> </span><span class="n">conn</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ameteo</span><span class="w"> </span><span class="n">icao</span>
<span class="w"> </span><span class="n">conn</span><span class="o">.</span><span class="n">callback</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">|</span><span class="n">resp</span><span class="o">|</span><span class="w"> </span><span class="n">f</span><span class="o">.</span><span class="n">resume</span><span class="p">(</span><span class="n">resp</span><span class="p">)</span><span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">conn</span><span class="o">.</span><span class="n">errback</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">|*</span><span class="n">errors</span><span class="o">|</span><span class="w"> </span><span class="n">f</span><span class="o">.</span><span class="n">resume</span><span class="p">(</span><span class="o">*</span><span class="n">errors</span><span class="p">)</span><span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="no">Fiber</span><span class="o">.</span><span class="n">yield</span>
<span class="k">end</span>
</code></pre></div>
<p>Usage is straightforward:</p>
<div class="highlight"><pre><span></span><code><span class="no">EventMachine</span><span class="o">.</span><span class="n">synchrony</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="nb">puts</span><span class="w"> </span><span class="n">meteo</span><span class="w"> </span><span class="s2">"LFRS"</span>
<span class="w"> </span><span class="nb">puts</span><span class="w"> </span><span class="n">meteo</span><span class="w"> </span><span class="s2">"LFML"</span>
<span class="w"> </span><span class="no">EventMachine</span><span class="o">.</span><span class="n">stop</span>
<span class="k">end</span>
</code></pre></div>
<p>Sequential actions are now, well, sequential. Revolutionary.</p>
<p>In order to transform you previously async methods into a Fiber-aware one, you may prefix the method with <em>a</em> (as in <em>a</em>sync), then wraps it within a fiber which will be resumed in the callbacks. It can be made systematic, have a look of how Synchrony monkey patches common libraries.</p>
<p>You could also explicitly ask for parallel actions, using an async variant of your code.</p>
<div class="highlight"><pre><span></span><code><span class="no">EventMachine</span><span class="o">.</span><span class="n">synchrony</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="n">responses</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">EventMachine</span><span class="o">::</span><span class="no">Synchrony</span><span class="o">::</span><span class="no">Iterator</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="sx">%w{LFRS LFLL LFML}</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="o">.</span><span class="n">map</span><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="o">|</span><span class="n">airport</span><span class="p">,</span><span class="w"> </span><span class="n">iter</span><span class="o">|</span>
<span class="w"> </span><span class="n">ameteo</span><span class="w"> </span><span class="n">airport</span><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="o">|</span><span class="n">response</span><span class="o">|</span>
<span class="w"> </span><span class="n">iter</span><span class="o">.</span><span class="n">return</span><span class="w"> </span><span class="n">response</span>
<span class="w"> </span><span class="k">end</span>
<span class="w"> </span><span class="k">end</span>
<span class="w"> </span><span class="nb">puts</span><span class="w"> </span><span class="n">responses</span>
<span class="w"> </span><span class="no">EventMachine</span><span class="o">.</span><span class="n">stop</span>
<span class="k">end</span>
<span class="no">EventMachine</span><span class="o">.</span><span class="n">synchrony</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="n">multi</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">EventMachine</span><span class="o">::</span><span class="no">Synchrony</span><span class="o">::</span><span class="no">Multi</span><span class="o">.</span><span class="n">new</span>
<span class="w"> </span><span class="n">multi</span><span class="o">.</span><span class="n">add</span><span class="w"> </span><span class="ss">:satolas</span><span class="p">,</span><span class="w"> </span><span class="n">ameteo</span><span class="p">(</span><span class="s2">"LFRS"</span><span class="p">)</span>
<span class="w"> </span><span class="n">multi</span><span class="o">.</span><span class="n">add</span><span class="w"> </span><span class="ss">:paris</span><span class="p">,</span><span class="w"> </span><span class="n">ameteo</span><span class="p">(</span><span class="s2">"LFLL"</span><span class="p">)</span>
<span class="w"> </span><span class="n">responses</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">multi</span><span class="o">.</span><span class="n">perform</span><span class="o">.</span><span class="n">responses</span><span class="o">[</span><span class="ss">:callback</span><span class="o">].</span><span class="n">values</span>
<span class="w"> </span><span class="nb">puts</span><span class="w"> </span><span class="n">responses</span>
<span class="w"> </span><span class="no">EventMachine</span><span class="o">.</span><span class="n">stop</span>
<span class="k">end</span>
</code></pre></div>Microservices : orchestrer, chorégraphier, visualiser et isoler2015-05-27T00:15:00+02:002015-05-27T00:15:00+02:00Mathieu Lecarmetag:blog.garambrogne.net,2015-05-27:/microservices-orchestrer-choregraphier-visualiser-isoler.html<p>Les microservices : un nouveau buzzword, de la formalisation de choses existantes, de nouveaux concepts, de nouvelles responsabilités et de nouveaux drames à découvrir.</p><h1>Microservices</h1>
<p>Le principe du microservce est simple : une application est composé de différents petits services, fortement découplés, et orientés métier. Ces services communiquent entre eux pour composer une application. L’application, naturellement distribuée, gagne en souplesse et en robustesse, l’utilisation des ressources est optimisée, les évolutions (changements et croissance) sont simplifiées.</p>
<p>Les microservices sont une nouvelle étape dans les architectures serveurs. Créées en opposition aux applications monolithiques, les microservices utilisent pleinement les possibilités du Cloud computing et des conteneurs.
Théorisés et mis en place par de très gros sites (Netflix, Twitter, Google …), les nouvelles abstractions, et les outils adéquats, qu’apportent les microservices sont maintenant utilisables par tout le monde.</p>
<p>Tout cela a bien sûr un coût : l’application est plus complexe, plus dure à tester et la gestion des latences devient une priorité. La robustesse n’existe que si les clients font l’effort d’implémenter une stratégie de tolérance aux pannes (refaire un appel échoué sur un nouveau noeud, gèrer les timeouts…).</p>
<h1>Routage</h1>
<p>Le point central de ce type d’application reste le routage, comme pour les classiques sites web.</p>
<p>Par contre, cette notion de routage est généralisée. Le traitement peut-être synchrone ou asynchrone, séquentiel ou parallèle, unitaire ou par lot, avec un ou plusieurs essais, ou un mix de tout ça.</p>
<p>Ces différentes approches existent actuellement de manière dispersée, avec le routage des applications webs, les services web, et les workers asynchrones avec ses files d’attente.</p>
<p>Une notion formelle de <span class="caps">RPC</span> peut être utilisé (<a href="https://thrift.apache.org/">thrift</a>, <a href="http://twitter.github.io/finagle/">finagle</a>…) mais une simple sérialisation (<span class="caps">JSON</span>, <a href="http://msgpack.org/">msgpack</a>, <a href="https://github.com/google/protobuf/">protobuff</a>…) avec du classique <span class="caps">REST</span>, ou un serveur de message (<a href="https://www.amqp.org/"><span class="caps">AMQP</span></a>, <a href="https://github.com/bitly/nsq"><span class="caps">NSQ</span></a>, Redis…) fonctionne tout aussi bien.</p>
<p>L’abstraction de message, bien connu dans le protocole <span class="caps">HTTP</span>, permet d’enrichir, de restreindre, de sécuriser, d’organiser, de compresser, de surveiller les échanges de messages indépendamment de leur contenu et de manière transparente pour l’application. Des outils classiques et éprouvés comme HAproxy ou Nginx ont tout à fait leur place dans le nouveau monde des microservices.</p>
<h1>Dynamisme</h1>
<p>Le découplage et la communication par message permettent de distribuer simplement les services (en faisant varier le nombre d’instances d’un même service). De manière similaire un service crashé peut être remplacé sans heurt. Les mises à jour peuvent être roulantes, ou même atomiques en refusant les nouvelles requêtes, attendant la fin de requêtes en cours, avant de basculer le routage sur la nouvelle version du service. Des choses plus complexes, comme du A/B testing sont réalisable simplement. </p>
<p>Le routage peut facilement devenir dynamique et même réactif, utilisant le concept de découvertes de service.</p>
<p>Pour garantir un tant soit peu de cohérence, il est possible d’utiliser un coordinateur comme le tant redouté <a href="https://zookeeper.apache.org">ZooKeeper</a>, ou une de ses alternatives comme <a href="https://github.com/coreos/etcd">Etcd</a> ou <a href="https://www.serfdom.io">Serf</a>.</p>
<p>Des outils de plus haut niveau se basent sur ces outils, comme <a href="https://consul.io/">consul</a>, <a href="https://github.com/coreos/fleet">fleet</a>, <a href="https://vulcand.io/">vulcan</a> ou <a href="https://github.com/Netflix/eureka">Eureka</a>.</p>
<p>Crée à l’origine comme base clef/valeur distribuée pour mettre à disposition une configuration pour un cluster, les produits ont évolué pour proposer du pub/sub (les clients intéressés sont prévenus d’un changement de clef et déclenche une action), puis du <span class="caps">DNS</span> (en exposant les valeurs de la base) et même du monitoring (la disparition d’un service devient un évènement, qui sera traité).</p>
<p>Un nouvel antagonisme apparait avec le routage dynamique : le choix entre l’orchestration et la chorégraphie. L’orchestration déclare les différentes routes de manière explicite, la chorégraphie donne des instructions pour ensuite laisser les différents éléments se débrouiller.</p>
<p>Pour valider la résilience de l’approche “chorégraphie”, il existe le mythique <a href="https://github.com/Netflix/SimianArmy/wiki/Chaos-Monkey">Chaos Monkey</a>, que seuls les plus braves osent l’utiliser en production.</p>
<h1>Inspecter</h1>
<p>Un service peut être composé d’un ou plusieurs services, eux-mêmes composés de services. Le client discutant avec une façade, il ne connait (ni se soucis) de l’implémentation du service. Cette approche aide au découplage des différents services, mais rend plus complexes le débug et l’optimisation des différentes latences.</p>
<p>Comme à chaque fois, la réponse à été donnée par Google il y a quelque temps (en 2010), sous la forme d’un white paper : <a href="http://research.google.com/pubs/pub36356.html">Dapper</a>. Comme souvent, une implémentation en Java en est faite, par Twitter, cette fois-ci : <a href="http://twitter.github.io/zipkin/">Zipkin</a>. Zipkin vise gros et il prévoit de travailler avec le reste de l’écosystème Twitter. Il existe heureusement un clone plus léger, <a href="https://github.com/sourcegraph/appdash">Appdash</a>, avec un protocole simple et bien spécifié.</p>
<p>Prévu initialement pour mesurer les latences sans perturber le service (avec de l’échantillonnage léger, 1 sur 1024), ces outils de tracing permettent aussi de voir simplement ce qu’il se passe, de repérer ce qu’il est pertinent de paralléliser, d’aider à optimiser et dimensionner.</p>
<p>Google insiste sur la notion d’ubiquité dans Dapper et instrumente tout son code à partir de quelques bibliothèques clefs (gestion de thread, du réseau…).</p>
<p>Avec du monkeypatch ou des hooks, il n’est pas compliqué d’instrumenter des frameworks <span class="caps">RPC</span> comme <a href="https://github.com/onefinestay/nameko">Nameko</a> : j’avais fait un <a href="https://gist.github.com/athoune/2542c2042d676764556f">prototype en monkeypatch</a>, j’ai eu droit à un commentaire proposant une meilleure approche avec un hook élégant.</p>
<p>Il est aussi possible de faire de l’instrumentation en boite noire avec des outils comme <a href="http://packetbeat.com/">Packet beat</a>.</p>
<h1>Visualiser</h1>
<p>L’ensemble des services forme des réseaux analysables et visualisables par des outils classiques comme <a href="https://networkx.github.io/">Networkx</a>. Ces réseaux peuvent être décrits de manière explicite, par introspection, ou en surveillant (en échantillonant) la communication engendrée par l’application ou des tests.</p>
<p>Plus sportif, il est possible de simuler et de tester le coté dynamique des routes avec <a href="https://github.com/adrianco/spigo">Spigo</a>, prometteur, mais pour l’instant très lié à l’écosystème de Netflix.</p>
<h1>Isoler</h1>
<p>Le découpage permet aussi d’isoler les services, pour simplifier la prise en main par différentes équipes, pour déployer chacun à son rythme.</p>
<p>Mais le découpage permet aussi de compartimenter l’accés aux ressources, pour éviter qu’un incident sur un service entraine l’ensemble et finisse par faire tomber l’application.</p>
<p>Le terme technique est disjoncteur. En cas d’abus, on coupe tout de suite, avant que ça casse tout. </p>
<p>Concrètement, une utilisation fine des timeouts permet d’écourter pas mal de drames.
Une allocation fixe des ressources permet de garantir que chaque service n’ira pas perturber son voisin. Les cgroups de Linux ont été conçues pour ça, et sont un des piliers de la conteneurisation.
Contraindre les accès aux disques durs et au réseau sera plus un poil plus complexe que le <span class="caps">CPU</span> et la <span class="caps">RAM</span>. Pour limiter la portée des bêtises (volontaire ou non), <a href="http://wiki.apparmor.net/index.php/Main_Page">apparmor</a> est simple à mettre en place.</p>
<p>Il est possible d’automatiser entièrement la mise à disposition de ressources matérielles avec <a href="http://mesos.apache.org/">Mesos</a>, mais le ticket d’entrée est conséquent, <a href="https://docs.docker.com/swarm/">Swarm</a> semble plus économe, mais reste encore très jeune.</p>
<p>Les outils de containerisation sont conçus pour restreindre l’utilisation des services, mais aussi pour mesurer leurs usages. Les outils de mesures sont en pleine effervescence, dispersés, mais <a href="https://github.com/google/cadvisor">cAdvisor</a> propose un produit complet, se connectant sur <a href="http://influxdb.com/">Influxdb</a> ou le très prometteur <a href="http://prometheus.io/">Prometheus</a>.</p>
<h1>La suite</h1>
<p>Les microservices sont bien entendu un buzzword de plus, mais ils existent bel et bien.
Pour l’instant peu formalisés, les microservices sont un ensemble de principe à suivre, pour généraliser et étendre l’architecture orientée service qui est actuellement la norme.
Les ennemis des microservices sont clairement identifiés (le couplage fort, les applications monolithiques, les latences, les problèmes de montée en puissance…), les drames possibles le sont un peu moins.</p>
<p>Toutes innovations amènent avec elles une catastrophe potentielle. Les gens qui ont fabriqué le Titanic ont aussi inventé le naufrage à grande échelle.
Il est donc important d’anticiper les drames potentiels pour ne pas se laisser surprendre, et de surveiller les erreurs que font les autres, pour éviter de les reproduire.</p>Un bac à sable pour Ruby2015-04-13T20:00:00+02:002015-04-13T20:00:00+02:00Mathieu Lecarmetag:blog.garambrogne.net,2015-04-13:/un-bac-a-sable-pour-ruby.html<p>Isoler du code ruby avec les outils des conteneurs.</p><h1>Un bac à sable pour Ruby</h1>
<p>Je ne sais plus trop comment cette histoire a commencé. Hum… une discussion engagée par un développeur de <a href="http://codepen.io">codepen.io</a> sur la possibilité d’utiliser des Dockers jetables pour “rendre” des fichiers <a href="http://haml.info/"><span class="caps">HAML</span></a>.</p>
<p><span class="caps">HAML</span> est un format de template pour Ruby très peu verbeux, basé sur l’indentation. Il permet, entre autres, de faire des choses dangereuses, comme <a href="http://haml.info/docs/yardoc/file.REFERENCE.html#ruby_evaluation">insérer du ruby dans le gabarit</a>. Cette option est débrayable, heureusement, mais cet outil n’a jamais été conçu dans une optique de sécurité, mais de rendre le développeur heureux. Comme tous les produits Ruby, en fait.</p>
<p>Proposer de rendre des fichiers <span class="caps">HAML</span> comme service web est donc dangereux, il ne doit pas être bien difficile de mettre le bazar en soumettant un <span class="caps">HAML</span> mal intentionné.</p>
<p>Dans l’absolu, je ne me sens pas super concerné par <span class="caps">HAML</span>, ni par les entrailles de Ruby. Mais le challenge est intéressant : proposer un bac à sable avec les outils modernes servant de base à la conteneurisation.</p>
<h2>Depuis l’intérieur de ruby</h2>
<p>Ruby est un langage interprété capable d’aller très loin dans les abstractions, les bidouilles et les astuces. Tout est permis, il n’y a aucune limite pour atteindre l’objectif sacré : “à la fin, le code est beau”, le but ultime du développeur Ruby.</p>
<p>Vouloir border l’exécution de code ruby est donc complètement utopiste, voire même insultant.</p>
<p>Il existe des outils liés à la <a href="https://ruby-hacking-guide.github.io/security.html">sécurité dans Ruby</a>, comme la variable $<span class="caps">SAFE</span>, mais ça semble être peu pratique, et <a href="https://bugs.ruby-lang.org/issues/8468">remis en cause dans les prochaines versions de Ruby</a>.</p>
<p>Il existe la gem <a href="https://github.com/tario/shikashi">shikashi</a> qui permet de faire des <code>eval</code> avec des listes blanches, mais pour l’utiliser, il faudrait patcher le code de la gem `haml.</p>
<p>Il y a visiblement des choses à faire dans Ruby même, mais bon, je n’ai pas super envie d’aller farfouiller dans ses entrailles, ni même beaucoup d’espoir de pouvoir y trouver quelque chose de déterministe ou systématique.</p>
<h2>Depuis l’extérieur de Ruby</h2>
<p>Docker a réussi à démocratiser la conteneurisation sur Linux, et à amener de la visibilité sur le sujet, beaucoup de visibilité.</p>
<p>Seul bémol, les conteneurs de Docker ne sont pas pour l’instant prévus pour isoler du code potentiellement agressif.
Une équipe a été montée pour travailler sur ce sujet, et RedHat s’y intéresse aussi.</p>
<p>L’isolation des process est confiée aux <a href="http://man7.org/linux/man-pages/man7/namespaces.7.html">namespaces</a>, une innovation apparue dans le kernel 3.8, il y a maintenant 2 ans, mais qui surtout est officiellement troués jusqu’à la version 3.14 du kernel. Pour rappel, Trusty, la version courante d’Ubuntu utilise un kernel 3.13.</p>
<p>Bien conscient du problème, Docker propose d’utiliser l’une des deux implémentations vedettes des <span class="caps">LSM</span> : Apparmor ou SELinux.
Je n’ai pas eu l’occasion de me documenter sur SELinux, et Apparmor a le bon gout d’être installé par défaut sur Ubuntu depuis des années (et Debian le gère sans soucis).
Pour l’instant, la documentation de Docker sur la sécurité est spartiate (les sources de <a href="https://github.com/docker/libcontainer">libcontainer</a> sont lisibles), et rien ne semble prévu pour demander à restreindre des accés à l’intérieur même du conteneur.</p>
<p>Github a réalisé un prototype pour emballer du Ruby générique dans un Docker : <a href="https://github.com/github/hoosegow">Hoosegow</a>. Ils donnent tous les liens vers les informations disponibles, mais font le pari de confier la sécurité à Docker.</p>
<h3>Découper en tranches</h3>
<p>Docker, c’est beaucoup de Kernel, un peu d’<span class="caps">API</span>, et pas mal de choix d’organisation (type de réseaux, système de fichiers en oignons, chemin des Cgroups …). Pour la partie isolation/coercition, le rôle est confié aux Namespaces et aux Cgroups, le tout emballé par Apparmor. Les Namespaces sont officiellement troués.
Deuxième soucis, Apparmor travaille avec des chemins complets, et Docker utilise un <span class="caps">UUID</span> au dernier moment, pour nommer le dossier racine du containers. Il y a donc un souci pour préparer le apparmor avant de pouvoir l’utiliser.</p>
<p>Docker fourni <code>nsinit</code>, un outil en ligne de commande pour coudre à la main des namespaces pour tailler sur mesure son propre conteneur. On ne peut pas tout à fait qualifier cet outil de grand public. Il m’a résisté. Je suis donc parti sur une solution simple, basée sur Apparmor et Cgroup, pour les Namespaces, on verra plus tard.</p>
<h3>Isoler un service</h3>
<p>La tactique est simple : isoler le code gérant le haml dans un serveur, et le mettre dans une boite qui interdit un maximum d’actions possibles :</p>
<ul>
<li>La boite est en lecture seule, sans réseau <span class="caps">IP</span>, sans <a href="http://linux.die.net/man/7/capabilities">capabilites</a>.</li>
<li>Seuls les fichiers nécessaires pour lancer le code ruby sont lisibles.</li>
<li>Le serveur va communiquer via une socket <span class="caps">UNIX</span>, seul élément accessible en écriture.</li>
<li>Le protocole n’utilise aucune sérialisation, juste des <a href="http://en.wikipedia.org/wiki/String_%28computer_science%29">Pascal strings</a> : une taille sur 4 octets, suivi du message.</li>
<li>Le client gère un timeout pour ne pas attendre un serveur bloqué.</li>
<li>Cgroup permet de limiter la mémoire, et la ration de <span class="caps">CPU</span> disponibles.</li>
</ul>
<h4>Bundler</h4>
<p>Bundler est utilisé pour installer les bibliothèques nécessaires de manière propre et déterministe. Le serveur sera lancé avec un <code>-I</code> pour ne pas devoir embarquer tout bundler dans Apparmor : pas de <code>bundle exec</code>. L’application a un shebang et un chemin en dur : <code>/opt/box/box</code> pour permettre l’interception par le démon de Apparmor.</p>
<h4>Apparmor</h4>
<p>La règle Apparmor est créée en ajoutant les fichiers nécessaires un par un, jusqu’à ce que le serveur se lance, puis que le client puisse lui parler.
C’est un peu laborieux, mais je suis moyennement convaincu par l’outillage actuel, avec l’activation d’un audit et surveillance automatique des logs.
On parle de 50 lignes de serveurs utilisant 1 bibliothèque et de 40 lignes de clients, rien de comparable avec la moindre application Rails.</p>
<h4>Eye</h4>
<p>L’application bénéficie d’un superviseur, <a href="https://github.com/kostya/eye">Eye</a>,qui se charge de lancer et relancer le service en cas d’incident. Pour l’instant, seul le service est isolé par Apparmor.</p>
<div class="highlight"><pre><span></span><code><span class="no">Eye</span><span class="o">.</span><span class="n">config</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="n">logger</span><span class="w"> </span><span class="s1">'/var/log/sandbox/eye.log'</span>
<span class="k">end</span>
<span class="no">Eye</span><span class="o">.</span><span class="n">application</span><span class="w"> </span><span class="s1">'sandbox'</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="n">working_dir</span><span class="w"> </span><span class="s1">'/var/run/box'</span>
<span class="w"> </span><span class="n">trigger</span><span class="w"> </span><span class="ss">:flapping</span><span class="p">,</span><span class="w"> </span><span class="ss">times</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span><span class="w"> </span><span class="ss">within</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="o">.</span><span class="n">minute</span><span class="p">,</span><span class="w"> </span><span class="ss">retry_in</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="o">.</span><span class="n">minutes</span>
<span class="w"> </span><span class="n">stdall</span><span class="w"> </span><span class="s1">'trash.log'</span><span class="w"> </span><span class="c1"># stdout,err logs for processes by default</span>
<span class="w"> </span><span class="c1"># eye daemonized process</span>
<span class="w"> </span><span class="n">process</span><span class="w"> </span><span class="ss">:box</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="n">pid_file</span><span class="w"> </span><span class="s1">'box.pid'</span><span class="w"> </span><span class="c1"># pid_path will be expanded with the working_dir</span>
<span class="w"> </span><span class="n">start_command</span><span class="w"> </span><span class="s1">'/usr/bin/ruby -I /opt/box/vendor/bundle/ruby/1.9.1/gems/ /opt/box/box'</span>
<span class="w"> </span><span class="n">daemonize</span><span class="w"> </span><span class="kp">true</span>
<span class="w"> </span><span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
<h4>Cgroups</h4>
<p>Un utilisateur est créé, pour profiter des droits <span class="caps">UNIX</span> classiques, et surtout pour pouvoir y accrocher des Cgroups. Il n’est pas nécessaire de dégainer des outils de haut niveau pour ça, Cgroups se configure avec de simples fichiers montés dans un point de montage de type <code>cgroup</code>.</p>
<h4>Init.d</h4>
<p>Le script init.d est laid comme tous les scripts init.d mais comme il est tombé en marche au bout de 10 minutes alors que la version upstart m’a résisté pendant tout le weekend, je ne critiquerai pas. L’abandon d’Upstart dans la prochaine version <span class="caps">LTS</span> d’Ubuntu est par contre une très bonne nouvelle. J’ai rarement vu un produit aussi pénible et incapable de dire où il a mal, puis qui se bloque au bout d’un moment, avec le reboot comme seule issue. La doc est pleine de promesses d’améliorations pour la prochaine release que personne ne verra jamais.</p>
<p>Le superviseur et le service tournent avec un utilisateur non privilégié lancé par un <code>start-stop-daemon</code>, init.d se charge de préparer le terrain puis d’attacher le service dans un cgroup.</p>
<div class="highlight"><pre><span></span><code>do_pre_start<span class="o">()</span>
<span class="o">{</span>
<span class="w"> </span>mkdir<span class="w"> </span>-p<span class="w"> </span>/run/box
<span class="w"> </span>chown<span class="w"> </span>box<span class="w"> </span>/run/box
<span class="w"> </span>mkdir<span class="w"> </span>-p<span class="w"> </span>/var/log/sandbox
<span class="w"> </span>chown<span class="w"> </span>box<span class="w"> </span>/var/log/sandbox
<span class="w"> </span>cgcreate<span class="w"> </span>-a<span class="w"> </span>box<span class="w"> </span>-t<span class="w"> </span>box<span class="w"> </span>-g<span class="w"> </span>memory,cpu:box
<span class="w"> </span><span class="c1"># 25%</span>
<span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="m">256</span><span class="w"> </span>><span class="w"> </span>/sys/fs/cgroup/cpu/box/cpu.shares
<span class="w"> </span><span class="c1"># 32Mo</span>
<span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="m">32000000</span><span class="w"> </span>><span class="w"> </span>/sys/fs/cgroup/memory/box/memory.limit_in_bytes
<span class="w"> </span>mkdir<span class="w"> </span>-p<span class="w"> </span>/opt/box/.eye
<span class="w"> </span>chown<span class="w"> </span>box<span class="w"> </span>/opt/box/.eye
<span class="o">}</span>
do_post_start<span class="o">()</span>
<span class="o">{</span>
<span class="w"> </span>sleep<span class="w"> </span><span class="m">3</span><span class="w"> </span><span class="c1"># Yes, this is ugly, it should be a loop</span>
<span class="w"> </span>cgclassify<span class="w"> </span>-g<span class="w"> </span>memory,cpu:box<span class="w"> </span><span class="sb">`</span>cat<span class="w"> </span>/run/box/box.pid<span class="sb">`</span>
<span class="o">}</span>
</code></pre></div>
<p>On retrouve la logique de poupées gigognes (les <a href="http://fr.wikipedia.org/wiki/Poupée_russe">matriochkas</a>), avec une séparation claire des responsabilités. L’init crée le contexte puis lance un superviseur, qui lance un service dans un contexte rigoriste. Ruby n’est jamais lancé avec l’utilisateur <code>root</code>.</p>
<p>Je n’ai aucune idée du niveau de sécurité qu’amène cette suite de bonne pratique, mais on est clairement au-dessus de pas mal de sites web.</p>
<h3>Améliorer</h3>
<p>Par principe, il faudrait mettre la boite dans une boite de virtualisation, utiliser un kernel patché avec GRSec, ajouté du log (<code>auditd</code> et <code>tcpspy</code>
) vers une machine distante, et attendre un drame pour ensuite corriger.</p>
<h3>Le code</h3>
<p>Le code est disponible sur github, avec une jolie licence <span class="caps">LGPL</span> : <a href="https://github.com/athoune/sandbox">Sandbox</a></p>L’échec de l’élastique comme un service2015-04-05T19:35:00+02:002015-04-05T19:35:00+02:00Mathieu Lecarmetag:blog.garambrogne.net,2015-04-05:/echec-elastique-comme-un-service.html<p>L’échec de l’élastique comme un service (et son renouveau)</p><h1>L’échec de l’élastique comme un service (et son renouveau)</h1>
<h2>Le Cloud est élastique</h2>
<p>Le Cloud n’a qu’une seule fonction intéressante : l’élasticité.
Tout le reste n’est que blabla commercial ou confusion sémantique.
Attention, je ne suis pas nostalgique du serveur cousu main, à l’ancienne.
Les offres d’hébergement sont en constante évolution et mettent à disposition plein de belles choses, mais ce ne sera que rarement du Cloud.</p>
<p>La définition du Cloud est pourtant simple : <em>Mise à disposition <span class="caps">DYNAMIQUE</span> de ressources via une <span class="caps">API</span> distante</em>.
Même <a href="http://fr.wikipedia.org/wiki/Cloud_computing">Wikipedia le dit</a>, enfin presque.
Je peux commander des ressources, et les utiliser dans les minutes qui suivent, en demander plus, puis les diminuer un peu plus tard.</p>
<p>Ces ressources peuvent être matérielles (bien que virtualisées) comme de la puissance de calcul ou du stockage, mais aussi de plus haut niveau, des services.</p>
<p>Le Cloud sait mettre à disposition des ressources de bas niveaux : <span class="caps">CPU</span>, <span class="caps">RAM</span>, réseau, disque… depuis maintenant presque 10 ans, grâce à la virtualisation.</p>
<p>Mais la virtualisation est beaucoup plus à l’aise pour dimensionner une machine à froid.
L’élasticité, ce serait pouvoir redimensionner certaines ressources à chaud, sans redémarrer. Techniquement, des choses sont faisables, mais il est très difficile de mettre à disposition un pool de ressources et de piocher dedans, l’unité de base reste la machine physique.</p>
<p>Il est beaucoup plus simple de travailler avec plusieurs machines (réelles ou non), et d’en ajouter ou d’en enlever, puis de demander à la partie applicative de gérer cette fluctuation en débranchant et rebranchant les différents éléments.</p>
<h2>Le web est élastique</h2>
<p>Le web se prête bien aux architectures distribuées dynamiques, avec son protocole sans état (en théorie).</p>
<p>La méthode immuable pour traiter les requêtes web est celle de l’aiguillage qui va demander le traitement de la demande a des workers.
Les workers peuvent être des process ou des threads sur une même machine, eux-mêmes répartis sur plusieurs serveurs.
<a href="http://www.haproxy.org">HAproxy</a> ou Nginx (plus faiblard), sont capable de répartir la charge entre plusieurs machines.</p>
<p>Le load-balancer devient une pièce maitresse des applications distribuées. Nginx évolue rapidement (principalement sur sa branche privatrice) pour reprendre sa place de proxy universelle, HAproxy va intégrer Lua, et des proxy applicatifs apparaissent, comme le maintenant historique <a href="https://github.com/hipache/hipache">Hipache</a> ou le prometteur <a href="http://www.vulcanproxy.com">Vulcan</a>.</p>
<p>Dire que le protocole <span class="caps">HTTP</span> est sans état est un mensonge, enfin, un raccourci. La notion de cookie puis celle de session sont apparues très vite, pour conserver l’état d’un client sur une période plus ou moins longue. Dans un environnement distribué, il faut pouvoir partager un état entre les workers, rôle traditionnel de <a href="http://memcached.org/">Memcache</a>, challengé par <a href="http://redis.io">Redis</a>.</p>
<p>De la même manière, il faut pouvoir gérer les uploads de fichiers de manière centralisée avec S3 et ses clones, ou le classique <span class="caps">NFS</span>.</p>
<p>Pour la persistance, les inébranlables bases de données relationnelles ont encore largement leur place, challengée par la vague du NoSQL, plus ou moins fiable, mais tellement plus adapté au redimensionnement.</p>
<p>Pour ceux qui ont encore faim, il est possible de gérer tout ça de manière dynamique.</p>
<p>Tout ça pour atteindre le Graal de l’élasticité.</p>
<h2>Élastique comme un service</h2>
<p>La complexité est quand même impressionnante pour un besoin que beaucoup de gens n’auront jamais.</p>
<p>Il y a eu une première vague de réponses pour simplifier l’accès à l’élastique : <a href="https://cloud.google.com/appengine/docs">Google App Engine</a>, puis <a href="http://aws.amazon.com/fr/elasticbeanstalk/">Elastic Beanstalk</a>.</p>
<p>Deux produits contraignants, qui, bien qu’utilisant des langages libres, enferment l’utilisateur dans une solution propriétaire.</p>
<p>Ces deux produits ont causé beaucoup de drames : des langages arbitrairement périmés, un environnement de dev ne ressemblant que très peu à la prod, des performances aléatoires, et surtout l’impossibilité d’utiliser des frameworks et des applications largement utilisées dans des environnements classiques. Une vraie catastrophe.</p>
<p>Et tout ça pour se faire siphonner sa carte bleue, si le site a la mauvaise idée de faire une belle audience. Le plafond de cout étant apparu sur le tard : “Attention, nous, on scale, mais pas ta carte bleue”.</p>
<h2>Le conteneur est élastique</h2>
<p>Donc l’approche bas-niveau ne suffit pas, et les approches de trop haut niveau sont encore pires. Il faut donc voir ce qui est faisable avec une approche plus modérée.</p>
<p>Les conteneurs, qui ne sont ni de la virtualisation, ni de l’applicatif, permettent d’embarquer une application avec tout ce dont elle a besoin, et de la lancer avec quelques paramètres dans un environnement maitrisé (accès aux ressources physiques, isolation). Le Cloud devient un simple pool de ressources dans lequel on va piocher en déployant ou déplaçant un conteneur là où il y a de la place.</p>
<p>Le conteneur peut être considéré comme <a href="https://fr.wiktionary.org/wiki/immutable">immutable</a> (en lecture seule), et il confiera ses besoins d’<span class="caps">IO</span> à des services réseau, ou à un point de montage avec un système de fichier. Le réseau est clairement plus souple, mais des systèmes de fichiers modernes permettent de faire de belles choses (comme <span class="caps">ZFS</span> avec <a href="https://docs.clusterhq.com/en/0.3.2/advanced/clustering.html#minimal-downtime-volume-migration">le double push de Flocker</a>). </p>
<p>On a donc une application tout ce qu’il y a de plus classique, en <span class="caps">PHP</span>, Python ou je ne sais quoi, qui va se retrouver sur un Linux en lecture seul (ou presque), et dont on va pouvoir multiplier les instances. Se passer du stockage local est intégré dans beaucoup de framework maintenant :</p>
<ul>
<li><a href="https://django-storages.readthedocs.org/en/latest/index.html">Django Storage</a></li>
<li><a href="https://github.com/carrierwaveuploader/carrierwave">Carrierwave</a>, <a href="https://github.com/refile/refile">refile</a></li>
<li><a href="https://github.com/KnpLabs/Gaufrette">Gaufrette</a>, <a href="http://flysystem.thephpleague.com/">Flysystem</a></li>
<li>…</li>
</ul>
<p>Pour le reste, il suffit de brancher une poignée de services (session, caches, base de données…) et avoir une première étape de scaling sans douleur.</p>
<p>La seconde étape arrive quand la base de données commence à crier, et il va falloir négocier pour aller au-delà du combo index/cache/tuning pour avancer.
Mais bon, cette étape arrive lorsque l’on a un joli trafic, et il est toujours possible de négocier en force en ajoutant un esclave plus gros (plus de coeurs, plus de <span class="caps">RAM</span>, plus de <span class="caps">SSD</span>…), et de le promouvoir, pour faire du scaling vertical.</p>
<p>Scaler la couche applicative en utilisant des conteneurs est la voie dans laquelle s’engagent (à fond) les gros du Cloud, avec des solutions ouvertes ou non :</p>
<ul>
<li><a href="http://kubernetes.io/">Kubernetes</a> (Google)</li>
<li><a href="http://aws.amazon.com/ecs/"><span class="caps">ECS</span></a> (Amazon)</li>
<li><a href="https://www.joyent.com/about/press/joyent-launches-triton-elastic-container-infrastructure">Triton</a> (Joyent)</li>
<li><a href="http://mesos.apache.org/">Mesos</a> (<span class="caps">UC</span> Berkeley et Twitter)</li>
<li>…</li>
</ul>
<p>Kubernetes, qui a le bon gout d’être libre, déchaine l’enthousiasme.
Red Hat se raccroche aux branches et profites de Kubernetes pour faire basculer vers les conteneurs son <a href="http://www.openshift.org/">Open Shift</a> pour la version3 en préparation.</p>
<p>Les conteneurs posent encore beaucoup de questions, et des startups proposent des réponses, comme Flocker de <a href="https://clusterhq.com/">ClusterHQ</a> pour avoir de la persistance qui peut migrer.
<a href="https://coreos.com/">CoreOS</a> continue de taper tous azimuts.
<a href="https://hashicorp.com/">HashiCorp</a> se contentait d’une gestion opportuniste des conteneurs, mais c’est en train de bouger. <a href="https://terraform.io/">Terraform</a> dans sa <a href="https://hashicorp.com/blog/terraform-0-4.html">version 0.4</a> gère Docker, et j’ai hâte de voir comment <a href="https://www.consul.io/">Consul</a> et les autres outils vont s’adapter.</p>
<p>La fusion du Cloud et des conteneurs est en train de se faire, là, maintenant. Il y aura des nouveautés, et des morts. Cette fusion amène de nouvelles réponses, et tout autant de questions. Rendre possible l’élasticité de la partie applicative fait partie des réponses apportées par la combinaison du Cloud et des conteneurs.</p>La cuisson avec Docker2015-03-19T22:40:00+01:002015-03-19T22:40:00+01:00Mathieu Lecarmetag:blog.garambrogne.net,2015-03-19:/la-cuisson-avec-docker.html<p>Préparer une application avec Docker avant sa mise en production</p><h1>Préparer une application</h1>
<p>Même si l’on code massivement avec des langages de script pour ne pas passer par la case compilation, il y a forcément une étape de préparation avant de pouvoir livrer une application à partir de ses sources : télécharger les modules et compiler les potentiels binding en C, mais surtout gérer tout le bazar lié aux assets : JavaScript et <span class="caps">CSS</span>.</p>
<p>Cette étape de préparation avant déploiement porte en anglais le nom poétique de <em>baking</em> : on cuit le pain avant de le livrer.</p>
<p>Il n’y a aucun intérêt à réaliser cette étape sur le serveur de production qui a surement mieux à faire : c’est long, ça requière des outils plus ou moins louches, et si l’application est distribuée, il faut recommencer sur chaque instance.</p>
<h1>Préparer Isso</h1>
<p>Voici un exemple commenté de préparation et déploiement de <a href="http://posativ.org/isso/">Isso</a>, un clone libre et local de Disqus, qui permet d’embarquer des commentaires depuis du JavaScript, sur un site qui peut même être statique.
Le serveur est en Python, et il fournit du JavaScript à embarquer dans ses pages <span class="caps">HTML</span>.</p>
<p>Pour ne pas pourrir ma machine en installant des outils bizarres, j’utilise des containers <a href="http://docker.com">Docker</a> à usage unique.
Pour avoir un build sans trop de répétitions, j’utilise un simple Makefile. Oui, Makefile est vieux et moche, mais pas plus que bash, et il est un standard de fait. Pour des taches de tailles raisonnables, il reste tout à fait pertinent.</p>
<p>En montant un dossier local dans le container, le résultat du traitement se retrouvera dans ce dossier. La source et le résultat sont locaux, le traitement distant.</p>
<p>Cerise sur le gâteau, grâce à <a href="http://boot2docker.io">boot2docker</a>,l’action lancée depuis un Mac, va faire travailler un Linux, et surtout, produire du contenu pour Linux.</p>
<p>Pour éviter toutes surprises, je n’utilise que des images officielles de Docker, forcément basé sur Debian (et Wheezy, si possible).</p>
<h2>Cuire le Python</h2>
<p>Isso est packagé sur Pypi : il s’installe simplement avec pip.
Je sais installer python, mais comme la vie est courte, autant utiliser <a href="https://registry.hub.docker.com/_/python/">python:2-wheezy</a>.</p>
<p>Makefile pense à l’envers, en pensant prérequis et dépendances.
Pour avoir l’application Isso, j’ai besoin d’un python dans un virtualenv, qui a besoin d’un dossier app. Ce qui donne :</p>
<div class="highlight"><pre><span></span><code><span class="nf">app/bin/isso</span><span class="o">:</span><span class="w"> </span><span class="n">app</span>/<span class="n">bin</span>/<span class="n">python</span>
<span class="w"> </span>docker<span class="w"> </span>run<span class="w"> </span>--volume<span class="o">=</span><span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>/app:/app<span class="w"> </span>--rm<span class="w"> </span>--workdir<span class="o">=</span>/app<span class="w"> </span>python:2-wheezy<span class="w"> </span>/app/bin/pip<span class="w"> </span>install<span class="w"> </span>isso
<span class="nf">app/bin/python</span><span class="o">:</span><span class="w"> </span><span class="n">app</span>
<span class="w"> </span>docker<span class="w"> </span>run<span class="w"> </span>--volume<span class="o">=</span><span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>/app:/app<span class="w"> </span>--rm<span class="w"> </span>--workdir<span class="o">=</span>/app<span class="w"> </span>python:2-wheezy<span class="w"> </span>virtualenv<span class="w"> </span>.
<span class="nf">app</span><span class="o">:</span>
<span class="w"> </span>mkdir<span class="w"> </span>app
</code></pre></div>
<p>Comme je suis radin, j’efface (—rm) le container dès qu’il a exécuté son action.</p>
<h2>Cuire le JavaScript</h2>
<p>Le JavaScript, de nos jours, utilise plus de bazars que du C++ pour être utilisable. Isso est sobre, il se contente de <a href="http://bower.io">Bower</a> pour aller chercher les bibliothèques et leurs dépendances, <a href="https://github.com/mishoo/UglifyJS">Uglify</a> pour compacter, et on échappe à <a href="http://gruntjs.com">Grunt</a>, un Makefile est utilisé à la place.</p>
<div class="highlight"><pre><span></span><code><span class="nf">js</span><span class="o">:</span><span class="w"> </span><span class="n">src</span>/<span class="n">isso</span>/<span class="n">js</span>/<span class="n">embed</span>.<span class="n">js</span>
<span class="w"> </span>docker<span class="w"> </span>run<span class="w"> </span>--volume<span class="o">=</span><span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>/src:/src<span class="w"> </span>--rm<span class="w"> </span>--workdir<span class="o">=</span>/src<span class="w"> </span>node:wheezy<span class="w"> </span>sh<span class="w"> </span>-c<span class="w"> </span><span class="s1">'npm install -g bower requirejs jade && make init && make js'</span>
<span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">"Your minified javascript files are here:"</span>
<span class="w"> </span>find<span class="w"> </span>src/isso/js<span class="w"> </span>-name<span class="w"> </span>*.min.js
<span class="nf">src/isso/js/embed.js</span><span class="o">:</span><span class="w"> </span><span class="n">src</span>
<span class="w"> </span>git<span class="w"> </span>clone<span class="w"> </span>https://github.com/posativ/isso.git<span class="w"> </span>src/
<span class="nf">src</span><span class="o">:</span>
<span class="w"> </span>mkdir<span class="w"> </span>src
</code></pre></div>
<p>Même tactique, une image jetable avec nodejs : <a href="https://registry.hub.docker.com/_/node/">node:wheezy</a> est utilisée. Petite astuce, “docker run” n’accepte qu’une seule commande en argument, du coup, il faut passer par un <code>sh -c 'pim && pam && poum'</code></p>
<h1>Livrer le gâteau</h1>
<p>La cuisson s’est faite dans des containers, mais le résultat est à chaque fois un simple dossier, tout ce qu’il y a de plus traditionnel.</p>
<p>Il est possible de déployer l’application ainsi, avec un <code>rsync</code> et un <a href="http://supervisord.org">supervisor</a>.</p>
<p>J’ai fait le choix de la déployer avec Docker. Le Dockerfile utilisé est minimaliste.</p>
<div class="highlight"><pre><span></span><code><span class="k">FROM</span><span class="w"> </span><span class="s">python:2-wheezy</span>
<span class="k">COPY</span><span class="w"> </span>app<span class="w"> </span>/app
<span class="k">VOLUME</span><span class="w"> </span><span class="s">/conf</span>
<span class="k">WORKDIR</span><span class="w"> </span><span class="s">/app</span>
<span class="k">EXPOSE</span><span class="w"> </span><span class="s">1234</span>
<span class="k">CMD</span><span class="w"> </span><span class="p">[</span><span class="s2">"/app/bin/isso"</span><span class="p">,</span><span class="w"> </span><span class="s2">"-c"</span><span class="p">,</span><span class="w"> </span><span class="s2">"/conf/isso.conf"</span><span class="p">,</span><span class="w"> </span><span class="s2">"run"</span><span class="p">]</span>
</code></pre></div>
<p>Le dossier contenant le fichier de configuration est réclamé, par contre, l’application est embarquée.</p>
<p>Pour configurer tout ça, un <em>docker-compose.yml</em> qui va utiliser le <em>Dockerfile</em>.</p>
<div class="highlight"><pre><span></span><code><span class="nn">---</span>
<span class="nt">isso</span><span class="p">:</span>
<span class="w"> </span><span class="nt">build</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">.</span><span class="w"> </span><span class="c1"># Dans le même dossier</span>
<span class="w"> </span><span class="c1"># Aucun privilège</span>
<span class="w"> </span><span class="nt">cap_drop</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">ALL</span>
<span class="w"> </span><span class="nt">ports</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="s">"1234:1234"</span>
<span class="w"> </span><span class="nt">volumes</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="s">"conf:/conf"</span><span class="w"> </span><span class="c1"># Pour la configuration</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="s">"data:/data"</span><span class="w"> </span><span class="c1"># Pour la base de données</span>
</code></pre></div>
<p>La configuration isso est tout aussi simple</p>
<div class="highlight"><pre><span></span><code><span class="k">[general]</span>
<span class="na">dbpath</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">/data/comments.db</span>
<span class="na">host</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">https://example.tld/</span>
<span class="k">[server]</span>
<span class="na">listen</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">http://0.0.0.0:1234/</span>
</code></pre></div>
<p>Le dossier <em>data</em> monté dans compose est utilisé pour accueillir la base sqlite, et le serveur écoute bien le port 1234.</p>
<p>Il faut ensuite tricoter les urls pour avoir le site web et Isso.</p>
<p>Le site en production n’aura aucune séquelle de son installation, pas de nodejs, pas de choses qui trainent, juste une Wheezy spartiate et une application.</p>
<p>Tout le <a href="https://github.com/athoune/isso-docker">code est disponible sur github</a>, sous Licence <span class="caps">BSD</span>.</p>Plus de machines, des services2015-03-19T22:40:00+01:002015-03-19T22:40:00+01:00Mathieu Lecarmetag:blog.garambrogne.net,2015-03-19:/plus-de-machines-des-services.html<p>La virtualisation n’était qu’une étape.</p><p>La virtualisation n’était qu’une étape.</p>
<h1>Le besoin de virtualisation</h1>
<p>La virtualisation est apparue pour en finir avec l’ajout sans cesse de machines dans les salles serveur.
Pour pouvoir garantir de la qualité, limiter la portée des incidents, permettre des cycles de vies différents, il est important de pouvoir isoler les services.
La virtualisation permet de découper des serveurs physiques.
En regroupant des services sur un serveur plus gros, on gagne en efficacité, en consommation d’énergie, et en souplesse pour réorganiser l’ensemble (il est possible de migrer une machine virtuelle sur une autre machine physique).</p>
<h1>Abstraction matérielle</h1>
<p>Cette nouvelle couche d’abstraction n’a pas été indolore, mais des optimisations sont arrivées rapidement.
Les processeurs se sont adaptés en rajoutant des jeux d’instructions spécifiques (Intel <span class="caps">VT</span> et <span class="caps">AMD</span>-V).
L’augmentation du nombre de coeurs au sein d’une même puce, et l’augmentation constante de la quantité de <span class="caps">RAM</span> disponible permet de passer en force.
La virtualisation pure et dure est rapidement passée à la paravitualisation, l’<span class="caps">OS</span> invité devant faire quelques efforts d’adaptation en échange d’un gain en performance important.</p>
<p>Techniquement, il est possible de mélanger les <span class="caps">OS</span> (Linux, Windows, FreeBSD…), mais rester sur du tout Linux, en se contentant de mélanger les versions ou les distributions, simplifie beaucoup de choses.</p>
<p>Il est possible de redimensionner ou de déplacer une <span class="caps">VM</span>, mais concrètement peu d’hébergeurs le proposent, à part <a href="https://www.gandi.net/hebergement/serveur/livescaling">Gandi Cloud</a>, peut-être. Le redimensionnement à froid est plus simple, et souvent, la migration vers une <span class="caps">VM</span> plus grosse est préconisée.</p>
<h1>Augmenter la densité</h1>
<p>Un serveur physique met à disposition un certain nombre de ressources (Processeur, <span class="caps">RAM</span>, réseau, stockage…). La virtualisation permet d’en mutualiser certaines (<a href="http://docs.openstack.org/openstack-ops/content/compute_nodes.html de 16:1 pour les CPUs, avec une option plus sage à 4:1">Openstack parle d’un ratio</a>, pour la <span class="caps">RAM</span>, on peut monter péniblement à 1.5:1, en mettant en commun des plages de mémoire. Dans les cas les plus courant, la mémoire est partagée à 1:1, et ce sont les accès disques qui sont bloquants. Un ordonnanceur essaye de répartir équitablement les ressources, mais le phénomène du voisin bruyant est bien connu des utilisateurs d’<span class="caps">AWS</span>.</p>
<h1>La plus petite tranche possible</h1>
<p>Pour pouvoir profiter d’un minimum de parallélisme, il faut au moins avoir 2 CPUs dans une <span class="caps">VM</span>. Le ratio <span class="caps">RAM</span>/<span class="caps">CPU</span> et de 1:1 pour les offres basses (<span class="caps">VPS</span> <span class="caps">OVH</span>, Digital Ocean par exemple) pour monter à 3.75:1 pour de l’Amazon Web Service et Google Cloud, et grimper autour de 7:1 pour des VMs spécialisées.
Ce qui fait quand même presque 8Go de <span class="caps">RAM</span> comme unité de base.
Quand on est plus petit que Netflix ou Twitter, ça fait quand même une grosse tranche, surtout si l’on veut doubler le nombre de machine pour avoir de la redondance.</p>
<h1>Découper différemment</h1>
<p>Les machines virtuelles sont une bonne réponse, mais quelle est la question, déjà?</p>
<p>Pouvoir isoler des services.</p>
<p>Les besoins d’isolations sont finalement divers et négociables :</p>
<ul>
<li>Répartir au plus juste la consommation des ressources (<span class="caps">CPU</span>, <span class="caps">RAM</span>, stockage, réseau).</li>
<li>Isoler les différents utilisateurs qui vont utiliser des ressources physiques communes (<span class="caps">CPU</span>, <span class="caps">RAM</span>, stockage, réseau)</li>
<li>Réstreindre les capacités des utilisateurs, pour limiter les possibilités de faire des bêtises, et donc les surfaces d’attaques.</li>
</ul>
<p>Ces différentes possibilités ont été explorées, en patchant directement le noyau Linux. <a href="http://linux-vserver.org/Overview">Vserver</a> et <a href="https://openvz.org/Main_Page">OpenVZ</a> pour le confinement, <a href="https://grsecurity.net">GRSec</a> pour la sécurité.</p>
<p>Ces pistes, prometteuses, mais intrusives, ont permis la création de nouveaux modules dans le noyau Linux, permettant ensuite la création de produits, sans avoir besoin d’un noyau patché.</p>
<p>Les <a href="http://linux.die.net/man/7/capabilities">capabilities</a> définissent les actions privilégiées que peut faire un process.
Pour isoler des processus, il y a maintenant les <a href="http://man7.org/linux/man-pages/man7/namespaces.7.html">namespaces</a>, pour répartir les ressources, Google à offert les <a href="https://www.kernel.org/doc/Documentation/cgroups/">CGroups</a>, pour la sécurité, les <a href="http://en.wikipedia.org/wiki/Linux_Security_Modules">Linux Security Modules</a> permettent avec quelques polémiques de brancher <a href="http://wiki.apparmor.net/index.php/Main_Page">Apparmor</a>, <a href="http://selinuxproject.org/page/Main_Page">SELinux</a> (cadeau de la <span class="caps">NSA</span>) ou des choses plus exotiques comme <a href="http://tomoyo.sourceforge.jp/documentation.html.en">Tomyo</a> ou <a href="http://en.wikipedia.org/wiki/Smack_(software)">Smack</a>. <a href="https://code.google.com/p/seccompsandbox/wiki/overview">Seccomp</a> (issu de Google Chrome) permet de créer des règles de sandboxing (par thread) en <a href="http://en.wikipedia.org/wiki/Berkeley_Packet_Filter"><span class="caps">BPF</span></a>. <span class="caps">BPF</span> est la toute nouvelle machine virtuelle universelle pour Linux qui permet de définir des règles en user space, qui seront utilisées en kernel space, sans avoir à faire d’aller-retour. <a href="https://github.com/seccomp/libseccomp">Libseccomp</a> propose une interface de plus haut niveau pour définir ces règles de sécurité.</p>
<p>Les solutions de conteneurs officiels sont des assemblages de ces différents outils :</p>
<ul>
<li><a href="https://github.com/google/lmctfy"><span class="caps">LMCTFY</span></a></li>
<li><a href="https://linuxcontainers.org/fr/"><span class="caps">LXC</span></a></li>
<li><a href="https://www.docker.com">Docker</a></li>
<li><a href="https://github.com/coreos/rocket">Rocket</a></li>
</ul>
<p>Les conteneurs sont une approche grandboutiennes, il est toujours possible d’avoir une approche petiboutiste, plus spécifique et du coup plus adapté, comme le propose <a href="http://uwsgi-docs.readthedocs.org/en/latest/">uwsgi</a>, un serveur d’application.</p>
<p>De toute façon ces outils se démocratisent rapidement, l’invasion <a href="http://freedesktop.org/wiki/Software/systemd/">Systemd</a> amène avec lui les Cgroups (et donc les namespaces), et Apparmor est installé par défaut sur Ubuntu depuis la 7.10.</p>
<h1>Découper le découpage</h1>
<p>Il est possible de découper à grandes tranches avec de la paravirtualisation, qui pour l’instant est la seule à garantir une isolation forte, puis de confier ensuite la finition à un kernel Linux récent.</p>
<h1>Simplifier le découpage</h1>
<p>Une fois la démarche de conteneurisastion amorcée, il est tentant d’optimiser la pile en enlevant la couche de paravirtualisation. C’est la démarche qu’à fait <a href="http://www.enterprisetech.com/2014/05/28/google-runs-software-containers/">Google, ils ont basculé vers le tout container</a>. Google ne fait pas tourner de code dont ils n’ont pas confiance, ce qui facilite largement le passage au tout container. Au pire, ils peuvent avoir du code bête, mais pas du code méchant. <span class="caps">LMCTFY</span>, leur solution maison, utilise quand même Apparmor.
Par contre, pour leur offre d’hébergement, <a href="https://cloud.google.com/compute/docs/faq">Google Cloud, utilise toujours <span class="caps">KVM</span></a>.</p>
<h1>Découper quoi?</h1>
<p>Une machine virtuelle a une taille minimale plutôt conséquente. Il n’est pas possible d’empiler trop de machines virtuelles sur une machine physique. Tout pousse à utiliser les machines virtuelles comme des machines physiques, en y faisant cohabiter différents services.
Certains services étant des prérequis (syslog, ntpd, dnsd, cron, atd…) d’autres étant un regroupement de services pour ne pas gâcher la place.</p>
<h1>Découper des services</h1>
<p>Un service peut être rendu par une ou plusieurs machines.
Les services distribués peuvent utiliser le classique pattern master/slave ou des choses plus égalitaires, à base de <a href="http://en.wikipedia.org/wiki/Paxos_(computer_science)">Paxos</a> ou de <a href="http://en.wikipedia.org/wiki/Raft_(computer_science)">Raft</a>.</p>
<p>Il est possible de redimensionner (confier plus ou moins de ressource) un service, ou d’en ajouter des instances, s’il supporte la distribution.</p>
<h1>Utiliser des services</h1>
<p>Un service est ce que va utiliser un développeur, il se fiche un peu des détails d’implémentation ou de paramétrages. Son application, qui n’est finalement qu’un service, va utiliser d’autres services pour fonctionner, et en bout de chaine, consommer des ressources.</p>
<p>Un développeur va utiliser des services, qui seront dimensionnés en fonction de la demande et du budget disponible.</p>
<p>Ces services pourront être classique, comme du stockage, du backup, une base de données relationnelle, mais aussi plus abstrait comme de l’antispam (<a href="http://akismet.com">Akismet</a>…), le stockage distribué de S3, la proximité d’un <span class="caps">CDN</span>, la surveillance d’un <a href="https://www.pingdom.com">pingdom</a>, la consolidation des erreurs d’un <a href="https://getsentry.com/welcome/">Sentry</a> (un projet libre), l’analyse de performance d’un <a href="http://newrelic.com">Newrelic</a>… la liste évolue de jour en jour.</p>
<p>L’informatique est maintenant de <a href="http://fr.wiktionary.org/wiki/de_plain-pied">plain-pied</a> dans l’ère des services.</p>