2 points par GN⁺ 2024-01-24 | 1 commentaires | Partager sur WhatsApp
  • Dans des environnements où le nombre de cibles de configuration augmente, comme Kubernetes, multiplier les fichiers YAML écrits à la main atteint vite ses limites, et une approche qui génère les données de configuration devient plus adaptée que les templates YAML
  • Les charts Helm injectent des valeurs via values.yaml et des templates Go, mais dès qu’entrent en jeu des champs optionnels, des tableaux ou des maps, le poids des conditions et de l’indentation augmente fortement
  • YAML impose des règles strictes sur les espaces, mais le parseur de templates Helm ne comprend pas la structure YAML, si bien que la combinaison de toYaml et indent conduit facilement à une génération de configuration fragile
  • YAML étant un sur-ensemble de JSON, les conversions entre les deux sont simples, et Jsonnet traite la génération d’objets de configuration comme du code grâce aux variables externes, aux champs conditionnels, à la composition de maps et à la fusion d’objets
  • kr8 adopte un flux basé sur Jsonnet pour créer et manipuler les configurations de plusieurs clusters Kubernetes, en préférant générer et transformer directement des objets plutôt que d’assembler des chaînes YAML complexes

La complexité de la configuration commence quand le nombre de fichiers YAML augmente

  • Dès que les applications et l’infrastructure dépassent une certaine taille, la complexité de configuration augmente rapidement
  • Quand il n’y a qu’une ou deux cibles de déploiement, écrire directement les fichiers de configuration YAML peut suffire, mais au-delà, il faut gérer la configuration de manière structurée
  • Si plusieurs fichiers de configuration sont nécessaires, c’est généralement parce que certaines valeurs diffèrent pour une même cible
    • des déploiements par environnement comme dev, stg, prod
    • des déploiements par région comme Europe, Amérique du Nord
  • Toutes les configurations ne sont pas différentes, mais si les écarts sont suffisamment importants, il faut séparer et gérer d’un côté les parties communes et de l’autre les différences
  • Le domaine de la gestion de configuration traite ce problème depuis longtemps, et divers outils ont utilisé YAML chacun à leur manière
  • hiera, inclus dans Puppet, permet de rechercher des variables de façon hiérarchique, ce qui est puissant et flexible, et réduit fortement le besoin de templatiser le YAML lui-même

Les problèmes des templates YAML mis en évidence par les charts Helm

  • Avec le cloud computing et Kubernetes, les cibles de configuration se sont étendues au-dessus de la couche système d’exploitation, et des outils comme CloudFormation et Helm sont apparus
  • Un chart Helm peut être rendu à partir de paramètres externes définis dans values.yaml
  • Les valeurs de chaîne simples sont relativement faciles à gérer
image: "{{ .Values.image }}"
  • Si une valeur image est définie dans values.yaml, elle est injectée dans le template
  • Les choses se compliquent dès qu’on commence à gérer des configurations plus complexes, comme les champs optionnels
{{- with .resourceGroup  }}
    resourceGroup: {{ .  }}
{{- end }}
  • Comme une valeur optionnelle ne peut pas simplement être laissée vide, il faut des conditions et des boucles, et le template devient vite désordonné
  • Pour insérer un tableau ou une map, il faut combiner toYaml et indent
{{- with .Values.podAnnotations  }}
      annotations:
{{ toYaml . | indent 8  }}
{{- end  }}
  • L’appel d’une fonction qui reconvertit du YAML en YAML avec toYaml paraît déjà maladroit, mais le vrai problème est la gestion des espaces

Le conflit entre les règles d’espacement de YAML et le moteur de templates

  • YAML impose des règles strictes d’indentation et d’espacement
  • L’exemple suivant n’est ni un YAML valide ni un YAML complet
something: nothing
  hello: goodbye
  • Si un humain l’écrit directement, quelques pressions sur Retour arrière suffisent à corriger le problème, mais générer du YAML avec un système de templates n’a rien de simple
  • Dès qu’on dépasse 5 à 10 fichiers de configuration, il devient nécessaire de générer la configuration plutôt que de l’écrire directement
  • Pour insérer la valeur de .Values.podAnnotations sous annotations, déjà indenté, il faut que cette valeur soit elle-même indentée exactement au bon niveau
  • Comme le parseur de templates Go ne comprend pas YAML, essayer d’indenter joliment la syntaxe de template pose lui aussi problème
{{- with .Values.podAnnotations }}
      annotations:
      {{ toYaml . | indent 6 }}
{{- end  }}
  • Quand un système de templates manipule à la fois les espaces et les conditions sans connaître la structure YAML, la génération de configurations complexes devient de plus en plus difficile
  • Écrire directement du JSON n’est pas non plus adapté, à cause de l’absence de commentaires et des problèmes de virgules oubliées ; c’est d’ailleurs ce genre d’inconfort qui a favorisé l’usage de YAML

Jsonnet est un langage de templates de données pour générer des configurations JSON

  • YAML est un sur-ensemble de JSON, donc les conversions entre JSON et YAML sont simples
  • De nombreuses applications et de nombreux langages savent parser ou convertir JSON et YAML nativement
  • En Python aussi, on peut lire du YAML et produire du JSON
python -c 'import json, sys, yaml ; y=yaml.safe_load(sys.stdin.read()) ; print(json.dumps(y))'
  • Jsonnet se présente comme un langage de templates de données, dont l’objectif principal est de générer des configurations JSON
  • Le contexte de conception de Jsonnet est expliqué dans sa design rationale

Variables externes et gestion des champs optionnels

  • Jsonnet permet d’injecter des valeurs de configuration via des variables externes
{

  image: std.extVar('image'),

}
  • En passant une variable externe en ligne de commande, on obtient un résultat JSON
jsonnet image.jsonnet -V image="my-image"
{
   "image": "my-image"
}
  • Les champs optionnels peuvent être exprimés comme des conditions dans le code, sans insérer de logique conditionnelle au milieu d’une chaîne de template
// define a variable - yes, jsonnet also has comments
local rg = null;
{

  image: std.extVar('image'),
  // if the variable is null, this will be blank
  [if rg != null then 'resourceGroup']: rg,

}
  • Si rg vaut null, le champ resourceGroup n’est pas inclus dans le résultat
  • Si une valeur est fournie, le champ est produit

Manipuler des maps et des objets est plus simple que gérer l’indentation YAML

  • Quand il faut placer une map dans une configuration, comme pour des annotations de pod Kubernetes, Jsonnet permet de définir la valeur comme variable puis de la placer dans l’objet
local annotations = {
  'nginx.ingress.kubernetes.io/app-root': '/',
  'nginx.ingress.kubernetes.io/enable-cors': true,
};

{
  metadata: { // annotations are nested under the metadata of a pod
    annotations: annotations,
  },

}
  • Cette approche est bien plus simple que d’ajuster l’indentation dans un template YAML
  • Le résultat généré est un objet JSON contenant la map d’annotations sous metadata.annotations
{
   "metadata": {
      "annotations": {
         "nginx.ingress.kubernetes.io/app-root": "/",
         "nginx.ingress.kubernetes.io/enable-cors": true
      }
   }
}
  • Ajouter une annotation à un objet existant peut aussi se faire dans Jsonnet avec l’opérateur +
local annotations = {
  'nginx.ingress.kubernetes.io/app-root': '/',
  'nginx.ingress.kubernetes.io/enable-cors': true,
};

{
  metadata: {
    annotations: annotations,
  },
} + { // this adds another JSON object
  metadata+: { // I'm using the + operator, so we'll append to the existing metadata
    annotations+: { // same as above
      something: 'nothing',
    },
  },
}
  • L’objet résultant contient l’annotation existante ainsi que something: "nothing"
{
   "metadata": {
      "annotations": {
         "nginx.ingress.kubernetes.io/app-root": "/",
         "nginx.ingress.kubernetes.io/enable-cors": true,
         "something": "nothing"
      }
   }
}
  • Dans un exemple simple, le code peut sembler plus long, mais plus la configuration devient complexe, plus cette façon de manipuler les objets devient utile

kr8 gère les configurations Kubernetes avec l’approche Jsonnet

  • kr8 utilise cette méthode pour créer et manipuler simplement les configurations de plusieurs clusters Kubernetes
  • Le principe central consiste à générer des objets de configuration JSON et à les transformer selon les besoins, plutôt qu’à assembler des templates YAML à coups d’espaces et de conditions

1 commentaires

 
GN⁺ 2024-01-24
Avis sur Hacker News
  • J’en ai désormais complètement assez des configurations écrites en YAML. C’est ce que je déteste le plus dans GitHub Actions, pire encore que sa fiabilité
    Quand je vois un excellent outil exiger un fichier YAML pour sa configuration, je m’inquiète immédiatement. Il en va de même pour les langages de configuration propriétaires comme le HCL de Terraform ou l’ASL d’AWS Step Functions
    Vouloir une API déclarative, c’est très bien, mais j’aimerais qu’on puisse générer cette déclaration par programme. Déclarer et générer la configuration avec du code a été une bien meilleure expérience, et AWS CDK le fait vraiment bien
    On peut écrire des définitions d’infrastructure cloud avec un langage typé et une bonne prise en charge par l’IDE, sans dépendre d’un plug-in qui n’a pas été mis à jour depuis deux ans

    • À ce stade, je trouve que le JSON pur est meilleur que YAML. Ce qui m’a décidé, c’est que deno fmt a un formateur JSON, mais pas de formateur YAML
      Le formateur JSON est un binaire unique qui s’exécute en quelques millisecondes, alors que pour formater automatiquement du YAML, il faut en pratique utiliser Prettier, qui dépend de la moitié de NPM et met environ 2 secondes à démarrer et à s’exécuter
      J’ai donc déplacé vers JSON tous les fichiers YAML qui pouvaient l’être dans le dépôt de l’entreprise, et au moins moi, j’en suis beaucoup plus satisfait. Personne ne s’en est plaint
      Plusieurs éditeurs prennent aussi en charge la balise $schema de JSON. Nous avons ajouté cette fonctionnalité au produit, et c’est vraiment agréable de pouvoir créer un fichier de configuration en appuyant simplement sur Tab, sans lire la documentation
      C’est aussi possible avec YAML via YAML language server, mais comme la touche Tab sert aussi à l’indentation, l’ergonomie est médiocre. JSON n’est pas parfait non plus, mais au moins le texte "no" n’est pas vrai
    • On entend souvent dire que l’avantage de YAML par rapport à JSON est que JSON n’a pas de commentaires, mais je ne comprends pas pourquoi il faudrait passer à un langage complètement différent
      Ne suffirait-il pas d’ajouter un filtre qui supprime les commentaires avant de lire la configuration ? Cela ne peut pas être plus difficile que de passer à YAML
      Je ne comprends pas non plus vraiment l’idée selon laquelle YAML serait plus lisible. La douleur liée à la gestion de la configuration ne vient pas du fait qu’on passe quelques secondes de moins à analyser des accolades et des crochets, mais du fait qu’il est difficile de comprendre ce qui ne va pas, au milieu de centaines de lignes de configuration, à cause d’un espace manquant ou d’une tabulation
    • GitHub Actions aurait été mauvais quel que soit le format de configuration. Le problème est qu’on essaie de décrire un programme avec des structures de données
      Ansible commet la même erreur, tout comme une multitude d’outils
    • Je pense à peu près la même chose depuis l’époque d’AWS CloudFormation. Il y a quatre ans, j’ai créé un générateur CloudFormation expérimental qui générait toutes les ressources et les annotations de type Python à partir d’un fichier JSON publié par AWS, et il fonctionnait plutôt bien : https://github.com/weberc2/nimbus/blob/master/examples/src/n...
      Je ne sais pas vraiment si CDK fonctionne de cette façon. Quand je l’ai un peu essayé, l’expérience était assez différente de celle de ma « génération CloudFormation », et je n’ai pas vraiment saisi les avantages de CDK
      J’avais l’impression qu’on remplaçait le problème YAML/templates par un problème d’héritage et de magie. J’aimerais entendre davantage de retours de personnes ayant utilisé AWS CDK, Terraform CDK et Pulumi
    • Dans GitHub Actions, c’est encore plus pénible parce que les ancres YAML ne sont pas prises en charge. C’est dommage, car elles auraient offert un minimum de composabilité
      https://github.com/actions/runner/issues/1182
  • Je suis d’accord pour dire que les templates YAML sont assez délirants, mais je n’ai jamais compris pourquoi on n’arrête pas d’utiliser de faux langages pour passer à un vrai langage de programmation
    Si l’on a besoin d’une logique complexe, il suffit de générer du YAML/JSON/ce qu’on veut avec un langage de programmation. Ruby, Python ou n’importe quel autre langage fournit ce qu’il faut, sans pseudo-langages bizarres comme Jsonnet ou les templates Go
    Il suffit d’écrire du code : on se fait beaucoup moins piéger par les problèmes opaques et étranges des moteurs de templates. N’importe quel vrai langage sera largement préférable

    • J’ai déjà été chargé de maintenir des playbooks Ansible exécutés périodiquement sur un nombre énorme de serveurs pour le bootstrap et les correctifs
      J’avais utilisé Chef pour des tâches similaires, et comme c’était du Ruby, j’appréciais de pouvoir définir facilement la logique voulue, utiliser des boucles et de vraies variables
      Je comprends qu’Ansible ait été conçu pour des non-programmeurs, mais pour quelqu’un à l’aise avec les bases de la programmation, rien n’est plus infernal que d’enfermer des playbooks Ansible riches en tâches conditionnelles et en itérations dans la syntaxe verbeuse des templates Jinja
    • Dans les stacks actuelles, l’architecture consistant à faire de l’embedding de langage semble presque oubliée. Autrefois, pour une application suffisamment complexe, on écrivait le cœur en C/C++/Java, etc., et si l’on avait besoin de scripting, on embarquait par-dessus quelque chose comme LISP ou Lua
      Aujourd’hui, la norme consiste plutôt à prendre un parseur JSON/TOML/YAML et à écrire une fonction readConfig, même dans des cas où un interpréteur embarqué serait plus adapté
      Du point de vue des développeurs, il est plus facile d’ajouter de la complexité à un format de configuration que de fournir un véritable embedding de langage avec des bindings vers l’application. On dirait donc qu’ils ont oublié la méthode elle-même, ou qu’ils n’imaginent même pas que ce soit possible
    • Pulumi est séduisant parce qu’il permet d’écrire dans le langage de son choix et de se débarrasser de HCL, mais personnellement je le trouve clairement pire. Le code d’infrastructure doit être déclaratif pour gagner en prévisibilité, en reproductibilité et en maintenabilité
      À l’époque de Chef/Puppet, beaucoup d’organisations ont commencé à mettre de la logique dans leur IaC et se sont retrouvées avec d’immenses bazars impossibles à mettre à niveau ou à maintenir. L’approche Chef/Pulumi est possible, mais elle exige des personnes très strictes sur le style et la maintenance
      Pour les grandes équipes et la maintenance à long terme, je pense que le modèle Terraform/Puppet est meilleur. Même si HCL est pénible et que Python/TypeScript, etc. donnent une impression de libération, le code purement déclaratif évite beaucoup de spaghetti
    • Le problème, c’est que les passionnés de langages créent des langages pour d’autres passionnés de langages
      Ils veulent y mettre les éléments de conception à la mode, veulent l’auto-hébergement, et finissent aussi par vouloir permettre d’écrire un serveur web multithread rapide, ce qui rend le tout conceptuellement complexe
      Il faudrait un langage jouet simple, à la Logo, pour les ingénieurs système/DevOps. À l’origine, il devrait pouvoir être expliqué dans un livre de la taille du K&R C
      Il faudrait un typage dynamique, des structures de contrôle apprenables en un week-end, pas de threading ni de concurrence, pas d’orienté objet ni d’héritage, une conception fonctionnelle/modulaire, et un modèle de FFI permettant d’appeler facilement d’autres langages et frameworks, et d’être appelé par eux
      Le problème, c’est que les passionnés de langages ne savent pas se retenir : ils ajoutent sans cesse des fonctionnalités, qui finissent dans les bibliothèques de base et les guides de style, si bien que même les débutants doivent tout apprendre
      Moi aussi, je serais tenté d’ajouter des fonctions du genre each/map aux tableaux/hashmaps, puis des fonctions de première classe et des closures, mais ce serait peut-être une erreur. Il existe déjà des langages fonctionnels immuables pour la configuration, mais plus de 95 % des gens qui utilisent du YAML templatisé ne veulent pas apprendre à programmer de cette manière, ce qui rend leur adoption difficile
    • Je veux insister sur le fait qu’on génère des fichiers de configuration. Il est très utile de limiter la configuration elle-même à une forme pouvant tenir dans des fichiers JSON, etc.
      La configuration devient plus simple, plus facile à consommer et plus facile à documenter. Mais lorsqu’on écrit les fichiers de configuration, il faut utiliser un langage de programmation, de préférence un langage statiquement typé offrant vérification des erreurs, autocomplétion et documentation inline
      AWS CDK en est un bon exemple. Écrire du CloudFormation pur est pénible, mais CDK n’ajoute pas des fonctionnalités de programmation à CloudFormation : il génère du CloudFormation. L’entrée consommée par AWS reste du CloudFormation relativement simple et stable
  • Rien qu’en voyant le titre, je me suis dit que ça parlerait de Kubernetes
    L’API Kubernetes est assez intuitive et dispose d’un schéma JSON bien défini. La majeure partie du temps passé à apprendre k8s devrait servir à comprendre comment utiliser l’API, mais en pratique elle sert à comprendre comment utiliser les charts Helm
    Je n’ai pas l’impression que Jsonnet, Ksonnet, Nu ou CUE aient connu un si grand succès. La plupart des gens semblent utiliser Kustomize, parce que c’est relativement intuitif et intégré à kubectl
    L’outil que je voudrais devrait fournir aux auteurs de définitions une vérification de types, une validation et des avertissements de dépréciation de versions pour le schéma k8s, produire un artefact unique que les utilisateurs puissent facilement inspecter, échouer atomiquement si le cluster ne prend pas en charge certains objets/versions, et être intégré à la chaîne d’outils de base
    Un script TypeScript Bun ou Deno qui exporterait une fonction prenant des arguments et renvoyant une liste de définitions irait probablement bien avec deno compile et autres, mais cela enfreindrait la condition d’intégration à la chaîne d’outils de base

    • C’est un schéma qu’on retrouve dans tout le logiciel. Au lieu d’apprendre les primitives et les bases sur lesquelles le système repose, on apprend tout un tas d’abstractions par-dessus, sous prétexte que c’est trop difficile
      On est protégé des détails de bas niveau, mais quand un problème survient, on doit affronter une énorme pile d’abstractions qui rend le diagnostic et le débogage difficiles
      Il devient beaucoup plus difficile de comprendre ce qui se passe réellement, et on dépend des couches d’abstraction, avec les mises à jour publiées par les fournisseurs et les autres problèmes dans le graphe des dépendances
    • Dans notre système, nous utilisons jsonnet, et ça n’a absolument rien à voir avec k8s. Ce n’est pas tant un outil devenu très populaire qu’un outil de niche pour les configurations complexes, et il n’a pas été beaucoup promu
      Il fait à peu près ce dont nous avons besoin sans problème, il est multiplateforme et utilisable dans plusieurs langages. Je l’ai intégré dans des exécutables C++, .NET et JVM
      La configuration JSON produite peut être utilisée avec un vaste écosystème d’outils qu’on trouve difficilement avec les alternatives toml/yaml/hocon/ini, etc. J’ai essayé d’utiliser HOCON dans des langages non JVM, mais il y avait toujours un cas limite qui coinçait
    • Il ne répond probablement pas au deuxième critère, et certainement pas au troisième, mais : https://cdk8s.io/docs/latest/
    • L’idée de rester simple est bonne, et pour le mode d’installation j’essaie aussi d’utiliser autant que possible kustomize ou du YAML pur
      Mais lorsqu’on gère réellement de gros systèmes, on ne peut finalement pas éviter les avantages des templates
    • kustomize, et surtout helm, sont beaucoup trop déroutants, alors que les fichiers YAML Kubernetes sont très faciles à écrire et à comprendre
  • C’est amusant de voir à quel point les développeurs réfléchissent peu à la bonne façon de gérer la configuration
    On dirait simplement un ensemble de clés et de valeurs stockées dans un fichier ou générées par du code, mais en réalité c’est tout. C’est la programmation elle-même
    Tout est configuration, et tous les arguments de fonction sont aussi une forme de configuration. Toute configuration dans un fichier externe finit d’une manière ou d’une autre par devenir un argument de fonction
    Le problème, c’est la représentation en texte brut du code. Les fichiers de configuration déclaratifs semblent pratiques parce qu’on peut tout voir au même endroit, mais quand on fait de la configuration un programme, il devient difficile de trouver quoi modifier
    Si le code s’exécutait en temps réel pour montrer la représentation de la configuration finale, et permettait de retracer comment chaque valeur finale a été générée, ce ne serait pas un problème. Pourtant, même si cette fonctionnalité est assez simple, aucun système n’est conçu de cette façon. La configuration est toujours une réflexion après coup
    Si l’on étend ce concept à toute la programmation, on devrait pouvoir voir tout le code qui dépend d’une valeur de configuration donnée, ainsi que ses transformations
    De plus, la plupart des configurations sont relationnelles ou orientées graphe, si bien qu’il vaudrait peut-être mieux les placer dans une base de données centrale. Les différentes valeurs de configuration sont liées entre elles. Il faudrait donc consulter la configuration dans une base de données ou un éditeur de graphe
    Dès qu’on sort du texte brut, les choses commencent à devenir beaucoup plus simples, mais les fonctionnalités de langage évoquées plus haut restent nécessaires

    • Un de mes clients essaie vraiment très sérieusement de faire quelque chose de similaire. Ils ont des fichiers de « configuration » de plus de 1 500 lignes par produit, qui servent à produire des plans techniques et des fichiers de fabrication
      Les configurations essaient d’utiliser des conventions de nommage pour regrouper les variables liées
      J’aimerais passer à de vraies structures de données imbriquées, probablement en JSON, mais les ingénieurs refusent absolument d’écrire du code, donc la configuration en tant que code est impossible. Il y a aussi les inconvénients mentionnés plus haut
      L’idée suivante est qu’il faudrait un meilleur moyen d’afficher et de modifier la configuration. J’ai pensé à une interface visuelle permettant d’explorer la représentation du produit final, de sélectionner des pièces et de modifier les paramètres de cette manière
      Je me demande si cette direction est la bonne. Sinon, j’aimerais qu’on m’explique un peu plus. Le cœur de cette application, c’est la configuration
  • Le pire, c’est que dans des endroits comme la CI/CD, YAML devient presque un langage de programmation. Un langage très verbeux, peu intuitif, mal spécifié et différent selon les fournisseurs

    • C’est presque comme répéter les erreurs de Java au début des années 2010. À l’époque, il arrivait souvent que des applications entières soient tenues ensemble par un énorme tas de XML configurant l’injection de dépendances
      Même avec les DTD et la validation XML, on retrouvait ces caractéristiques familières : des erreurs qui n’apparaissaient que tardivement et dont les messages étaient difficiles à interpréter
      À l’époque, beaucoup de frustrations visaient XML, mais quand on voit l’enfer YAML du milieu des années 2020, le problème n’était pas le langage de balisage lui-même
    • Exactement. Nous utilisons ytt[0], qui est une « version légèrement modifiée du langage de programmation Starlark, un dialecte de Python »
      Je déteste vraiment enfouir de la logique quelque part dans des templates YAML
      [0] https://tanzu.vmware.com/developer/guides/ytt-gs/
    • Dans certains environnements qui travaillent avec Kubernetes, on emploie sérieusement le terme ingénieur YAML
    • YAML est le Bradford Pear des formats de sérialisation. Au début, ça a l’air bien, mais quand le projet vieillit et que le YAML grossit, il s’effondre sous le poids de ses propres branches
    • Le pire, c’est que chaque génération répète cette erreur. Je ne sais pas si les S-expressions sont la réponse, mais Terraform HCL n’aurait jamais dû être créé
  • Je suis vraiment triste que Helm ait gagné. Au travail, je m’occupe de sujets open source liés à k8s, et 100 % des utilisateurs nous ont demandé de créer des charts Helm, donc on a fini par devoir le faire
    C’est misérable à travailler. Les fichiers s’appellent foo.yaml, mais en réalité ce n’est pas du YAML, donc l’éditeur ne peut pas aider. Il faut faire passer toutes les données par indent 4 pour que l’alignement YAML soit correct
    Le plus déprimant, c’est qu’il faut réexposer toutes les fonctionnalités de Kubernetes à sa propre manière. Si quelqu’un veut ajouter deployment.spec.template.spec.fooBars, il faut ajouter deploymentFooBars dans values.yaml et le relier. Ça se répète pour chaque fonctionnalité
    C’est vraiment un cas où « worse is better » a mal tourné. Moi aussi, il m’est arrivé de faire des horreurs comme sed -e s/$FOO/foo/g pour implémenter des templates, et Helm a probablement commencé comme ça aussi. Le résultat est un désordre
    Personnellement, j’utilise Kustomize depuis avant son intégration à kubectl, et j’en ai toujours été assez satisfait. Il a beaucoup de bizarreries, mais au moins il comprend la sémantique des objets qu’il génère, ce qui fait gagner du temps
    Jsonnet est bien meilleur. Dans le cadre de notre appli k8s, nous fournissons aussi un déploiement Envoy pour du routage de trafic complexe ; la configuration d’Envoy est verbeuse, mais facile à gérer avec Jsonnet : https://github.com/pachyderm/pachyderm/blob/master/etc/gener...
    J’envisage sérieusement de transpiler jsonnet vers le langage de templates de Go et de tout implémenter en Jsonnet. Au moins, ce serait un peu maintenable, et comme helm install fonctionnerait tout simplement, personne ne s’en rendrait compte
    Mais j’ai l’impression que Helm finira par causer la perte de Kubernetes. Si un outil concurrent d’allocation de machines / d’exécution de conteneurs arrive avec un vrai langage de configuration, tout le monde basculera du jour au lendemain

    • Quand on a envie d’utiliser sed -e s/$FOO/foo/g, il vaut mieux jeter un œil à envsubst, une solution un peu plus standard et meilleure
      Si le sujet est de templater ou modifier des charts Helm avec jsonnet, Tanka peut aussi aider : https://tanka.dev/helm
    • J’aimerais croire à cette prédiction selon laquelle Helm causera la perte de Kubernetes
      Cela dit, les endroits où j’ai travaillé ont encore peur du changement et continuent d’utiliser tf/hcl et helm tels quels. Au moins, dans mes projets personnels, je respire un peu mieux
  • Je pense qu’il y a un problème ici. Mais je ne sais pas trop si le genre de personne qui choisit YAML comme langage de configuration y verra un problème
    Il y a un conflit direct entre la représentation des données centrée sur l’humain et celle centrée sur la machine. Les ordinateurs aiment quelque chose qui ressemble à Lisp, les humains aiment quelque chose qui ressemble à Python
    Si l’on veut manipuler la configuration Kubernetes par programme, le fait que Kubernetes utilise YAML devient assez agaçant. Mais la communauté Kubernetes semble surtout composée de gens du camp YAML ; pourquoi se soucieraient-ils du fait que travailler sur des fichiers de configuration devient horrible dès qu’on y ajoute de la logique de programmation ?
    C’est précisément l’un des défauts de YAML dans ce contexte, et je pense que les gens autour de k8s sont généralement assez intelligents pour l’avoir anticipé
    Je ne pense pas que « YAML est un sur-ensemble de JSON » soit vrai en pratique, quoi que les auteurs de la spécification écrivent dans la documentation. Si l’on convertissait toute la configuration YAML en JSON, les équipes DevOps se mettraient en colère
    Les deux formats de données peuvent représenter la même sémantique, mais c’est aussi le cas de tous les langages qui se compilent vers la même architecture CPU. JSON et YAML sont distincts en pratique, et les mélanger n’est pas une bonne idée

    • Ironiquement, si je me souviens bien, les manifests k8s étaient dès le départ censés être générés par machine, pas écrits directement par des humains
      Bien sûr, les gens ont fini par les écrire à la main, puis quand c’est devenu insupportable, ils ont commencé à y ajouter des templates. Les choses semblent toujours évoluer comme ça
      Le texte écrit à la main n’est pas remplacé par du texte sérialisé de configuration générée par machine ; il est généralement remplacé par du texte toujours écrit à la main, mais avec des templates par-dessus
    • « YAML est un sur-ensemble de JSON » signifie seulement que tout document JSON est un document YAML valide. Cela ne veut pas dire que YAML est identique à JSON
  • Mon principe personnel est qu’il ne faut pas utiliser l’interpolation de chaînes pour générer du code destiné à être lu par des machines. Un langage de template n’est rien d’autre qu’une interpolation de chaînes sophistiquée
    On a tous vu les conséquences de l’injection SQL et du cross-site scripting. Tant qu’on continuera à envoyer du texte arbitraire à un interpréteur, ce genre de choses continuera à arriver
    C’est pourquoi je pense qu’il ne faut pas non plus utiliser de fichiers de template pour produire du HTML
    Comme alternatives aux langages de template pour HTML, il y a Haml en Ruby et Pug en JavaScript. Ces langages fournissent une méthode définie pour spécifier l’arbre complet des balises, attributs et nœuds de texte
    Si vous n’aimez pas l’indentation significative à la Python, il existe JSX en JavaScript. Les parties de JSX qui ressemblent à du HTML sont compilées en expressions createElement qui construisent l’arbre du document web, lequel peut ensuite être sérialisé en HTML si nécessaire
    Haml, Pug et JSX peuvent produire du HTML, mais ce ne sont pas des langages de template. De la même façon, JSON.stringify(myObj) n’est pas un langage de template pour JSON
    Le code lu par des machines devrait, quand c’est possible, être généré par des outils qui comprennent et exploitent la structure connue du langage cible

    • Dire que Haml, Pug et JSX ne sont pas des langages de template n’a aucun sens, sauf à utiliser une définition personnelle selon laquelle un langage de template serait une “interpolation de chaînes sophistiquée”
      Haml est un système de templates destiné à éviter le code inline dans les documents web et à rendre le HTML plus propre, et Pug est un moteur de templates riche en fonctionnalités pour Node.js
      Je peux admettre que JSX n’est pas, à proprement parler, un langage de template
      Au final, ils se compilent tous en HTML. Simplement, ce n’est pas de l’interpolation de chaînes : ce sont des langages analysés en arbres syntaxiques puis rendus en HTML à partir d’une compréhension interne de la structure valide
      Les templates YAML sont de l’interpolation de chaînes sophistiquée, et ne sont pas des langages de template, ou du moins ce sont des langages de template très mal implémentés
    • Tous les langages de template ne sont pas des langages de templates de chaînes. Par exemple, si l’on considère PHP comme un langage de template pour du texte, alors, selon la même logique, XQuery est un langage de template pour XML
    • C’est le fond du problème. YAML et les templates ne font que détourner l’attention. Au bout du compte, cela revient au fait que string est un type beaucoup trop général et que nous l’utilisons paresseusement
      Ma règle personnelle est que chaque fois qu’une valeur est insérée dans une chaîne, elle doit impérativement être encodée correctement
      J’ai écrit un billet à ce sujet il y a quelque temps : https://kevincox.ca/2022/02/08/escape-everything/
      En résumé, toutes les chaînes ont un format à respecter, comme HTML, SQL ou une sortie de terminal destinée à des humains. Chaque fois qu’on insère une valeur dans une chaîne, il faut l’encoder correctement pour ce format, mais nous ne le faisons presque jamais
  • Nous sommes en train de migrer vers cuelang [1]. Personnellement, je trouve sa conception meilleure que celle de Jsonette
    Kubernetes dispose déjà d’une réconciliation d’état ; la seule chose qui manquait dans cette configuration était la suppression, et c’est désormais possible avec la fonctionnalité prune [2]
    [1] https://cuelang.org/docs/integrations/k8s/
    [2] https://kubernetes.io/blog/2023/05/09/introducing-kubectl-ap...

    • Je peux recommander cuelang. Nous avons commencé à l’utiliser au travail et c’est vraiment très bien
      Certains messages d’erreur sont un peu difficiles à interpréter, mais comme il détecte énormément d’erreurs en amont, c’est acceptable. Désormais, les rares moments où je dois écrire du YAML directement me semblent terriblement fastidieux en comparaison
  • Dans ce genre de situation, j’interviens généralement avec : « Avez-vous entendu parler de notre sauveur CUELang ? » : https://cuelang.org/
    Il n’est pas encore Turing-complet, mais il est suffisamment expressif pour éliminer les doublons ; on peut définir schémas et données dans le même langage, dans le même fichier ou dans des fichiers séparés, et il prend aussi en charge les types union
    Il peut générer du YAML ou du JSON, et valider ses propres fichiers ainsi que des fichiers YAML/JSON
    Son principal inconvénient est que l’implémentation actuelle est uniquement en Go, ce qui peut imposer de passer par des sous-processus ou du FFI

    • Nous avons un pipeline qui accepte des fichiers cuelang très concis
      Ensuite, il produit des fichiers JSON pour chaque application, un outil génère des définitions XML, ces définitions sont appliquées à un fichier XLS détenu par les architectes, puis celui-ci recrache le YAML à appliquer aux charts Helm
      Le chart déploie le client k8s, et ce client interagit avec le cluster principal via l’API, en JSON
      Cela a pris un peu de temps, mais nous utilisons le meilleur outil pour chaque tâche
    • Comment cela se compare-t-il à dhall ?