Customizing Kubernetes functionality through Operators provides developers with immense power to automate tasks, manage complex applications, and handle domain-specific logic. Kubernetes Operators extend beyond built-in functionality, offering specialized custom controllers to manage application components like databases, caches, or even entire application lifecycles.
This guide covers what Operators are, when to use them, how to write a simple Operator with Kubebuilder or the Operator SDK, key elements like reconcilers, custom resource definitions (CRDs), and finalizers. We’ll also include a real-world example of managing a database cluster, with examples in Spring Boot for integration.
Table of Contents
- What Are Kubernetes Operators & When to Use Them
- Writing a Simple Operator with Kubebuilder or Operator SDK
- Key Concepts of Operators
- Real-World Example: Managing Database Clusters
- Final Thoughts
What Are Kubernetes Operators & When to Use Them
What Is an Operator?
A Kubernetes Operator automates administrative tasks for an application in a Kubernetes cluster. It’s essentially a custom controller that extends Kubernetes to handle domain-specific logic by watching and reconciling custom resources (CRs). Operators codify human operational tasks into software.
Use Cases for Operators
- Complex Stateful Applications: Automate tasks like scaling, backups, and failover for databases, caches, etc.
- Custom Workflows: Manage domain-specific logic (e.g., custom deployment pipelines, app configurations).
- Self-Healing: Automatically detect and correct application issues.
- Declarative Management: Translate high-level application descriptions into precise runtime configurations.
Operator Lifecycle Management
Operators typically handle the following lifecycle tasks:
- Provisioning: Create resources like Pods, ConfigMaps, etc.
- Monitoring: Ensure desired state matches the actual state.
- Scaling: Dynamically adjust resources based on usage.
- Teardown: Clean up resources when no longer needed.
Example Use Case:
- A database operator might handle deployment, scaling, database user creation, backups, and restoring from snapshots.
Writing a Simple Operator with Kubebuilder or Operator SDK
Step 1 – Set Up the Development Framework
There are two popular frameworks for writing Operators:
- Kubebuilder (backed by Kubernetes SIGs).
- Operator SDK (built on Kubebuilder, with additional scaffolding and functionality).
Install the necessary CLI tools:
Installing Kubebuilder:
brew install kubebuilder
Installing Operator SDK:
brew install operator-sdk
Step 2 – Scaffold the Operator
Using Kubebuilder:
- Initialize a project:
kubebuilder init --domain yourdomain.com --repo your-operator
- Create an API:
kubebuilder create api --group database --version v1 --kind MySQLCluster
This generates folders for controllers and APIs:
.
├── api/
│ └── v1/
│ ├── mysqlcluster_types.go # Define CRDs here
├── controllers/
│ └── mysqlcluster_controller.go # Reconciliation logic
Using Operator SDK:
- Initialize a project:
operator-sdk init --domain yourdomain.com --repo your-operator
- Create an API:
operator-sdk create api --group database --version v1 --kind MySQLCluster --resource --controller
Step 3 – Define the Custom Resource
Edit mysqlcluster_types.go
to define the custom resource schema:
type MySQLClusterSpec struct {
Replicas int `json:"replicas"`
Version string `json:"version"`
}
type MySQLClusterStatus struct {
Nodes []string `json:"nodes"`
}
Run the following command to generate CRDs:
make generate && make manifests
CRDs describe how custom resources will behave in the Kubernetes cluster.
Key Concepts of Operators
Reconcilers
The reconciler watches resource changes and ensures the cluster’s actual state matches the desired state.
Example Reconcile Loop:
func (r *MySQLClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
// Fetch the MySQLCluster instance
var cluster databasev1.MySQLCluster
if err := r.Get(ctx, req.NamespacedName, &cluster); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// Example logic - ensure desired number of Pods
deployment := &appsv1.Deployment{}
if *deployment.Spec.Replicas != cluster.Spec.Replicas {
deployment.Spec.Replicas = &cluster.Spec.Replicas
err := r.Update(ctx, deployment)
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
Custom Resource Definitions (CRDs)
CRDs define the schema for your Operator resources.
An example CR for MySQLCluster
:
apiVersion: database.yourdomain.com/v1
kind: MySQLCluster
metadata:
name: my-cluster
spec:
replicas: 3
version: "8.0"
Finalizers
Finalizers ensure resources are cleaned up before being deleted.
Example:
func (r *MySQLClusterReconciler) finalizeMySQLCluster(ctx context.Context, instance *databasev1.MySQLCluster) error {
return r.Delete(ctx, &pv)
}
Real-World Example: Managing Database Clusters
Operators are commonly used for managing database ecosystems like MySQL, PostgreSQL, or MongoDB clusters.
Example Use Case
The MySQLCluster
Operator handles:
- Provisioning primary Pods and replicas.
- Scaling based on CPU/memory usage.
- Automating backups via CronJobs.
Example Reconciliation Logic in the Operator:
- Check for the existence of the Deployment.
- Create/update ConfigMaps or Secrets for database configuration.
- Handle Pod scaling based on CPU/memory metrics.
Kubernetes Example Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-primary
spec:
replicas: 1
template:
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: root-password
Spring Boot Example
Integrate the Operator-managed cluster with a Spring Boot application.
- Externalize Database Configuration: Configure
application.properties
to fetch database credentials:spring.datasource.url=jdbc:mysql://mysql-primary.default.svc.cluster.local/mydb spring.datasource.username=root spring.datasource.password=${MYSQL_PASSWORD}
- Using Secrets to Inject Credentials: Create Kubernetes Secrets:
kubectl create secret generic mysql-secret --from-literal=root-password="securepassword"
- Database Connection in Spring Boot: Use Spring Data JPA to interact with the database:
@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; } @Repository public interface UserRepository extends JpaRepository<User, Long> {}
The Spring Boot application automatically connects to the Operator-managed MySQL cluster!
Final Thoughts
Kubernetes Operators enable you to extend and customize Kubernetes functionality for complex, domain-specific use cases. By leveraging tools like Kubebuilder or Operator SDK, you can write robust controllers to automate repetitive tasks, enforce cluster policies, and manage application lifecycles.
Whether you’re scaling databases, configuring apps, or orchestrating workflows, Operators pair well with frameworks like Spring Boot to build seamless integrations between applications and Kubernetes. Start small, iterate on your Operator logic, and unlock bold new levels of automation in your Kubernetes infrastructure!