Kubernetes предоставляет мощный, но стандартизированный набор ресурсов и контроллеров. Когда ваши задачи выходят за рамки стандартных Pod, Deployment или StatefulSet, на помощь приходят Custom Resource Definitions (CRD) и пользовательские контроллеры. Этот механизм позволяет адаптировать API кластера под специфичные требования бизнес-логики, автоматизируя управление сложными приложениями или инфраструктурными компонентами. В этой статье мы создадим с нуля простой, но полностью рабочий оператор для управления кастомным ресурсом, используя фреймворк Kubebuilder. Вы получите готовый пример, который можно сразу адаптировать и запустить в своем кластере.
Разработка собственного оператора требует понимания его архитектуры. Оператор Kubernetes - это паттерн, состоящий из двух ключевых компонентов: Custom Resource Definition (CRD), который расширяет API Kubernetes новым типом объекта, и контроллера (Controller), который реализует логику управления жизненным циклом этих объектов. Контроллер постоянно наблюдает за изменениями как кастомных ресурсов, так и других объектов кластера, и выполняет цикл реконсиляции (reconcile loop), приводя реальное состояние системы к желаемому, описанному в ресурсе.
Получение практического, работоспособного примера
Начнем с создания оператора для гипотетического ресурса AppMonitor, который будет автоматически разворачивать Pod с указанным образом и создавать ServiceMonitor для Prometheus при его наличии. Мы используем Kubebuilder - фреймворк, который генерирует каркас проекта и берет на себя рутинные задачи.
Шаг 1: Инициализация проекта и определение API
Установите Kubebuilder и инициализируйте новый проект:
mkdir appmonitor-operator && cd appmonitor-operator
kubebuilder init --domain my.domain --repo my.domain/appmonitor-operator
kubebuilder create api --group monitoring --version v1alpha1 --kind AppMonitor --resource --controller
Команда create api создает структуры для CRD (api/v1alpha1/appmonitor_types.go) и контроллера (controllers/appmonitor_controller.go).
Шаг 2: Определение структуры Custom Resource
Откройте файл api/v1alpha1/appmonitor_types.go и определите поля spec и status:
// AppMonitorSpec defines the desired state of AppMonitor
type AppMonitorSpec struct {
// Image is the container image to run.
Image string `json:"image"`
// Replicas is the number of desired pods.
Replicas int32 `json:"replicas"`
// PrometheusMonitor enables ServiceMonitor creation.
PrometheusMonitor bool `json:"prometheusMonitor,omitempty"`
}
// AppMonitorStatus defines the observed state of AppMonitor
type AppMonitorStatus struct {
// Conditions represent the latest available observations of the resource state.
Conditions []metav1.Condition `json:"conditions,omitempty"`
// PodNames holds the names of the managed pods.
PodNames []string `json:"podNames,omitempty"`
}
После изменения типов необходимо сгенерировать манифесты CRD:
make manifests
Эта команда создаст YAML-файл Custom Resource Definition в config/crd/bases/. Вы можете изучить его, чтобы понять структуру вашего нового API-ресурса. Для более глубокого понимания процесса создания CRD с валидацией схемы и разработки оператора, обратитесь к нашему полному практическому руководству по созданию CRD и операторов для Kubernetes 2026.
Шаг 3: Реализация логики контроллера
Логика содержится в Reconcile методе файла controllers/appmonitor_controller.go. Вот упрощенный пример, который создает Deployment:
func (r *AppMonitorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := ctrl.LoggerFrom(ctx)
log.Info("Starting reconcile", "appmonitor", req.NamespacedName)
// Fetch the AppMonitor instance
appMonitor := &monitoringv1alpha1.AppMonitor{}
if err := r.Get(ctx, req.NamespacedName, appMonitor); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// Check if the Deployment already exists, if not create it
dep := &appsv1.Deployment{}
err := r.Get(ctx, types.NamespacedName{Name: appMonitor.Name, Namespace: appMonitor.Namespace}, dep)
if err != nil && apierrors.IsNotFound(err) {
// Define a new Deployment
dep = r.constructDeploymentForAppMonitor(appMonitor)
if err := r.Create(ctx, dep); err != nil {
log.Error(err, "Failed to create Deployment")
return ctrl.Result{}, err
}
log.Info("Deployment created successfully")
} else if err != nil {
log.Error(err, "Failed to get Deployment")
return ctrl.Result{}, err
}
// Update status
appMonitor.Status.PodNames = getPodNames(dep)
if err := r.Status().Update(ctx, appMonitor); err != nil {
log.Error(err, "Failed to update AppMonitor status")
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
Контроллер использует паттерн оператора для автоматизации. Чтобы увидеть более сложный пример, где оператор управляет полным жизненным циклом stateful-приложения, изучите наш гайд по созданию оператора Kubernetes для управления распределенными базами данных.
Шаг 4: Локальный запуск и тестирование
Установите CRD в кластер и запустите контроллер локально (используя kubeconfig):
make install
make run
В другом терминале создайте экземпляр вашего кастомного ресурса (config/samples/monitoring_v1alpha1_appmonitor.yaml):
kubectl apply -f config/samples/monitoring_v1alpha1_appmonitor.yaml
Вы должны увидеть, как контроллер обработает событие и создаст соответствующий Deployment. Это основа, которую можно расширять: добавить обработку обновлений, удалений (finalizers), создание Service, ConfigMap и других зависимых ресурсов.
Снижение рисков и избежание ошибок при внедрении
Разработка оператора для production-среды требует соблюдения best practices, чтобы избежать нестабильности кластера.
Идемпотентность Reconcile
Метод Reconcile может быть вызван многократно для одного и того же объекта. Ваша логика должна быть идемпотентной: многократное выполнение одних и тех же действий должно приводить к одинаковому результату. Всегда проверяйте текущее состояние перед внесением изменений (например, через r.Get).
Версионирование CRD
При изменении схемы CRD после его использования в production необходимо правильно управлять версиями. Kubebuilder поддерживает множественные версии (v1alpha1, v1beta1, v1) в одном проекте с конвертацией между ними через файл api/v1alpha1/appmonitor_conversion.go. Планируйте эволюцию API с самого начала.
Безопасность и RBAC
Сгенерированные манифесты RBAC (config/rbac/role.yaml) предоставляют контроллеру широкие права. Следует применять принцип минимальных привилегий. Внимательно проверяйте правила, особенно для кластерных ресурсов, и сужайте их под конкретные нужды вашего оператора.
Обработка ошибок и повторные попытки
Контроллер должен корректно обрабатывать временные ошибки (например, проблемы с сетью или API-сервером). Возвращайте ctrl.Result{Requeue: true}, err для повторной попытки через некоторое время. Логируйте ошибки с достаточным контекстом для отладки. Если ваш кастомный ресурс ведет себя неожиданно, воспользуйтесь нашим руководством по полной диагностике Custom Resources в Kubernetes.
Тестирование
Используйте envtest (поставляется с Kubebuilder) для модульного тестирования логики контроллера без полноценного кластера. Пишите интеграционные тесты, которые разворачивают оператор и проверяют его работу в реальных сценариях.
Сравнение и выбор инструмента (Kubebuilder vs Operator SDK)
Два основных фреймворка для создания операторов на Go - Kubebuilder и Operator SDK (на базе controller-runtime). Выбор зависит от конкретных задач и предпочтений команды.
Kubebuilder изначально создавался как фреймворк для построения API-серверов и позже стал основой для разработки операторов. Он тесно интегрирован с контроллер-рантаймом и имеет четкую структуру проекта. Его сильные стороны - отличная документация, встроенная поддержка вебхуков (валидации и мутации) и конвертации версий CRD. Он отлично подходит для проектов, где требуется строгий контроль над API и его эволюцией.
Operator SDK предоставляет более высокоуровневый подход и поддерживает написание операторов не только на Go, но и на Ansible или Helm. Это может быть преимуществом для команд, не владеющих Go, но желающих автоматизировать управление приложениями. Однако для сложной логики и максимальной производительности Go-оператор предпочтительнее.
Сравнительная таблица ключевых критериев:
| Критерий | Kubebuilder | Operator SDK (Go) |
|---|---|---|
| Основа | Использует controller-runtime напрямую, «стандарт де-факто» | Использует controller-runtime через обертку |
| Поддержка языков | Только Go | Go, Ansible, Helm |
| Сложность обучения | Средняя, требует понимания Kubernetes API | Средняя для Go, низкая для Ansible/Helm |
| Гибкость и контроль | Высокая | Высокая для Go, ограниченная для Ansible/Helm |
| Экосистема и сообщество | Очень активная, поддерживается CNCF | Активная, поддерживается Red Hat |
Для большинства новых проектов на Go сегодня рекомендуется Kubebuilder из-за его прямой интеграции с экосистемой controller-runtime и статуса как наиболее стандартизированного пути. Если ваша задача - быстро создать оператор для уже существующего Helm-чарта или Ansible-плейбука, Operator SDK с соответствующим плагином будет более быстрым решением. Для комплексного анализа архитектуры и применения операторов в различных сценариях, включая управление базами данных и CI/CD, изучите статью «Операторы Kubernetes в 2026: архитектура, применение и практика для DevOps».
Понимание архитектуры и принципов работы для адаптации
Чтобы адаптировать оператор под свои уникальные задачи, необходимо понимать его внутреннюю механику.
Цикл реконсиляции (Reconciliation Loop)
Сердце оператора - это бесконечный цикл, в котором контроллер для каждого кастомного ресурса в своей очереди событий выполняет метод Reconcile. Цель - привести фактическое состояние кластера (например, существующие Deployments, Services) к желаемому состоянию, описанному в spec кастомного ресурса. Контроллер также следит за изменениями зависимых ресурсов (например, если кто-то вручную удалит Pod, созданный оператором), чтобы повторно синхронизировать состояние.
Работа с событиями (Watch)
Контроллер не опрашивает API-сервер постоянно. Он устанавливает watch-соединения на интересующие типы ресурсов (как кастомные, так и стандартные, например, Pod). При любом изменении объекта API-сервер отправляет событие в очередь контроллера. Это обеспечивает реактивность и эффективность.
Модель статуса (Status Subresource)
Поле status в кастомном ресурсе используется как разделяемая память между контроллером и пользователем. Контроллер записывает туда наблюдаемое состояние (например, IP-адреса созданных Pod, текущие условия - Conditions), а пользователь через поле spec задает желаемую конфигурацию. Это разделение позволяет избежать конфликтов при одновременном обновлении.
Интеграция с существующими инструментами и практиками
Для использования в production оператор должен быть упакован и интегрирован в рабочие процессы.
Упаковка в Helm chart
Хотя Kubebuilder генерирует базовые манифесты установки (config/), для управления версиями, конфигурацией и зависимостями удобно создать Helm chart. В chart можно включить CRD (как зависимость или как глобальный ресурс), Deployment оператора, необходимые RBAC роли и ServiceAccount, а также примеры кастомных ресурсов в values.
CI/CD пайплайн
Автоматизируйте сборку образа оператора, запуск unit- и integration-тестов (envtest), линтинг кода и security scanning. Используйте kaniko или Buildah для сборки образов внутри пайплайнов. При успешном прохождении тестов можно автоматически публиковать образ в container registry и обновлять Helm chart в репозитории.
Мониторинг и логирование
Инструментируйте код оператора, экспортируя метрики Prometheus (controller-runtime имеет встроенную поддержку). Отслеживайте количество реконсиляций, их длительность, ошибки. Настройте централизованное логирование (например, через Loki) для сбора логов контроллера. Это критически важно для оперативного реагирования на проблемы в кластере.
Выбор между CRD и ConfigMap
Не всегда для кастомной конфигурации нужен полноценный оператор. Для простых сценариев, где не требуется строгая валидация схемы или сложная реактивная логика, может быть достаточно ConfigMap или Secret. Чтобы сделать взвешенный выбор, ознакомьтесь с нашим практическим руководством, где мы подробно сравниваем CRD и ConfigMap для конфигурации в Kubernetes.
FilesExpand file tree
Структура проекта, сгенерированного Kubebuilder, логична и стандартизирована. В корне находятся go.mod, Makefile (основные команды: make manifests, make install, make run, make docker-build) и PROJECT (файл конфигурации Kubebuilder). Директория api/ содержит определения типов для каждой версии API (v1alpha1, v1beta1 и т.д.). В controllers/ находится реализация контроллеров. Директория config/ включает сгенерированные YAML-манифесты для установки CRD (crd/bases), RBAC (rbac), развертывания контроллера (manager) и образцы кастомных ресурсов (samples).
Latest commit
Разработка оператора - итеративный процесс. После каждого изменения в API (*_types.go) необходимо выполнить make manifests для обновления CRD. Изменения в логике контроллера требуют пересборки образа. Используйте теги версий в git для контроля состояния кода, соответствующего определенной версии CRD и образа оператора. Это особенно важно при обновлении схемы CRD в работающем кластере.
History
Паттерн оператора появился в экосистеме Kubernetes как способ кодирования операционных знаний о конкретных приложениях. CoreOS, а затем Red Hat популяризировали этот подход. Сначала операторы писались вручную с использованием client-go. Затем появились фреймворки, такие как Operator SDK и Kubebuilder, которые значительно упростили разработку, стандартизировали архитектуру и снизили порог входа. Сегодня операторы являются ключевым компонентом cloud-native landscape, позволяя управлять сложными stateful-приложениями, базами данных, мониторингом и CI/CD системами с уровнем автоматизации, близким к автономности.
File metadata and controls
Каждый файл в проекте играет свою роль. api/v1alpha1/appmonitor_types.go определяет структуру данных вашего ресурса и содержит маркеры (например, //+kubebuilder:subresource:status), которые управляют кодогенерацией. controllers/appmonitor_controller.go содержит бизнес-логику. config/crd/bases/monitoring.my.domain_appmonitors.yaml - это итоговый манифест CRD, который применяется к кластеру. Контроль за версиями этих файлов и их согласованностью - основа стабильной работы оператора.
Создание Custom Resource Definitions и контроллеров открывает мощные возможности для адаптации Kubernetes под уникальные требования вашей инфраструктуры. Начиная с простого примера, подобного приведенному выше, вы можете постепенно добавлять сложную логику, вебхуки для валидации, мультиверсионную поддержку и интеграцию с внешними системами. Этот подход превращает Kubernetes из готовой платформы в программируемую основу, способную воплощать ваши собственные абстракции и автоматизировать рутинные операции, что является высшей формой владения этой технологией.
Для тех, кто работает с ИИ-моделями и ищет единый интерфейс для доступа к различным API, включая GPT, Gemini и Claude, может быть полезен агрегатор AiTunnel. Он позволяет управлять ключами и бюджетами, интегрироваться через библиотеки OpenAI и оплачивать услуги в рублях без необходимости использования VPN.