KubeFed v2 beta: пилот с двумя кластерами в разных ЦОД и почему это оказалось сложнее, чем выглядело
KubeFed v2 вышел в beta: запустили пилот на двух кластерах в разных ЦОД. Single API для федерированных Deployment-ов работает, но ценой нетривиальной настройки.
KubeFed v2 (Kubernetes Federation v2) достигает beta: управление политиками и рабочими нагрузками в нескольких кластерах через единый API
В июле мы писали про Kubernetes 1.15, и там одной строчкой упомянули, что тема федерации - это отдельный разговор. Вот этот разговор.
KubeFed v2 вышел в beta в рамках sig-multicluster, и мы решили не ждать GA, а запустить пилот прямо сейчас: два кластера на Kubernetes 1.16, в двух разных ЦОД одного из клиентов, задача - DR без ручной синхронизации манифестов.
Контекст задачи
У клиента два ЦОД с разным железом, оба с собственными кластерами Kubernetes. До последнего времени DR выглядел классически: второй кластер держат «тёплым», манифесты синхронизируют через отдельный GitOps-пайплайн, при переключении меняют DNS вручную. Работает, но при обновлении конфигурации приложения нужно помнить про оба кластера - и рано или поздно они расходятся.
Идея KubeFed v2: единый API-слой поверх нескольких кластеров, который сам распределяет ресурсы по кластерам согласно политикам. Звучит ровно как наш сценарий.
Что такое KubeFed v2 на практике
Ключевые объекты, с которыми пришлось разобраться:
- FederatedDeployment. Это не просто Deployment с дополнительными полями - это отдельный CRD, который содержит шаблон (template), переопределения для каждого кластера (overrides) и политику размещения (placement). Всё в одном объекте.
- KubeFedCluster. Регистрация кластера в федерации со своими kubeconfig-секретами и health-check-ом. Без этого кластер невидим для федерационного контроллера.
- ReplicaSchedulingPreference. Политика распределения реплик по кластерам - можно задать минимумы, максимумы и вес. Вещь полезная, но с нюансами.
Установка через Helm в host-кластер. Контроллер федерации живёт в одном из кластеров и управляет остальными через API. В нашем случае host-кластером стал основной ЦОД.
Что оказалось сложнее ожидаемого
Предполагали, что сложность будет в концепциях. На деле концепции легли понятно, а сложность оказалась операционной.
Первое - управление kubeconfig-секретами. Для каждого кластера в федерации нужен отдельный ServiceAccount с правами в целевом кластере, сертификат, endpoint. Всё это хранится в секрете в host-кластере. При ротации сертификатов или изменении endpoint нужно обновлять вручную - автоматики нет. Это не баг, это просто beta.
Второе - namespace federation. Namespace нужно явно включать в федерацию через FederatedNamespace. Если забыл - ресурсы просто не уйдут в целевой кластер, и контроллер об этом сообщит не сразу и не слишком громко. Потеряли минут сорок на этом при первой попытке.
Третье - CRD нужно синхронизировать отдельно. Если в кластерах есть кастомные ресурсы, их CRD нужно создавать в каждом кластере самостоятельно - KubeFed v2 не распространяет CRD автоматически. Для нашего пилота с обычными Deployment-ами это не проблема, но для реального продакшна с несколькими операторами - заметный пробел.
Четвёртое - observability федерационного слоя. Метрики контроллера есть, Prometheus-эндпоинт поднят, но понять почему конкретный FederatedDeployment не синхронизируется - это kubectl describe плюс логи контроллера. Нормального статуса в духе «вот что происходит с каждым кластером прямо сейчас» у объекта нет. Есть условия (conditions), но их набор скромный.
Что работает хорошо
При этом то, ради чего затеяли пилот, работает.
Один FederatedDeployment в host-кластере - и Deployment появляется в обоих ЦОД с нужными параметрами. Обновили образ в шаблоне - обновилось в обоих кластерах. Для DR-сценария без кастомных CRD это закрывает главную боль: манифесты больше не расходятся, потому что источник истины один.
ReplicaSchedulingPreference позволила задать минимум реплик для DR-кластера независимо от основного. Если основной перегружен или недоступен, федерационный контроллер может перераспределить реплики - логика рабочая, хотя мы её пока тестируем в нагрузочном стенде, не в живом трафике.
apiVersion: scheduling.kubefed.io/v1alpha1
kind: ReplicaSchedulingPreference
metadata:
name: app-backend
namespace: production
spec:
targetKind: FederatedDeployment
targetName: app-backend
clusters:
dc1:
minReplicas: 3
maxReplicas: 10
weight: 70
dc2:
minReplicas: 1
maxReplicas: 5
weight: 30
Overrides для специфики кластеров - тоже работают. У нас в ЦОД разные storage class-ы и разные node selector-ы под часть подов, это решается через поле overrides в FederatedDeployment без дублирования всего манифеста.
Где мы сейчас
Пилот работает вторую неделю на managed-инфраструктуре в режиме «наблюдаем». Основной трафик по-прежнему через старый GitOps-пайплайн, KubeFed v2 управляет несколькими некритичными сервисами. Хотим поймать хотя бы один реальный failover прежде чем перекладывать туда что-то важное.
Главный вывод пока такой: концепция федерации через единый API работает, и для DR без кастомных операторов - это рабочий инструмент уже сейчас. Но beta есть beta: операционная часть требует ручного труда там, где хотелось бы автоматики, а прозрачность состояния федерационного слоя оставляет желать лучшего. Если идти в production - нужен нормальный runbook для нескольких сценариев отказа, которые мы сейчас и составляем.
В любом случае альтернатива - поддерживать два независимых набора манифестов вручную - хуже. Так что продолжаем.