Kubernetes 1.9: Workloads API в GA - планируем миграцию stateful-сервисов
Kubernetes 1.9 переводит Workloads API в GA: DaemonSet, Deployment, StatefulSet - стабильные контракты. Разбираем что это значит для production-миграции.
Релиз Kubernetes 1.9 - Workloads API (Deployment, DaemonSet, StatefulSet, ReplicaSet) переведён в General Availability в группе apps/v1
Kubernetes 1.9 вышел в конце декабря, и главная новость для нас - Workloads API официально в General Availability. Deployment, DaemonSet, StatefulSet, ReplicaSet переезжают из apps/v1beta2 в apps/v1. Это не просто смена строчки apiVersion в YAML: фиксируется контракт, гарантируется обратная совместимость, бета-оговорки снимаются. И для нас это момент серьёзно поговорить о том, что мы раньше откладывали - миграции stateful-сервисов в кластер.
Что изменилось в Workloads API
В 1.7 в GA вышел RBAC, мы тогда перерабатывали сервисные аккаунты. Теперь GA получает другой слой - объекты управления нагрузкой. Ключевые изменения в apps/v1:
- Deployment. В beta
spec.selectorбыл необязателен и мог меняться. В GAspec.selector- иммутабельное поле после создания объекта. Это ломающее изменение для тех кто пишет контроллеры или пересоздаёт деплойменты через манипуляцию полями. - DaemonSet. Та же история с
spec.selector. Плюсspec.templateGenerationубрано - оно было в beta, в GA его нет. - StatefulSet.
spec.selectorиммутабелен, добавлены поля для управленияupdateStrategy.rollingUpdate.partitionв стабильном виде. - ReplicaSet. Аналогично Deployment, но большинство работает с ним через Deployment, так что на практике заметно меньше.
Если у вас CI/CD pipeline строит манифесты на лету и ставит selector из каких-то переменных - проверьте логику. В нашем случае несколько Helm chart-ов писали selector через шаблон который вычислялся из лейблов - пришлось убедиться что логика идемпотентна.
Почему StatefulSet в GA важен именно сейчас
Статика в Kubernetes мы щупали ещё в 1.5, тогда StatefulSet был в beta и воспринимался как эксперимент. Механика работала, но производственного доверия не было: API мог поменяться, поведение хранения томов - тоже.
Сейчас ситуация другая. apps/v1 StatefulSet с гарантированным контрактом - это аргумент для разговора с командами которые держат stateful-сервисы на голых виртуалках «потому что Kubernetes для stateful нестабилен». Нестабильность была обоснованной в 1.5. В 1.9 - уже нет.
Конкретно на наших managed-кластерах это открывает разговор о нескольких группах сервисов:
- Zookeeper и Kafka - на двух проектах они живут рядом с кластером но не в нём. С StatefulSet в GA + PersistentVolumeClaim с правильным StorageClass это технически обоснованно переносить внутрь.
- Redis в sentinel-режиме - StatefulSet даёт стабильные имена подов (pod-0, pod-1...), что критично для sentinel-конфигурации где реплики должны знать адреса друг друга.
- PostgreSQL-реплики для чтения - primary пока вне кластера, но read-реплика внутри с репликацией через стандартный streaming - технически вполне рабочая схема.
Мы не говорим «перенести всё немедленно». Мы говорим «теперь есть основание составить план», а не откладывать до следующей беты.
Storage: что принесла 1.9
Отдельного внимания заслуживает работа с хранилищем. В 1.9 LocalPersistentVolume переведён в alpha - это возможность использовать локальные диски ноды как PersistentVolume с учётом топологии. Планировщик будет учитывать где физически находится том при выборе ноды для пода. Пока alpha, но вектор понятен.
Что стабильно и уже работает на production в 1.9:
- StorageClass с
volumeBindingMode: WaitForFirstConsumer- задержка биндинга тома до момента планирования пода, а не сразу при создании PVC. Для мультизональных кластеров это критично: том создаётся в той же зоне где под. - Расширение PVC остаётся в beta (
ExpandPersistentVolumes), но на ряде плагинов уже работает. Мы пробовали на gp2 в AWS - resize тома без пересоздания PVC работает, хотя требует перезапуска пода.
Что мы делаем с legacy-манифестами
После перехода на 1.9 beta-apiVersion начинают возвращать предупреждения, а не тихо работать. К выходу следующих версий старые apps/v1beta1 и apps/v1beta2 будут deprecated. Значит сейчас самое время пройтись по манифестам.
На наших кластерах мы написали небольшой скрипт который через kubectl get deployments,daemonsets,statefulsets --all-namespaces -o json выгружает все объекты и проверяет apiVersion. Результат предсказуемый: mix из extensions/v1beta1, apps/v1beta1, apps/v1beta2 в зависимости от того когда что писалось.
Конвертация механически простая - меняется apiVersion и добавляется явный spec.selector. Содержательно - надо проверить что selector соответствует реальным лейблам подов, иначе после kubectl apply контроллер потеряет управление своими подами.
Алгоритм который используем:
- Выгрузить текущий объект через
kubectl get -o yaml - Проверить
spec.selector.matchLabels- должен однозначно совпадать сspec.template.metadata.labels - Поменять
apiVersionнаapps/v1 - Применить через
kubectl apply- если selector не изменился, объект обновится без пересоздания
Деплоить всё разом не стали - идём namespace за namespace на тестовом кластере сначала.
Итог
GA-статус Workloads API снимает последнюю содержательную причину держаться за «stateful - не в Kubernetes». Не значит что миграция лёгкая или что надо делать это прямо сейчас - но отговорка «API нестабильный» закрыта. Что остаётся: операционная готовность, резервное копирование томов, runbook для восстановления. Это работа, но она конечна.
По кластерам под нашим управлением начинаем систематически конвертировать манифесты в apps/v1. По stateful-миграциям - разговариваем с командами, составляем план. Торопиться некуда, но и откладывать больше нечем.