логотип PurpleSchool
логотип PurpleSchool

Работа с Manager в Kubernetes

Автор

Олег Марков

Введение

В Kubernetes одним из ключевых понятий при разработке контроллеров и операторов является объект Manager. Он служит центральной точкой для управления контроллерами, согласованием их работы и унификации процессов работы с ресурсами кластера. Понимание структуры и возможностей Manager важно не только для разработчиков собственных CRD-контроллеров и операторов, но и для тех, кто хочет глубже разобраться, как устроена автоматизация в Kubernetes.

В этой статье я покажу, как использовать Manager для управления жизненным циклом контроллеров, расскажу о типичных сценариях настройки и расширения Manager, а также о его взаимодействии с другими компонентами в экосистеме Kubernetes.

Что такое Manager и зачем он нужен

Manager — это часть библиотеки controller-runtime и ядро большинства современных Kubernetes-операторов. Его задача — упростить создание, интеграцию и управление различными контроллерами и вебхуками как единой системой.

Основные задачи Manager

  • Инициализация зависимостей (например, клиентов к API-серверу, логгирования, схемы типов).
  • Управление жизненным циклом контроллеров и вебхуков.
  • Реализация паттернов graceful shutdown и leader election.
  • Централизация наблюдения и ограничения прав доступа (RBAC).
  • Работа с кэшами для увеличения производительности.

Manager позволяет регистрировать несколько контроллеров, которые зачастую взаимодействуют с разными объектами Kubernetes и реализуют разную бизнес-логику, но их запуском и синхронизацией заведует один общий объект.

Где используется Manager

  • В большинстве операторов, созданных с помощью Kubebuilder, Operator SDK и controller-runtime
  • При разработке пользовательских контроллеров для поддержки новых CRD или расширения поведения стандартных ресурсов

Создание и настройка Manager

Давайте посмотрим, как создать объект Manager и настроить его основные параметры на примере простого контроллера.

Установка controller-runtime

Начнем с установки необходимых зависимостей:

go get sigs.k8s.io/controller-runtime@v0.14.1

Эту библиотеку вы будете использовать для создания Manager.

Пример создания Manager

Вот пример кода, который показывает, как инициализируется Manager. Я дополню его комментариями, чтобы вам было легче ориентироваться.

package main

import (
    "os"
    "time"
    ctrl "sigs.k8s.io/controller-runtime"
    "sigs.k8s.io/controller-runtime/pkg/manager"
)

func main() {
    // Настройка опций Manager
    options := manager.Options{
        MetricsBindAddress: ":8080", // Метрика для Prometheus
        Port:               9443,    // Порт для webhook-сервера
        SyncPeriod:         pointerToDuration(10 * time.Minute), // Синхронизация контроллеров
        LeaderElection:     true,    // Активация leader election (важно для HA)
        LeaderElectionID:   "example-operator-leader",
    }

    // Создаем Manager
    mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), options)
    if err != nil {
        // Важная проверка на ошибку создания Manager
        os.Exit(1)
    }

    // Запускаем Manager (он инициализирует все контроллеры, webhook'и, health checks)
    if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
        os.Exit(1)
    }
}

func pointerToDuration(d time.Duration) *time.Duration {
    return &d
}

Обратите внимание, что все опции Manager задаются через структуру manager.Options. Вы можете кастомизировать практически любой аспект поведения.

Ключевые опции менеджера

Рассмотрим основные поля manager.Options, которые чаще всего встречаются:

  • MetricsBindAddress — на каком порту будут слушаться метрики Prometheus
  • Port — порт для webhook-сервера
  • SyncPeriod — периодичность синхронизации состояния ресурсов в absence событий
  • LeaderElection — нужен ли leader election для устранения конфликтов в высокодоступных раскатах
  • Namespace — ограничение области наблюдения (можно следить за одним namespace’ом, а не всем кластером)
  • Scheme — обязательная схема, через которую Manager узнает о поддерживаемых типах ресурсов

Теперь рассмотрим, как регистрировать контроллеры и работать с зависимостями.

Регистрация контроллеров через Manager

Один из мощнейших аспектов Manager — централизованная регистрация контроллеров и гарантированная обработка их жизненного цикла.

Пример контроллера и добавление его в Manager

Допустим, у вас есть кастомный контроллер, реализующий логику для работы с ресурсом App.

package controllers

import (
    "context"
    "sigs.k8s.io/controller-runtime/pkg/client"
    ctrl "sigs.k8s.io/controller-runtime"
    "sigs.k8s.io/controller-runtime/pkg/reconcile"
)

type AppReconciler struct {
    client.Client
    Scheme *runtime.Scheme
}

// Метод Reconcile — основная точка синхронизации объекта
func (r *AppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    // Здесь пользовательская логика — синхронизация состояния
    return ctrl.Result{}, nil
}

Теперь нужно зарегистрировать этот контроллер через Manager.

func main() {
    mgr, _ := ctrl.NewManager(ctrl.GetConfigOrDie(), manager.Options{})
    
    // Регистрация контроллера
    err := (&controllers.AppReconciler{
        Client: mgr.GetClient(),
        Scheme: mgr.GetScheme(),
    }).SetupWithManager(mgr)
    if err != nil {
        os.Exit(1)
    }

    // Запуск Manager (он запустит контроллеры)
    if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
        os.Exit(1)
    }
}

Вызов SetupWithManager(mgr) интегрирует ваш контроллер с общим лупом:

  • Менеджер будет следить за events, поступающими для наблюдаемых ресурсов
  • Reconcile-метод вашего контроллера будет асинхронно вызываться при каждом изменении объектов

Преимущества такого способа регистрации

  • Единая точка управления ресурсами
  • Удобное внедрение зависимостей через manager (client, scheme, event recorder и так далее)
  • Стандартизированный процесс graceful shutdown: Manager корректно завершает работу всех контроллеров

Работа с зависимостями через Manager

Когда вы создаете контроллеры через Manager, у вас автоматически появляется доступ к полезным зависимостям:

  • Синглтон-клиент (ctrl-runtime/pkg/client), работающий через shared кэш
  • Scheme для поддержки различных ресурсов
  • Канал сигналов для корректного shutdown
  • Возможность настройки event recorder для удобного логирования

Пример внедрения зависимостей показан в объявлении структуры контроллера выше.

type AppReconciler struct {
    client.Client         // Клиент для загрузки и сохранения объектов
    Scheme *runtime.Scheme // Схема для декодирования ресурсов
}

Manager инкапсулирует работу с кэшем (Informer-кэш), а каждый контроллер через него получает "живую" копию объекта и минимизирует количество запросов к API-серверу.

Расширение Manager (работа с health checks, webhook'ами, задачами)

Manager — не только точка запуска контроллеров. Он может расширяться за счет дополнительных возможностей:

  • Health checks (жизнестойкость вашего оператора)
  • Webhook-серверы (валидация изменений CRD)
  • Добавление кастомных runnable процессов

Пример добавления health и ready check

err := mgr.AddHealthzCheck("healthz", healthz.Ping)
if err != nil {
    // Добавление health-probe не удалось
    os.Exit(1)
}
err = mgr.AddReadyzCheck("readyz", healthz.Ping)
if err != nil {
    os.Exit(1)
}

Теперь liveness и readiness probe можно пробрасывать через контейнер в deployment спецификации.

Пример добавления webhook

webhookServer := mgr.GetWebhookServer()
webhookServer.Register("/validate-v1-app", &webhook.Admission{
    Handler: &MyValidator{}, // Здесь ваша логика валидации
})

Добавление кастомных процессов (Runnables)

Если вашему оператору требуется фоновые задачи, их можно добавить как runnables.

mgr.Add(manager.RunnableFunc(func(ctx context.Context) error {
    // Здесь ваша фоновая задача
    <-ctx.Done()  // Ждем сигнала на shutdown
    return nil
}))

Здесь я показал, как легко расширять Manager дополнительной логикой.

Лидер-элект: как Manager организует отказоустойчивость

Если вы деплоите оператор в несколько экземпляров (replica > 1), по умолчанию все экземпляры будут пытаться работать одновременно. Это создаст конкуренцию и потенциальные сбои. Защита — leader election.

Manager поддерживает leader election "из коробки":

options := manager.Options{
    LeaderElection:   true,
    LeaderElectionID: "my-operator",
}
  • Только один экземпляр оператора будет выполнять reconcile
  • Остальные становятся "standby", захватывая лидерство при падении ведущего

Manager использует built-in механизмы Kubernetes (ConfigMap или Lease на выбранном объекте).

Namespace scope: ограничение области видимости

Еще один частый сценарий — ограничить область контроля одной областью:

mgr, _ := ctrl.NewManager(ctrl.GetConfigOrDie(), manager.Options{
    Namespace: "my-namespace",
})

Теперь все контроллеры и вебхуки, которые регистрируются через этот Manager, будут работать только в рамках my-namespace.

Как Manager реализует graceful shutdown

Manager заботится о подготовленном завершении работы:

  • При получении SIGTERM или SIGINT вызывает stop-колбеки у каждого контроллера и webhook'а
  • Документированная реакция на канал ctx.Done() в Add-функциях
  • Автоматическая остановка HTTP-серверов, задаваемых в health/webhook

Это разгружает вас от ручной обработки сигналов.

Как устроен жизненный цикл Manager

Менеджер запускается, выполняет инициализацию зависимостей (client, кэшей, схемы, HTTP/probes, leader election), регистрирует контроллеры, запускает их reconcile-потоки, следит за сигналами завершения и завершает работу элегантно.

В часто используемых фреймворках типа Kubebuilder, весь код main.go строится вокруг manager.

Практические паттерны для работы с Manager

Конфигурируемость через параметры командной строки

Флаги удобно замапить в параметры Manager через os.Args и pflag:

var metricsAddr string
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "Address for metrics.")

mgr, _ := ctrl.NewManager(restConfig, manager.Options{
    MetricsBindAddress: metricsAddr,
})

Разделение логики по пакетам

  • В main.go только Manager и загрузка флагов
  • Контроллеры, scheme-registration, webhook-регистрация в отдельных пакетах
  • Каждая сущность (controller, webhook) самостоятельна и внедряется через SetupWithManager

Это облегчает тестирование и поддержку.

Заключение

Manager из controller-runtime — это универсальный инструмент построения контроллеров и операторов в Kubernetes. Он берет на себя самую рутинную и рискованную работу по инициализации зависимостей, управлению жизненным циклом, отказоустойчивостью и graceful shutdown. Использование Manager позволяет писать структурированные кластере-приложения и сокращает вероятность ошибок в продакшене. Именно поэтому современные Kubernetes-операторы почти всегда строятся вокруг него.

Частозадаваемые технические вопросы по теме статьи и ответы на них

1. Как добавить стандартный логгер ко всем контроллерам Manager?

import "sigs.k8s.io/controller-runtime/pkg/log/zap"

ctrl.SetLogger(zap.New(zap.UseDevMode(true)))

Добавьте это в начало main.go для инициализации логгера, до создания Manager.

2. Можно ли динамически регистрировать контроллеры после старта Manager?

Нет, все контроллеры должны быть зарегистрированы до запуска mgr.Start(). Manager фиксирует конфигурацию при старте для безопасности и целостности процессов.

3. Как получить REST-конфиг для собственных клиентов из Manager?

restConfig := mgr.GetConfig()
// Теперь restConfig можно использовать для создания собственных клиентов

4. Что делать, если нужно смотреть за несколькими namespaces?

Используйте MultiNamespacedCacheBuilder: go mgr, _ := ctrl.NewManager(restConfig, manager.Options{ NewCache: cache.MultiNamespacedCacheBuilder([]string{"ns1", "ns2"}), }) Позволяет работать с несколькими неймспейсами.

5. Как убрать сервер healthz или webhook из Manager?

Параметры manager.Options:

  • HealthProbeBindAddress: "0" — отключит HTTP-сервер healthz/readyz;
  • WebhookServer: nil — отключит webhook-сервер (только если вы не планируете их использовать).
Использование команды Run в KubernetesСтрелочка вправо

Постройте личный план изучения Kubernetes до уровня Middle — бесплатно!

Kubernetes — часть карты развития DevOps

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по Kubernetes

Terraform и Kubernetes инфраструктура как кодНастройка и использование Runners в KubernetesНастройка и деплой PostgreSQL в KubernetesСравнение и интеграция Openshift и KubernetesПримеры интеграции GitHub Actions и KubernetesDeploy приложений в Kubernetes - пошаговое руководство для начинающих и не толькоКак настроить CD в KubernetesИнтеграция Ansible в KubernetesИнтеграция CI/CD с Jenkins и KubernetesИнтеграция Kubernetes с GitLab - Автоматизация CI CD в облачной инфраструктуреГайд по DevOps инструментам в KubernetesОсобенности платформы Deckhouse в Kubernetes
Открыть базу знаний

Лучшие курсы по теме

изображение курса

Kubernetes и Helm

Антон Ларичев
AI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.9
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Микросервисы

Антон Ларичев
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий