Kubernetes Operators Explained

Kubernetes Operators extend Kubernetes functionality by automating complex application management using Custom Resource Definitions (CRDs) and controllers.


Why Operators Matter

  • Automated Management: Deploy, scale, backup, and upgrade apps
  • Consistency: Maintain state as defined in CRDs
  • Complex Applications: Manage stateful apps like databases efficiently
  • Extensibility: Add custom logic for operational tasks

Workflow Example

  1. Define a Custom Resource (CR) for the application
  2. Deploy the Operator which watches the CR
  3. Operator performs tasks: deployment, scaling, upgrades, backup
  4. Monitor application state and metrics

Visual Diagram

flowchart TD A[Custom Resource] --> B[Kubernetes Operator] B --> C[Application Deployment & Management] C --> D[Monitor & Self-Heal] D --> E[Update Custom Resource] E --> B

Real-World Example: Deploying a Database Operator

Step 1: Create a Custom Resource Definition

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
    name: databases.example.com
spec:
    names:
        kind: Database
        plural: databases
    scope: Namespaced
    versions:
    - name: v1
        served: true
        storage: true
        schema:
            openAPIV3Schema:
                type: object
                properties:
                    spec:
                        type: object
                        properties:
                            engine:
                                type: string
                                enum: [postgres, mysql]
                            version:
                                type: string
                            backupSchedule:
                                type: string

Step 2: Deploy the Operator

apiVersion: apps/v1
kind: Deployment
metadata:
    name: database-operator
spec:
    replicas: 1
    selector:
        matchLabels:
            app: database-operator
    template:
        metadata:
            labels:
                app: database-operator
        spec:
            containers:
            - name: operator
                image: database-operator:v1.0
                env:
                - name: WATCH_NAMESPACE
                    value: ""

Step 3: Create an Instance Using the CR

apiVersion: example.com/v1
kind: Database
metadata:
    name: production-db
spec:
    engine: postgres
    version: "14"
    backupSchedule: "0 2 * * *"

Step 4: Operator Reconciliation Logic (Go)

func (r *DatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
        db := &examplev1.Database{}
        if err := r.Get(ctx, req.NamespacedName, db); err != nil {
                return ctrl.Result{}, err
        }
        
        // Create StatefulSet for database
        statefulSet := constructStatefulSet(db)
        if err := r.Create(ctx, statefulSet); err != nil {
                return ctrl.Result{RequeueAfter: 5 * time.Second}, err
        }
        
        // Update CR status
        db.Status.Phase = "Running"
        r.Status().Update(ctx, db)
        
        return ctrl.Result{}, nil
}

Best Practices

  • Use operators for stateful or complex applications
  • Monitor operator logs and events
  • Keep CRDs and operator code versioned
  • Test operators in staging before production

Common Pitfalls

  • Using operators for simple stateless apps unnecessarily

  • Not handling errors or retries in reconciliation

  • Ignoring resource consumption of operators

Conclusion

Kubernetes Operators allow DevOps teams to automate operational complexity, ensuring reliable management of stateful and complex applications.