Custom Resource Definitions (CRDs)
Extend the Kubernetes API with your own resource types and understand the Operator pattern.
The Kubernetes API is Extensible
The built-in Kubernetes resource types (Pod, Deployment, Service, ConfigMapβ¦) are not hardcoded into the API server. They are registered resource definitions. And you can add your own.
Custom Resource Definitions (CRDs) let you register new resource kinds with the API server. Once registered, your custom resources are full citizens of the Kubernetes API β you can kubectl get, kubectl apply, kubectl delete, and kubectl describe them just like Pods.
CRD Controller Reconciliation Architecture
+-------------------------------------------------------------+
| Kubernetes API Server |
| +----------------+ +--------+ +---------------+ |
| | Mutating/Valid | ---> | etcd | ---> | HTTP/2 Watch | |
| | Webhooks | +--------+ | stream | |
+--+----------------+----------------------+-------+-------+--+
|
| (Informer / Reflector)
v
+--------------------------------------------------+----------+
| Operator Controller Process |
| +-------------------+ +--------------------------+ |
| | Local Cache | <----> | SharedIndexInformer | |
| | (Lister) | +------------+-------------+ |
| +-------------------+ | |
| v (events: add/upd/del)
| +-------------------+ +--------------------------+ |
| | Reconcile Loop | <----- | Rate-Limiting WorkQueue | |
| | (Desired vs Actual| +--------------------------+ |
| +---------+---------+ |
+------------|------------------------------------------------+
|
| (1. Read Desired State / 2. Inspect Actual)
v
+-------------------------------------------------------------+
| Real World |
| +--------------------+ +-------------------------+ |
| | Cloud Infrastructure| | Cluster Resources | |
| | (AWS RDS, LB, etc.) | | (Pods, Services, etc.) | |
| +--------------------+ +-------------------------+ |
+-------------------------------------------------------------+CRD vs CR
| Term | Description |
|---|---|
| CRD (CustomResourceDefinition) | The schema β tells the API server "a new kind called Database exists, with these fields" |
| CR (Custom Resource) | An instance of a CRD β like a Pod is an instance of the Pod kind |
The relationship: CRD is to CR as a Go struct is to a Go variable.
CRDs Alone Do Nothing
This is the crucial insight. When you create a CRD and then create a CR, the API server:
- Validates the CR against the CRD schema
- Stores the CR in etcd
- Does nothing else
No actual database is created. No process is started. The CR is just structured data in etcd. To make something happen, you need a controller.
Low-Level Controller Architecture: Informers and Finalizers
Custom controllers (often written in Go using controller-runtime or client-go) run as workloads in the cluster and implement a specific reconciliation pattern:
- SharedIndexInformer: Instead of querying the API server continuously, the controller uses an Informer. The Informer opens an HTTP/2 chunked transfer stream (Watch API) to the API server, caches the retrieved objects in a local thread-safe index (
Lister), and propagates event hooks when objects are added, updated, or deleted. - WorkQueue: Events are pushed to a rate-limiting work queue. This queues tasks, merges duplicate keys, and retries reconciliations using exponential backoff when errors occur.
- Finalizers (metadata.finalizers): To clean up external resources (like cloud databases or disks) before a CR is deleted from etcd, the controller adds a finalizer string to the CR. When a user deletes the CR, the API server sets a
deletionTimestampbut blocks deletion until all finalizers are removed. The controller detects the deletion timestamp, cleans up the external infrastructure, and then removes its finalizer, allowing the CR to be purged from etcd. - Status Subresource (/status): CRDs expose a dedicated
/statussubresource. This isolates the status updates from spec changes. Spec edits incrementmetadata.generation, triggering a reconcile. Status updates do not. RBAC rules can restrict normal users from modifying the status, ensuring only the controller has permission to write status fields.
The Controller Pattern
A controller is a reconciliation loop:
loop forever:
desired = read CR from API server
actual = observe current state (running processes, resources)
if desired != actual:
take action to move actual β desiredCRD Schema Validation
CRDs support OpenAPI v3 schema validation. The API server rejects CRs that violate the schema at admission time β before they are stored in etcd:
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
required: ["engine", "version"]
properties:
engine:
type: string
enum: ["postgresql", "mysql", "redis"]
version:
type: string
storage:
type: stringOperator Pattern
A Kubernetes Operator is a custom controller that extends Kubernetes to manage complex, stateful applications. It packages operational knowledge (how to deploy, scale, backup, upgrade an application) into code that runs as a controller inside the cluster.
How Operators Work
Custom Resource (CR)
β
β create/update/delete
βΌ
Operator Controller (watches CRs)
β
β reconcile loop
βΌ
Kubernetes API (creates Deployments, Services, Secrets, etc.)The operator watches for Custom Resources of a specific Kind and reconciles the cluster state to match the CR's spec β the same control loop pattern used by the built-in Deployment controller.
Operator Frameworks
| Framework | Language | Description |
|---|---|---|
kubebuilder | Go | Official CNCF project; generates controller scaffolding |
operator-sdk | Go / Helm / Ansible | Red Hat; supports non-Go operators |
kopf | Python | Simple framework for Python-based operators |
Installing Existing Operators
You usually install operators via:
- Helm chart (most common):
helm install cert-manager jetstack/cert-manager \ --namespace cert-manager --create-namespace \ --set installCRDs=true
- Operator Lifecycle Manager (OLM) β a system for managing operator installation and upgrades from OperatorHub:
kubectl apply -f https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v0.28.0/crds.yaml kubectl apply -f https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v0.28.0/olm.yaml
- OperatorHub.io β the catalog of community operators (similar to Docker Hub but for operators)
Why Operators vs Plain Helm?
| Helm | Operator | |
|---|---|---|
| Day-1 (install) | β | β |
| Day-2 (upgrade, backup, failover) | β Limited | β Full automation |
| Reactive to cluster state | β | β Reconciliation loop |
| Domain logic | β Templates only | β Arbitrary Go/Python |