Kubernetes Operatörleri: Kendi Kontrolcünü Yazmak
Kubernetes, Deployment ve Service gibi kaynaklarla stateless (durumsuz) uygulamaları yönetmek konusunda harikadır. Bir web sunucusunu ölçeklemek mi istiyorsunuz? replicas: 5 yaparsınız ve biter. Bir pod ölürse yenisi açılır, kimse fark etmez.
Ancak iş veritabanlarına, cache kümelerine veya mesaj kuyruklarına (StatefulSet) geldiğinde işler karışır. Bir PostgreSQL kümesini yönetmek, sadece pod'ları ayağa kaldırmaktan ibaret değildir.
- Master hangisi olacak?
- Replica'lar senkronize mi?
- Master çökerse (Failover) kim devralacak?
- Backup ne zaman alınacak?
- Versiyon yükseltirken veri kaybı nasıl önlenecek?
Bu soruların cevabı "insan bilgisi" (domain knowledge) gerektirir. İşte "Operator Pattern", bu insan bilgisini koda döküp Kubernetes'e öğretme sanatıdır.
Operatör Nedir?
Operatör, Kubernetes API'sini genişleten (CRD - Custom Resource Definition) ve bu yeni kaynakları yöneten bir kontrolcüdür (Controller). Basitçe, Kubernetes'e yeni bir kelime öğretirsiniz.
Örneğin, PostgresCluster diye bir kaynak tanımlayabilir ve Kubernetes'e şunu diyebilirsiniz:
"Bana 3 nodelu, versiyon 14 olan, 100GB diskli bir Postgres kümesi ver."
apiVersion: db.enginhan.com/v1
kind: PostgresCluster
metadata:
name: main-db
spec:
replicas: 3
version: "14"
storage: "100Gi"
Siz bu YAML'ı apply ettiğinizde, arka planda çalışan Operatör (sizin yazdığınız Go kodu) uyanır ve şunları yapar:
- Gerekli StatefulSet'i oluşturur.
- Service ve Secret'ları ayarlar.
- Pod'lar ayağa kalkınca replikasyonu konfigüre eder.
- Gerektiğinde backup alır.
Nasıl Çalışır? (Reconciliation Loop)
Operatör mantığı, Kubernetes'in kalbi olan basit bir döngüye dayanır: Reconciliation Loop.
- Observe (Gözlemle): Mevcut durum ne? (Örn: Şu an 2 pod çalışıyor)
- Diff (Karşılaştır): İstenen durum (Desired State) ne? (Örn: Kullanıcı 3 pod istemiş)
- Act (Eylem): Farkı kapat. (Örn: 1 pod daha başlat)
Bu döngü sonsuza kadar döner. Eğer biri gidip manuel olarak bir pod'u silerse, operatör bunu fark eder ve hemen yenisini açar. Sistem sürekli kendini iyileştirir (Self-healing).
Kendi Operatörünü Yazmak
Operatör yazmak için en popüler araçlar Operator SDK ve Kubebuilder'dır. Genellikle Go dili tercih edilir çünkü Kubernetes'in kendisi de Go ile yazılmıştır ve kütüphaneler çok gelişmiştir.
Adım 1: API Tanımı (CRD)
Önce veri yapımızı Go struct'ı olarak tanımlarız:
type PostgresClusterSpec struct {
Replicas int32 `json:"replicas"`
Version string `json:"version"`
Storage string `json:"storage"`
}
type PostgresClusterStatus struct {
ReadyReplicas int32 `json:"readyReplicas"`
State string `json:"state"` // "Creating", "Ready", "Failed"
}
Adım 2: Controller Mantığı
Sonra "Reconcile" fonksiyonunu yazarız. Burası beynin olduğu yerdir:
func (r *PostgresClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := log.FromContext(ctx)
// 1. CRD'yi getir
var cluster myv1.PostgresCluster
if err := r.Get(ctx, req.NamespacedName, &cluster); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// 2. StatefulSet var mı kontrol et
var sts appsv1.StatefulSet
err := r.Get(ctx, req.NamespacedName, &sts)
if errors.IsNotFound(err) {
// Yoksa yarat
sts = r.buildStatefulSet(&cluster)
if err := r.Create(ctx, &sts); err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{Requeue: true}, nil
}
// 3. Replica sayısını kontrol et, gerekirse scale et
if *sts.Spec.Replicas != cluster.Spec.Replicas {
sts.Spec.Replicas = &cluster.Spec.Replicas
if err := r.Update(ctx, &sts); err != nil {
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}
Ne Zaman Kullanmalı?
Her uygulama için operatör yazmak "Over-engineering" olabilir. Basit bir Helm chart çoğu zaman yeterlidir. Ancak:
- Karmaşık, stateful bir uygulamanız varsa (DB, Cache, Queue),
- Operasyonel süreçleriniz (Backup, Restore, Upgrade) manuel ve hataya açıksa,
- Uygulamanızı "As a Service" olarak şirket içinde sunmak istiyorsanız,
Operatör yazmak, yatırım getirisini (ROI) fazlasıyla karşılar. Bu, DevOps'tan Platform Mühendisliğine geçişin en somut adımıdır.