Introduction
Kubernetes is powerful, but what if its built-in objects like Pods, Services, and Deployments aren’t enough for your application’s needs? That’s where Custom Resource Definitions (CRDs) come in!
In this post, I’ll walk you through:
Why CRDs are needed
How to create a CRD from scratch
Implementing a custom controller
Deploying and managing custom resources
Why Extend Kubernetes?
Kubernetes comes with a standard set of APIs (like apps/v1 for Deployments), but many applications require domain-specific concepts that Kubernetes doesn’t provide natively.
For example:
A database team might want a Database object instead of manually managing StatefulSets.
A security team might want a FirewallRule object to enforce policies at the cluster level.
With CRDs, you can define custom objects tailored to your use case and make them first-class citizens in Kubernetes!
Step 1: Creating a Custom Resource Definition (CRD)
A CRD allows Kubernetes to recognize new object types. Let’s create a CRD for a PostgreSQL database instance.
Save the following YAML as postgresql-crd.yaml:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: postgresqls.mycompany.com
spec:
group: mycompany.com
names:
kind: PostgreSQL
plural: postgresqls
singular: postgresql
scope: Namespaced
versions:
- name: v1alpha1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
databaseName:
type: string
storageSize:
type: string
replicas:
type: integer
Apply the CRD to Kubernetes
kubectl apply -f postgresql-crd.yaml
Now, Kubernetes knows about the PostgreSQL resource!
Step 2: Creating a Custom Resource Instance
Let’s create an actual PostgreSQL instance using our CRD.
Save the following YAML as postgresql-instance.yaml:
apiVersion: mycompany.com/v1alpha1
kind: PostgreSQL
metadata:
name: my-database
spec:
databaseName: mydb
storageSize: "10Gi"
replicas: 2
Apply the Custom Resource
kubectl apply -f postgresql-instance.yaml
Kubernetes now understands PostgreSQL objects, but it won’t do anything with them yet. That’s where controllers come in!
Step 3: Building a Kubernetes Controller
A controller watches for changes in custom resources and performs necessary actions.
Here’s a basic Go-based controller using controller-runtime:
package controllers
import (
"context"
"fmt"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
)
type PostgreSQLReconciler struct {
client.Client
}
func (r *PostgreSQLReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
fmt.Println("Reconciling PostgreSQL instance:", req.NamespacedName)
// Fetch the PostgreSQL instance
var pgInstance PostgreSQL
if err := r.Get(ctx, req.NamespacedName, &pgInstance); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// Implement database provisioning logic here
return ctrl.Result{}, nil
}
func (r *PostgreSQLReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&PostgreSQL{}).
Complete(r)
}
Deploying the Controller
To deploy this, we use Kubebuilder and the Operator SDK:
operator-sdk init --domain mycompany.com --repo github.com/mycompany/postgres-operator
operator-sdk create api --group mycompany --version v1alpha1 --kind PostgreSQL --resource --controller
make manifests
make install
make run
Your Kubernetes Operator is now watching for PostgreSQL objects and taking action!
Step 4: Deploying and Testing the Operator
Apply the CRD and PostgreSQL resource:
kubectl apply -f postgresql-crd.yaml
kubectl apply -f postgresql-instance.yaml
Check if the custom resource is recognized:
kubectl get postgresqls.mycompany.com
Check the controller logs to see it processing the custom resource:
kubectl logs -l control-plane=controller-manager
If everything works, your PostgreSQL resource is being managed automatically!
Conclusion: Why Use CRDs?
- Encapsulate Business Logic: No need to manually configure every deployment—just define a custom resource, and the operator handles it.
- Standard Kubernetes API: Developers can use kubectl to interact with custom resources just like native Kubernetes objects.
- Automated Workflows: Kubernetes Operators can provision, update, and heal application components automatically.
By implementing Custom Resource Definitions and Operators, you extend Kubernetes the right way—without hacking it!
What are some use cases where CRDs and Operators helped your team? Let’s discuss in the comments!![]()