2 Punkte von GN⁺ 2024-01-24 | 1 Kommentare | Auf WhatsApp teilen
  • In Umgebungen wie Kubernetes, in denen die Zahl der Konfigurationsziele wächst, stößt der Ansatz, YAML-Dateien einfach manuell zu vermehren, schnell an seine Grenzen; geeigneter ist ein Ansatz, der Konfigurationsdaten erzeugt statt nur YAML-Templates zu bauen
  • Helm Charts injizieren Werte über values.yaml und Go-Templates, aber sobald optionale Felder, Arrays und Maps ins Spiel kommen, werden Bedingungen und Einrückungen schnell zur Belastung
  • YAML hat strenge Leerraumregeln, aber der Helm-Template-Parser versteht die YAML-Struktur nicht, sodass die Kombination aus toYaml und indent leicht zu einer fragilen Konfigurationserzeugung führt
  • YAML ist eine Obermenge von JSON, daher ist die wechselseitige Umwandlung einfach; Jsonnet behandelt die Erzeugung von Konfigurationsobjekten mit externen Variablen, bedingten Feldern, Map-Kombinationen und Objekt-Merging wie Code
  • kr8 nutzt einen Jsonnet-basierten Ansatz, um Konfigurationen für mehrere Kubernetes-Cluster zu erstellen und zu bearbeiten, und entscheidet sich dafür, Objekte direkt zu erzeugen und zu verändern statt komplexe YAML-Strings zusammenzusetzen

Konfigurationskomplexität beginnt, wenn die Zahl der YAML-Dateien wächst

  • Sobald Anwendungen und Infrastruktur eine gewisse Größenordnung überschreiten, steigt die Konfigurationskomplexität schnell an
  • Wenn es nur 1–2 Deployments gibt, reicht es aus, YAML-Konfigurationsdateien direkt zu schreiben; werden es mehr, muss man Konfigurationen systematisch verwalten
  • Der Grund, warum mehrere Konfigurationsdateien nötig sind, ist meist, dass sich selbst beim gleichen Ziel einige Werte unterscheiden
    • Deployments je Umgebung wie dev, stg, prod
    • Deployments je Region wie Europe, North America
  • Nicht alle Konfigurationen sind unterschiedlich, aber wenn die Abweichungen groß genug sind, sollte man gemeinsame und unterschiedliche Teile getrennt verwalten
  • Der Bereich Konfigurationsmanagement beschäftigt sich schon lange mit diesem Problem, und verschiedene Tools haben YAML auf ihre eigene Weise genutzt
  • hiera, das zu Puppet gehört, kann Variablen hierarchisch auflösen und ist dadurch leistungsfähig und flexibel, was die Notwendigkeit stark reduziert, YAML selbst zu templatisieren

Die Probleme von YAML-Templates, wie sie in Helm Charts sichtbar werden

  • Mit Cloud Computing und Kubernetes haben sich Konfigurationsziele auf Schichten oberhalb des Betriebssystems ausgeweitet, wodurch Tools wie CloudFormation und Helm entstanden sind
  • Helm Charts können externe Parameter annehmen, die in values.yaml definiert sind, und daraus Renderings erzeugen
  • Einfache String-Werte sind vergleichsweise unkompliziert
image: "{{ .Values.image }}"
  • Wenn in values.yaml ein image-Wert gesetzt wird, landet dieser Wert im Template
  • Sobald man komplexere Konfigurationen wie optionale Felder behandelt, werden die Probleme größer
{{- with .resourceGroup  }}
    resourceGroup: {{ .  }}
{{- end }}
  • Optionale Werte können nicht einfach leer gelassen werden, daher braucht man Bedingungen und Schleifen, und Templates werden schnell unübersichtlich
  • Wenn Arrays oder Maps eingefügt werden, muss man toYaml und indent kombinieren
{{- with .Values.podAnnotations  }}
      annotations:
{{ toYaml . | indent 8  }}
{{- end  }}
  • Schon der Funktionsaufruf, mit toYaml YAML erneut in YAML umzuwandeln, wirkt seltsam, aber das größere Problem ist die Behandlung von Leerraum

Der Konflikt zwischen den Leerraumregeln von YAML und der Template-Engine

  • YAML hat strenge Regeln für Einrückung und Leerraum
  • Das folgende Beispiel ist kein gültiges oder vollständiges YAML
something: nothing
  hello: goodbye
  • Wenn ein Mensch das direkt schreibt, kann er es mit ein paar Backspaces korrigieren, aber beim Erzeugen von YAML mit einem Template-System ist das nicht so einfach
  • Sobald man mehr als 5–10 Konfigurationsdateien hat, braucht man eher Konfigurationserzeugung als manuelles Schreiben
  • Wenn der Wert von .Values.podAnnotations unter einem bereits eingerückten annotations eingefügt werden soll, muss auch der Wert selbst exakt auf der richtigen Ebene eingerückt sein
  • Weil der Go-Template-Parser YAML nicht versteht, entstehen Probleme, selbst wenn man die Template-Syntax zur besseren Lesbarkeit einrücken will
{{- with .Values.podAnnotations }}
      annotations:
      {{ toYaml . | indent 6 }}
{{- end  }}
  • Wenn ein Template-System Leerraum und Bedingungen behandelt, ohne die YAML-Struktur zu kennen, wird die Erzeugung komplexer Konfigurationen zunehmend schwieriger
  • Auch JSON direkt zu schreiben ist wegen fehlender Kommentare und Problemen mit vergessenen Kommata nicht ideal; aus genau diesen Unbequemlichkeiten wird YAML verwendet

Jsonnet ist eine Daten-Template-Sprache zur Erzeugung von JSON-Konfigurationen

  • YAML ist eine Obermenge von JSON, daher ist die Umwandlung zwischen JSON und YAML einfach
  • Viele Anwendungen und Programmiersprachen können JSON und YAML von Haus aus parsen oder umwandeln
  • Auch in Python kann man YAML einlesen und als JSON ausgeben
python -c 'import json, sys, yaml ; y=yaml.safe_load(sys.stdin.read()) ; print(json.dumps(y))'
  • Jsonnet bezeichnet sich selbst als Daten-Template-Sprache, deren Kernzweck die Erzeugung von JSON-Konfigurationen ist
  • Den Hintergrund des Jsonnet-Designs kann man in der design rationale nachlesen

Externe Variablen und optionale Felder

  • Jsonnet kann externe Variablen verwenden, um Konfigurationswerte einzuspeisen
{

  image: std.extVar('image'),

}
  • Wenn man externe Variablen über die CLI übergibt, wird ein JSON-Ergebnis erzeugt
jsonnet image.jsonnet -V image="my-image"
{
   "image": "my-image"
}
  • Optionale Felder lassen sich als Bedingungen im Code ausdrücken, ohne Template-Bedingungen in Strings einzubetten
// 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,

}
  • Wenn rg null ist, wird das Feld resourceGroup nicht ins Ergebnis aufgenommen
  • Wenn ein Wert gesetzt ist, wird das Feld ausgegeben

Maps und Objektmanipulation sind einfacher als YAML-Einrückung

  • Wenn man eine Map wie Kubernetes-Pod-Annotationen in eine Konfiguration einfügt, kann man in Jsonnet den Wert als Variable definieren und dann im Objekt platzieren
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,
  },

}
  • Das ist viel einfacher, als in einem YAML-Template die Einrückung korrekt auszubalancieren
  • Das erzeugte Ergebnis ist ein JSON-Objekt, in dem sich die Annotations-Map unter metadata.annotations befindet
{
   "metadata": {
      "annotations": {
         "nginx.ingress.kubernetes.io/app-root": "/",
         "nginx.ingress.kubernetes.io/enable-cors": true
      }
   }
}
  • Auch das Hinzufügen von Annotationen zu einem bestehenden Objekt kann in Jsonnet mit dem +-Operator behandelt werden
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',
    },
  },
}
  • Im Ergebnisobjekt wird zu den bestehenden Annotationen something: "nothing" hinzugefügt
{
   "metadata": {
      "annotations": {
         "nginx.ingress.kubernetes.io/app-root": "/",
         "nginx.ingress.kubernetes.io/enable-cors": true,
         "something": "nothing"
      }
   }
}
  • In einfachen Beispielen kann der Code länger wirken, aber je komplexer Konfigurationen werden, desto nützlicher wird diese Art, Objekte zu manipulieren

kr8 behandelt Kubernetes-Konfigurationen mit dem Jsonnet-Ansatz

  • kr8 nutzt diese Methoden, um Konfigurationen für mehrere Kubernetes-Cluster einfach und unkompliziert zu erstellen und zu bearbeiten
  • Der Kernansatz besteht darin, JSON-Konfigurationsobjekte zu erzeugen und nach Bedarf zu verändern, statt YAML-Templates mit Leerraum und Bedingungen zusammenzusetzen

1 Kommentare

 
GN⁺ 2024-01-24
Kommentare auf Hacker News
  • Konfigurationen in YAML habe ich inzwischen gründlich satt. Das ist der Teil, den ich an GitHub Actions am meisten hasse, sogar noch mehr als die Zuverlässigkeit
    Wenn ich sehe, dass ein tolles Tool für die Konfiguration eine YAML-Datei verlangt, werde ich sofort unruhig. Dasselbe gilt für proprietäre Konfigurationssprachen wie HCL von Terraform oder ASL von AWS Step Functions
    Es ist völlig in Ordnung, eine deklarative API zu wollen, aber ich wünschte, man könnte diese Deklarationen programmatisch erzeugen. Konfiguration, die man in Code deklariert und generiert, war eine deutlich bessere Erfahrung, und AWS CDK hat das wirklich gut gemacht
    Man kann Definitionen für Cloud-Infrastruktur in einer typsicheren Sprache und mit guter IDE-Unterstützung schreiben, ohne auf Plugins angewiesen zu sein, die seit zwei Jahren nicht mehr aktualisiert wurden

    • An diesem Punkt finde ich reines JSON besser als YAML. Ausschlaggebend war, dass deno fmt einen JSON-Formatter hat, aber keinen YAML-Formatter
      Der JSON-Formatter ist ein einzelnes Binary, das in Millisekunden läuft; für automatisches Formatieren von YAML muss man praktisch Prettier verwenden, und Prettier hängt von halb NPM ab und braucht etwa zwei Sekunden zum Starten und Ausführen
      Deshalb habe ich in den Repositories der Firma alle YAML-Dateien, die sich auf JSON umstellen ließen, nach JSON migriert, und zumindest ich bin damit viel zufriedener. Niemand hat sich beschwert
      Mehrere Editoren unterstützen außerdem das $schema-Tag von JSON. Wir haben diese Funktion in unser Produkt aufgenommen, und es ist wirklich angenehm, Konfigurationsdateien erstellen zu können, indem man einfach Tab drückt, ohne die Dokumentation zu lesen
      Mit dem YAML Language Server geht das auch für YAML, aber weil die Tab-Taste auch für Einrückungen gebraucht wird, ist die Usability eher schlecht. JSON ist auch nicht perfekt, aber wenigstens ist der Text "no" nicht wahr
    • Als Vorteil von YAML hört man oft, dass JSON keine Kommentare hat, aber ich verstehe nicht, warum man deshalb gleich auf eine komplett andere Sprache wechseln muss
      Könnte man nicht einfach einen Filter einbauen, der vor dem Lesen der Konfiguration die Kommentare entfernt? Das kann doch nicht schwieriger sein, als auf YAML umzusteigen
      Auch die Behauptung, YAML sei leicht zu lesen, leuchtet mir nicht ganz ein. Der Schmerz beim Umgang mit Konfiguration kommt nicht daher, dass man ein paar Sekunden weniger geschweifte Klammern und eckige Klammern parsen muss, sondern daher, dass man in Konfigurationen mit Hunderten Zeilen wegen fehlender Leerzeichen oder Tabs kaum erkennt, was schiefgelaufen ist
    • GitHub Actions wäre wohl unabhängig vom Konfigurationsformat nicht besonders gut gewesen. Denn es versucht, Programme als Datenstrukturen zu beschreiben
      Ansible macht denselben Fehler, und unzählige Tools ebenso
    • Seit den Zeiten von AWS CloudFormation denke ich ähnlich. Vor vier Jahren habe ich einen experimentellen CloudFormation-Generator gebaut, der aus einer von AWS veröffentlichten JSON-Datei alle Ressourcen und Python-Type-Hints erzeugt, und das funktionierte ziemlich gut: https://github.com/weberc2/nimbus/blob/master/examples/src/n...
      Ich weiß nicht genau, ob CDK so funktioniert. Als ich es kurz ausprobiert habe, fühlte es sich ziemlich anders an als meine Erfahrung mit „CloudFormation erzeugen“, und ich habe die Vorteile von CDK nicht wirklich erkannt
      Es fühlte sich an, als würde man das YAML-/Template-Problem gegen ein Vererbungs-/Magie-Problem eintauschen. Ich würde gern mehr von Leuten hören, die AWS CDK, Terraform CDK und Pulumi verwendet haben
    • Bei GitHub Actions ist es noch schmerzhafter, weil YAML-Anker nicht unterstützt werden. Schade, denn das hätte wenigstens ein Mindestmaß an Komponierbarkeit geboten
      https://github.com/actions/runner/issues/1182
  • Ich stimme zu, dass YAML-Templates ziemlich verrückt sind, aber ich habe nie verstanden, warum man nicht einfach mit diesen Pseudosprachen aufhört und echte Programmiersprachen verwendet.
    Wenn komplexe Logik nötig ist, kann man YAML/JSON/was auch immer mit einer Programmiersprache erzeugen. Ruby, Python oder praktisch jede andere Sprache liefern einem, was man braucht, ganz ohne seltsame Quasi-Sprachen wie Jsonnet oder Go-Templates.
    Schreibt einfach Code, dann tappt man viel seltener in undurchsichtige Merkwürdigkeiten der Template-Engine. Jede echte Sprache ist deutlich besser.

    • Ich war früher einmal dafür zuständig, Ansible-Playbooks zu pflegen, die regelmäßig zum Bootstrap und Patchen auf einer riesigen Zahl von Servern liefen.
      Für ähnliche Aufgaben hatte ich Chef benutzt, und weil es Ruby war, konnte man die gewünschte Logik leicht definieren sowie Schleifen und richtige Variablen verwenden, was angenehm war.
      Ich verstehe, dass Ansible für Nicht-Programmierer entworfen wurde, aber für jemanden, der mit grundlegender Programmierung vertraut ist, gibt es kaum eine größere Hölle, als Ansible-Playbooks mit vielen bedingten Tasks und Schleifen in die geschwätzige Syntax von Jinja-Templates zu sperren.
    • In heutigen Stacks scheint die Architektur der Spracheinbettung fast in Vergessenheit geraten zu sein. Früher baute man bei hinreichend komplexen Anwendungen den Kern in C/C++/Java usw., und wenn Scripting nötig war, bettete man darüber etwas wie LISP oder Lua ein.
      Heute holt man sich dagegen typischerweise einen JSON/TOML/YAML-Parser, schreibt eine readConfig-Funktion und behandelt selbst Fälle so, in denen ein eingebetteter Interpreter besser passen würde.
      Aus Entwicklersicht ist es einfacher, einem Konfigurationsformat Komplexität hinzuzufügen, als eine vollständige Spracheinbettung und Application Bindings bereitzustellen. Deshalb scheint man diese Methode entweder vergessen zu haben oder gar nicht mehr auf die Idee zu kommen, dass sie möglich ist.
    • Pulumi ist attraktiv, weil man in der bevorzugten Sprache schreiben und HCL loswerden kann, aber persönlich halte ich es eindeutig für schlechter. Infrastructure as Code sollte deklarativ sein, damit Vorhersagbarkeit, Reproduzierbarkeit und Wartbarkeit höher sind.
      In der Chef/Puppet-Ära gab es viele Orte, die anfingen, Logik in IaC zu packen, und am Ende ein riesiges Chaos hatten, das sich weder upgraden noch warten ließ. Der Chef/Pulumi-Ansatz ist zwar möglich, erfordert aber jemanden, der bei Stil und Wartung extrem streng ist.
      Für große Teams und langfristige Wartung halte ich das Terraform/Puppet-Modell für besser. Auch wenn HCL nervt und Python/TypeScript usw. sich befreiend anfühlen, verhindert rein deklarativer Code viel Spaghetti.
    • Das Problem ist, dass Sprach-Nerds Sprachen für andere Sprach-Nerds bauen.
      Sie wollen die gerade angesagten Elemente des Sprachdesigns einbauen, sie wollen Self-Hosting, und sie wollen, dass man damit auch schnelle multithreaded Webserver schreiben kann; dadurch wird es konzeptionell komplex.
      Für System Engineers/DevOps braucht es eine einfache Spielzeugsprache wie Logo. Im Grunde sollte sie sich in einem Buch von der Größe des K&R-C-Buchs erklären lassen.
      Dynamische Typisierung, Kontrollstrukturen, die man an einem Wochenende lernen kann, kein Threading oder Concurrency, keine Objektorientierung oder Vererbung, funktionales/modulares Design und ein FFI-Modell, mit dem sie sich leicht aus anderen Sprachen und Frameworks heraus aufrufen lässt und umgekehrt aufrufen kann.
      Das Problem ist, dass Sprach-Nerds sich nicht im Griff haben und ständig Features hinzufügen; diese landen dann in den Kernbibliotheken und Styleguides, sodass auch Anfänger alles lernen müssen.
      Ich selbst würde vermutlich auch each/map-artige Funktionen für Arrays/Hashmaps hinzufügen sowie First-Class Functions und Closures einbauen wollen, aber das könnte ein Fehler sein. Es gibt bereits unveränderliche funktionale Sprachen für Konfiguration, aber mehr als 95 % der Leute, die templatisiertes YAML verwenden, wollen Programmieren nicht auf diese Weise lernen, weshalb sich so etwas nur schwer breit durchsetzt.
    • Ich möchte betonen, dass es darum geht, Konfigurationsdateien zu generieren. Es ist sehr nützlich, die Konfiguration selbst auf eine Form zu beschränken, die etwa in JSON-Dateien passen kann.
      Dadurch wird die Konfiguration einfacher, leichter zu konsumieren und besser zu dokumentieren. Beim Schreiben der Konfigurationsdateien sollte man jedoch eine Programmiersprache verwenden, möglichst eine statisch typisierte Sprache mit Fehlerprüfung, Autovervollständigung und Inline-Dokumentation.
      AWS CDK ist ein gutes Beispiel. Reines CloudFormation zu schreiben ist schmerzhaft, aber CDK fügt CloudFormation keine Programmierfunktionen hinzu, sondern generiert CloudFormation. Die Eingabe, die AWS konsumiert, bleibt weiterhin das vergleichsweise einfache und stabile CloudFormation.
  • Beim Titel dachte ich sofort, es würde um Kubernetes gehen
    Die Kubernetes API ist ziemlich intuitiv und hat ein gut definiertes JSON-Schema. Den Großteil der Zeit, in der man k8s lernt, sollte man eigentlich damit verbringen, die Nutzung der API zu verstehen; tatsächlich verbringt man sie aber damit, herauszufinden, wie man Helm-Charts benutzt
    Ich glaube nicht, dass Jsonnet, Ksonnet, Nu oder CUE so enorm populär geworden sind. Die meisten scheinen Kustomize zu verwenden, wohl weil es relativ intuitiv ist und in kubectl eingebaut ist
    Das gewünschte Tool müsste Autor:innen von Definitionen Typprüfung, Validierung und Warnungen zu Versionsabkündigungen für das k8s-Schema bieten, ein einzelnes Artefakt liefern, das Nutzer leicht prüfen können, atomar fehlschlagen, wenn der Cluster irgendein Objekt/eine Version nicht unterstützt, und in die Standard-Toolchain eingebaut sein
    Wenn ein Bun- oder Deno-TypeScript-Skript eine Funktion exportiert, die Argumente annimmt und eine Liste von Definitionen zurückgibt, könnte das mit deno compile usw. gut passen, verstößt aber gegen die Bedingung, in die Standard-Toolchain eingebaut zu sein

    • Das ist ein Muster in der gesamten Software. Statt die primitiven Bausteine und Grundlagen zu lernen, auf denen ein System basiert, lernt man haufenweise Abstraktionen darüber, weil es angeblich zu schwierig ist
      Man wird vor Details auf niedrigerer Ebene geschützt, muss sich aber bei Problemen mit einem riesigen Abstraktions-Stack herumschlagen, der Diagnose und Debugging erschwert
      Es wird viel schwieriger herauszufinden, was tatsächlich passiert, und man wird von Abstraktionsschichten abhängig, sodass man auch die Updates des Anbieters und andere Probleme im Dependency-Graph mit übernimmt
    • In unserem System verwenden wir jsonnet, und das hat überhaupt nichts mit k8s zu tun. Es ist weniger ein riesiger Publikumserfolg als vielmehr ein Nischen-Tool für komplexe Konfigurationen, und auch keines, für das viel Werbung gemacht wurde
      Es erledigt ziemlich problemlos, was wir brauchen, ist plattformübergreifend und lässt sich über mehrere Sprachen hinweg nutzen. Ich habe es schon in C++-, .NET- und JVM-Executables eingebettet
      Die resultierende JSON-Konfiguration kann mit einem umfangreichen Tooling genutzt werden, das man bei Alternativen wie toml/yaml/hocon/ini usw. nur schwer findet. Ich habe versucht, HOCON in Nicht-JVM-Sprachen zu verwenden, aber immer gab es irgendeinen Edge Case
    • Die zweite Anforderung erfüllt es vermutlich nicht, die dritte ganz sicher nicht, aber: https://cdk8s.io/docs/latest/
    • Die Idee, es einfach zu halten, ist gut, und als Installationsmethode versuche ich möglichst kustomize oder reines YAML zu verwenden
      Aber wenn man in der Praxis große Systeme verwaltet, lassen sich die Vorteile von Templates irgendwann nicht mehr vermeiden
    • kustomize und besonders helm sind viel zu verwirrend, während Kubernetes-YAML-Dateien sehr leicht zu schreiben und zu verstehen sind
  • Es ist schon lustig, wie wenig Entwickler darüber nachdenken, wie man Konfiguration richtig handhabt
    Es sieht einfach aus wie eine Sammlung von Schlüsseln und Werten, die in Dateien gespeichert oder per Code erzeugt werden, aber im Grunde ist das alles. Es ist Programmieren selbst
    Alles ist Konfiguration, und jedes Funktionsargument ist auch eine Art Konfiguration. Jede Konfiguration in externen Dateien wird am Ende auf die eine oder andere Weise zu Funktionsargumenten
    Das Problem ist die reine Textdarstellung von Code. Deklarative Konfigurationsdateien wirken gut, weil man alles an einer Stelle sehen kann, aber wenn man Konfiguration zu einem Programm macht, wird es schwer zu finden, wo man etwas ändern muss
    Wäre es so, dass der Code live ausgeführt wird, die Darstellung der finalen Konfiguration zeigt und man nachverfolgen kann, wie jeder endgültige Konfigurationswert erzeugt wurde, wäre das kein Problem. Aber obwohl diese Funktion ziemlich simpel ist, gibt es keine Systeme, die so entworfen wurden. Konfiguration ist immer ein Nachgedanke
    Überträgt man dieses Konzept auf das Programmieren insgesamt, müsste man sämtlichen Code und alle Transformationen sehen können, die von einem einzelnen Konfigurationswert abhängen
    Außerdem sind die meisten Konfigurationen relational/graphartig, daher wäre es möglicherweise besser, sie in einer zentralen Datenbank abzulegen. Unterschiedliche Konfigurationswerte hängen miteinander zusammen. Also sollte man Konfiguration in einem Datenbank-/Graph-Editor betrachten
    Wenn man sich von reinem Text löst, wird es deutlich einfacher, aber die zuvor erwähnten Sprachfunktionen braucht man weiterhin

    • Bei einem Kunden, der etwas Ähnliches macht, versuchen wir das wirklich intensiv. Es gibt pro Produkt „Konfigurations“-Dateien mit über 1500 Zeilen, und sie werden verwendet, um technische Zeichnungen und Produktionsdateien zu erstellen
      Die Konfigurationen versuchen, über Namenskonventionen zusammengehörige Variablen zu gruppieren
      Ich würde gern zu echten verschachtelten Datenstrukturen wechseln, vermutlich JSON, aber die Ingenieure wollen auf keinen Fall Code schreiben, daher ist Konfiguration als Code nicht möglich. Dazu kommen die oben genannten Nachteile
      Der nächste Gedanke ist, dass es eine bessere Möglichkeit geben muss, die Konfiguration anzuzeigen und zu bearbeiten. Ich habe an eine visuelle UI gedacht, in der man die Darstellung des Endprodukts durchsucht, Bauteile auswählt und auf diese Weise Parameter ändert
      Ich frage mich, ob das die richtige Richtung ist. Falls nicht, würde ich mich über eine etwas ausführlichere Erklärung freuen. Der Kern dieser Anwendung ist Konfiguration
  • Noch schlimmer ist, dass YAML in Bereichen wie CI/CD praktisch zu einer Programmiersprache wird. Und zwar zu einer sehr ausschweifenden, wenig intuitiven, schlecht spezifizierten und je nach Anbieter unterschiedlichen Sprache

    • Das ist fast eine Wiederholung der Fehler von Java in den frühen 2010er-Jahren. Damals hingen ganze Anwendungen oft an einem riesigen XML-Klumpen, der die Dependency Injection konfigurierte
      Trotz DTDs und XML-Validierung gab es die vertraute Eigenschaft, dass Fehler spät auftraten und schwer verständliche Fehlermeldungen lieferten
      Damals richtete sich viel Frust gegen XML, aber wenn man die YAML-Hölle der Mitte der 2020er betrachtet, war das Problem nicht die Markup-Sprache selbst
    • Genau. Wir verwenden ytt[0], und das ist „eine leicht modifizierte Version der Programmiersprache Starlark, eines Python-Dialekts“
      Ich hasse es wirklich, Logik irgendwo in YAML-Templates zu vergraben
      [0] https://tanzu.vmware.com/developer/guides/ytt-gs/
    • In manchen Umgebungen, die mit Kubernetes arbeiten, spricht man ernsthaft von YAML-Ingenieuren
    • YAML ist wie die Bradford Pear unter den Serialisierungsformaten. Anfangs sieht es gut aus, aber wenn das Projekt altert und das YAML wächst, bricht es unter dem Gewicht seiner eigenen Äste zusammen
    • Noch schlimmer ist, dass jede Generation diesen Fehler wiederholt. Ich weiß nicht, ob S-Ausdrücke die Antwort sind, aber Terraform HCL hätte von Anfang an nicht entstehen sollen
  • Es ist wirklich traurig, dass Helm gewonnen hat. Ich arbeite in einem Unternehmen an Open-Source-Themen rund um k8s, und 100 % der Nutzer wollten, dass wir Helm-Charts erstellen, also mussten wir es am Ende tun.
    Die Arbeit daran ist elend. Die Dateien heißen zwar foo.yaml, sind aber in Wirklichkeit kein YAML, sodass der Editor nicht helfen kann. Man muss alle Daten durch indent 4 schleusen, damit die YAML-Ausrichtung stimmt.
    Am deprimierendsten ist, dass man sämtliche Kubernetes-Funktionen auf eigene Weise erneut offenlegen muss. Wenn jemand deployment.spec.template.spec.fooBars hinzufügen möchte, muss man deploymentFooBars in values.yaml ergänzen und verdrahten. Das wiederholt sich für jedes Feature.
    Das ist wirklich ein Fall von „schlechter ist besser“, der schiefgelaufen ist. Ich habe selbst schon grauenhafte Dinge wie sed -e s/$FOO/foo/g gemacht, um Templating umzusetzen, und wahrscheinlich hat Helm auch so angefangen. Das Ergebnis ist ein Durcheinander.
    Persönlich habe ich Kustomize schon verwendet, bevor es in kubectl aufgenommen wurde, und war immer ziemlich zufrieden damit. Es hat viele Merkwürdigkeiten, aber immerhin versteht es die Bedeutung der Objekte, die es erzeugt, und spart dadurch Zeit.
    Jsonnet ist viel besser. Als Teil unserer k8s-App liefern wir ein Envoy-Deployment für komplexes Traffic-Routing mit; Envoy-Konfigurationen sind ausführlich, lassen sich mit Jsonnet aber gut handhaben: https://github.com/pachyderm/pachyderm/blob/master/etc/gener...
    Ich erwäge ernsthaft, jsonnet in die Go-Template-Sprache zu transpilieren und alles in Jsonnet umzusetzen. Das wäre zumindest ein wenig wartbarer, und weil helm install einfach funktionieren würde, würde es niemand merken.
    Aber ich glaube, Helm wird das Ende von Kubernetes sein. Wenn irgendein konkurrierendes Tool für Rechner-Allokation/Container-Ausführung mit einer vernünftigen Sprache für Konfiguration daherkommt, werden die Leute über Nacht wechseln.

    • Wenn man versucht ist, sed -e s/$FOO/foo/g zu verwenden, sollte man sich als etwas standardnähere und bessere Lösung envsubst ansehen.
      Für das Templating oder Anpassen von Helm-Charts mit jsonnet könnte auch Tanka hilfreich sein: https://tanka.dev/helm
    • Ich würde gern an die Prognose glauben, dass Helm das Ende von Kubernetes sein wird.
      Allerdings nutzen die Orte, an denen ich gearbeitet habe, weiterhin unverändert tf/hcl und Helm, weil Veränderung ihnen Angst macht. Zumindest bei persönlichen Projekten kann man etwas aufatmen.
  • Ich sehe hier ein Problem. Allerdings bin ich mir nicht sicher, ob die Art Mensch, die YAML als Konfigurationssprache auswählt, das als Problem ansehen würde.
    Zwischen menschenzentrierter und computerzentrierter Datendarstellung gibt es einen direkten Konflikt. Computer mögen etwas Lisp-Ähnliches, Menschen mögen etwas Python-Ähnliches.
    Wer Kubernetes-Konfigurationen per Computer manipulieren möchte, dürfte sich insgeheim darüber ärgern, dass Kubernetes YAML verwendet. Aber die Kubernetes-Community scheint überwiegend aus YAML-Leuten zu bestehen; warum sollte es sie also kümmern, dass Programmierlogik die Arbeit mit Konfigurationsdateien schrecklich macht?
    Genau das ist der Nachteil von YAML in dieser Situation, und ich denke, die k8s-Leute sind im Allgemeinen klug genug, um so etwas vorherzusehen.
    Die Aussage „YAML ist eine Obermenge von JSON“ halte ich in der Praxis nicht für richtig, egal was die Spezifikationsautoren in ihre Dokumente schreiben. Wenn man sämtliche YAML-Konfigurationen in JSON umwandelt, wird das DevOps-Team sauer sein.
    Die beiden Datenformate können dieselbe semantische Darstellung haben, aber das gilt auch für alle Sprachen, die auf dieselbe CPU-Architektur kompiliert werden. JSON und YAML sind in der Praxis verschieden, und sie zu vermischen ist keine gute Idee.

    • Ironischerweise waren k8s-Manifeste, wenn ich mich richtig erinnere, von Anfang an für maschinelle Generierung gedacht und nicht dafür, von Menschen direkt geschrieben zu werden.
      Natürlich haben Menschen sie am Ende trotzdem von Hand geschrieben, und als das unerträglich wurde, begannen sie, Templates daraufzusetzen. So läuft es offenbar immer.
      Von Hand geschriebener Text wird nicht durch serialisierten, maschinell erzeugten Konfigurationstext ersetzt, sondern zunächst durch weiterhin von Hand geschriebenen Text, auf den Templates aufgesetzt werden.
    • Die Aussage „YAML ist eine Obermenge von JSON“ bedeutet lediglich, dass jedes JSON-Dokument ein gültiges YAML-Dokument ist. Sie bedeutet nicht, dass YAML dasselbe ist wie JSON.
  • Mein persönlicher Grundsatz ist, dass man String-Interpolation nicht verwenden sollte, um Code zu erzeugen, der von Maschinen gelesen wird. Template-Sprachen sind letztlich nur schicke String-Interpolation.
    Wir haben die Folgen von SQL-Injection und Cross-Site-Scripting alle gesehen. Solange man beliebigen Text weiter in Interpreter steckt, wird so etwas weiterhin passieren.
    Deshalb denke ich auch, dass man beim Erzeugen von HTML keine Template-Dateien verwenden sollte.
    Alternativen zu Template-Sprachen für HTML sind Rubys Haml und JavaScripts Pug. Diese Sprachen bieten eine definierte Methode, um den gesamten Baum aus Tags, Attributen und Textknoten anzugeben.
    Wenn man Pythons semantische Einrückung nicht mag, gibt es in JavaScript JSX. Die wie HTML aussehenden Teile von JSX werden zu createElement-Ausdrücken kompiliert, die einen Web-Dokumentbaum erzeugen, und dieser Baum kann bei Bedarf als HTML ausgegeben werden.
    Haml, Pug und JSX sind keine Template-Sprachen, auch wenn sie HTML ausgeben können. Genauso ist JSON.stringify(myObj) keine Template-Sprache für JSON.
    Von Maschinen gelesener Code sollte, wenn möglich, mit Werkzeugen erzeugt werden, die die bekannte Struktur der Zielsprache verstehen und nutzen.

    • Dass Haml, Pug und JSX keine Template-Sprachen seien, ergibt keinen Sinn, außer man verwendet die persönliche Definition von Template-Sprache als „schicke String-Interpolation“.
      Haml ist ein Template-System, das Inline-Code in Webdokumenten vermeiden und HTML sauberer machen soll, und Pug ist eine funktionsreiche Template-Engine für Node.js.
      Bei JSX kann ich zustimmen, dass es streng genommen keine Template-Sprache ist.
      Am Ende werden sie alle zu HTML kompiliert. Nur eben nicht durch String-Interpolation, sondern als Sprachen, die zu Syntaxbäumen geparst und auf Basis eines internen Verständnisses gültiger Strukturen zu HTML gerendert werden.
      YAML-Templates sind schicke String-Interpolation und keine Template-Sprache – oder zumindest eine schlecht implementierte Template-Sprache.
    • Nicht alle Template-Sprachen sind String-Template-Sprachen. Wenn man zum Beispiel PHP als Template-Sprache für Text betrachtet, dann ist nach derselben Logik XQuery eine Template-Sprache für XML.
    • Das ist der Kern des Problems. YAML und Templates lenken nur ab. Am Ende läuft es darauf hinaus, dass String ein viel zu allgemeiner Typ ist und wir ihn nachlässig verwenden.
      Meine persönliche Regel lautet: Jedes Mal, wenn ein Wert in einen String eingefügt wird, muss er korrekt kodiert werden.
      Ich habe früher einmal darüber geschrieben: https://kevincox.ca/2022/02/08/escape-everything/
      Kurz gesagt hat jeder String ein Format, das eingehalten werden muss, etwa HTML, SQL oder menschenlesbare Terminalausgabe. Jedes Mal, wenn man einen Wert in einen String einfügt, muss man ihn passend zu diesem Format korrekt kodieren – aber fast nie tun wir das wirklich.
  • Wir sind gerade dabei, auf cuelang umzusteigen [1]. Persönlich finde ich das Design besser als bei Jsonette.
    Kubernetes hat bereits Zustandsabgleich; das Einzige, was in dieser Konfiguration fehlte, war Löschen, und das lässt sich jetzt mit der Prune-Funktion lösen [2].
    [1] https://cuelang.org/docs/integrations/k8s/
    [2] https://kubernetes.io/blog/2023/05/09/introducing-kubectl-ap...

    • Ich kann cuelang empfehlen. Wir haben es in der Firma eingeführt, und es ist wirklich gut.
      Einige Fehlermeldungen sind etwas schwer zu deuten, aber es fängt im Voraus so viele Fehler ab, dass das akzeptabel ist. Die wenigen Momente, in denen man noch direkt YAML schreiben muss, wirken im Vergleich inzwischen unglaublich mühsam.
  • In solchen Fällen werfe ich normalerweise ein: „Haben Sie schon von unserem Erlöser CUELang gehört?“: https://cuelang.org/
    Es ist noch nicht Turing-vollständig, aber ausdrucksstark genug, um Duplikate zu entfernen; man kann Schema und Daten in derselben Sprache in derselben Datei oder in getrennten Dateien definieren, und es gibt auch Union Types.
    Es kann YAML oder JSON erzeugen und sich selbst oder YAML-/JSON-Dateien validieren.
    Der größte Nachteil ist, dass die aktuelle Implementierung nur in Go vorliegt, sodass man eventuell Subprozesse oder FFI braucht.

    • Wir haben eine Pipeline, die sehr knappe cuelang-Dateien entgegennimmt.
      Danach erzeugt sie JSON-Dateien für jede Anwendung, irgendein Tool erstellt XML-Definitionen, diese Definitionen werden auf eine XLS-Datei angewendet, die den Architekten gehört, und daraus kommt YAML heraus, das auf Helm-Charts angewendet wird.
      Die Charts deployen einen k8s-Client, und dieser Client interagiert über die API per JSON mit dem Hauptcluster.
      Es hat eine Weile gedauert, aber wir verwenden für jede Aufgabe das jeweils beste Werkzeug.
    • Wie schneidet es im Vergleich zu dhall ab?