- 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
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
deno fmteinen JSON-Formatter hat, aber keinen YAML-FormatterDer 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 lesenMit 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 wahrKö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
Ansible macht denselben Fehler, und unzählige Tools ebenso
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
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.
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.
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.
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.
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.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
kubectleingebaut istDas 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 compileusw. gut passen, verstößt aber gegen die Bedingung, in die Standard-Toolchain eingebaut zu seinMan 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
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
Aber wenn man in der Praxis große Systeme verwaltet, lassen sich die Vorteile von Templates irgendwann nicht mehr vermeiden
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
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
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
Ich hasse es wirklich, Logik irgendwo in YAML-Templates zu vergraben
[0] https://tanzu.vmware.com/developer/guides/ytt-gs/
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 durchindent 4schleusen, 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.fooBarshinzufügen möchte, muss mandeploymentFooBarsinvalues.yamlergä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/ggemacht, 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
kubectlaufgenommen 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 installeinfach 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.
sed -e s/$FOO/foo/gzu 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
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.
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.
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.
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.
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...
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.
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.