Hands-on: operator examples (CloudNativePG, Trivy Operator) in sharifndlovu123/devops-directive-kubernetes-course. Built-in resources are in Kubernetes Resource Types.
Kubernetes is built on one idea: declare the desired state and let controllers reconcile reality to it. That idea is not limited to containers. Anyone can add new resource types and controllers, which is how the ecosystem extends the platform.
Parts at a glance
| Term | Meaning |
|---|---|
| CustomResourceDefinition (CRD) | Teaches the API server a new resource type; objects are stored in etcd like built-ins |
| Custom resource (CR) | An instance of that type (for example a database Cluster) |
| Controller | A loop that watches resources and acts to match the desired state |
| Operator | A controller plus CRDs that encode the knowledge of a human operator for one application |
| Admission webhook | Code that validates or modifies requests before they are stored |
flowchart LR
U[You: kubectl apply Cluster.yaml] --> API[API server + etcd]
API --> OP[Operator / controller]
OP -->|creates and manages| R[Pods, Services, PVCs, Secrets, backups]
R -->|status| OP
OP -->|updates .status| API
$ kubectl get crds
$ kubectl api-resources | grep postgresql
$ kubectl explain cluster.spec # works for custom resources too
Three Ways People Extend Kubernetes
1. Extend existing capabilities
Traefik, installed as an ingress controller, adds its own CRDs on top of the standard Ingress: IngressRoute, Middleware, TLSOption and more. They expose features the base Ingress spec lacks (middleware chains, TCP/UDP routing). Look at what a tool adds with kubectl api-resources.
2. Embed operator logic
Stateful applications need carefully ordered operations (failover, backup, upgrade). A StatefulSet alone cannot express them, so an operator does what an on-call database administrator would.
CloudNativePG is a PostgreSQL operator. It installs CRDs such as Cluster, Backup, ScheduledBackup and Pooler, then a few lines of YAML give you a replicated database with failover, backups to object storage and connection pooling:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata: { name: app-db }
spec:
instances: 3
storage: { size: 10Gi }
backup:
barmanObjectStore:
destinationPath: s3://my-unique-bucket-12345/backups
s3Credentials:
accessKeyId: { name: backup-creds, key: ACCESS_KEY_ID }
secretAccessKey: { name: backup-creds, key: ACCESS_SECRET_KEY }
---
apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata: { name: nightly }
spec:
schedule: "0 0 2 * * *"
cluster: { name: app-db }
The course shows a minimal cluster and one with periodic backups to Google Cloud Storage and to another cloud's object store.
3. Manage external infrastructure
Operators can also drive things outside the cluster. Crossplane is "infrastructure as code that runs inside Kubernetes": custom resources map to cloud resources (buckets, databases, networks), and the controller creates them and continuously fixes drift, with no separate pipeline needed. Compare with 2. Terraform and 1. Infrastructure as Code.
Useful Operators and Add-Ons
| Tool | What it does |
|---|---|
| CloudNativePG | Run PostgreSQL clusters with failover and backup |
| Trivy Operator | Scans every running image and reports CVEs and misconfigurations as resources; re-scans on a schedule, catching vulnerabilities found after the image was built |
| cert-manager | Issues and renews TLS certificates (Let's Encrypt and others) for Ingresses and Services |
| External Secrets Operator | Syncs secrets from a cloud secret store into Kubernetes Secrets |
| Argo CD / Flux | GitOps controllers: Upgrades, CI-CD and GitOps |
| Prometheus Operator | Run monitoring declaratively with ServiceMonitor and PrometheusRule |
| Crossplane | Provision cloud resources from Kubernetes |
Notes on adoption: install operators with Helm (Packaging and Multi-Environment Deploys), check each project's maintenance and support, and watch resource use (every operator is more software to patch).
Writing Your Own
| Approach | Notes |
|---|---|
| Kubebuilder / operator-sdk (Go) | The standard frameworks; generate CRDs and controller scaffolding |
| controller-runtime | The library underneath them |
| Python (Kopf), Java (Java Operator SDK), shell-operator | Easier entry in other languages |
The controller loop in pseudo-code:
watch(MyResource)
on change:
desired = spec of the resource
actual = look at the world (pods, cloud APIs)
if actual != desired: act to close the gap
write result to the resource's .status
Design rules: idempotent (safe to run repeatedly), level-triggered (react to the current state, not to individual events), use finalizers to clean up external resources, and report progress in .status and conditions.
Why It Matters
The same reconcile pattern appears elsewhere: Terraform plan/apply, 1. CI-CD Pipelines promotion and the GitOps loop. Once you see "desired state in, actual state converged", Kubernetes feels like a general-purpose control system rather than only a container scheduler. See 1. Orchestration Platforms for how the managed offerings compare.