Разработка кастомного Kubernetes Operator: от идеи до продакшена

Когда стандартные инструменты не справляются с автоматизацией специфичных процессов, на помощь приходит кастомный Kubernetes Operator. Мы разрабатываем операторы под ключ — от проектирования до продакшена, обеспечивая надёжность и идемпотентность. Наша команда берёт на себя весь цикл: аудит, разработку, внедрение и последующую поддержку.
Часто задаваемые вопросы

Готовые операторы есть для PostgreSQL, Kafka, Redis, Elasticsearch. Но когда нужно автоматизировать специфичный процесс — например, провижининг tenant-окружений при создании нового клиента или управление feature-флагами через API Kubernetes — без кастомного оператора не обойтись. На одном из проектов мы автоматизировали создание tenant-окружений, что сократило время развёртывания с 2 часов до 15 минут — в 8 раз быстрее. Мы разрабатываем такие операторы под ключ, гарантируя идемпотентность, правильную обработку ошибок и полное покрытие тестами. Закажите консультацию — оценим задачу за 2 дня.

Когда разрабатывать кастомный оператор

Operator — не первый инструмент, который нужно брать. Прежде чем начинать разработку, проверьте три условия. Во-первых, нельзя ли решить задачу существующим оператором с кастомной конфигурацией. Во-вторых, подойдёт ли CronJob с простым скриптом. В-третьих, достаточно ли Helm с ArgoCD ApplicationSet для декларативного управления.

Оператор оправдан, когда нужна реакция на события Kubernetes (создали Custom Resource — выполни последовательность действий) и reconciliation loop (постоянно следи за состоянием и исправляй отклонения). Также если бизнес-логика слишком сложна для скрипта. Например, при автоматическом создании изолированных окружений для каждого клиента с настройкой NetworkPolicy, PVC и мониторинга. В таких случаях оператор даёт до 90% экономии времени по сравнению с ручным управлением.

Инструмент Когда использовать Сложность Время настройки
Helm + ApplicationSet Простое декларативное управление Низкая Часы
CronJob Периодические задачи Средняя Дни
Кастомный Operator Сложная бизнес-логика, события Высокая Недели

Как выбрать фреймворк: 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. Анализ бизнес-процесса и проектирование Custom Resources.
  2. Генерация скелета оператора с помощью Kubebuilder.
  3. Реализация reconciliation loop с идемпотентностью и finalizer.
  4. Настройка RBAC, NetworkPolicy и мониторинга (Prometheus метрики).
  5. Unit-тесты с envtest и e2e-тесты на kind-кластере.
  6. CI/CD пайплайн для сборки и деплоя оператора в целевой кластер.
  7. Документация по развёртыванию и эксплуатации.
  8. Гарантия работоспособности в течение 3 месяцев после сдачи.

Типовой проект экономит клиенту до $5,000 ежемесячно за счёт автоматизации. Стоимость разработки начинается от $2,000 — точную сумму назовём после аудита.

Почему идемпотентность — основа оператора?

Reconcile может вызываться многократно для одного объекта — при изменении, рестарте контроллера или по таймеру. Каждый вызов должен приводить к одному результату, независимо от текущего состояния. Это критично для предотвращения дублирования ресурсов и гонок. Мы всегда реализуем идемпотентные операции: проверяем существование ресурса перед созданием, используем finalizer для корректного удаления, а статус обновляем через Status subresource.

Как устроен кастомный оператор?

Инициализация проекта с Kubebuilder:

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 — структура Custom Resource
  • controllers/tenant_controller.go — логика reconciliation
  • config/crd/ — CRD манифесты
  • config/rbac/ — RBAC для оператора

Основная работа — в Reconcile функции контроллера:

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
}

Какие принципы важны при разработке оператора?

Идемпотентность

Каждый вызов Reconcile должен приводить к одному результату, независимо от текущего состояния. Это достигается проверками существования ресурсов и использованием CreateOrUpdate паттернов.

Finalizers

Если оператор создаёт внешние ресурсы (DNS-записи, облачные объекты, записи в БД), нужен finalizer — он блокирует удаление Custom Resource до завершения cleanup-логики. Без него удаление ресурса оставит «мусор» в системе.

Status subresource

Состояние ресурса должно отражаться в .status, а не в .spec. Это позволяет kubectl get tenant -o wide показывать текущее состояние. Статус может содержать фазу, условия и метрики.

Обработка ошибок

Возврат ошибки из Reconcile заставляет controller-runtime поставить объект обратно в очередь с экспоненциальной задержкой. Для запланированных повторных проверок используйте ctrl.Result{RequeueAfter: duration}.

Чек-лист типичных ошибок при разработке оператора

  • Отсутствие finalizer для внешних ресурсов — ресурсы не очищаются.
  • Неидемпотентные операции — дублирование namespace или другой сущности.
  • Игнорирование ошибок при создании ресурсов — оператор зависает в цикле.
  • Хранение состояния в .spec вместо .status — усложняет отладку.
  • Отсутствие unit-тестов — баги в reconciliation остаются незамеченными.

Сроки и стоимость

Простой оператор (1-2 Custom Resources, линейная логика reconciliation) — от 2 недель до месяца с учётом тестов и документации. Сложный оператор с управлением stateful-сервисами, failover-логикой и мульти-ресурсной топологией — от 2 до 3 месяцев. Стоимость рассчитывается индивидуально после аудита задачи. Инвестиция в оператора обычно окупается за 2–3 месяца за счёт сокращения ручного труда. Если вы хотите автоматизировать рутинные операции в Kubernetes — получите консультацию, мы разработаем оператор под ваш сценарий. Наш опыт в DevOps — 5+ лет, более 50 успешных проектов автоматизации.

Источник: Kubebuilder documentation

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1393
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1368
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    815
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1139