Переопределение значений: -f и --set на практике

Два инструмента переопределения и чёткие правила, когда какой уместен.

-f файл.yaml — для устойчивой, версионируемой конфигурации окружения. --set ключ=значение — для разовых, динамических или секретных переопределений в момент запуска.

Когда -f, когда --set

СлучайИнструмент
Конфиг окружения (prod/staging)-f prod.yaml в git
Тег образа из CI-переменной--set image.tag=$CI_SHA
Секрет из переменной пайплайна--set (не в git!)
Много связанных настроек-f (читаемо)
Одна-две правки поверх--set

Комбинируем слои

Типичный прод-вызов комбинирует общий базовый файл, файл окружения и динамику из CI:

helm upgrade --install web ./chart -n prod   -f values/base.yaml   -f values/prod.yaml   --set image.tag="$CI_COMMIT_SHA"   --atomic --timeout 5m

Логика читается сверху вниз: общие настройки → специфика прода → конкретная сборка. Тег образа меняется на каждый коммит, поэтому он динамический через --set, а не в файле.

Почему именно так разделяют «что в файл, а что в флаг»? Здравый критерий — спросить себя: «это значение известно заранее и живёт долго, или оно вычисляется в момент запуска?». Хост ingress, число реплик, лимиты ресурсов известны заранее и меняются редко — им место в версионируемом файле, где история правок видна в git и проходит code review. Тег образа, идентификатор сборки, значение из секрет-стора вычисляются в пайплайне здесь и сейчас — их естественно подать через --set. Это разделение заодно решает вопрос воспроизводимости: по файлам окружения можно восстановить, какой была конфигурация месяц назад, а эфемерные значения и не должны жить в истории.

Сколько -f можно складывать

Ограничения на число -f нет, но злоупотреблять стопкой из пяти-шести файлов не стоит: чем больше слоёв, тем труднее в уме проследить, откуда пришло конкретное значение. Здоровый предел на практике — два-три файла: общий base.yaml, файл окружения и, при необходимости, файл региона или кластера. Если слоёв становится больше, это обычно знак, что пора либо пересмотреть, что лежит в base, либо переходить к инструменту-обёртке вроде Helmfile, который описывает весь набор релизов декларативно и снимает с вас ручную сборку длинных команд.

Синтаксис --set детально

--set a.b.c=value             # вложенность через точку
--set 'list={x,y,z}'         # задать список целиком
--set 'arr[0].name=web'      # элемент списка по индексу
--set 'a.b=c\,d'             # экранировать запятую внутри значения
--set-string version=1.0     # форсировать строку

Запятая в --set — разделитель пар, поэтому в значениях её экранируют \,. Точка — разделитель уровней, в ключах её тоже экранируют. Из-за этих экранирований сложные значения чаще выносят в файл.

Отладка: всегда смотрите результат

Главная привычка: перед боевым запуском прогоните рендер и сравните с ожиданием.

# увидеть итоговые манифесты с вашими переопределениями
helm template web ./chart -f values/prod.yaml --set image.tag=abc123

# показать только итоговые значения (debug)
helm template web ./chart -f values/prod.yaml --set image.tag=abc123   --show-only templates/deployment.yaml

--show-only выводит лишь один манифест — удобно, когда чарт большой.

Есть и третий, самый прямой способ убедиться, что значение доехало: посмотреть итоговые .Values целиком. Это снимает класс ошибок «кажется, я переопределил, но не сработало», потому что вы видите не догадку, а реальную карту, с которой Helm пошёл рендерить.

# показать итоговые объединённые значения как YAML
helm template web ./chart -f values/prod.yaml --set image.tag=abc123   --debug 2>&1 | head -n 40

# для уже установленного релиза — что реально применено
helm get values web -n prod --all

Привычка «сначала template, потом upgrade» стоит секунд, а экономит часы: вы ловите опечатку в пути, неверный тип значения или забытый файл окружения до того, как это уедет в кластер. Особенно ценно это в связке с --atomic: template отлавливает ошибки рендера, а --atomic страхует от полуприменённого релиза, если что-то всё же пойдёт не так на стороне Kubernetes.

Антипаттерн: секреты в файле в git

Не кладите пароли и токены в values/prod.yaml под git. Для секретов: либо --set из защищённой CI-переменной, либо специализированные инструменты (helm-secrets/sops, внешние секрет-сторы) — об этом отдельный урок. Файл значений в git виден всем, у кого есть доступ к репозиторию.

Важно понимать, чем именно опасен секрет в git, кроме очевидного «его увидят коллеги». Git хранит всю историю: даже если вы потом удалите пароль коммитом сверху, он останется в прошлых ревизиях, и вычистить его — отдельная болезненная операция с переписыванием истории. Плюс репозитории попадают в зеркала, форки, кэши CI и резервные копии — однажды закоммиченный секрет нужно считать скомпрометированным и ротировать, а не просто «убрать». Поэтому правило жёсткое: секрет вообще не должен оказаться в файле под версионным контролем, даже на минуту.

У --set для секретов тоже есть нюанс: значение попадает в командную строку, а значит может засветиться в логах CI и в выводе ps на раннере. Защищённые переменные пайплайна обычно маскируются в логах, но это зависит от платформы. Когда секретов много или они чувствительные, чище подключать их через Kubernetes Secret (созданный отдельно или внешним контроллером вроде External Secrets), а в values держать лишь ссылку на имя секрета — тогда само значение нигде в цепочке деплоя не светится.

Как --set парсится под капотом

Строка a.b[0].c=val разбирается слева направо в дерево: точки создают вложенные карты, [i] — элементы списка, = отделяет значение. Тип значения определяется эвристикой: true/false → булев, число-похожее → число, остальное → строка. Затем это маленькое дерево сливается поверх остальных слоёв с наивысшим приоритетом. Именно из-за эвристики типов --set tag=1.0 превращается в число, и существует --set-string, отключающий угадывание. Понимание, что --set строит именно дерево, объясняет, почему опечатка в пути не вызывает ошибку — она просто создаёт новую неиспользуемую ветку в .Values.

Частые ошибки

  • Секреты в git-файле values. Утечка через репозиторий; используйте CI-переменные или sops.
  • Опечатка в пути --set. Молча создаёт мёртвую ветку, шаблон её не читает — «значение не применилось».
  • Незаэкранированная запятая. --set urls=a,b воспримется как две пары; нужно a\,b или файл.

Итог

  • -f — для версионируемых конфигов окружений; --set — для динамики и секретов из CI.
  • Комбинируйте слоями (base → env → CI); проверяйте через helm template и --show-only.
  • Опечатка в пути --set не ошибка, а мёртвая ветка — отсюда «не применилось».
Проверьте себя
1. Для чего предпочтительнее -f, а не --set?
AДля тега образа из CI
BДля версионируемой конфигурации окружения (prod/staging) в git
CДля секретов
D--set всегда лучше
2. Почему опечатка в пути --set не вызывает ошибку?
AHelm её исправляет
B--set строит дерево значений, и неверный путь создаёт неиспользуемую ветку в .Values
CОпечатки запрещены
DОна вызывает ошибку
3. Что выводит helm template ... --show-only templates/deployment.yaml?
AВсе манифесты
BТолько указанный отрендеренный манифест — удобно для отладки
CСписок values
DОшибки