Learning Platform
Глоссарий Troubleshooting
Урок 19.02 · 22 мин
Продвинутый
Operatorcontrollerreconcile loopKubebuilderOperator SDKcert-managerPrometheus Operator

Operator pattern: CRD + custom controller

Operator — это паттерн расширения Kubernetes, который инкапсулирует «как управлять stateful-приложением в кластере» в коде кастомного контроллера. Если built-in Deployment controller знает, как разворачивать stateless приложения (rolling update, replicas, история), то Postgres operator знает, как разворачивать Postgres-кластер: primary + replicas, репликация, ежедневные бэкапы, failover, point-in-time recovery. Это знание DBA, перенесённое в код. Пользователь пишет 30-строчный YAML PostgresCluster, оператор делает всё остальное.


Производственные операции Kafka: метрики и управление

Что такое Operator формально

Operator состоит ровно из двух частей:

  1. CRD — описывает желаемое состояние domain-specific объекта. Например, kind: PostgresCluster со spec instances: 3, version: 16, backupSchedule: "0 2 * * *".
  2. Controller — программа (обычно Deployment в namespace <operator-name>-system), которая через Watch API подписана на CR этого типа. Когда CR создаётся / меняется / удаляется, controller вызывает свою функцию Reconcile(req), которая:
    • читает текущее состояние мира (Pods, StatefulSets, Secrets, и т.д.),
    • сравнивает с spec CR,
    • применяет diff: создаёт / обновляет / удаляет нужные built-in ресурсы.

Это тот же reconcile loop, что у built-in controllers (Deployment, ReplicaSet, StatefulSet) — просто живёт в Pod вне kube-controller-manager и работает над кастомными ресурсами.

Анатомия Operator: CR → Controller → built-in resources
kubectl apply PostgresClusterПользователь создаёт CR. 30 строк YAML: kind, name, instances: 3, version: 16, storageSize: 50Gi, backupSchedule: cron. Это desired state в domain-specific терминах.
apiserverПринимает CR, валидирует по schema CRD, сохраняет в etcd. И отправляет watch event на всех подписчиков: operator controller среди них.
Operator PodДолгоживущий Deployment в namespace pg-operator. Подписан на CR PostgresCluster через informer. Когда приходит ADDED/MODIFIED event — кладёт ключ объекта в workqueue.
Reconcile(req)Функция Go: читает CR (или null если delete), читает текущие Pods/STS/Secrets с тем же selector, считает diff с желаемым, применяет изменения через apiserver. Идемпотентна — можно вызывать N раз с одним результатом.
apply built-in ресурсыСоздаёт StatefulSet для Pods, Service для discovery, Secret для credentials, PVC для PV, CronJob для backups. Всё через kubectl apply (server-side apply) — owned by CR через ownerReferences.
apiserverПринимает created/updated объекты от operator, валидирует, сохраняет в etcd. Built-in controllers (sts-controller, replicaset-controller) реагируют на них дальше, создают Pods.
status updateПосле reconcile operator обновляет CR.status: какие replicas ready, последний backup, события. Пользователь видит kubectl get postgrescluster и понимает, что происходит.
ownerReferencesCreated Pods/STS/Secret имеют ownerReferences: [PostgresCluster]. Когда CR удаляют — garbage collector каскадно удаляет всё owned. Никакого orphan state.
continuous reconcileReconcile вызывается не только на CR события, но и при любом изменении watched ресурсов (Pod упал — re-reconcile parent CR), плюс периодически (resync, обычно 10 минут). Это self-healing.

Reconcile loop: контракт level-based, а не edge-based

Operator controller следует тому же контракту, что built-in controllers — это не accident, а сознательное design решение:

  • Level-based, не edge-based. Reconcile получает CURRENT state мира и реагирует на него. Если он пропустил event (Pod упал, пока operator перезапускался), при следующем reconcile он всё равно увидит missing Pod в currentReplicas != desiredReplicas и создаст его. Edge-based reagировал бы на change event и пропустил бы missed updates.
  • Идемпотентность. Reconcile(req) можно вызвать 10 раз подряд для одного объекта — результат тот же. Это потому что внутри проверяется текущее состояние перед action, а не действия «делать +1 replica».
  • Retry с backoff. Если reconcile вернул error — controller-runtime автоматически requeue-ит работу с exponential backoff. Никаких «забыл, что failed».
  • Watch + workqueue. Controller подписан на CR (свой kind) И на produced ресурсы (Pods, STS, etc) через owns(...). Любое изменение в owned ресурсе тоже триггерит reconcile parent CR — self-healing.
// Контракт controller-runtime (Kubebuilder)
func (r *PostgresClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    var cluster examplev1.PostgresCluster
    if err := r.Get(ctx, req.NamespacedName, &cluster); err != nil {
        // Если deleted — игнорируем (ownerReferences сделают GC)
        return ctrl.Result{}, client.IgnoreNotFound(err)
    }

    // 1. Прочитать текущее состояние мира
    var sts appsv1.StatefulSet
    err := r.Get(ctx, types.NamespacedName{Name: cluster.Name, Namespace: cluster.Namespace}, &sts)
    // ...

    // 2. Построить desired StatefulSet по spec CR
    desired := buildStatefulSet(&cluster)

    // 3. Применить (create или update)
    if err := r.Patch(ctx, desired, client.Apply, client.ForceOwnership); err != nil {
        return ctrl.Result{}, err
    }

    // 4. Обновить status
    cluster.Status.ReadyReplicas = sts.Status.ReadyReplicas
    if err := r.Status().Update(ctx, &cluster); err != nil {
        return ctrl.Result{}, err
    }

    return ctrl.Result{RequeueAfter: 30 * time.Second}, nil
}

Operator frameworks: Kubebuilder и Operator SDK

Писать controller с нуля можно через client-go + informer factory, но это десятки тысяч строк boilerplate (queues, leader election, metrics). Поэтому используются framework-и:

  • Kubebuilder — официальный SIG-API-machinery framework, основан на controller-runtime. Лидирующий выбор. Генерирует scaffolding: PROJECT, Dockerfile, Makefile, CRD manifest из Go-структур (через kubebuilder:object markers), RBAC из markers, webhooks. Полностью Go.
  • Operator SDK (RedHat) — построен поверх Kubebuilder для Go-операторов, но также поддерживает Helm-operators (CRD → Helm chart) и Ansible-operators (CRD → Ansible playbook). Helm/Ansible операторы не требуют Go-кода, но менее гибкие.
  • KUDO, Metacontroller — менее популярные alternatives с другой философией (declarative composition).
TIP

Если задача — обернуть существующий Helm chart в operator без кодирования (например, для UI lifecycle через OpenShift OperatorHub), Helm operator — самый быстрый путь. Если нужна сложная operational логика (custom backup workflow, rolling upgrade с health checks, in-place schema migration), нужен Go operator на Kubebuilder.


Реальные операторы экосистемы

OperatorCRDsЧто делает
cert-managerCertificate, Issuer, ClusterIssuer, CertificateRequest, Order, ChallengeАвтоматический выпуск TLS-сертификатов (Let’s Encrypt, self-signed, CA). Watches Certificate, заказывает сертификат у issuer, кладёт в Secret.
Prometheus OperatorPrometheus, Alertmanager, ServiceMonitor, PodMonitor, PrometheusRule, ProbeДеплоит Prometheus / Alertmanager как StatefulSet. ServiceMonitor — декларативный scrape config. PrometheusRule — alerting rules. Используется через kube-prometheus-stack Helm chart.
PostgreSQL operators (Zalando postgres-operator, CloudNative-PG, Crunchy)PostgresCluster, Backup, PoolerScheduleProvisioning кластера, репликация, рестарт, бэкапы (WAL-G в S3), failover, point-in-time recovery, версионные апгрейды.
MongoDB operators (mongodb-kubernetes-operator, percona)MongoDBCommunity, MongoDB, OpsManagerReplica sets, sharded clusters, automation.
ArgoCDApplication, AppProject, ApplicationSetGitOps: следит за Git-репо, syncs манифесты в кластер, статусы deployments.
Argo WorkflowsWorkflow, WorkflowTemplate, CronWorkflowDAG-based pipelines (CI/CD, data pipelines) as native K8s ресурсы.
Argo RolloutsRollout, Experiment, AnalysisRunCanary и blue-green deployments с автоматической промоушн/откатом по метрикам.
IstioVirtualService, DestinationRule, Gateway, AuthorizationPolicy, PeerAuthentication, Sidecar, и ещё ~20Service mesh: traffic management, mTLS, observability, policy.
External Secrets OperatorExternalSecret, SecretStore, ClusterSecretStoreSync секретов из Vault / AWS Secrets Manager / Azure Key Vault в нативные Kubernetes Secrets.
Crossplaneсотни CRDs (Bucket, Database, Cluster, …)Provisioning cloud-ресурсов (AWS, GCP, Azure) как Kubernetes-native объектов. K8s превращается в universal control plane.
StrimziKafka, KafkaTopic, KafkaUser, KafkaConnectApache Kafka в Kubernetes: brokers, topics, ACLs, MirrorMaker.
MinIO OperatorTenantS3-compatible object storage.

Operator capability levels (OperatorHub)

OperatorHub описывает 5 уровней зрелости оператора:

  • Level 1 — Basic install. Установка приложения и конфигурация через CR. Никакого upgrade, никакого backup.
  • Level 2 — Seamless upgrades. Operator знает, как обновлять managed app: rolling restart, version migrations, schema updates.
  • Level 3 — Full lifecycle. Backup, restore, scale-up/scale-down, failover, replication.
  • Level 4 — Deep insights. Метрики Prometheus, alerts, custom events о health managed app. Logs централизованы.
  • Level 5 — Auto-pilot. Auto-scaling по нагрузке, auto-healing после ошибок, auto-tuning параметров (cache sizes, connection pools). Самообслуживающий platform.

cert-manager — Level 4-5. Сложные DB-операторы (CloudNative-PG, Strimzi) — Level 4. Helm-operator чаще всего Level 1-2.


Поток жизни Operator-ом: Pod на Deployment

Operator физически работает как обычный Deployment в кластере:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: cert-manager
  namespace: cert-manager
spec:
  replicas: 1                    # обычно одна replica (leader election не нужно)
  selector:
    matchLabels: {app: cert-manager}
  template:
    spec:
      serviceAccountName: cert-manager
      containers:
      - name: controller
        image: quay.io/jetstack/cert-manager-controller:v1.16.0
        args:
        - --leader-elect=true     # если несколько replicas — лидер один

ServiceAccount cert-manager имеет ClusterRole с правами на все Certificate, Issuer, CertificateRequest, плюс на нативные Secrets (создавать/обновлять). Без RBAC controller не сможет работать.

WARNING

Leader election в операторах: если запустить два Pod-а одного controller параллельно — каждый будет реагировать на каждый CR независимо, и они начнут конкурировать (двойной reconcile, дубликаты, конфликты update). Поэтому в большинстве операторов включён leader election: один Pod забирает Lease object и работает; остальные ждут в hot standby. При его падении один из standby берёт Lease и продолжает.


CKAD scope: что нужно знать про операторы

CKAD не требует писать оператор. Типичные задачи:

  • Понять, что в кластере установлен оператор: kubectl get crd | grep <vendor>, kubectl get deployment -A | grep operator.
  • Создать CR через готовый оператор (например, Certificate через cert-manager).
  • Проверить, что CR обработан: kubectl describe certificate my-cert, посмотреть на .status.conditions.
  • Если CR долго не реконсилируется — найти Pod оператора и почитать его логи: kubectl logs -n cert-manager deployment/cert-manager.
  • Объяснить, почему данный ресурс не Pod / Service / Deployment: «это custom resource из cert-manager оператора».
# Найти все CRDs от cert-manager
kubectl get crd -l app.kubernetes.io/name=cert-manager

# Создать Certificate
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: example-com
  namespace: default
spec:
  secretName: example-com-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames: [example.com, www.example.com]
EOF

# Проверить статус
kubectl describe certificate example-com
# State: Ready/NotReady, Events с reason=Generated/Issuing/Failed

# Найти оператор
kubectl get pods -n cert-manager
kubectl logs -n cert-manager deployment/cert-manager --tail=100

Killer-моменты

  • Operator = CRD + controller. Без controller CRD — это просто etcd-storage; без CRD controller не знает, на что реагировать.
  • Reconcile level-based и идемпотентен. Это позволяет ему быть устойчивым к network glitches, restart-ам, missed events. Тот же контракт у built-in controllers — Deployment-controller тот же level-based loop.
  • Operator живёт как Deployment в кластере. Это не сторонний agent, не demon вне K8s; это обычный workload с RBAC и ServiceAccount.
  • Owner references дают cascade GC. CR удаляют — все owned Pods/STS/Secrets каскадно удаляются. Никакого orphan state без явных усилий.
  • Operators — это не «cool кодинг», а перенос operational knowledge в код. Postgres operator делает то, что десятилетиями делал DBA вручную. Это автоматизация по-настоящему.

Проверка знанийKnowledge check
В чём принципиальное отличие operator от обычного controller, и почему вообще нужно отдельное название?
ОтветAnswer
Принципиально — никакого. Operator — это просто controller, написанный для custom resource через CRD. Тот же reconcile loop, тот же level-based pattern, тот же controller-runtime / informer-механизм. Отдельное название появилось потому, что 'operator' обозначает специфический use case: инкапсулировать operational knowledge о stateful-приложении (как разворачивать Postgres, как делать backup, как failover) в код. Built-in K8s controllers (Deployment, ReplicaSet) делают то же самое для stateless и базовых workloads. Operator расширяет этот pattern на domain-specific приложения. Архитектурно — то же самое.
Проверка знанийKnowledge check
Что делает reconcile loop, если controller рестартовал в момент применения CR, и пропустил create event?
ОтветAnswer
При старте controller через informer делает LIST всех существующих CR этого kind и кладёт каждый в workqueue как initial sync. То есть никакой event не пропускается — даже если controller был выключен. Это и есть suть level-based: реагируем не на event ('появилось новое CR!'), а на STATE ('вот текущий список CR в кластере'). Каждый reconcile вызывает r.Get(ctx, name) — читает current state и сравнивает с миром. Если CR существует, а managed StatefulSet нет — operator его создаёт. Если CR удалён, ownerReferences через garbage collector каскадно удалят owned ресурсы (operator может даже ничего не делать в этом случае). Это даёт устойчивость к restart-ам и crashes.
Проверка знанийKnowledge check
Зачем в operator-Deployment включают leader election, если replicas: 1?
ОтветAnswer
На случай rolling update Deployment-а. Когда applying новой версии operator-а, старый Pod ещё работает, а новый стартует — на короткое время два Pod-а активны одновременно. Без leader election оба начнут реагировать на одни и те же CR — двойной reconcile, конфликты update, гонки. С leader election один Pod держит Lease object (kind: coordination.k8s.io/v1.Lease), второй ждёт в hot standby. Когда старый завершается (SIGTERM, освобождение Lease), новый сразу подхватывает. То же самое для high availability — для critical operators обычно ставят replicas: 2-3, и leader election гарантирует, что только один активен. Остальные — failover-готовы.
Проверка знанийKnowledge check
Что произойдёт, если удалить namespace, в котором стоит operator (cert-manager), при том что в других namespaces есть Certificate CRs?
ОтветAnswer
Certificate CRs останутся в etcd (они в других namespaces, и CRD scope=Namespaced означает, что они принадлежат своим namespaces). Но controller mounting в cert-manager namespace мёртв — никто не реконсилирует их. Существующие выданные Secret-сертификаты продолжат работать (они уже в Secrets), но при истечении не будут renewed. Новые Certificate CRs не получат сертификат. После переустановки cert-manager (тот же namespace или другой) controller через initial sync увидит все существующие Certificate и начнёт их обрабатывать. Главное — НЕ удалять CRD certificates.cert-manager.io, потому что при удалении CRD garbage collector каскадно удалит ВСЕ Certificate CRs во всём кластере, что снесёт всю TLS-конфигурацию.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. Поставлен cert-manager (operator), создан Certificate CR. Через несколько секунд kubectl describe certificate показывает status.conditions Ready=True, и появился Secret с TLS-сертификатом. Какие компоненты были задействованы?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5