Journal # Du serverless au FaaS - et du Golang -, c'est l'été

Posté par  . Licence CC By‑SA.
Étiquettes :
12
7
sept.
2022

Sommaire

Bonsoir,

0. Introduction

L'été, sa chaleur écrasante et ses travaux périscolaires : cette année, je m'étais donné deux objectifs, la définition et la mise en œuvre d'une (petite) solution FaaS (function as a service) et l'apprentissage de Go.

Difficile de faire de l'architecture de SI si on ne se contente que de la définition de l'ouvrage, et depuis des années j'entends du bien (et aussi beaucoup de mal) sur le langage de Google : alors autant tester soi-même, l'un et l'autre.

Je n'avais pas prévu d'en faire un article, mais lorsqu'il y a quelques jours (à date de rédaction de ces lignes), un article a attiré mon attention : "2022, l'année de l'explosion du serverless" (article du JDN), cela m'a motivé.

Car voilà quelques années, j'avais eu l'occasion d'écrire une thèse pro sur des sujets similaires. La présentation de la soutenance y fait directement référence, en associant FaaS et RDF comme deux des outils modernes et indispensables pour produire des traitements automatiques hautement distribués et porteur de sens partagés entre les organisations.

C'est dire si le sujet me passionne. Pour autant le code que j'ai produit n'a pas d'autre perspective qu'un travail de meilleure compréhension du sujet - et accessoirement d'avoir un outil pour mes propres besoins, sur mon serveur de développement. Je donne plus loin le lien vers le dépôt, sous licence MIT.

De plus, c'est pile dans l'actualité : nous parlerons de mise à l'échelle, particulièrement ici de réduction vers 0 du coût des processus. En cette période de "fin d'abondance", c'est bienvenu…

Enfin et comme vous pouvez vous en doutez, les lignes que vous lisez comme le travail de développement m'ont demandés quelques heures au carré. Malgré tout le soin que j'ai apporté à l'écriture de cet article et au reste, j'ai la bonne certitude d'avoir laissé de (grosses ?) boulettes. Merci de votre modération dans vos commentaires (je suis [très] ouvert à une critique quand elle fait plus de trois mots !).

Le sujet serverless que j'aborde n'est pas le plus simple et, reconnaissons-le, le plus limpide. Il est soumis à l'interprétation et donc à nos propres biais. C'est une discipline "jeune" où l'aspect commercial agit beaucoup sur la communication et la formation qui en est faite (pour le meilleur et le pire). Je mets ici le terme jeune entre guillemets car certaines approches sont des héritages lointains, qui ont parfois des dizaines d'années. Mais cet article est déjà considérablement trop long pour en faire l'exégèse…


1. Serverless, Ce que c'est

Du concept global

La définition n'est pas facile, car le concept reste globalement flou. Évidemment c'est le concept est un trompe-l’œil : il y a toujours un serveur quelque part. Sa gestion est simplement et totalement transparente pour l'équipe de développement, qui n'assure pas la gestion d'un parc d'infrastructure. Au-delà, c'est la gestion même d'un "service" (c'est-à-dire un service rendu au client) qui est également géré par celui qui l'héberge l'applicatif.

Pour l'organisation qui porte le développement (entreprise, association, projet ouvert, etc.), cela a plusieurs avantages :
- généralement il y a la fonction "scale-to-zero", c'est-à-dire que l'applicatif ainsi hébergé, s'il n'y est pas utilisé, ne consomme pas de ressources vives (seulement son stockage). La facture en cas de non-utilisation est donc soit particulièrement réduite, soit nulle ;
- le déploiement est facile, au sens où l'on utilise généralement une API Web ou via une ligne de commande simple et courte ;
- la sécurité, la sûreté reposent en partie sur le service sous-jacent, même si les fonctions d'authentification, de protection contre les attaques de force brute ou contre les déni de service, ne sont pas toujours compris (voir point suivant) ;
- la facture se fait au "hit", à l'appel à l'applicatif…

… Ce qui est là un des principaux défauts, dû à la gestion économe des ressources : le temps de démarrage. Du fait que l'applicatif est éteint (parfois "mis en pause" mais généralement le coût n'est alors pas nul pour l'hébergement), le premier appel porte le coût de démarrage. Ce coût peut être très important comme nous le verrons plus loin. Il faut donc avoir des stratégies efficaces de démarrage, que l'applicatif doit porter en son sein, notamment par exemple, en l'optimisant avec un tampon local important lorsque c'est possible.

Autre point possiblement problématique, c'est la montée en charge ou mise à l'échelle (la fameuse "scalabilité"). Lorsque le développeur poussera son applicatif, il ne saura pas ni combien de noeuds sont en usage - ni même s'il y en aura plusieurs -, et selon quelle configuration, son infrastructure sera à un instant donné. Si le tampon doit être "unique", au sens où toutes les instances doivent partager le même, il faut donc prévoir un service dédié type memcached ou redis.

C'est là des principaux points que j'énonce mais loin d'être exhaustif. Si l'approche vous tente et que vous ne dépassez pas le cadre d'une organisation à quelques personnes pour une charge anecdotique ou presque, mieux vaut se tourner sur une solution serverless très légère, mono-noeud par principe, car vous ne gagnerez que les avantages. Bien évidemment, si vous n'êtes pas dans ces situations, avoir une part de serverless dans votre architecture n'est pas sans impact voire peut-représenter un risque majeur.

D'autant que les solutions actuelles de serverless "à destination des développeurs" et notamment celle qui me semble la plus (re)connue OpenFaas, fonctionne quasi-systématiquement avec un "gros machin" comme Kubernetest (même si des contre-exemples existent ici pour l'explication et pour le dépôt). C'est-à-dire qu'on a une mise en oeuvre loin d'être évidente pour aboutir à cette abstraction tant désirée. Pour l'avoir testé sur une infrastructure personnelle - un Rancher à deux fournisseurs de VM distincts -, c'est vite inommable en terme de débogue…

Du particulier

A cet instant de l'article, vous vous direz peut-être "serverless n'est pas FaaS !" et globalement je serais d'accord avec vous. En effet, vous pouvez parfaitement utiliser un service quelconque (DaaS ou SaaS), afin de pousser votre logique métier quelque part (ici, le "as a service" est le support à cette logique métier). Vous ne payerez au mieux qu'à concurrence de la consommation réelle ou au pire au forfait et, parfois, l'adaptation d'une équipe de développeur sans aucune notion d'infrastructure (hélas ça existe), peut alors s'intégrer dans ces types de services sans (trop) douleur. Cependant vous risquez d'être extrêmement limité dans le format du code exécuté, de sa taille et de sa complexité.

Ou alors avoir de l'IaaS ou du PaaS : c'est une abstraction moins forte, qui permet d'avoir facilement un environnement prêt à l'emploi - mais la notion de serveur à configurer reste encore largement présente.

En résumé, tout dépend de votre volonté d'abstraction de la réalité physique des bits sur un bout de plastique couvert de silicium et autres métaux. Ce n'est une frontière entre tout et rien : c'est une échelle parfois assez subtile.
Chacun des concepts que j'ai énoncé, recouvre une portion plus ou moins importante de la couche ultime que constitue le serverless. La frontière de ce dernier (me) semble comme pour les autres concepts, une frontière poreuse où l'on répond à un besoin, il est donc normal, notamment pour le point évoqué du PaaS, que les deux concepts se chevauchent par endroit.

Pourtant ils sont bien dissemblables et c'est Cloudfare qui porte - avis personnel -, le meilleur exemple :

"L'informatique sans serveur peut être comparée au fait de tirer de l'eau d'un robinet, l'eau représentant la puissance de calcul. Dans une maison moderne, on peut ouvrir un robinet à tout moment pour obtenir toute l'eau dont on a besoin. Le PaaS s'apparente davantage à un distributeur d'eau avec un service de livraison de bonbonnes. Il est toujours possible d'obtenir autant d'eau potable que nécessaire, mais il ne suffit pas d'ouvrir le robinet ; le consommateur doit demander au fournisseur de lui livrer davantage de bonbonnes si la demande augmente. Dans les deux cas, un tiers s'occupe des services en « backend » c'est-à-dire en arrière plan - en purifiant l'eau, en l'acheminant l'eau au bâtiment, etc. - mais seule l'eau du robinet peut être dosée avec précision, à la demande et en temps réel.
(…) Pour poursuivre avec la métaphore de l'eau, les consommateurs qui utilisent l'eau du robinet paient exactement la quantité d'eau qu'ils consomment. De même, la facturation de l'informatique sans serveur est extrêmement précise, et les développeurs ne paient que ce qu'ils utilisent. Certains fournisseurs d'informatique sans serveur ne facturent aux développeurs que le temps précis d'exécution de leurs fonctions, jusqu'à quelques fractions de seconde pour chaque instance individuelle de chaque fonction. D'autres fournisseurs facturent selon le nombre de demandes.
Les consommateurs qui utilisent un distributeur d'eau et qui se font livrer des bonbonnes d'eau ne paient également que ce qu'ils utilisent, mais ils paient à la bonbonne et non au mètre cube. De la même manière, certains fournisseurs de PaaS ne facturent aux développeurs que ce que leur application utilise. Cependant, la facturation est loin d'être aussi précise que pour l'informatique sans serveur. D'autres fournisseurs de PaaS facturent leurs services au mois. Les développeurs peuvent généralement définir la quantité de puissance de calcul qu'ils souhaitent obtenir. Cependant, cette décision est prise à l'avance et n'est pas une réaction aux augmentations et aux diminutions de l'utilisation en temps réel.
Cette différence ne signifie pas nécessairement que l'architecture sans serveur est toujours plus abordable. De même qu'il devient rapidement très cher de laisser couler un robinet d'eau en permanence, les applications web dont le flux d'utilisation est important et constant sans grande fluctuation peuvent être coûteuses à faire fonctionner en utilisant l'informatique sans serveur."

De l'usage réel

Une fois ces considérations aqueuses passées, reste à formaliser le FaaS. Imaginez que le serverless est l'ensemble des technologies pour faire une abstraction (quasi-)totale du sous-jacent technique et d'infrastructure. Le FaaS est une modalité du _serverless : c'est la technique (et souvent un framework pour accompagner le déploiement du code) permettant au développeur non seulement de faire abstraction de la partie hébergement, mais aussi même dans le cas extrême, d'où provient la charge de travail.

Le périmètre d'action est alors très réduit : vous produisez une fonction qui sera appelée dans un processus que vous ne maîtrisez pas. Mais dont la mise à l'échelle, la sécurité et la sûreté, la gestion en temps réel, est porté pour vous.

Du fait que nous sommes au XXIe siècle, les appels sont principalement (voire exclusivement dans de nombreux cas), portés seulement par le réseaux. Revenons à OpenFaas quelques instants et le premier exemple donné :

import requests
import json

def handle(req):
    result = {"found": False}
    json_req = json.loads(req)
    r = requests.get(json_req["url"])
    if json_req["term"] in r.text:
        result = {"found": True}

    print json.dumps(result)

… et c'est tout. Le reste se résume en un fichier de pré-requis et un fichier de déclaration de cette fonction. En deux-trois commandes, vous envoyez ça dans un coin de l'univers, en supportant une charge virtuellement sans limite (ou seulement celui de votre compte en banque). Si vous n'avez pas d'activité, aucune charge pour la fonction n'existe (pas de processus en activité) : c'est le scale-to-zero, le graal du Green (Washing) IT.

Ici OpenFaas a assuré plusieurs fonctionnalités essentielles pour nous :
- l'écoute du réseau sur un serveur où l'instance d'OpenFaas tourne,
- la récupération de la requête entrante,
- son éventuelle répartition de charge vers d'autres instances OpenFaas,
- le contrôle de l'intégrité de la requête (parse)
- la détection de la fonction appelée puis éventuellement, le contrôle de sécurité pour l'appel,
- le démarrage d'un nouveau conteneur pour la prendre en charge (ou l'envoi à un conteneur existant),
- l'envoi d'une requête à ce conteneur dans un format attendu,
- la réception par le processus au sein du conteneur de la requête,
- le lancement la fonction handle définie plus haut,
(… ici le traitement par la fonction …)
- la récupération d'erreur (try / except) ou du retour normal,
- l'envoi du processus vers l'extérieur du contenur,
- la récupération du retour par l'instance d'OpenFaas,
- le contrôle de l'intégrité du retour,
- la production d'un retour pour le client final,
- l'envoi de ce retour au client final.

Rien que ça - et je vous ai épargné la réalité des appels de signaux, des traitements réseaux, etc.

On ne mesure parfois pas bien, et moi le premier, la quantité absolument folle d'énergie, de travail, de synergie qu'il faut pour arriver à produire un tel système informatique. Le nombre incalculable de choses qui peuvent mal tourner. Je trouve ça vertigineux. Bref aparté : si vous connaissez le paradoxe de la veste en laine, ce vertige ne doit être que plus grand encore (sinon courrez voir la chaîne de Mr Phi)…

On atteint d'un certain côté à une division du travail quasi-parfaite entre des compétences diverses ; faisant perdre l'aspect "généraliste" de l'informatique des premiers temps, que tant de passionnés comme moi apprécient…

Des considérations

L'aspect intéressant du FaaS au-delà d'une baisse toujours plus grande de la barrière d'entrée dans le développement (notamment pour les profils junior), est aussi le contrôle laissé par le développeur à son infrastructure pour optimiser ses propres moyens. Comme vous ne vous préoccupez pas du processus qui porte la "fonction" ni "où" ce processus est exécuté, l'infrastructure est (et doit) être en capacité de rationaliser sa consommation de ressources : bref, elle va couper tout ce qui est inutile.

On en revient à la partie aqueuse : on coupe le robinet pendant qu'on se brosse les dents. On "scale-to-zero" (on réduit l'échelle en bon français !).

Le FaaS n'existe pourtant pas de manière unique, comme si on abordait systématiquement le problème avec une même approche. D'autant que j'ai passé ici sous un relatif silence l'aspect "coût en temps" qui est le point noir général de la solution.

Le FaaS peut-être vu alors avec deux principales applications :
- un processus qui existe tant qu'il y a de l'activité, et qui est éteint lorsque celle-ci s'arrête (avec un délai modifiable). On se rapproche là considérablement d'un serveur classique. L'avantage : le temps de démarrage est porté seulement par la première requête et qu'un cache partagé entre plusieurs requêtes peut exister. L’inconvénient : il est délicat de définir, voire totalement contre-productif d'avoir un délai d'extinction fixe (rarement un SI se comporte avec une grande régularité et si tel est le cas, il existe d'autres solutions bien plus simples que le FaaS…) ;
- un processus déclenché par requête, comme on peut l'avoir avec FastCGI. L'avantage est alors une plus grande isolation entre les requêtes, ainsi qu'un coût réellement nul s'il n'y a pas de requête (le processus s'arrête à la fin du traitement de sa requête). Le coût de démarrage est souvent sensible dans le coût temporel total et il est porté individuellement par l'ensemble des requêtes.

Évidemment ces applications négligent volontairement dans l’approche, un aspect essentiel : il faut toujours un processus d'écoute actif, qui déclenchera l'une ou l'autre. Il n'y a pas de magie : si un requête arrive, du client final ou d'une balance de charge, il faut pouvoir être en écoute et répondre, mettre en œuvre la solution.

Pour cela, plusieurs solutions existent. Cela que je propose plus loin, avec systemd, permet une réduction possible de 100% de la charge sur un OS pour la solution de FaaS : elle est elle-même allumée et éteinte par le système d'exploitation suivant l'activité. Du reste je n'ai pas poussé l'exemple (et le vice) jusqu'à éteindre mon ordinateur - mais vous imaginez bien qu'avec une balance de charge extérieure, on peut sans difficulté éteindre jusqu'aux machines physiques. Le FaaS permet alors de réduire pour une entreprise, de réduire sa dépense au seul coût de possession.

La solution de FaaS pour être tout à fait professionnelle ou à tout le moins complète, dispose donc nécessairement de plusieurs composants et règles de gestion, pour faire coller la partie développement logi(ciel/que) à l'infrastructure.


2. Le cas d'étude

La présentation générale : FAASS - Function As A (Simple) Service

Le dépôt est ici : https://github.com/JGarderon/faass. Il contient une seule branche, et j'ai posté une version alpha2. Je n'ai pas de "plan sur la comète" - si ça intéresse quelqu'un, je suis prêt à échanger et à aider à l'usage, mais ce travail est pensé et prévu comme un exercice d'entraînement et pour un usage véritablement personnel (remarquez, Linus disait ça aussi… ;)).

A ce jour, je prends en charge aujourd'hui deux types différents de "route", qui sont les appels possibles :
- une "fonction" (function), qui est exécuté dans un conteneur dédié. C'est véritablement du FaaS. Le mécanisme derrière, s'appuie sur stdin/stdout pour la communication avec le serveur. C'est un choix motivé pour ne pas à réinventer la route côté processus piloté : on se met d'accord sur une procédure d'échange (ici un corps en deux parties, chacune avec une taille définie par un entier déclaré à leur début). Donc pas besoin d'un framework d'exécution comme dans OpenFaas ;
- un "service" (service), qui est un conteneur lancé et arrêté en fonction des besoins. C'est donc moins dans la logique FaaS que celle du serverless. Ici l'échange se fait en HTTP sur un port défini dans la configuration. Le service est arrêté passé une période d'inactivé et rédémarré lorsqu'un client revient.

Forcément en fonction de la situation, il sera plus ou moins efficace d'utiliser l'un ou l'autre :
- pour des traitements par lot, la version "service" est efficace car elle restera active sans discontinuité durant le temps nécessaire ;
- par contre pour un appel très occasionnel, la version "fonction" est suffisante.

Autre élément à prendre en compte : la partie "fonction" est la seule qui permet de déclarer via API, un script ou un exécutable qui sera lancé au sein du conteneur. Le service ne dispose qu'un point de montage vers le dossier temporaire.

Si vous souhaitez tester les performances dans les mêmes conditions que les miennes, voici un squelette de ma commande locale :
bash
ab -n 1500 -c 50 -s 2 http://127.0.0.1:9090/lambda/{la route à tester}

La mise en route

A cette heure, je n'ai pas eu le temps d'étoffer les tests fonctionnels, donc ne vous inquiétez pas du retour anecdotique de la console :

julien@julien-Vostro-7580:~/Developpement/faasd/test-go-faas$ ./build-and-test.sh
---             ---             ---      start

tmp path existent
cache path existent
example function existent

---             ---             ---      build

2022-09-06 23:27:28,188 - general - SPECIAL - 

  FaasS = Function as a (Simple) Service
  ---
  Created by Julien Garderon (Nothus)
  from August 01 to September 06, 2022
  MIT Licence
  ---
  This is a POC - Proof of Concept -, based on the idea of the OpenFaas project
  /!\ NOT INTENDED FOR PRODUCTION, only dev /!\

2022-09-06 23:27:28,188 - general - OK - all is ready for work !
2022-09-06 23:27:28,189 - general - INFO - build asked
2022-09-06 23:27:28,488 - general - OK - build finished
2022-09-06 23:27:28,488 - general - SPECIAL - ending of script

---             ---             ---      functional tests

2022-09-06 23:27:28,612 - general - SPECIAL - 

  FaasS = Function as a (Simple) Service
  ---
  Created by Julien Garderon (Nothus)
  from August 01 to September 06, 2022
  MIT Licence
  ---
  This is a POC - Proof of Concept -, based on the idea of the OpenFaas project
  /!\ NOT INTENDED FOR PRODUCTION, only dev /!\

2022-09-06 23:27:28,612 - general - OK - all is ready for work !
2022-09-06 23:27:31,784 - build_and_run - OK - execute test 'test_lambda_url_notfound_get' (pass)
2022-09-06 23:27:31,785 - build_and_run - OK - execute test 'test_lambda_url_invalid_get' (pass)
2022-09-06 23:27:31,787 - build_and_run - OK - execute test 'test_home_get' (pass)
2022-09-06 23:27:32,853 - build_and_run - OK - execute test 'test_lambda_services_get' (pass)
2022-09-06 23:27:33,708 - build_and_run - OK - execute test 'test_lambda_functions_get' (pass)
2022-09-06 23:27:34,404 - build_and_run - OK - run without error
2022-09-06 23:27:34,404 - general - SPECIAL - ending of script

---             ---             ---      stop

Même si certains de vos tests passent en rouge sur votre poste, l'applicatif a déjà été construit et est donc prêt à l'emploi. Du fait de la puissance des machines hôtes, avec les délais de connexion, le script de tests peut partir en erreur (faux positif).

Vous aurez ensuite besoin d'un certificat auto-signé pour démarrer l'applicatif (pour l'instant c'est obligatoire, je rajoute prochainement cet aspect comme option dans la configuration). De nombreux tutoriels existent…

Tout est prêt pour le lancement. Commençons simplement par… l'arrêt via ctl+c :

julien@julien-Vostro-7580:~/Developpement/faasd/test-go-faas$ ./faass 
INFO: 2022/09/04 12:13:46 main.go:68: UI path found : /home/julien/Developpement/faasd/test-go-faas/content
INFO: 2022/09/04 12:13:46 main.go:75: Authorization secret found ; API active
^CINFO: 2022/09/04 12:13:48 main.go:127: interrupt received ; shutting down
WARNING: 2022/09/04 12:13:48 server.go:23: Shutdown ListenAndServeTLS terminated
INFO: 2022/09/04 12:13:48 main.go:138: process gracefully stopped

Si vous souhaitez repartir de zéro pour la configuration voir d'un environnement vierge (effacez ./conf.json), faass le fera pour vous :

julien@julien-Vostro-7580:~/Developpement/faasd/test-go-faas$ ./faass -prepare
WARNING: 2022/09/04 12:16:19 utils.go:74: Unable to create environment for UI contents "/home/julien/Developpement/faasd/test-go-faas/content" : mkdir /home/julien/Developpement/faasd/test-go-faas/content: file exists ; pass

Il créera ainsi l'arborescence locale si nécessaire.

A partir de maintenant, je vous conseille d'avoir trois consoles ouvertes :
- la première pour ./faass (utilisez GODEBUG=http2debug=2 ./faass pour du très verbeux) ;
- la deuxième avec docker stats pour suivre l'activité réelle des conteneurs ;
- la troisième où vous tapez les commandes de requêtes.

Dans cette dernière console, je vous conseille de tirer les images demandées dans la configuration, l'application ne prennant pas encore cette étape au démarrage :
- docker pull nginx ;
- docker pull python:3.

Là, tout est prêt !

Lancement d'une fonction (sans authentification par défaut)

Dans la console n°3 :

julien@julien-Vostro-7580:~/Developpement/faasd/test-go-faas$ curl -k -X GET 'https://127.0.0.1:9090/lambda/example-function' --data-raw 'coucou'
coucou

Notez ici, que le fichier example-function.py, se comporte comme un serveur d'écho, en rajoutant simplement un entête HTTP supplémentaire. Dans la console n°2, vous aurez la sortie suivante :

INFO: 2022/09/04 20:01:43 main_server.go:152: known real desired url : /lambda/example-function
INFO: 2022/09/04 20:01:43 main_server.go:168: known desired url : example-function
WARNING: 2022/09/04 20:01:43 main_server.go:95: run container for route 'exampleFunction'

Enfin dans la console n°1, vous aurez (si vous êtes rapide !), un aperçu du conteneur. Ce dernier a été entièrement "effacé" de votre système lorsque le processus Python s'est arrêté (comportement de l'arguemnt --rm).

Lancement d'un service (avec authentification par défaut)

Dans la console n°3 :

julien@julien-Vostro-7580:~/Developpement/faasd/test-go-faas$ curl -k -X GET -H "Authorization: Basic YWRtaW46YXplcnR5" https://127.0.0.1:9090/lambda/example-service
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Dans la console n°2, vous aurez la sortie suivante :

INFO: 2022/09/04 20:03:08 main_server.go:152: known real desired url : /lambda/example-service
INFO: 2022/09/04 20:03:08 main_server.go:168: known desired url : example-service
DEBUG: 2022/09/04 20:03:09 main_server.go:202: running container for desired route : 172.17.0.2 (cId 152a81009abb8b0af39d0b81d2e3fab899327259d7405ddd331a73a45b9e3b9d )
DEBUG: 2022/09/04 20:03:09 main_server.go:214: new url for desired route : http://172.17.0.2:80/ (cId 152a81009abb8b0af39d0b81d2e3fab899327259d7405ddd331a73a45b9e3b9d )
DEBUG: 2022/09/04 20:03:09 main_server.go:244: result of desired route : 200 (cId 152a81009abb8b0af39d0b81d2e3fab899327259d7405ddd331a73a45b9e3b9d )

Puis au bout d'une trentaine de secondes, le message :

INFO: 2022/09/04 20:03:32 utils.go:112: Container exampleService (cId  152a81009abb8b0af39d0b81d2e3fab899327259d7405ddd331a73a45b9e3b9d ) stopped

La console n°1 n'aura alors aucun conteneur actif. Si vous relancez la même commande, vous constatez que c'est le même conteneur qui a été relancé (son ID est le même).

Usage de l'API (actif seulement s'il y a une authentification)

Cas simple de récupération :

julien@julien-Vostro-7580:~/Developpement/faasd/test-go-faas$ curl -k -X GET -H "Authorization: Basic YWRtaW46YXplcnR5" https://127.0.0.1:9090/api/functions/example-function
{"name":"exampleFunction","service":false,"script":"/home/julien/Developpement/faasd/test-go-faas/tmp/example-function.py","cmd":["python3","/function"],"authorization":"Basic YWRtaW46YXplcnR5","env":{"faass-example":"true"},"image":"python:3","timeout":1000,"retry":0,"delay":0,"port":0,"LastRequest":"0001-01-01T00:00:00Z","Id":"","IpAdress":"","Mutex":{}}

Si vous tentez d'appeler un item avec le mauvais type de ressource, vous serez refoulé :

julien@julien-Vostro-7580:~/Developpement/faasd/test-go-faas$ curl -k -X GET -H "Authorization: Basic YWRtaW46YXplcnR5" https://127.0.0.1:9090/api/functions/example-service
{"message":"this route is a service, no a function"}

Enfin pour la récupération d'un service, notez que l'objet retourné est de la même construction :

julien@julien-Vostro-7580:~/Developpement/faasd/test-go-faas$ curl -k -X GET -H "Authorization: Basic YWRtaW46YXplcnR5" https://127.0.0.1:9090/api/services/example-service
{"name":"exampleService","service":true,"script":"","cmd":null,"authorization":"Basic YWRtaW46YXplcnR5","env":{"faass-example":"true"},"image":"nginx","timeout":250,"retry":3,"delay":8,"port":80,"LastRequest":"0001-01-01T00:00:00Z","Id":"","IpAdress":"","Mutex":{}}

Pour les autres actions possibles :
- Configuration :
- GET : obtenir la copie actuelle de la configuration en mémoire,
- PATCH : modifier une des valeurs de la configuration globale (seul delay est implémenté),
- l'export de la configuration en mémoire n'est pas encore implementé ;
- Functions :
- POST : soumettre un fichier (et un seul !) qui sera l'exécutable ou le script au sein du conteneur (écrase si existant),
- PATCH : soumettre l'entité de configuration associée (liée à la configuration ; écrase si existant),
- GET : obtenir la copie actuelle de la fonction définie dans la configuration,
- DELETE : supprimer la fonction (ne supprime pas encore le fichier associé) ;
- Services :
- POST : soumettre l'entité de configuration (écrase si existant),
- GET : obtenir la copie actuelle de la fonction définie dans la configuration,
- DELETE : supprimer le service.

Comme vous le constatez, il manque encore beaucoup de pièces pour avoir un ensemble digne d'agir en production… mais on a déjà un ensemble fonctionnel, largement suffisant pour des besoins personnels.

Descendons encore un peu

Nous avons donc un serveur FaaS, mais ce dernier reste en permanence en écoute, même s'il n'y a pas de clients depuis un certain temps. Ce processus-même est donc indésirable car il n'apporte rien.

L'étape suivante va être de le stopper lui-aussi, grâce aux fonctionnalités intéressantes de systemd. L'idée est expliquée dans un article de RedHat fort intéressant, où le principe du serverless est d'ailleurs expliqué.

Crééons trois fichiers, ici pour mon utilisateur courant : ~/.config/systemd/user :

  • faass-httpd-proxy.socket, le fichier décrivant le port d'écoute entrant. Ici c'est mon IP de routeur, derrière ma box. Le port en écoute est le port 50000. Il va être dans notre scénario un déclencheur.
[Socket]
ListenStream=192.168.2.110:50000

[Install]
WantedBy=sockets.target
  • faass-httpd.service, le fichier décrivant le service pour gérer notre programme FaaS, avec le contexte. Notez la commande ExecStartPost, qui va retarder d'une seconde l'exécution du fichier suivant - ce n'est pas du tout anecdotique : "ExecStartPost= commands are only run after the commands specified in ExecStart= have been invoked successfully (…) Note that the execution of ExecStartPost= is taken into account for the purpose of Before=/After= ordering constraints." (voir la documentation).
[Unit]
Description=Serverless Faass-httpd.service
Wants=network.target
After=network-online.target
StopWhenUnneeded=yes

[Service]
Restart=on-failure
ExecStartPre=/bin/echo "faass va démarrer"
WorkingDirectory=/home/julien/Developpement/faasd/test-go-faas/
ExecStart=/home/julien/Developpement/faasd/test-go-faas/faass 
ExecStartPost=/bin/sleep 1
ExecStop=/bin/echo "faass va s'éteindre"
  • faass-httpd-proxy.service est le service, une unité, qui se chargera de "rassembler" les deux parties précédentes, en utilisant un utilitaire d'intermédiation entre le port entrant côté "OS" (systemd) et le port entrant côté programme faass. Cet intermédiaire se coupera ici de lui-même après 30 secondes d'inactivité, indépendamment de l'écoute réelle du port déclaré dans faass-httpd-proxy.socket.
[Unit]
Requires=faass-httpd.service
After=faass-httpd.service
Requires=faass-httpd-proxy.socket
After=faass-httpd-proxy.socket

[Service]
ExecStart=/usr/lib/systemd/systemd-socket-proxyd --exit-idle-time=30s 127.0.0.1:9090

Nous pouvons maintenant déclencher les hostilités et regarder ce qui se passe :
bash
systemctl --user daemon-reload \
&& systemctl --user enable --now faass-httpd-proxy.socket \
&& journalctl --user -u faass-httpd.service -f -n 0

Lorsque je charge ma page côté navigateur (https://192.168.2.110:50000/lambda/toto?bidule), notre programme FaaS démarre puis, en l'absence d'activité, sera coupé par le système :

août 14 20:34:24 julien-Vostro-7580 systemd[2861]: Starting Serverless Faass-httpd.service...
août 14 20:34:24 julien-Vostro-7580 echo[222328]: faass va démarrer
août 14 20:34:24 julien-Vostro-7580 faass[222329]: WARNING: 2022/08/14 20:34:24 faass.go:91: new value for delay cleaning containers : min 5 (seconds)
août 14 20:34:24 julien-Vostro-7580 faass[222329]: INFO: 2022/08/14 20:34:24 faass.go:948: UI path found : /home/julien/Developpement/faasd/test-go-faas/content
août 14 20:34:24 julien-Vostro-7580 faass[222329]: INFO: 2022/08/14 20:34:24 faass.go:955: Authorization secret found ; API active
août 14 20:34:25 julien-Vostro-7580 systemd[2861]: Started Serverless Faass-httpd.service.
août 14 20:34:25 julien-Vostro-7580 faass[222329]: INFO: 2022/08/14 20:34:25 faass.go:780: known real desired url : /lambda/toto?bidule
août 14 20:34:25 julien-Vostro-7580 faass[222329]: INFO: 2022/08/14 20:34:25 faass.go:790: unknow desired url : toto ( unknow routes )
août 14 20:37:46 julien-Vostro-7580 systemd[2861]: Stopping Serverless Faass-httpd.service...
août 14 20:37:46 julien-Vostro-7580 echo[222599]: faass va s'éteindre
août 14 20:37:46 julien-Vostro-7580 systemd[2861]: Stopped Serverless Faass-httpd.service.

Vous pouvez vous rendre compte en visitant la page https://127.0.0.1:9090/lambda/toto?bidule à différent moment, soit qu'elle est accessible si une requête a été adressée sur 192.168.2.110:50000, soit qu'elle est inaccessible. La visite par l'adresse localhost ne produit dans tous les cas, aucun affichage côté journalctl et c'est bien normal : il est squizzé…

Enfin, un dernier moyen facile (au-delà du classique ps), c'est de regarder à plusieurs moment quels ports sont en écoute (notez la ligne avec LISTEN 221837/faass ) :

julien@julien-Vostro-7580:~/.config/systemd/user$ sudo netstat -ltpn 
Connexions Internet actives (seulement serveurs)
Proto Recv-Q Send-Q Adresse locale          Adresse distante        Etat       PID/Program name    
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      136133/cupsd        
tcp        0      0 192.168.2.110:50000     0.0.0.0:*               LISTEN      2861/systemd        
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      1407/systemd-resolv 
tcp        0      0 192.168.122.1:53        0.0.0.0:*               LISTEN      1967/dnsmasq        
tcp6       0      0 :::9090                 :::*                    LISTEN      221837/faass        
tcp6       0      0 :::1716                 :::*                    LISTEN      3322/kdeconnectd    
tcp6       0      0 ::1:631                 :::*                    LISTEN      136133/cupsd        
julien@julien-Vostro-7580:~/.config/systemd/user$ sudo netstat -ltpn 
Connexions Internet actives (seulement serveurs)
Proto Recv-Q Send-Q Adresse locale          Adresse distante        Etat       PID/Program name    
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      136133/cupsd        
tcp        0      0 192.168.2.110:50000     0.0.0.0:*               LISTEN      2861/systemd        
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      1407/systemd-resolv 
tcp        0      0 192.168.122.1:53        0.0.0.0:*               LISTEN      1967/dnsmasq        
tcp6       0      0 :::1716                 :::*                    LISTEN      3322/kdeconnectd    
tcp6       0      0 ::1:631                 :::*                    LISTEN      136133/cupsd    

Nous pouvons dire désormais que nous sommes vraiment dans un comportement scale-to-zero !


3. "L'expérience Go"

Je ne veux pas déchaîner ici un débat passionné sur ce langage, qui a des qualités (notamment defer) et des défauts ; si vous voulez faire votre hooligan, je vous en prie, mais n'attendez pas une réponse de ma part.

Par contre je suis curieux d'avoir vos avis (surtout s'ils sont nuancés).

Globalement je ressors de cette expérience d'apprentissage du Go, un côté plutôt pénible et peu satisfaisant. Pas un échec, mais pas une réussite non plus (j'imagine que le jugement définitif vient des connaisseurs qui auront le courage de lire mon code). J'ai réussi mon objectif de départ, mais sans l'effet "waaah".

Contrairement à Rust, Haskell ou Python - et l'ultime : Common LISP ! -, je n'ai pas eu "l'étincelle". Pour un "développeur" qui a le choix, c'est un critère important car on tient là à la motivation de long terme. Je l'ai choisi pour cet exercice car il me semblait adapté (même si je n'ai pas repris les modules Docker en Go, car je trouve que c'était œuvrer d'un marteau pour écraser une mouche), et sans grande envie.

Cela fait longtemps que j'en entends parler : mais après quelques recherches, on tombe vite sur quelques articles avec des arguments plutôt bétonnés sur ses défauts. La gestion catastrophique des modules (chemin, gestion des versions, etc.), une compilation qui ne garantie pas si bien que ça les défauts logiques (pointeur nul notamment), une grammaire sans enthousiasme… ça n'aide pas.

Dans une vidéo qui m'avait découragé à l'époque sur Golang, il y aussi parfois dans le choix de ce langage et de le porter au nu (ou de le démolir), pas mal de hype. Et c'est pas moi qui le dit mais un ingénieur de chez Docker, Jean-Laurent de Morlhon (sa conférence est d'ailleurs très intéressante) :

Je crois beaucoup à la comparaison entre Rust et Go. Les deux ont vocation au moment de leur création, à répondre au défi (1). de la croissance d'un projet en équipe nombreuse (interne à une organisation ou en open-source à travers de nombreux contributeurs), mais plus encore (2). à remplacer le C (et le C++), dans un grand nombre de situations.

Je crois que Rust gagne sur la plupart des tableaux face à Go, y compris sur la plan de l'apprentissage : Rust ne vous permet pas de ne pas comprendre ce que vous faites. Le problème n'est pas sa grammaire, mais notre incapacité assez générale, à imaginer qu'on comprend ce que l'on fait tout le temps, sans restriction, dans toutes les conséquences. Rust nous oblige à formaliser précisément notre pensée : ce n'est pas une courbe d'apprentissage, mais aussi la courbe de soumission à la réalité que notre code est souvent… un peu pourri (moi le premier).

Go n'est pas différent sur le principe : il chercher à rationaliser la manière de faire, à rendre universel et collectif. C'est le même objectif à mon sens, mais avec deux approches strictement distinctes.

Comme l'indique certains responsables de Google sur le "pourquoi" de ce langage, Go répond à cette problématique centrale d'aligner ces "manières de faire" pour gommer les différences entre les profils dans les équipes. Parfois en des termes un peu verts (cf les propos de Rob Pike) ! La volonté est louable, mais ça donne parfois l'impression de pisser du code. Certes j'ai été productif, et j'ai une version naïve d'un serveur FaaS en un millier de lignes (bouclé en quelques semaines, depuis je fais du zèle !). Mais je ne suis pas raisonnablement sûr et raisonnablement satisfait de mon résultat.

Plus largement Go souffre de défauts moins de jeunesse que de conception et de choix - dont trois me laissent vraiment perplexe :
- garbage collector à la place d'une désallocation statique… ?!
- la gestion des erreurs dans les retours (non, non, non),
- les go routines qui soient loin d'être aussi souples et faciles qu'affichées (si si si).

Je comprends parfaitement ceux qui l'apprécient car il a des qualités - et il a un support de poids, qui lui garantie une pérennité quasi-absolue. Mais je ne veux pas être dupe sur les alternatives : tant mieux si Google y trouve son compte, mais je ne suis pas Google, je n'y travaille pas et mon activité ne hasarde pas sur leurs problématiques.

Ce langage subit des failles selon moi dans sa construction, notamment des génériques "un peu à l'arrache", qui doivent être identiques pour tous les arguments d'une même fonction et cache la misère générale du système de type (duck typing et c’est tout… ?). La mise en œuvre semble avoir été une réponse rapide à un besoin de la communauté. A mon sens on ne le remplit que partiellement.

A défaut d'avoir les génériques immédiatement, on aurait pu naïvement imaginer que Go se dote de macros, y compris des macros à l'exécution, comme celles de LISP. Certes il existe des approches "préprocesseur" en jouant un peu avec Makefile mais objectivement, ce n'est absolument pas une manière idiomatique d'écrire du code Go - simplement de réécrire une partie d'un fichier en le remplaçant par un autre.

Pourtant, combiner à un système de signature comme pour les génériques à partir de Go 1.18 et des macros à la construction, le côté répétitif de certaines fonctions aurait été considérablement allégées ou la gestion des retours et des erreurs… Non, le sujet est encore trop douloureux…

Certes le langage Go est intéressant pour du développement de utilitaires ou d'applicatifs métier - sssi vous êtes une grande entreprise ou un projet vaste, ou un truc rapide, à caser entre deux repas. Cependant à mon échelle, dans mes besoins immédiats (pro ou perso), il n'existera pas vraiment comme un choix plausible - trop de concurrence intéressante.

Enfin il y a l'émergence de Carbon, là aussi poussé par des équipes chez Google - difficile pour moi de ne pas y voir un concurrent aussi à Go, et vu les échanges sur les forums ça et là, je ne suis pas le seul à me poser la question…

Bref Golang a gagné certaines de ses lettres de noblesses au travers de projet de qualité (et très, très visible, ou Kubernetes porté par… Google) comme Docker mais comme le montre la vidéo plus haut, c'était autant un choix commercial et de gestion, que véritablement un choix technique contraint où Go serait différenciant.

Ce langage j'en voie bien l'intérêt mais j'en vois bien aussi, et c'est gênant lorsqu’on le débute, les limites.

D'ici quelques semaines, mois ou années, peut-être j'y reviendrais avec un état d'esprit différent. L'âge fait gagner en maturité - qui est une façon élégante de dire qu'on est davantage patient et moins attentif à une forme de perfection. Alors, peut-être, il m'intéressera davantage.

En attendant je suis content d'avoir une PdC d'un serveur_ FaaS_ - peu importe en quoi il est écrit.

A la suite de ces lignes, je vais le lancer à pleine vitesse contre un platane, histoire de le tester.

Bonne nuit.

  • # Post-scriptum

    Posté par  . Évalué à 1.

    Il reste probablement plein de coquilles malgré la relecture… Par avance, veuillez m'en excuser !

  • # Ne pas craindre la Faucheuse

    Posté par  (site web personnel) . Évalué à 9.

    Plus largement Go souffre de défauts moins de jeunesse que de conception et de choix - dont trois me laissent vraiment perplexe :
    - garbage collector à la place d'une désallocation statique… ?!

    Les ramasses miettes modernes sont plutôt efficaces et simplifient énormément la vie des développeurs.

    A moins d'être en train d'écrire un driver, un langage à GC est un bon choix.

    Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

    • [^] # Re: Ne pas craindre la Faucheuse

      Posté par  . Évalué à 1.

      Les ramasses miettes modernes sont plutôt efficaces et simplifient énormément la vie des développeurs.

      Jusqu'au moment où ils la compliquent fortement, et on sait jamais quand ça va arriver.

      A moins d'être en train d'écrire un driver, un langage à GC est un bon choix.

      J'aurais tendance à dire partout où tu veux avoir des performances stables.

      • [^] # Re: Ne pas craindre la Faucheuse

        Posté par  (site web personnel) . Évalué à 5.

        Tu crains l'effet "stop the world" ? J'ai l'impression que le monde a été traumatisé par le Java des années 90 :-)

        Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

        • [^] # Re: Ne pas craindre la Faucheuse

          Posté par  . Évalué à 2.

          Tu dois être dans une dimension parallèle parce que je remarque que même des boites qui ont du goulot dans l'utilisation de java on eu récemment des soucis avec. Trois jeux en java que j'ai en tête :
          Minecraft
          Project Zomboid
          Star sector

          • [^] # Re: Ne pas craindre la Faucheuse

            Posté par  (site web personnel) . Évalué à 5.

            Un GC n'est pas magique : si tu gardes beaucoup d'objets en mémoire, il faudra du temps pour les libérer.

            C'est pareil avec une gestion manuelle : si tu gardes beaucoup d'objets en mémoire, il faudra du temps pour les libérer.

            Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

            • [^] # Re: Ne pas craindre la Faucheuse

              Posté par  . Évalué à 2.

              C'est le soucis, ça simplifie la vie du développeur dans certains cas. Et ça la rend plus pénible dans d'autres.

              Avec une gestion manuelle tu peux facilement prendre des mesures pour l'évacuer petit à petit, et choisir ce qui est primordial d'évacuer en fonction du contexte, et avoir une garantie de stabilité dans les performances.

              Enfin bon, je sais que je ne t'apprends rien :-)

              Mais je suppose qu'il existe une obscure classe java qui permet de choisir quels types d'objets tu peux libérer et pendant combien de temps, exécuté à la demande.. ?

              • [^] # Re: Ne pas craindre la Faucheuse

                Posté par  (site web personnel) . Évalué à 6. Dernière modification le 08 septembre 2022 à 17:07.

                Il y a des options de la jvm pour choisir un type de GC et le paramétrer.

                Sur mes jeux Java et Javascript, je fais simple : j'évite d'allouer de la mémoire pendant un niveau, je fais les allocations au chargement, je nettoie mes références à la fin.

                Quand la mémoire ne bouge pas et que le GC n'a rien à nettoyer, ça se passe bien.

                Ces "techniques" sont valables avec une gestion manuelle :-)

                Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

                • [^] # Re: Ne pas craindre la Faucheuse

                  Posté par  . Évalué à 2.

                  Quand la mémoire ne bouge pas et que le GC n'a rien à nettoyer, ça se passe bien.

                  je vois qu'on en arrive à la même conclusion :)

              • [^] # Re: Ne pas craindre la Faucheuse

                Posté par  . Évalué à 3.

                Avec une gestion manuelle tu peux facilement prendre des mesures pour l'évacuer petit à petit, et choisir ce qui est primordial d'évacuer en fonction du contexte, et avoir une garantie de stabilité dans les performances.

                Pas simplement. Les use-after-free, double-free, etc n'existeraient pas par exemple.

                La mémoire n'est pas une question triviale indépendamment du langage que tu utilise et la manière de simplifier ça c'est de se placer dans des cas typiques qui vont rendre mécaniques la gestion de la mémoire. Et quand elle est mécanique le faire à la main, via RAII, gc,… est simple.

                Ne pas s'intéresser à la question et se la prendre quand on découvre que ça fait des semaines, mois, années qu'on applique aucune règle posera toujours problème.

                rust te contrains à entrer dans des cas qu'il sait gérer (mais il y a des patterns qu'il ne sait pas gérer) dès le départ.

                https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                • [^] # Re: Ne pas craindre la Faucheuse

                  Posté par  . Évalué à 1.

                  Ne pas s'intéresser à la question et se la prendre quand on découvre que ça fait des semaines, mois, années qu'on applique aucune règle posera toujours problème.
                  rust te contrains à entrer dans des cas qu'il sait gérer (mais il y a des patterns qu'il ne sait pas gérer) dès le départ.

                  Perso c'est ce que j'apprécie dans Rust : je sais que je suis une quiche pour gérer la mémoire finement (manque de temps, de compétences), et il me garantie que les patterns qu'il contraint, forment la meilleure option qui m'est offerte face à l'étendu de mon ignorance. J'ai à la fois une garantie que ça fonctionne sans problème, que mon code est "juste" et avec plutôt les bonnes pratiques sur ces questions (parce que j'ai confiance dans la qualité de la communauté des développeurs derrière). Royal !

                  J'en profite pour mettre le lien vers la vidéo évoqué dans l'article qui hélas n'est pas passée dans le contenu de l'article : "Pourquoi Maurice ne doit surtout pas coder en GO", de Jean-Laurent de Morlhon (directement sur l'extrait sur le "pourquoi" réel de Go pour Docker).

                  • [^] # Re: Ne pas craindre la Faucheuse

                    Posté par  (site web personnel) . Évalué à 5.

                    Rust te propose des aides, mais il ne te garantie absolument pas que ton code gère bien la mémoire :-)

                    Pareil avec un GC : si tu gardes une référence vers un objet inutilement, ni Rust, ni Go, ni aucun langage ne va t'aider.

                    Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

                    • [^] # Re: Ne pas craindre la Faucheuse

                      Posté par  . Évalué à 1.

                      Pareil avec un GC : si tu gardes une référence vers un objet inutilement, ni Rust, ni Go, ni aucun langage ne va t'aider.

                      La gestion de l'ownership réduit pas mal le risque, mais tu as toujours la possibilité d'écrire des algos monstrueux d'un point de vue mémoire.

                      Je suis tombé il y a quelques semaines sur un exercice de simplification sur twitter où pour passer d'un algo en O(n4) à O(n2) pas mal de participants alloués une map de n2 éléments.

                      Même s'il n'a pas de garantie contre ça le borrow checker va limiter les risques de bugs quand tu voudra corriger.

                      La JVM et son outillage sont aussi de grandes aides pour détecter et corriger des problèmes mémoire. Je ne connais pas d'équivalent à Memory Analyser ailleurs par exemple.

                      https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                    • [^] # Re: Ne pas craindre la Faucheuse

                      Posté par  . Évalué à 1.

                      Rust te propose des aides, mais il ne te garantie absolument pas que ton code gère bien la mémoire :-)

                      J'entends bien ! :) Cependant comme l'indique barmic :

                      La gestion de l'ownership réduit pas mal le risque, mais tu as toujours la possibilité d'écrire des algos monstrueux d'un point de vue mémoire.

                      Si mon algo est foireux, mon code est de toute façon foireux. Je distingue bien les deux : la mise en œuvre (Rust y apporte des garanties sur la propriété et donc indirectement sur la gestion de la mémoire, le partage et la [dés]allocation) et la définition de la résolution (où aucun langage ne t'apporte une garantie, parce que par nature, ça relève du travail - pour l'instant - humain).

                  • [^] # Re: Ne pas craindre la Faucheuse

                    Posté par  . Évalué à 3.

                    J'en profite pour mettre le lien vers la vidéo évoqué dans l'article qui hélas n'est pas passée dans le contenu de l'article : "Pourquoi Maurice ne doit surtout pas coder en GO", de Jean-Laurent de Morlhon (directement sur l'extrait sur le "pourquoi" réel de Go pour Docker).

                    C'est moins un pourquoi qu'une petite pique.

                    https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                    • [^] # Re: Ne pas craindre la Faucheuse

                      Posté par  . Évalué à 1. Dernière modification le 10 septembre 2022 à 09:46.

                      Sur cette partie oui, mais le début de l'intervention est dans la même veine : "c'était nouveau", "pour pas avoir de débat des dév sur le choix du langage", etc. Dans son conclusion il le rappelle : c'était Go parce que système et "efficace" (efficient) du point de vue du projet (temps de compilation, organisation, etc.). Et qu'à la base, c'était parti d'un hack total.

                      Du reste au-delà du choix de Go par Docker, il y a sa critique pas très subtile des choix du langage, qui me semblent justifiée.

                • [^] # Re: Ne pas craindre la Faucheuse

                  Posté par  . Évalué à 2.

                  On est d'accord que rien n'est simple, mais tu as au moins des outils pour te permettre de jouer finement avec les désallocation pour rester dans une frame de temps spécifique. Ce qui, après une rapide recherche sur les les garbage collectors de java, js et (à moindre mesure ?) python, ne me semble pas être le cas.

                  • [^] # Re: Ne pas craindre la Faucheuse

                    Posté par  . Évalué à 2.

                    Mais tu as des outils d'analyse mémoire en java (et tout ce qui est basé sur la jvm) qui sont sans commune mesure avec ce que tu trouve ailleurs. Rien à voir avec valgrind.

                    Ensuite toujours pour java, tu as du tweak de jvm par rapport à ton code.

                    https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                    • [^] # Re: Ne pas craindre la Faucheuse

                      Posté par  . Évalué à 3.

                      Ensuite toujours pour java, tu as du tweak de jvm par rapport à ton code.

                      Veux-tu dire par qu'il est possible de s'assurer que le GC n'interrompe pas ou ne ralentisse pas l’exécution d'une portion de code a un moment inopiné ?

                      • [^] # Re: Ne pas craindre la Faucheuse

                        Posté par  . Évalué à 2.

                        Garantir non, mais que ce soit le cas dans les faits oui. Dis autrement, tu ne peux pas le prouver, tout en n'observant pas de cas où ça se produit.

                        Après je connais peu de gens qui s'assurent que leur soft ne soient pas préempté par le noyau. Si tu cherches du temps réel dur clairement, il faut soit pas utiliser de gc soit des gc faits pour ça (tu as des gc qui n'ont pas de stop the world). Si tu ne veux juste pas de freezes c'est tout à fait envisageable et il est possible de trouver des exemples qui fonctionnent avec et sans gc et des exemples qui fonctionnent pas avec et sans gc.

                        Bref si pour toi avoir des performances stable c'est du temps réel dur, c'est en effet compliqué, mais c'est du coup un usage assez limité.

                        https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                        • [^] # Re: Ne pas craindre la Faucheuse

                          Posté par  . Évalué à 3.

                          Après je connais peu de gens qui s'assurent que leur soft ne soient pas préempté par le noyau.

                          Pareil, mais ici on est pas du tout à ce niveau. Je reprends le cas de jeux parce que c'est ma seule expérience avec ce genre de soucis. Dans un cas standard avec un ordonnanceur qui fait bien son travail, le GC peut plomber les performance simplement parce que la désallocation se fait quand il l'a décidé, sans se soucier du temps qu'il a de disponible. C'est le cas avec le GC par défaut, les GC alternatifs proposés, et je n'ai pas vu d'alternative dédiée au respect du temps d'une frame quand j'ai fait mes maigres recherches à ce sujet. Mais je ne doute pas qu'il doit exister des GC exotiques que tu peux utiliser en patchant la jvm…

                          Et, en particulier dans le cas d'un jeu, avoir des performances généralement moins bonnes, ou d'avoir un gros drop de fps à cause d'une désallocation massive à un moment précis, ben ça fait la différence entre un jeu qui reste jouable ou non. Et c'est au développeur de trouver une parade.

                          Bref, pour en revenir sujet du thread, les GC peuvent être un soucis simplement quand le but est d'avoir un minimum de garantie de performances stables et sans que ce soit dans le contexte d'un driver.

                          • [^] # Re: Ne pas craindre la Faucheuse

                            Posté par  (site web personnel) . Évalué à 2. Dernière modification le 12 septembre 2022 à 10:58.

                            La parade est vite trouvée : ne pas faire de grosses allocations pendant les phases "dynamiques" du jeu :-)

                            Sinon en Java, il y a un ramasse miette qui ne ramasse aucune miette :-)

                            https://openjdk.org/jeps/318

                            Et aussi plein d'autres plus utiles: https://medium.com/swlh/a-brief-overview-of-garbage-collectors-in-java-because-cleanliness-is-necessary-f3dd9babc2cb

                            Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

                            • [^] # Re: Ne pas craindre la Faucheuse

                              Posté par  . Évalué à 2.

                              C'est le lien que j'ai posté plus haut :-)

                              Résumé, si tu dois allouer tout en ayant des perfs stables, mieux vaut virer les GC..

                              Note, ceci dit, que python fait vachement mieux que java dans ce domaine.

                              • [^] # Re: Ne pas craindre la Faucheuse

                                Posté par  (site web personnel) . Évalué à 3. Dernière modification le 12 septembre 2022 à 11:39.

                                Il y a sans doute plus de jeux écrit en Java qu'en Python :-)

                                Mais entre Python, C#, Java, Lua et Javascript, ça fait un paquet de jeux qui utilisent un ramasse miette sans souci.

                                Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

                                • [^] # Re: Ne pas craindre la Faucheuse

                                  Posté par  . Évalué à 3.

                                  Il y a sans doute plus de jeux écrit en Java qu'en Python :-)

                                  En même temps, vu le matraquage des évangélistes dès qu'on parle de java c'est pas étonnant que beaucoup n'aient pas osé chercher plus loin ;)

                                  Mais entre Python, C#, Java, Lua et Javascript, ça fait un paquet de jeux qui utilisent un ramasse miette sans souci.

                                  Oui, et c'est pas pour rien que le jeux qui ont besoin de beaucoup de données se passent de ces langages. Ou alors tu vois apparaître des bugs report avec GC dedans une fois que le jeu commence à avoir une taille conséquente de données à gérer.

                              • [^] # Re: Ne pas craindre la Faucheuse

                                Posté par  . Évalué à 2.

                                Note, ceci dit, que python fait vachement mieux que java dans ce domaine.

                                Je suis curieux. Tu as un pointeur ?

                                https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                          • [^] # Re: Ne pas craindre la Faucheuse

                            Posté par  . Évalué à 3.

                            Je parle de gc particuliers pour du temps réel, pour ne pas avoir de problème de temps de réponse les gc inclus dans openjdk font le travail. Non seulement ils font la majorité de leur travail sans bloquer ton application, mais en plus ils ont des options pour les contraindre en temps et en fréquence d'exécution.

                            Après tu as le tuning de jvm indépendent du gc. Tu peux dimensionner les générations et spécifier des configurations pour que les étapes du gc s'exécutent au moment opportun (ça n'est pas programmatique et impératif avec System.gc()).

                            Si tu te pose la question de frame de temps, c'est que tu contrôle finement tes allocations et tu va t'assurer que tes objets sont soient en old gen, soit qu'ils soient en young gen. Ta young gen est dimensionnée pour une seule de tes frame et le gc s'exécutera une fois par frame systématiquement et jamais plus que le temps que tu lui as donné. Son temps pour nettoyer ta young gen est tout à fait prédictible. C'est du grosse maille (oui faire du temps réel même soft c'est un métier), mais le gc peut t'expliquer quand il a fait quoi et pourquoi ça couplé aux autres possibilités d'observabilité de la jvm t'es pas laissé tout seul pour te dépatouiller et comprendre ce qu'il se passe.

                            Il a bon dos le gc, pas mal de fois où je vois le gc mis en cause il y a surtout des io synchrones utilisées un peu de partout et on essai de gagner 10ms de temps en temps sur le garbage collection sans s'inquiéter des 100ms de blocage fréquent dû à une io. Pour l'asynchrone c'est déjà bien plus hard de s'y retrouver, je sais qu'il y a un outil qui peut te prévenir si ton code sur la jvm fais des io bloquantes mais l'asynchrone peut rendre un code complexe et surtout fait perdre en prédictabilité des performances. Garantir que tu réponds sous un certain seuil demande à avoir des timeouts sur tout ce qui est asynchrone par exemple (du coup async/await t'aident plus beaucoup).

                            Désolé je suis un peu fleuve c'est pour rentrer un peu dans le détail et montrer l'approche. Il est bien sûr possible de faire mieux en faisant tout à la main, mais tu troque un contrôle plus simple (au sens KISS) pour un outillage très riche qui t'aide énormément à comprendre ce qui se passe. Le plafond indépassable en java ne semble pas à la portée de la première application même s'il existera toujours (mais est repoussé régulièrement encore l'an dernier avec zgc et Shenandoah).

                            En plus de ça tu peux toujours ne pas mettre les objets les plus volumineux dans la heap. Les assets sont volumineux et peuvent avoir des cycles de vie simples (on les charges et les libèrent à des points clefs) et ça permet de rester sur la simplicité d'usage pour le gros du code (celui qui est complexe, qui gère la partie logique de l'application) qui va interagir avec un gestionnaire d'assets qui sait quand charger/libérer les gros volumes. Ça marche aussi en utilisant un gc en bibliothèque en rust ou c++.

                            Bref j'ai pas été bref

                            https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                            • [^] # Re: Ne pas craindre la Faucheuse

                              Posté par  . Évalué à 3.

                              Tu peux dimensionner les générations et spécifier des configurations pour que les étapes du gc s'exécutent au moment opportun (ça n'est pas programmatique et impératif avec System.gc())

                              Donc, tu ne peux pas dire "il me reste 14ms pour cette frame-ci, gc pendant ce temps" et à la frame suivante lui demander de le faire pendant 5ms parce que tu as du faire du calcul un peu plus intensif.

                              Ta young gen est dimensionnée pour une seule de tes frame et le gc s'exécutera une fois par frame systématiquement et jamais plus que le temps que tu lui as donné

                              Mais rien de tout ça n'est dynamique vu ce que tu as écrit plus haut.

                              mais le gc peut t'expliquer quand il a fait quoi et pourquoi ça couplé aux autres possibilités d'observabilité de la jvm t'es pas laissé tout seul pour te dépatouiller et comprendre ce qu'il se passe.

                              C'est bien beau, mais quand ça dépend de l'utilisateur on est loin d'un truc qui scale automagiquement

                              Désolé je suis un peu fleuve c'est pour rentrer un peu dans le détail et montrer l'approche. Il est bien sûr possible de faire mieux en faisant tout à la main, mais tu troque un contrôle plus simple (au sens KISS) pour un outillage très riche qui t'aide énormément à comprendre ce qui se passe.

                              On est d'accord que l'utilisation d'une VM avec ses propres types te permet plus de possibilité au niveau de l'auscultation de ta mémoire. Mais si tu te retrouves dans le cas ci-dessus avec les garbage collector actuels, t'as rien d'autre à faire que tortiller tes données et virer des features pour pouvoir éviter d'avoir à nettoyer quand une gestion manuelle aurait pu t'éviter ce soucis.

                              Je ne dis pas qu'il n'est pas possible d'avoir un GC qui fonctionne sur ce principe, mais visiblement ça court pas les rues..

                              • [^] # Re: Ne pas craindre la Faucheuse

                                Posté par  . Évalué à 3.

                                Donc, tu ne peux pas dire "il me reste 14ms pour cette frame-ci, gc pendant ce temps" et à la frame suivante lui demander de le faire pendant 5ms parce que tu as du faire du calcul un peu plus intensif.

                                Je vois pas de cas où ça serait utile, cette gestion a un coût et peu être facilement source de bug.

                                Mais rien de tout ça n'est dynamique vu ce que tu as écrit plus haut.

                                Quand tu parle en frame tu n'es pas dynamique. Une frame est une période de temps. Quand tu construit ta logique avec ça, tu considère ton travail max à faire dans ta frame et c'est comme ça que tu dimensionne. Dans le cas des jeux puisque c'est l'exemple dont tu parlais, c'est pour cette raison là que tu as des limites de nombres de sprites par exemple.

                                mais le gc peut t'expliquer quand il a fait quoi et pourquoi ça couplé aux autres possibilités d'observabilité de la jvm t'es pas laissé tout seul pour te dépatouiller et comprendre ce qu'il se passe.

                                C'est bien beau, mais quand ça dépend de l'utilisateur on est loin d'un truc qui scale automagiquement

                                C'est une question de dimensionnement. Tu as une frame tu organise ce que tu peux faire ou non dans cette période. Tu met toujours une limite à ce qu'il est possible de faire sinon tu ne peux plus donner aucune garantie.

                                Mais si tu te retrouves dans le cas ci-dessus avec les garbage collector actuels, t'as rien d'autre à faire que tortiller tes données et virer des features pour pouvoir éviter d'avoir à nettoyer quand une gestion manuelle aurait pu t'éviter ce soucis.

                                "Tortiller tes données" oui et non. Il s'agit d'organiser ton logiciel autour des contraintes que tu lui donne. Si tu donne des contraintes forte de performance ça se verra sur ton code quelque soit le langage que tu choisi. Venir poser des contraintes très fortes pour ensuite reprocher que ça a un impact sur le code c'est assez vouloir le beurre et l'argent du beurre. Si tu fais des parallels arrays par exemple ou toute autre technique de data oriented design, tu va faire des choix qui vont s'approcher d'un "tortillage de donnée". C'est l'objectif même de rust de te contraindre à des formes de torillage de données pour ton bien et ceux que la mémoire soit un sujet ou non dans ton programme et il va te demander de débrailler des fonctionnalités (en particulier le borrow checker) pour arriver à ce que tu souhaite (un exemple de comment tu peux en arriver à tortiller tes données ou désactiver des fonctionnalités pour faire des références circulaires en rust).

                                Je vais arrêter là par contre, j'ai exposé, il me semble, des points précis sur comment faire et je n'ai pas l'impression que tes arguments aillent plus loin que « c'est pas assez parce que dans un contexte théorique on pourrait faire mieux », sans rentrer dans un cas pratique qui demanderait beaucoup de travail je ne vois pas comment aller plus loin dans la discussion et j'avoue que ça me fatigue un peu.

                                Bonne journée

                                https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                                • [^] # Re: Ne pas craindre la Faucheuse

                                  Posté par  . Évalué à 2.

                                  Je vois pas de cas où ça serait utile, cette gestion a un coût et peu être facilement source de bug.

                                  Oui, ça a un coût, ça peut être source de bug (tout comme n'importe quel code), mais tu maîtrises avec précision quand tu veux que ce coût t'impacte et tu peux l'étaler à ta convenance en fonction du contexte d'exécution.

                                  Je vais arrêter là par contre, j'ai exposé, il me semble, des points précis sur comment faire.

                                  Non. Tu t'es contenté d'essayer d'adapter le cas de départ pour qu'il puisse fonctionner avec les différentes solutions à base de GC que tu avais en mains, ce qu'on ferait si on était contraint d'en utiliser un, ou de dire que tu ne vois pas où ça serait utile.

                                  Bref, toujours pas de solution à base de GC pour garantir une frame, donc de la stabilité…

                                  • [^] # Re: Ne pas craindre la Faucheuse

                                    Posté par  (site web personnel) . Évalué à 4.

                                    Il faut voir quelle est la stabilité que tu cherches, car il y a bien d'autres sources d'instabilités que les quelques ms du GC:

                                    • le disque : si tu charges des textures dynamiquement depuis un disque dur, tu ne peux pas savoir si le temps de lecture sera stable ;
                                    • le réseau : dans un jeu multijoueur, tu ne peux pas compter sur un ping ou un débit stable ;
                                    • le CPU : l'OS peut décider d'allouer du temps à d'autres programmes ;
                                    • le GPU : le temps de rendu est-il vraiment stable ? A tester…
                                    • la carte son : même question ;
                                    • le jeu lui même : le joueur parcourt le donjon salle par salle, tout est fluide. Il le traverse en mode Benny Hill, il se retrouve avec 666 enemis à ses trousses, leur IA fait ramer le jeu.

                                    Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

                                  • [^] # Re: Ne pas craindre la Faucheuse

                                    Posté par  . Évalué à 1.

                                    Bon je remet une petite pièce.

                                    Tu t'es contenté d'essayer d'adapter le cas de départ pour qu'il puisse fonctionner avec les différentes solutions à base de GC que tu avais en mains,

                                    Tu a précisé le contexte dans le quel tu voulais te placer et je n'ai remis en cause que ton dernier point que je n'ai jamais vu appliqué.

                                    ce qu'on ferait si on était contraint d'en utiliser un

                                    C'était exactement l'objectif tu pense que c'était quoi ma thèse ? Montrer comment on fait avec un gc, en particulier sur la jvm que je connais mieux que les autres.

                                    […] ou de dire que tu ne vois pas où ça serait utile.

                                    Oui en absence de cas pratique où c'est utilisé, je veux bien qu'on me montre.

                                    Bref, toujours pas de solution à base de GC pour garantir une frame, donc de la stabilité…

                                    Ta façon de répéter continuellement comme s'il s'agissait d'une joute que tu souhaiterais gagner plutôt que comme un échange de comment parvenir à un objectif est extrêmement irritant. Bravo, tu as gagné. Tu m'a écrasé. Je n'ai plus d'argument et tu peux te conforter dans ton idée initial. L'échange a dû te rassurer au détriment de t'enrichir. Tant pis.

                                    https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

                                    • [^] # Re: Ne pas craindre la Faucheuse

                                      Posté par  . Évalué à 2.

                                      Oui en absence de cas pratique où c'est utilisé, je veux bien qu'on me montre.

                                      Ceux qui en avaient besoin ont migré vers d'autres langages où tu peux gérer finement la mémoire a force de se faire dire qu'ils n'en avaient pas besoin :-)

                                      Ta façon de répéter continuellement comme s'il s'agissait d'une joute que tu souhaiterais gagner plutôt que comme un échange de comment parvenir à un objectif est extrêmement irritant.

                                      J'essaie juste de recentrer sur le sujet initial qui a mené à cette échange de threads…

      • [^] # Re: Ne pas craindre la Faucheuse

        Posté par  . Évalué à 1. Dernière modification le 08 septembre 2022 à 20:27.

        Jusqu'au moment où ils la compliquent fortement, et on sait jamais quand ça va arriver.

        Source ?

        J'aurais tendance à dire partout où tu veux avoir des performances stables.

        OK…

        "Dis moi que tu n'as jamais utilisé de langage à GC depuis 20 ans sans dire que tu n'as jamais utilisé de langage à GC depuis 20 ans."

        "Quand certains râlent contre systemd, d'autres s'attaquent aux vrais problèmes." (merci Sinma !)

      • [^] # Re: Ne pas craindre la Faucheuse

        Posté par  . Évalué à 1.

        Jusqu'au moment où ils la compliquent fortement, et on sait jamais quand ça va arriver.

        Mais ça veut dire quoi en vrai ? Avec un exemple de code ?

        performances stables

        J'en ai constamment depuis 20 ans, suis-je un génie ?

        "Quand certains râlent contre systemd, d'autres s'attaquent aux vrais problèmes." (merci Sinma !)

  • # Go remplace Java

    Posté par  (site web personnel, Mastodon) . Évalué à 5.

    Il faut voir la réalité en face, Rust remplace C et C++ partout mais Go à changer de cible : Java. Et face à Java, il a de sacré avantages:
    - il n'a pas de royalties, ni sur le nom, ni sur la VM…
    - Il a des performances comparable mais est moins verbeux.
    - Il priduit un binaire sans dépendances alors que Java dépends non seulement de jar mais aussi de la version de la VM et pire de la conf de cette dernière.

    D'ailleurs, il n'y a qu'a regarder les aficionados de Go sont les entreprises la ou elles mettaient Java. Idem Rust va là où était C/C++…

    Sous licence Creative common. Lisez, copiez, modifiez faites en ce que vous voulez.

    • [^] # Re: Go remplace Java

      Posté par  . Évalué à 3.

      Tes arguments sont justes et je suis d'accord avec toi, j'ai l'impression qu'une partie des applications bascules de Java à Go. Surtout sur la portabilité de plus en plus simple et naturelle entre plateformes à l'ère des conteneurs, ça marginalise à mon sens la JVM.

      Par contre est-ce qu'il y a suffisamment de bibliothèques disponibles pour lui faire une vraie "ombre" ? (vraie question)

    • [^] # Re: Go remplace Java

      Posté par  (site web personnel) . Évalué à 6. Dernière modification le 09 septembre 2022 à 08:45.

      Et surtout Go est simple et il compile vite, deux points où Rust est très très très mauvais.

      Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

      • [^] # Re: Go remplace Java

        Posté par  . Évalué à 6.

        Tellement rapide. Je modifie un fichier, j'enregistre, le code est formaté, les imports ajustés, les erreurs de types détectées, le temps d'aller sur le bureau d'à côté et de recharger ma page le bouzin est déjà recompilé. C'est plus rapide que quand de relancer un serveur python !

  • # Knative, le graal.

    Posté par  . Évalué à 4.

    J'ai trouvé le graal du serverless sans la contrainte de réécrire les apps ! Ca s'appelle Knative, ça scale de zéro… Je dis knative pour ne pas citer Scaleway et CloudRun qui le proposent « as a service ». Le principe est simple, l'application doit écouter sur le port 8080 comme n'importe quel serveur http.
    Longtemps j'étais réticent à quitter l'infra à la papa où rien n'a changé ou presque sous le capot depuis 30 ans et qui permet de comprendre ce qui se passe et agir à tous les niveaux. Mais là j'avoue, c'est bluffant. Git push et voilà.

    L'avantage du Go dans ce cas il est simple, un Dockerfile avec un distroless on ne plus simple et le démarrage à froid se fait dans la seconde, une empreinte mémoire ridicule, un cpu qui reste au raz des pâquerettes. Tout ce qu'il faut pour faire un serveur http est dans la lib standard, même les templates html, même l'embarquement des assets dans le binaire !

    Autre avantage de Go c'est de pouvoir jongler soit avec des goroutines+channel en interne soit reproduire exactement la même chose en microservices séparés (les channels sont à sens unique). Il m'est arrivé plusieurs fois de passer de l'une à l'autre méthode en changeant quelques lignes de code simplement.

    Niveau de la simplicité du langage personnellement ça me rappelle le C de ma jeunesse, que de bons souvenirs !

    Bref, à part la partie cachée du serverless, je retrouve le côté simple d'unix un binaire qui vet voilà.

  • # MicroVM ?

    Posté par  . Évalué à 2.

    Pourquoi ne pas plutôt utiliser une microvm (firecracker ou qemu) qui me semble avoir été fait pour ce case d'utilisation plutôt que docker podman. En effet une microvm devrait booter en moins de 300ms.

    A l'origine l'idée à développer pour Amazon lambda : https://firecracker-microvm.github.io/
    et il a y une api rest.

    Sinon autant rester kiss et utiliser qemu via socket activation de systemd : https://qemu.readthedocs.io/en/latest/system/i386/microvm.html

    • [^] # Re: MicroVM ?

      Posté par  . Évalué à 1.

      Je ne connaissais pas Firecracker, et l'approche semble très intéressante. Par contre ça limite un environnement Linux (ce qui n'est pas pour me déplaire….).

      L'avantage que j'y vois, ce sont les cas un peu limite des conteneurs ou si la volonté d'isolation avec l'hôte est prioritaire. Par contre ça impose plus de travail amont vis-à-vis d'un conteneur sur la question des images (image d'environnement vs image kernel). Sur ce point, la prédominance et la facilité de Docker ou Podman, me semblent dominer.

      Quant au démarrage, je pense que c'est un débat d'implémentation. Je ne doute pas que Firecracker ou Qemu soit aussi rapide que Docker, peut-être davantage à la marge des conteneurs (c'est un sentiment, j'ai pas de preuves particulières pour étayer). Docker (ou Podman) peut être très rapide si on passe directement via sa socket et sans demander une conf complexe (ce qui est mon cas).

      Là j'ai fait un choix qui est un compromis sur l'implémentation, en passant par une invocation de commande, qui est le pire scénario en lancement individuel ("fonction"), mais très raisonnable pour la partie "service". Par contre je suis plus robuste sur un portage vers d'autres OS - à quelques ajustements prêts.

      Autres éléments à prendre en compte : l'API de Firecracker est certes fournie, mais elle correspond à la partie infrastructure / montage de la VM. Ce que je propose là, c'est cette abstraction justement, au profit d'une API qui se concentre sur "ajouter une route qui conduit vers un truc qui porte ma fonction" (avec des délais, une authentification, etc.).
      L'API de Firecracker ne gère donc aucun espace de nom pour organiser les microVM et faire la liaison entre une requête entrante / une microVM. Idem pour Qemu avec une socket activation.

      Firecracker n'est pas Amazon Lambda, mais seulement un bout de celui-ci. C'est un sous-ensemble parfaitement justifié pour les problématiques d'isolation, mais dans le cas que je présente (réseau privé et/ou de développement), rajoute un élément extérieur à mon binaire solitaire (qui plus est en Go, et la comm' Rust/Go…), sans révolutionner la nature de la problématique métier que je présente.

      • [^] # Re: MicroVM ?

        Posté par  . Évalué à 1.

        "à quelques ajustements près". Horreur.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.