!Astro.props.image &&
← Назад к блогу

Почему я всё ещё пишу манифесты Kubernetes руками

Helm и Kustomize хорошие инструменты, но начинаю я не с них. Почему так и где это правило перестаёт работать.

Виктор Ведмич · 5 мин чтения

kubernetes opinion

Большая часть моей работы с Kubernetes начинается одинаково: папка с голыми манифестами и kubectl apply -f. Ни чарта, ни оверлеев. Напрашивается вопрос: почему бы просто не взять Helm? Объясню, почему нет, и где этот ответ перестаёт быть верным.

Отладка

По голым манифестам легко искать, их легко сравнивать и вставлять в Slack. Когда в два часа ночи что-то ломается, мне не хочется продираться через три слоя шаблонов, чтобы понять, какое переопределённое значение породило сломанный YAML.

Типичная поломка в Helm: вложенный include подтягивает значение из зависимости родительского чарта, а ошибка разбора YAML указывает на строку в отрендеренном выводе. Ни в одном шаблоне, который можно открыть, такой строки нет. helm template --debug покажет результат рендеринга, даже если YAML не разбирается. Но сопоставлять его с исходниками придётся самому.

Диффы в Git тоже остаются честными. git diff по манифесту показывает ровно то поле, которое поменялось. В чарте дифф покажет изменённые параметры или шаблон на Go, а чтобы понять, что поменяется в самих ресурсах, ревьюеру придётся сначала отрендерить чарт.

А теперь страшная тайна: голый YAML тоже не равен тому, что работает в кластере. Он избавляет от рендеринга шаблонов, но не от API-сервера. Сервер сам заполняет значениями по умолчанию поля, которых в файле нет. Мутирующие admission-контроллеры могут добавить tolerations, запросы ресурсов по умолчанию из LimitRange или сайдкар сервис-меша. Контроллеры дописывают статус, аннотации ревизий и число реплик от автоскейлера. Поэтому при отладке смотри на живой деплоймент, на созданные им поды и на предупреждения в событиях:

# что изменится, если применить файл прямо сейчас
kubectl diff -f deployment.yaml
# живой объект, уже со значениями по умолчанию и правками admission
kubectl get deployment web -o yaml
# поды, которые он создал: сюда попадают дефолты LimitRange и сайдкары
kubectl get pods -l app=web -o yaml
# отказы и другие предупреждения
kubectl events --types=Warning

С голым YAML это единственный разрыв. С чартом их два: как параметры превратились в YAML и чем этот YAML отличается от живого объекта.

Держать API в голове

Когда пишешь манифесты руками, API не выветривается из головы. Я знаю, какие поля есть у деплоймента, потому что много раз набирал их сам. Знаю, чем strategy.type: RollingUpdate отличается от strategy.type: Recreate, потому что видел обе выкатки своими глазами. Рано или поздно абстракция ломается. Тогда я всё равно понимаю, что уходит в API-сервер. Helm генерирует YAML. Если не понимаешь этот YAML, отлаживаешь вслепую.

Частая картина: инженер учил Kubernetes через Helm и застывает на кластере, где нет ни одного шаблона. Править values.yaml он умеет. А вот объяснить, почему деплоймент в неймспейсе с меткой pod-security.kubernetes.io/enforce: restricted так и не получает подов, уже не может. Что вообще разрешено его ServiceAccount, тоже не скажет. Абстракция стала для него потолком, а не полом.

Оба ответа короткие, если знаешь API. Pod Security Admission пришёл на смену PodSecurityPolicy, которую удалили в Kubernetes 1.25. В режиме enforce он отклоняет поды, которые нарушают политику, а сам деплоймент пропускает, поэтому отказ всплывёт в событиях ReplicaSet (режимы warn и audit предупреждают и по шаблону подов в деплойменте). При стандартном RBAC сервисный аккаунт может узнать, какие API есть в кластере, и посмотреть свои права, и только. Для доступа к подам или секретам нужна RoleBinding или ClusterRoleBinding на сам аккаунт или его группу. Приложению, которое не ходит в API, привязка для запуска не нужна. Это знание работает на любом кластере. А знание параметров конкретного чарта пригодится только с ним.

Налог на абстракцию

Для маленького сервиса чарт добавляет больше когнитивной нагрузки, чем сам сервис. Приложение из трёх файлов (Deployment, Service, HTTPRoute) превращается в Chart.yaml, values.yaml, _helpers.tpl и каталог templates/. Один только helm create создаёт дюжину файлов заготовки. Для одного сервиса в одном окружении налог платишь (язык шаблонов, жизненный цикл релизов, версии чартов), а история релизов и helm rollback редко его окупают.

Оверлеи Kustomize тоже не бесплатны. В стеке из четырёх слоёв (база, кластер, неймспейс, приложение) приходится открыть четыре каталога, чтобы понять, откуда у итогового объекта взялась аннотация. На доске изящно, в два часа ночи непроглядно.

Налог окупается, когда сервисов и окружений много, а конфигурации действительно различаются. Особенно если общей инфраструктурой занимается платформенная команда. Когда кластеров немного и все они похожи, не окупается.

Когда Helm оправдан

Когда однотипных сервисов много, шаблоны начинают окупаться. Никто не хочет копировать сотню деплойментов и вручную менять теги образов. Helm даёт параметры, версионированные чарты и helm rollback. Здесь налог обходится дешевле, чем сотня наборов манифестов, которые правят руками.

Второй случай: готовый софт. Команде нужна работающая база данных или стек мониторинга, а начинать с документации по StatefulSet не хочется. В чарте с активной поддержкой уже учтён чужой опыт эксплуатации. Для тех, кто не живёт в Kubernetes каждый день, это настоящая ценность.

Моё эмпирическое правило

Начинай с голого YAML. Мой личный порог, за которым я от него ухожу: примерно пять окружений или десять сервисов. Это правило из моей собственной практики, а не закон. Настоящий сигнал в другом: насколько сильно различается конфигурация и как часто она меняется.

Если у окружений общая база, а различаются только реестр образов, класс хранилища или число реплик, удобно взять Kustomize. Это всё тот же голый YAML плюс патчи, и он встроен в kubectl: kubectl apply -k. Нужны настоящие параметры или хочется версионировать деплойменты как артефакты? Тогда Helm своё место заслужил. В обоих случаях чем скучнее, тем лучше. В Helm выноси то, что меняется, в values.yaml, а логику шаблонов держи простой. Нужно одно место, где меняется тег образа, а не тьюринг-полный язык конфигурации.

Настоящий вопрос

Настоящий вопрос не «Helm или голые манифесты?», а «какой уровень абстракции подходит команде по масштабу и навыкам?»

Разработчик-одиночка с двумя сервисами быстрее выкатится на голых манифестах, чем выучит Helm. Платформенной команде с пятьюдесятью микросервисами в десяти окружениях почти наверняка понадобятся шаблоны или оверлеи: Helm, Kustomize или оба сразу. Но масштаб сам по себе инструмент не выбирает. Выбирают вариативность конфигурации, требования к упаковке и то, как устроена работа команды.

Я пишу манифесты руками, потому что чаще решаю небольшие задачи: делаю демо, поднимаю пилотные кластеры, разбираю поломки. Там важно видеть, что именно ушло в API-сервер. Там Helm скорее тормозил бы меня, чем ускорял.

У тебя может быть иначе. Выбирай уровень абстракции, на котором ещё сможешь разобраться с поломкой в два часа ночи.

Открыть поиск

Esc
↑ ↓ навигация ↵ открыть