Kubernetes 1.9: Workloads API в GA - проводим массовый рефакторинг YAML на apps/v1
Kubernetes 1.9 переводит Deployment, DaemonSet, StatefulSet и ReplicaSet в GA. Закрываем технический долг: меняем extensions/v1beta1 на apps/v1 во всех клиентских манифестах.
Kubernetes 1.9 - Workloads API (Deployment, DaemonSet, StatefulSet, ReplicaSet) переведены в GA под apps/v1; Windows поддержка в beta; расширенный alpha CSI
Kubernetes 1.9 вышел в середине декабря. Главное событие - Workloads API наконец переведён в GA: Deployment, DaemonSet, StatefulSet и ReplicaSet теперь живут под apps/v1. Для повседневной эксплуатации это означает одно: последний повод держаться за extensions/v1beta1 в манифестах официально исчез, и мы решили разобраться с накопившимся техническим долгом сразу, не откладывая.
Что изменилось в apps/v1
Переход не просто переименование группы. В apps/v1 есть несколько ломающих отличий от extensions/v1beta1, которые надо учесть при рефакторинге.
Первое - selector стал обязательным и неизменяемым. В старом API selector мог отсутствовать - apiserver вычислял его из template.metadata.labels. В apps/v1 поле spec.selector обязательно и после создания объекта не меняется. Это правильно: неявный selector порождал ситуации когда Deployment подхватывал чужие поды при совпадении меток. Теперь это невозможно по контракту.
Второе - spec.replicas по умолчанию равен 1, а не 0. Звучит как мелочь, но если в манифесте не было явного replicas: 0, а вы рассчитывали на нулевой дефолт - получите сюрприз.
Третье - rolling update параметры переехали. В extensions/v1beta1 поле spec.rollbackTo существовало. В apps/v1 его нет - откат делается через kubectl rollout undo, что логичнее.
Для DaemonSet добавилось явное поле updateStrategy - раньше стратегия обновления в extensions/v1beta1 тоже работала, но менее предсказуемо.
Масштаб задачи
У нас несколько десятков managed-кластеров разной степени загруженности. Манифесты хранятся в git-репозиториях - у кого-то один монорепо на весь кластер, у кого-то разбиты по командам. YAML-файлов с extensions/v1beta1 набралось порядочно: Deployment почти везде, DaemonSet для агентов мониторинга и сбора логов, StatefulSet для баз данных.
Подход выбрали осторожный - не скриптом по всем репозиториям разом, а по одному кластеру, с проверкой. Причина простая: обязательный selector в apps/v1 означает что при пересоздании объекта через kubectl apply с изменённым selector кластер выдаст ошибку - нужно удалять и создавать заново. А это простой.
Поэтому план такой: обновляем apiVersion, добавляем явный selector, убираем устаревшие поля - затем делаем kubectl apply и смотрим что происходит. Для объектов где selector не совпадал с тем что генерировал apiserver раньше - сначала kubectl delete, потом apply.
Сам процесс рефакторинга
Типичный манифест Deployment до:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 3
template:
metadata:
labels:
app: api-server
spec:
containers:
- name: api-server
image: registry.example.com/api-server:1.4.2
После:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 3
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
containers:
- name: api-server
image: registry.example.com/api-server:1.4.2
Diff небольшой, но каждый файл нужно просмотреть руками: убедиться что метки в selector.matchLabels совпадают с template.metadata.labels, и что никаких неочевидных расхождений нет.
DaemonSet-манифесты оказались чуть сложнее - у некоторых в extensions/v1beta1 стратегия обновления была OnDelete, что давно считалось нормой для агентов node-exporter и filebeat. В apps/v1 явно прописали updateStrategy: type: RollingUpdate там где это безопасно, и оставили OnDelete где нода требует ручного дрейна перед обновлением агента.
Что с обратной совместимостью
extensions/v1beta1 в Kubernetes 1.9 никуда не делся - он deprecated, но работает. Apiserver принимает старые манифесты, конвертирует и хранит в новой версии внутри. Иллюзия совместимости полная - поэтому многие проекты годами живут на устаревших API без видимых проблем.
Но deprecated означает что API уберут в одном из следующих релизов без особых церемоний. Мы видели это с ThirdPartyResources в 1.8 - объявили deprecated в 1.7, убрали в 1.8, и несколько проектов в экосистеме получили срочный рефакторинг. Повторять этот опыт с Workloads API не хочется.
Где сейчас
Первые несколько кластеров прошли рефакторинг чисто. Сейчас разбираемся с репозиториями где манифесты генерируются из шаблонов - там правка в одном месте раскатывается на десяток объектов, что проще, но требует аккуратности при проверке selector-ов.
Параллельно обновили внутренние шаблоны и примеры для новых проектов - extensions/v1beta1 из них убрали совсем. Новые кластеры сразу стартуют с правильным API.
RBAC мы прошли похожим путём после 1.8 - тогда тоже был массовый прогон манифестов под rbac.authorization.k8s.io/v1. Workloads в каком-то смысле проще: меньше объектов требуют удаления и пересоздания, в основном достаточно добавить selector и поменять apiVersion. Но скучно не бывает.