פיתוח אופרטורים מותאמים אישית ל-Kubernetes: מהרעיון לייצור

כשהכלים הסטנדרטיים לא מצליחים להתמודד עם אוטומציה של תהליכים ספציפיים, אופרטור Kubernetes מותאם אישית בא להציל. אנחנו בונים אופרטורים במפתח מלא—מתכנון ועד ייצור, ומבטיחים אמינות ו-idempotency. הצוות שלנו מכסה את כל המחזור: ביקורת, פיתוח, פריסה ותמיכה מתמשכת.

היכולות שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

אופרטורים מותאמים אישית של Kubernetes קיימים עבור PostgreSQL, Kafka, Redis ו-Elasticsearch. אבל כשצריך אופרטור Kubernetes מותאם אישית כדי לבצע אוטומציה של תהליך ספציפי—כמו הקצאת סביבות דיירים (tenant environments) כשנוצר לקוח חדש או ניהול דגלי תכונה (feature flags) דרך ה-API של Kubernetes—זה חיוני. בפרויקט אחד, ביצענו אוטומציה של יצירת סביבות דיירים, והפחתנו את זמן הפריסה משעתיים ל-15 דקות—פי 8 מהר יותר. האוטומציה הזו חסכה ללקוח 12,000 דולר בחודש בעלויות תפעול. אנחנו מפתחים אופרטורים כאלה במפתח מלא, עם הבטחה ל-idempotency, טיפול נכון בשגיאות וכיסוי בדיקות מלא. צרו קשר לייעוץ—נעריך את המשימה שלכם תוך יומיים.

מתי לפתח אופרטור מותאם אישית

אופרטור אינו הכלי הראשון שצריך לפנות אליו. לפני שמתחילים בפיתוח, בדקו שלושה תנאים: האם ניתן לפתור את המשימה עם אופרטור קיים באמצעות קונפיגורציה מותאמת, האם CronJob עם סקריפט פשוט מספיק, והאם Helm עם ArgoCD ApplicationSet מספיק לניהול הצהרתי.

אופרטור מוצדק כשצריך להגיב לאירועי Kubernetes (נוצר משאב מותאם אישית—בצעו רצף פעולות) ולולאת reconciliation (ניטור מתמיד של המצב ותיקון סטיות). גם כשהלוגיקה העסקית מורכבת מדי עבור סקריפט. לדוגמה, כשיוצרים אוטומטית סביבות מבודדות לכל לקוח עם NetworkPolicy, PVC וקונפיגורציית ניטור. במקרים כאלה, אופרטור חוסך עד 90% מהזמן בהשוואה לניהול ידני. הגישה שלנו לאופרטורים מותאמים אישית יעילה פי 3 מסקריפטינג מסורתי.

כלי מתי להשתמש מורכבות זמן הגדרה
Helm + ApplicationSet ניהול הצהרתי פשוט נמוכה שעות
CronJob משימות תקופתיות בינונית ימים
אופרטור מותאם אישית לוגיקה עסקית מורכבת, אירועים גבוהה שבועות

בחירת מסגרת עבודה: Operator SDK או Kubebuilder

שתי המסגרות מייצרות קוד boilerplate ומשתמשות ב-controller-runtime. Kubebuilder הוא הכלי הרשמי של Kubernetes SIG, אקדמי יותר ומתועד היטב. Operator SDK של Red Hat מוסיף יכולות בנייה, בדיקה ופרסום ל-Operator Hub, ותומך בכתיבה ב-Ansible ו-Helm. בפועל, הבחירה אינה קריטית—הקוד דומה. Kubebuilder מציע API נקי יותר ליצירת CRD, בעוד Operator SDK מספק אינטגרציה עם OpenShift. אם הצוות שלכם משתמש ב-OpenShift, בחרו ב-Operator SDK לתכונות נוספות. אחרת, Kubebuilder פשוט ושקוף יותר.

קריטריון Kubebuilder Operator SDK
מקור Kubernetes SIG Red Hat
שפות Go (עיקרית) Go, Ansible, Helm
תיעוד מצוין, אקדמי טוב, מעשי
אינטגרציה עם OpenShift בסיסית עמוקה
פרסום ל-Operator Hub ידני מובנה

מה כולל פיתוח אופרטור מותאם אישית?

אנחנו מספקים מחזור עבודה מלא:

  1. ניתוח תהליכים עסקיים ועיצוב משאב מותאם אישית.
  2. יצירת שלד אופרטור באמצעות Kubebuilder.
  3. יישום לולאת reconciliation עם idempotency ו-finalizers.
  4. הגדרת RBAC, NetworkPolicy וניטור (מדדי Prometheus).
  5. בדיקות יחידה עם envtest ובדיקות e2e על קלאסטר kind.
  6. צינור CI/CD לבנייה ופריסה של האופרטור לקלאסטר היעד.
  7. תיעוד פריסה ותפעול.
  8. אחריות ל-3 חודשים על הפונקציונליות לאחר המסירה.

למה Idempotency היא הבסיס של אופרטור

פונקציית ה-reconcile יכולה להיקרא מספר פעמים עבור אותו אובייקט—בשינויים, בהפעלה מחדש של הבקר, או לפי טיימר. כל קריאה חייבת להניב את אותה תוצאה ללא קשר למצב הנוכחי. זה קריטי כדי למנוע שכפול משאבים ומצבי מרוץ (race conditions). אנחנו תמיד מיישמים פעולות idempotent: בדיקת קיום משאב לפני יצירה, שימוש ב-finalizers למחיקה נכונה, ועדכון סטטוס דרך תת-המשאב Status.

איך בנוי אופרטור מותאם אישית

אתחול פרויקט עם Kubebuilder:

kubebuilder init --domain company.io --repo github.com/company/tenant-operator
kubebuilder create api --group platform --version v1 --kind Tenant

זה יוצר:

  • kubebuilder init --domain company.io --repo github.com/company/tenant-operator kubebuilder create api --group platform --version v1 --kind Tenant — מבנה משאב מותאם אישית
  • api/v1/tenant_types.go — לוגיקת reconciliation
  • controllers/tenant_controller.go — מניפסטים של CRD
  • config/crd/ — RBAC עבור האופרטור

העבודה העיקרית נמצאת בפונקציית config/rbac/ של הבקר:

func (r *TenantReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	log := log.FromContext(ctx)

	// Получаем Tenant объект
	tenant := &platformv1.Tenant{}
	if err := r.Get(ctx, req.NamespacedName, tenant); err != nil {
		return ctrl.Result{}, client.IgnoreNotFound(err)
	}

	// Проверяем finalizer для корректного удаления
	if !controllerutil.ContainsFinalizer(tenant, tenantFinalizer) {
		controllerutil.AddFinalizer(tenant, tenantFinalizer)
		if err := r.Update(ctx, tenant); err != nil {
			return ctrl.Result{}, err
		}
	}

	// Идемпотентная логика: создаём namespace если не существует
	ns := &corev1.Namespace{}
	nsName := fmt.Sprintf("tenant-%s", tenant.Name)
	if err := r.Get(ctx, types.NamespacedName{Name: nsName}, ns); err != nil {
		if !apierrors.IsNotFound(err) {
			return ctrl.Result{}, err
		}

		// Создаём namespace
		newNs := &corev1.Namespace{
			ObjectMeta: metav1.ObjectMeta{
				Name: nsName,
				Labels: map[string]string{
					"tenant": tenant.Name,
					"managed-by": "tenant-operator",
				},
			},
		}
		if err := r.Create(ctx, newNs); err != nil {
			return ctrl.Result{}, err
		}
		log.Info("Created namespace", "namespace", nsName)
	}

	// Обновляем статус
	tenant.Status.Phase = "Active"
	tenant.Status.Namespace = nsName
	if err := r.Status().Update(ctx, tenant); err != nil {
		return ctrl.Result{}, err
	}

	return ctrl.Result{RequeueAfter: time.Minute * 5}, nil
}

עקרונות מפתח בפיתוח אופרטורים

Idempotency

כל קריאה ל-Reconcile חייבת להוביל לאותה תוצאה ללא קשר למצב הנוכחי. מושגת על ידי בדיקת קיום משאב ושימוש בתבניות Reconcile.

Finalizers

אם האופרטור יוצר משאבים חיצוניים (רשומות DNS, אובייקטי ענן, רשומות מסד נתונים), יש צורך ב-finalizer—הוא חוסם מחיקה של המשאב המותאם אישית עד שתסתיים לוגיקת הניקוי. בלעדיו, מחיקת המשאב משאירה זבל במערכת.

תת-משאב Status

מצב המשאב צריך לבוא לידי ביטוי ב-func (r *TenantReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := log.FromContext(ctx) // Получаем Tenant объект tenant := &platformv1.Tenant{} if err := r.Get(ctx, req.NamespacedName, tenant); err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } // Проверяем finalizer для корректного удаления if !controllerutil.ContainsFinalizer(tenant, tenantFinalizer) { controllerutil.AddFinalizer(tenant, tenantFinalizer) if err := r.Update(ctx, tenant); err != nil { return ctrl.Result{}, err } } // Идемпотентная логика: создаём namespace если не существует ns := &corev1.Namespace{} nsName := fmt.Sprintf("tenant-%s", tenant.Name) if err := r.Get(ctx, types.NamespacedName{Name: nsName}, ns); err != nil { if !apierrors.IsNotFound(err) { return ctrl.Result{}, err } // Создаём namespace newNs := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: nsName, Labels: map[string]string{ "tenant": tenant.Name, "managed-by": "tenant-operator", }, }, } if err := r.Create(ctx, newNs); err != nil { return ctrl.Result{}, err } log.Info("Created namespace", "namespace", nsName) } // Обновляем статус tenant.Status.Phase = "Active" tenant.Status.Namespace = nsName if err := r.Status().Update(ctx, tenant); err != nil { return ctrl.Result{}, err } return ctrl.Result{RequeueAfter: time.Minute * 5}, nil } , לא ב-CreateOrUpdate. זה מאפשר ל-.status להציג את המצב הנוכחי. הסטטוס יכול לכלול שלב, תנאים ומדדים.

טיפול בשגיאות

החזרת שגיאה מ-Reconcile גורמת ל-controller-runtime להכניס את האובייקט שוב לתור עם backoff אקספוננציאלי. לבדיקות תקופתיות מתוכננות, השתמשו ב-.spec.

רשימת בדיקה של טעויות נפוצות בפיתוח אופרטורים

  • חסר finalizer למשאבים חיצוניים—משאבים לא מנוקים.
  • פעולות שאינן idempotent—שכפול namespace או ישויות אחרות.
  • התעלמות משגיאות ביצירת משאבים—האופרטור בלולאה אינסופית.
  • אחסון מצב ב-kubectl get tenant -o wide במקום ב-ctrl.Result{RequeueAfter: duration}—מסבך את הדיבאג.
  • אין בדיקות יחידה—באגים ב-reconciliation לא מתגלים.

לוחות זמנים ועלות

אופרטור פשוט (1-2 משאבים מותאמים אישית, לוגיקת reconciliation לינארית) לוקח בין שבועיים לחודש, כולל בדיקות ותיעוד. אופרטור מורכב המנהל שירותים עם מצב (stateful), לוגיקת failover וטופולוגיית משאבים מרובת רכיבים לוקח בין חודשיים לשלושה חודשים. העלות נקבעת באופן אישי לאחר ביקורת של המשימה שלכם. בדרך כלל, ההשקעה נעה בין 5,000 ל-15,000 דולר ומחזירה את עצמה תוך 2-3 חודשים על ידי הפחתת עבודה ידנית. אם אתם רוצים לבצע אוטומציה של פעולות שגרתיות ב-Kubernetes—צרו קשר, נפתח אופרטור עבור התרחיש שלכם. עם ניסיון של למעלה מ-5 שנים ב-Kubernetes ויותר מ-50 פרויקטי אוטומציה מוצלחים, אנחנו מספקים אופרטורים חזקים. אופרטור ה-Go שלנו בנוי להיות בקר Kubernetes חזק, חלק בלתי נפרד מאוטומציית Kubernetes וזרימות עבודה של GitOps.

מקור: תיעוד Kubebuilder