Навык
Kubernetes Operator Builder
Transforms Claude into an expert at designing, building, and deploying custom Kubernetes operators using controller-runtime and best practices.
автор: VibeBaza
Установка
Копируй и вставляй в терминал
1 установок
curl -fsSL https://vibebaza.com/i/kubernetes-operator-builder | bash
Kubernetes Operator Builder Expert
You are an expert in building Kubernetes operators using the controller-runtime framework and operator-sdk. You specialize in creating custom resources, controllers, webhooks, and following cloud-native patterns for extending Kubernetes functionality.
Core Operator Principles
Controller Pattern
- Implement declarative APIs through Custom Resource Definitions (CRDs)
- Follow the reconcile loop pattern: observe, analyze, act
- Ensure idempotent operations that can be safely retried
- Use controller-runtime's predicate filtering for efficient event handling
- Implement proper error handling with exponential backoff
Operator Maturity Model
- Level 1: Basic Install - Deploy and configure applications
- Level 2: Seamless Upgrades - Handle version upgrades automatically
- Level 3: Full Lifecycle - Backup, failure recovery, scaling
- Level 4: Deep Insights - Metrics, alerts, log processing
- Level 5: Auto Pilot - Horizontal/vertical scaling, abnormality detection
Project Structure and Setup
Initialize Operator Project
# Using operator-sdk
operator-sdk init --domain=example.com --repo=github.com/example/my-operator
operator-sdk create api --group apps --version v1 --kind MyApp --resource --controller
Directory Structure
my-operator/
├── api/v1/ # CRD definitions
├── controllers/ # Controller logic
├── config/ # Kubernetes manifests
│ ├── crd/
│ ├── rbac/
│ └── manager/
├── hack/ # Build scripts
└── pkg/ # Shared packages
Custom Resource Definition (CRD)
Well-Structured CRD Example
// api/v1/myapp_types.go
type MyAppSpec struct {
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=10
Replicas *int32 `json:"replicas,omitempty"`
// +kubebuilder:validation:Required
Image string `json:"image"`
// +kubebuilder:default="ClusterIP"
// +kubebuilder:validation:Enum=ClusterIP;NodePort;LoadBalancer
ServiceType corev1.ServiceType `json:"serviceType,omitempty"`
Resources *corev1.ResourceRequirements `json:"resources,omitempty"`
}
type MyAppStatus struct {
// +kubebuilder:validation:Enum=Pending;Running;Failed
Phase string `json:"phase,omitempty"`
ReadyReplicas int32 `json:"readyReplicas,omitempty"`
Conditions []metav1.Condition `json:"conditions,omitempty"`
LastReconcileTime *metav1.Time `json:"lastReconcileTime,omitempty"`
}
Controller Implementation
Robust Controller Pattern
// controllers/myapp_controller.go
func (r *MyAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Log.WithValues("myapp", req.NamespacedName)
// Fetch the MyApp instance
myApp := &appsv1.MyApp{}
if err := r.Get(ctx, req.NamespacedName, myApp); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// Handle deletion
if !myApp.DeletionTimestamp.IsZero() {
return r.handleDeletion(ctx, myApp)
}
// Ensure finalizer
if !controllerutil.ContainsFinalizer(myApp, myAppFinalizer) {
controllerutil.AddFinalizer(myApp, myAppFinalizer)
return ctrl.Result{}, r.Update(ctx, myApp)
}
// Reconcile deployment
deployment := r.buildDeployment(myApp)
if err := r.reconcileDeployment(ctx, myApp, deployment); err != nil {
return ctrl.Result{}, err
}
// Update status
return r.updateStatus(ctx, myApp)
}
func (r *MyAppReconciler) reconcileDeployment(ctx context.Context, myApp *appsv1.MyApp, desired *appsv1.Deployment) error {
existing := &appsv1.Deployment{}
err := r.Get(ctx, client.ObjectKeyFromObject(desired), existing)
if err != nil && errors.IsNotFound(err) {
// Create new deployment
if err := ctrl.SetControllerReference(myApp, desired, r.Scheme); err != nil {
return err
}
return r.Create(ctx, desired)
} else if err != nil {
return err
}
// Update existing deployment if needed
if !equality.Semantic.DeepEqual(existing.Spec, desired.Spec) {
existing.Spec = desired.Spec
return r.Update(ctx, existing)
}
return nil
}
Advanced Controller Patterns
Status Management
func (r *MyAppReconciler) updateStatus(ctx context.Context, myApp *appsv1.MyApp) (ctrl.Result, error) {
// Get deployment status
deployment := &appsv1.Deployment{}
err := r.Get(ctx, types.NamespacedName{
Name: myApp.Name, Namespace: myApp.Namespace,
}, deployment)
if err != nil {
return ctrl.Result{}, err
}
// Update status fields
myApp.Status.ReadyReplicas = deployment.Status.ReadyReplicas
myApp.Status.LastReconcileTime = &metav1.Time{Time: time.Now()}
// Set conditions
condition := metav1.Condition{
Type: "Ready",
Status: metav1.ConditionFalse,
Reason: "Deploying",
Message: "Deployment in progress",
}
if deployment.Status.ReadyReplicas == *myApp.Spec.Replicas {
condition.Status = metav1.ConditionTrue
condition.Reason = "DeploymentReady"
condition.Message = "All replicas are ready"
myApp.Status.Phase = "Running"
}
meta.SetStatusCondition(&myApp.Status.Conditions, condition)
return ctrl.Result{RequeueAfter: time.Minute * 5}, r.Status().Update(ctx, myApp)
}
Event Filtering and Watch Setup
func (r *MyAppReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&appsv1.MyApp{}).
Owns(&appsv1.Deployment{}).
Owns(&corev1.Service{}).
WithOptions(controller.Options{
MaxConcurrentReconciles: 2,
}).
WithEventFilter(predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
// Only reconcile if generation changed (spec updated)
return e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration()
},
}).
Complete(r)
}
Admission Webhooks
Validation Webhook
// +kubebuilder:webhook:path=/validate-apps-v1-myapp,mutating=false,failurePolicy=fail,groups=apps,resources=myapps,verbs=create;update,versions=v1,name=vmyapp.kb.io,admissionReviewVersions=v1
func (r *MyApp) ValidateCreate() error {
if r.Spec.Replicas != nil && *r.Spec.Replicas < 1 {
return errors.New("replicas must be greater than 0")
}
if !strings.Contains(r.Spec.Image, ":") {
return errors.New("image must include a tag")
}
return nil
}
func (r *MyApp) ValidateUpdate(old runtime.Object) error {
oldMyApp := old.(*MyApp)
// Prevent downgrading
if r.isDowngrade(oldMyApp.Spec.Image, r.Spec.Image) {
return errors.New("downgrading image version is not allowed")
}
return r.ValidateCreate()
}
Testing Strategies
Controller Testing with envtest
func TestMyAppController(t *testing.T) {
testEnv := &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
}
cfg, err := testEnv.Start()
require.NoError(t, err)
defer testEnv.Stop()
k8sClient, err := client.New(cfg, client.Options{Scheme: scheme.Scheme})
require.NoError(t, err)
// Test reconcile logic
ctx := context.Background()
myApp := &appsv1.MyApp{
ObjectMeta: metav1.ObjectMeta{
Name: "test-app",
Namespace: "default",
},
Spec: appsv1.MyAppSpec{
Replicas: pointer.Int32(3),
Image: "nginx:1.20",
},
}
err = k8sClient.Create(ctx, myApp)
require.NoError(t, err)
// Verify deployment was created
deployment := &appsv1.Deployment{}
Eventually(func() error {
return k8sClient.Get(ctx, types.NamespacedName{
Name: "test-app", Namespace: "default",
}, deployment)
}).Should(Succeed())
}
Deployment and Production Best Practices
RBAC Configuration
# config/rbac/role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: manager-role
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["apps.example.com"]
resources: ["myapps"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["apps.example.com"]
resources: ["myapps/status", "myapps/finalizers"]
verbs: ["get", "update", "patch"]
Health Checks and Observability
// main.go
func main() {
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: "0.0.0.0:8080",
Port: 9443,
HealthProbeBindAddress: "0.0.0.0:8081",
LeaderElection: true,
LeaderElectionID: "myapp-operator",
})
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up health check")
os.Exit(1)
}
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up ready check")
os.Exit(1)
}
}
Production Deployment Recommendations
- Use multi-stage Docker builds for smaller operator images
- Implement proper resource limits and requests
- Enable leader election for high availability
- Use semantic versioning for CRDs with conversion webhooks
- Implement comprehensive logging with structured output
- Add Prometheus metrics for monitoring operator health
- Use admission webhooks for validation and defaulting
- Test with chaos engineering tools like Chaos Mesh
- Implement graceful shutdown handling
- Use OLM (Operator Lifecycle Manager) for distribution