Envision VM (part 1), Environnement et Architecture Générale
Cet article est le premier d’une série en quatre parties sur le fonctionnement interne de la machine virtuelle Envision : le logiciel qui exécute les scripts Envision. Voir part 2, part 3 et part 4. Cette série ne traite pas du compilateur Envision (peut-être une autre fois), donc supposons simplement que le script a d’une manière ou d’une autre été converti en bytecode que la machine virtuelle Envision prend en entrée.
Une pipeline d’optimization de la supply chain couvre un large éventail de besoins en traitement de données : ingestion et augmentation des données, extraction de caractéristiques, prévisions probabilistes, production de décisions optimales sous contraintes, exportation des données, analyses et création de tableaux de bord. La pipeline de chaque entreprise est différente, avec ses propres entrées, règles, contraintes et sorties. Le volume de données est important : d’après l’expérience de Lokad, même nos plus petits comptes doivent traiter des gigaoctets de données chaque jour, et nos comptes plus importants dépassent largement un téraoctet par jour. Comme la pipeline d’optimization attend généralement les entrées quotidiennes provenant du reste du traitement de données interne de l’entreprise, elle ne dispose que de quelques heures de temps de calcul pour optimiser les décisions de demain à partir des données d’aujourd’hui.

Ce n’est pas difficile. Pas vraiment. Traiter plusieurs téraoctets en quelques heures est un objectif de performance tout à fait à la portée d’une équipe d’ingénieurs logiciels compétents.
Le défi de Lokad est de construire de telles pipelines sans ingénieurs logiciels. Bien sûr, des ingénieurs logiciels travaillent chez Lokad, mais ils développent des outils et une infrastructure pour l’ensemble de l’entreprise, et non pour des clients individuels. À la place, nous disposons de nos Supply Chain Scientists - une équipe d’experts en supply chain qui comprennent parfaitement les problèmes spécifiques de chaque client et conçoivent les solutions. Dans une structure d’entreprise plus traditionnelle, ce seraient les chefs de produit, ceux qui écoutent les clients puis disent en détail aux ingénieurs logiciels ce qui doit être implémenté. Notre philosophie de développement, et la raison de la création d’Envision, notre propre langage de programmation, est qu’il devrait être plus rapide pour un Supply Chain Scientist d’implémenter lui-même la solution, que d’écrire les spécifications pour que quelqu’un d’autre l’implémente.
Pour y parvenir sans sacrifier l’échelle ni les performances, il y a trois fonctionnalités que Envision doit offrir gratuitement1, sans aucun effort de la part du Supply Chain Scientist.
- La gestion de la mémoire doit être automatique. Cela inclut évidemment la présence d’un ramasse-miettes, mais cela signifie également qu’Envision doit prendre en charge des ensembles de données à grande échelle de manière transparente. Créer un tableau de dix gigaoctets est une condition sine qua non : ce n’est même pas trois milliards de nombres ! La capacité de travailler avec un ensemble de données plus grand que la mémoire d’une seule machine est attendue. En fait, Envision supporte des ensembles de données qui dépassent la mémoire de l’ensemble du cluster, en déversant astucieusement sur des disques NVMe.
- Le parallélisme multi-cœurs et multi-machines doit être automatique. Les opérations embarrassingly parallel devraient être réparties sur autant de cœurs que possible dans le cluster, sans intervention humaine. Un script devrait survivre à la panne d’une machine dans le cluster sans avoir à recommencer depuis le début. Un script devrait être capable de s’exécuter même si le cluster est réduit à une seule machine. Et, bien entendu, deux exécutions d’Envision devraient pouvoir s’exécuter simultanément sur le même cluster.
- Nous attendons d’Envision qu’il requière peu de matériel pour s’exécuter. De nombreux problèmes de performance peuvent être résolus en dépensant des millions de dollars pour du matériel haut de gamme et/ou des licences serveurs, mais nous préférerions éviter une situation où cliquer sur le bouton “Run” d’un script coûterait des centaines de dollars.
Les langages de programmation à usage général ne fournissent pas ces fonctionnalités, et même s’ils peuvent généralement être combinés avec des frameworks qui le font (Scala + Spark, Python + Dask, et autres), cela laisse trop d’arêtes vives exposées à l’utilisateur. En ce sens, Envision est plus comparable à SQL s’exécutant sur BigQuery.
Environnement
Envision ne peut pas être installé ou exécuté localement. À la place, tous les utilisateurs se connectent à la plateforme en ligne de Lokad, qui offre un IDE basé sur le navigateur pour éditer et exécuter des scripts. Les données et les tableaux de bord sont également stockés en ligne et accessibles via une interface web, ainsi que via SFTP et FTPS.
Lorsqu’un utilisateur exécute un script via l’interface web, cela crée une mission qui est envoyée à un cluster Envision pour exécution.
Envision fonctionne en mode batch : chaque exécution lit l’intégralité des données d’entrée et produit une sortie complète. Cela peut prendre entre 5 secondes pour un script très simple fonctionnant sur peu de données, et 30-40 minutes pour un script d’augmentation de données important, voire plusieurs heures pour certaines tâches de machine learning.
D’autres modes d’exécution, tels que le traitement en flux (à l’écoute de nouvelles entrées et produisant la sortie correspondante à la volée) ou l’accès transactionnel (ne lisant que quelques lignes de données, en écrivant quelques lignes en retour) ne sont pas pris en charge : les primitives du langage et les détails d’implémentation de bas niveau nécessaires pour exécuter ces modes avec une performance décente entrent en contradiction avec ceux requis pour le traitement par lots.
Structure du Cluster
Depuis 2021, Envision fonctionne entièrement sur .NET 5, hébergé sur des machines virtuelles Ubuntu 20.04 dans le cloud Microsoft Azure. Un cluster est composé de 2 à 6 instances Standard_L32s_v2: 32× cœurs AMD EPYC 7551, 256GiB de mémoire, et 4× disques NVMe totalisant 7,68TB d’espace de stockage. Nous appelons ces machines des workers.
Le cluster entretient une association M-à-N entre les workers et les missions : un seul worker peut exécuter plusieurs scripts Envision simultanément, et si un script est assigné à plusieurs workers, ils coopéreront pour le terminer plus rapidement.
Chaque cluster dispose également d’un scheduler, un Standard_B2ms plus petit avec 2× cœurs et 8GiB de mémoire. Le scheduler fournit les points d’accès API pour que des applications externes soumettent de nouvelles missions et collectent les résultats des missions terminées. Il est également responsable de la répartition des missions sur une ou plusieurs machines du cluster. Selon la charge et le degré de parallélisme disponible pour chaque script à tout moment, le scheduler peut ajouter ou supprimer des workers d’une mission.
L’ensemble du système a été conçu pour être résilient : une fois qu’une mission a été assignée à un worker, même si tous les autres workers du cluster, ainsi que le scheduler, se déconnectent, le worker survivant pourra tout de même terminer la mission. En ce sens, la coopération entre workers et les réaffectations de missions effectuées par le scheduler sont des optimisations de performance, elles ne sont pas nécessaires à l’achèvement de la mission.
Blob Storage
Lokad n’utilise pas de bases de données SQL pour les données clients. Les solutions hébergées ne peuvent pas facilement contenir les ensembles de données de nos plus grands clients (ils ont tendance à plafonner entre 4TB et 16TB), et faire fonctionner nos propres serveurs nécessiterait des efforts que nous préférerions consacrer ailleurs.
En revanche, Envision fonctionne en mode batch, ce qui élimine le besoin de requêtes plus complexes que “Read column X between lines L and M”: une fois les données d’entrée chargées, le worker sera capable de les indexer et de les retraiter si nécessaire.
Parce que de cela, nous utilisons Azure Blob Storage comme stockage principal. Il nous permet de stocker plus d’un pétaoctet à moins de 1% du coût des bases de données SQL hébergées, et la performance des requêtes de lecture se situe de manière fiable entre 30MB/s et 60MB/s.
Nous avons également rendu nos blobs immuables : il est possible de créer de nouveaux blobs, mais pas de modifier ceux existants. Cela garantit que les entrées d’un script ne peuvent pas être modifiées pendant son exécution, et que les sorties d’un script ne peuvent être consultées qu’une fois l’exécution terminée et les identifiants des nouveaux blobs retournés.
Pour être précis, Lokad a construit un Content-Addressable Store au-dessus d’Azure Blob Storage. Il est open source, et disponible sous forme d’une paire de packages NuGet Lokad.ContentAddr et Lokad.ContentAddr.Azure. Connaître le hash des fichiers individuels permet à Envision de déterminer que certains de ses inputs n’ont pas changé, afin qu’il puisse réutiliser les valeurs calculées conservées en cache d’une exécution précédente.
Déploiement
Envision n’utilise aucune containerisation (comme Docker), car les avantages des conteneurs ne justifient pas la complexité supplémentaire impliquée.
Premièrement, le calcul haute performance requiert chaque dernière goutte de CPU, RAM et stockage de nos workers, et il n’est donc pas possible d’exécuter plusieurs applications sur la même machine.
Deuxièmement, pour empaqueter une application avec toutes ses dépendances de manière indépendante de la plateforme, nous avons trouvé que dotnet publish
était suffisant (et, en fait, plus rapide que docker build
). Avec .NET 5, Microsoft offre un support multi-plateformes exceptionnel, et il suffit littéralement de copier les résultats d’une compilation depuis une machine Windows vers un hôte Linux.
Enfin, créer rapidement de nouvelles instances à partir de zéro est quelque chose que nous évitons activement. Bien que nous puissions arrêter des workers pour réduire les coûts, créer de nouveaux clusters ou ajouter davantage de workers aux clusters existants est une décision financière : nous ne facturons pas nos clients en fonction de l’utilisation des ressources, de sorte que nous n’avons aucun moyen de répercuter les coûts supplémentaires.
La semaine prochaine, nous plongerons dans le modèle d’exécution d’Envision, et dans la manière dont le travail à accomplir est représenté au sein du cluster.
Petit encart promotionnel : nous recrutons des software engineers. Le télétravail est possible.
-
rien n’est vraiment gratuit, et nous en payons le prix en sacrifiant la capacité d’Envision à agir comme un langage de programmation à usage général. Par exemple, la parallélisation automatique signifie que nous ne prendrons jamais en charge le contrôle explicite des threads dans Envision ; le traitement automatique multi-machines signifie qu’il n’y aura jamais le concept de “local machine” dans Envision. ↩︎