✦ For everyone, free.

Practical knowledge for real and everyday life

Home

Kubernetes Extensibility Guidelines

Kubernetes Extensibility Guidelines explain how to customize and extend clusters with plugins, operators, and custom resources.

Kubernetes Extensibility Guidelines describe the practices for extending the Kubernetes API and control plane itself — through Custom Resource Definitions, controllers and operators, and API aggregation — so that domain-specific concepts and automation can be expressed using the same declarative, reconciliation-driven model Kubernetes uses natively, rather than bolting external tooling onto the cluster that behaves inconsistently with everything else running on it.


Custom Resource Definitions

Extending the API Surface Declaratively

A CustomResourceDefinition (CRD) registers a new resource type with the Kubernetes API server, after which instances of that type are created, read, updated, and deleted through the same API, kubectl, and RBAC mechanisms as any built-in resource. This is the foundation that lets domain-specific concepts — a Database, a Certificate, a BackupSchedule — become first-class, declarative objects rather than being managed through bespoke scripts or external systems disconnected from cluster state.

Schema Validation via OpenAPI

CRDs should define a strict OpenAPI v3 schema (required fields, types, enums, format constraints) rather than accepting arbitrary unstructured data, so that malformed custom resources are rejected by the API server at admission time rather than being accepted and only failing later when a controller attempts to process them.

Versioning Custom Resources

CRDs support multiple versions with conversion webhooks between them, which is essential for evolving a custom resource's schema over time without breaking existing stored objects or clients still using an older version — a CRD design that doesn't plan for versioning from the start tends to accumulate breaking changes that are painful to migrate later.


Controllers and the Reconciliation Pattern

The Reconciliation Loop

A controller watches for changes to resources (built-in or custom) and continuously reconciles actual cluster state toward the desired state expressed in those resources, retrying on failure and re-evaluating on any relevant change. This level-based reconciliation model — always converging toward desired state rather than reacting to individual discrete events — is what gives controllers their resilience to missed events, restarts, and partial failures.

Idempotency as a Correctness Requirement

Because a reconciliation loop may run its logic multiple times for the same desired state (on startup, after a missed update, after an error retry), reconcile logic must be idempotent — applying it repeatedly to the same state must not produce a different or incorrect result. Non-idempotent reconcile logic is a common source of operators that behave unpredictably during ordinary retry scenarios.

Operators as Domain-Specific Controllers

An operator is a controller paired with domain-specific operational knowledge — encoding the procedures a human operator would otherwise follow manually (provisioning a database cluster, handling its backup and failover) into reconciliation logic triggered by custom resource changes. Operators are appropriate when a workload's operational complexity genuinely benefits from being encoded as automated, continuously-reconciled logic rather than a one-time setup script.


Building and Operating Custom Controllers

Client-Go, Kubebuilder, and Operator SDK

Purpose-built tooling (client-go informers/listers for lower-level control, Kubebuilder or Operator SDK for scaffolded, convention-driven controller projects) handles the boilerplate of watching resources efficiently, maintaining a local cache, and structuring reconcile logic correctly — writing a controller from raw API polling rather than using an informer-based approach both wastes API server load and is easy to get subtly wrong around missed updates.

RBAC Scoped to Actual Controller Needs

A controller's own ServiceAccount should be granted only the permissions its reconcile logic actually requires on the resources it manages, following the same least-privilege principle covered under identity and access guidelines — a controller with unnecessarily broad cluster-wide permissions becomes an outsized target if compromised, since it can act on behalf of every namespace it wasn't actually scoped to touch.

Leader Election for Controller Availability

Running multiple replicas of a controller for availability requires leader election, ensuring only one replica actively reconciles at a time while the others stand by, since concurrent, uncoordinated reconciliation from multiple active replicas risks conflicting writes and race conditions against the same underlying resources.


API Aggregation and Admission Webhooks

Aggregated API Servers

For cases where a CRD's limitations (no custom storage backend, limited validation flexibility) are insufficient, an aggregated API server registers an entirely separate API server implementation behind the same kube-apiserver front door, appropriate for genuinely advanced extensibility needs but carrying substantially more operational complexity than a CRD-based approach — this route should only be taken once CRDs have been evaluated and found insufficient, not chosen by default.

Webhooks as Extension Points

Beyond admission control (covered separately under admission policy guidelines), conversion webhooks handle CRD version translation and are a required piece of any CRD that supports multiple served versions with differing schemas.


Operational Maturity for Custom Extensions

Observability for Custom Controllers

Controllers should expose the same metrics, structured logs, and health probes expected of any other production workload — reconciliation error rate, reconcile duration, queue depth — since a custom controller silently failing to reconcile is otherwise invisible until its managed resources are noticed to have drifted from their desired state.

Testing Reconciliation Logic

Reconcile logic should be tested against realistic sequences of resource state transitions, including partial failures and out-of-order updates, since a controller's correctness depends heavily on behaving correctly across the full space of possible intermediate states it might observe, not just the happy path from creation to steady state.


Example Configuration

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: databaseclusters.codartium.example.com
spec:
  group: codartium.example.com
  names:
    kind: DatabaseCluster
    plural: databaseclusters
  scope: Namespaced
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              required: ["replicas", "storageSize"]
              properties:
                replicas:
                  type: integer
                  minimum: 1
                storageSize:
                  type: string

Practical Consequences

Well-designed extensibility produces domain-specific automation that behaves consistently with the rest of the cluster — declarative, RBAC-governed, observable, and resilient to restarts and partial failures. Poorly designed custom controllers commonly become a source of subtle, hard-to-diagnose incidents: non-idempotent reconcile logic that corrupts state on retry, overly broad RBAC turning a compromised controller into a cluster-wide risk, or a controller failing silently with no metrics to reveal that its managed resources have quietly drifted out of their intended state.