Configuration Objects
I’m going to pick holes in an approach which I think is actively harmful to our industry and the maintainability of the systems we write and manage. The problem is one of approach, and nothing is unfixable.
The way is which YAML is used and abused is symptomatic of flaws in how we approach systems design and configuration. This is not the fault of YAML, as whichever language we use would suffer the same fate.
It is reasonable to not want to tie the configuration of a system to one language.
It is reasonable to not want to force the use of interpreters for various engines deep into your stack; to not want to make it impossible to add new flexibility later unless either some language maintainers agree with you, or you want to take on maintaining a language interpreter yourself.
When you choose to use a structured data format as the only input, if the format is human editable then the danger is people try layering as little as possible onto that format at first, and instead of getting a well designed data generator, you accrete generations of hacks.
Kubernetes
For instance, it’s reasonable for Kubernetes to pick something like YAML as the object interchange, but only if you demonstrate from the beginning that YAML is only an object representation layer; one with schemas available, and other good things; and demonstrate this with examples of how to do things otherwise.
Most Kubernetes admins should touch a tiny bit of YAML for initial bootstrap. Thereafter, they should know it as “that format the state snapshots and backups are written in”.
But because a human editable representation was used, with the supplied tools not geared for taking other formats, people stick with YAML.
We see anchors and references, and then Helm becoming a Go text/template wrapper with distribution mechanisms,
and incomprehensible messes as supplied Helm Charts either try to be all things to all people with a bazillion configuration options,
or say “learn to write patch objects, then you can change anything, we merge those in” and suddenly most admins can’t tune their charts.
If Protobufs had been used to represent the Kubernetes API objects, it would have been less approachable but it would have been much more obvious that the configuration objects should be merely output artifacts from the configuration management for a set of clusters, not the native language for humans to edit.
I hope that in the future we look back on people using YAML with text templating layers on top of it with the same cautious respect with which we look at people writing raw Sendmail configs and bemoaning the use of macros by newcomers who aren’t real mail administrators until they do. I have a suspicion that attempts to nudge the Kubernetes culture away from YAML will meet just that sort of reaction.
And this is not specific to Kubernetes.
I see it with many things using YAML.
While YAML might have its problems (or NO problems),
it’s broadly a decent human-editable way to represent some structured data.
OCI Image References
People write references to OCI images and write :latest,
and then they get told off because you don’t know what a given machine will see at any point in time.
It becomes a shibboleth, to tell people that :latest is wrong, rather than fixing the problem, and define the in-group of service admins who “understand why you should never use :latest”.
The problem is that the configuration describing the overall desired state is being used as the same configuration for telling the machines what to run now when they see the configuration.
Loosely, the human using :latest probably really does want a given deploy to grab the latest,
but to have it be consistent across the deploy,
and subject to being able to switch to a pinned version if needed,
and perhaps :latest being interpreted as “the latest to pass our malware scanners, not the latest upstream”.
The build step for deployment configurations might be written in the same configuration language as the “desired state when we do deploys” configurations, but it is semantically different. There needs to be a translation layer.
Taking a desired state should resolve OCI image tags to checksums and lock down specific versions, without the human needing to care. They should know that they can see what it was for a given release/deploy but not be forced to manually maintain the lower layer configuration.
Summary
Configuration to describe what we want is a higher level of abstraction than configuration to be interpreted by individual machines during a deploy. Using the exact same files and models for both is an anti-pattern.
The runtime deploy configuration objects should have a schema which doesn’t allow for the human mutable labels which drift, perhaps mid-deploy. The human edited objects should have LSP integrations letting you see how a label might be resolved now, and why, or switching to how it was resolved, for a given past deploy, referencing a history of the generated configurations.
We should probably be using JSON or Protobufs or CBOR or “something” for the machine configurations. Just about anything will do, except ASN.1 or XML (I have my prejudices, you have yours). We don’t want comments there, or encouraging editing except in emergencies.
YAML might be a fair simple representation as a starting-point for human editing, but as long as there are solid schemas for the backend target, it should not be forced.
Other posts
Readers have pointed me towards the following posts which are relevant:
- https://leebriggs.co.uk/blog/2019/02/07/why-are-we-templating-yaml
- Suggests Jsonnet as a solution for the human layer
Updates (excluding other posts links):
- Updated 2023-01-23 with YAML reference in intro for clarity and clarify the shibboleth