TT Lab
Get started
Learn Learning paths Courses

CRDs and Operators

Turning a Pile of values Into an API

Continue in TT Lab

In one sentence

A CRD is not a device for adding new functionality. It lends the capabilities the API server already has to a type you define.

Why it was needed

Imagine a Helm chart that deploys an internal service. A values.yaml that started out with two entries, image and replicas, grows to forty lines a year later. And the same things keep happening.

There is a single root cause. To the API server, values.yaml is just a blob of text. Nothing validates it, it is stored inside a release Secret and is hard to query, the audit log does not record who changed what and when at the level of individual resources, and you cannot use RBAC to grant permission to change just one value.

A CRD turns this problem around: "Then let's make it a real API object."

How it works

Once you apply a single CRD, the API server opens an endpoint at /apis/<그룹>/<버전>/namespaces/<ns>/<복수형> (group, version, and plural name), and from that moment the following come for free.

Capability values.yaml CR
Schema validation None (found only after rendering) Rejected at apply time by OpenAPI v3
Default injection The default function inside the template The API server fills in the schema default
Unknown fields Silently ignored Pruned, or explicitly preserved
Storage Release Secret As an object in etcd
Query helm get values kubectl get, label selectors, custom columns
Change watching None Watch stream
Permission separation Per chart RBAC per resource and subresource
Audit Pipeline logs Per object in the API audit log

One more thing comes with this. You can use CEL rules inside the schema. Cross-field constraints such as self.tier != 'prod' || self.replicas >= 2 are checked inside the API server, without a webhook server. What used to require running a validating webhook is now one line in the schema.

And remember this without fail. A CRD alone makes nothing happen. When you apply a CR, the validated data is simply stored in etcd; no Pod starts and no backup runs. Only when a controller is attached that watches that type and reconciles does it become an Operator.

CRD          = 새 어휘 (무엇을 원하는지 말하는 언어)
컨트롤러      = 그 어휘를 현실로 만드는 두뇌
오퍼레이터    = CRD + 컨트롤러

What you see in the field

First, self-service for platform teams. Most teams building an internal developer platform sell their abstractions through CRDs. A developer writes a single WebService, and the platform translates it into a Deployment, Service, Ingress, HPA, and NetworkPolicy. The surface a developer has to learn shrinks from 40 lines of values to a 5-line CR.

Second, the Prometheus Operator's ServiceMonitor. Instead of people editing one huge Prometheus configuration file, each team creates a small CR and declares, "scrape my service's metrics." The conflicts that came from many teams editing one configuration file turn into per-resource ownership.

Third, there are clear cases where you should not use one. If any of the following applies, a CRD is overkill.

Fourth, a CRD is an API contract, not code. Controller code can be redeployed at any time, but you cannot casually change the thousands of CRs already stored in etcd or the manifests users have committed to Git. If you create one field wrongly, it follows you for years, from v1alpha1 to v1. That is why you start with a small API surface and pave the way for evolution in advance.

What to look for in the next check

In the quiz that follows, you will confirm why a CRD must be designed not as a mere YAML format but as an API contract with long-term compatibility. After reviewing the criteria for names, scope, schema, and version conversion, you will define the WebService type in the apps.labhub.io group yourself in the next module.