Организация деплоя в множество k8s окружений с помощью helmfile

Thank you for reading this post, don't forget to subscribe!

Helmfile — обёрт­ка для helm, кото­рая поз­во­ля­ет в одном месте опи­сы­вать мно­же­ство helm рели­зов, пара­мет­ри­зо­вать их чар­ты для несколь­ких окру­же­ний, а так­же зада­вать поря­док их деплоя.

О самом helmfile и при­ме­рах его исполь­зо­ва­ния мож­но почи­тать в readme и best practices guide.

Допу­стим, у нас есть пач­ка helm-чар­тов (для при­ме­ра пусть будет postgres и некое backend при­ло­же­ние) и несколь­ко окру­же­ний (несколь­ко kubernetes кла­сте­ров, несколь­ко namespace'ов или несколь­ко и того, и дру­го­го). Берём helmfile, чита­ем доку­мен­та­цию и начи­на­ем опи­сы­вать наши окру­же­ния и релизы:

helmfile.yaml

У нас полу­чи­лось 2 окру­же­ния: develproduction — в каж­дом нахо­дят­ся свои зна­че­ния для helm чар­тов рели­зов. Мы будем деп­ло­ить в них так:

helmfile -n <namespace> -e <env> apply

 

Разные версии helm чартов в разных окружениях

Что делать, если нам надо выка­ты­вать раз­ные вер­сии бэкен­да в раз­ные окру­же­ния? Как пара­мет­ри­зо­вать вер­сию рели­за? На помощь при­хо­дят зна­че­ния окру­же­ния, доступ­ные через {{ .Values }}

helmfile.yaml

Разный набор приложений в разных окружениях

Отлич­но, но что если нам не надо в production выка­ты­вать postgres, пото­му что мы зна­ем, что не надо базу дан­ных пихать в k8s и для про­да у нас есть заме­ча­тель­ный отдель­ный кла­стер postgres? Для реше­ния этой про­бле­мы у нас есть лейб­лы (labels)

helmfile -n <namespace> -e devel apply

helmfile -n <namespace> -e production -l app=backend apply

Это здо­ро­во, но лич­но я пред­по­чту опи­сы­вать, какие при­ло­же­ния раз­во­ра­чи­вать в окру­же­нии не с помо­щью аргу­мен­тов запус­ка, а в опи­са­нии самих окру­же­ний. Что делать? Мож­но поме­стить опи­са­ние рели­зов в отдель­ную пап­ку, в опи­са­нии окру­же­ния заве­сти спи­сок нуж­ных рели­зов и "под­цеп­лять" толь­ко нуж­ные рели­зы, игно­ри­руя остальные

helmfile.yaml

releases/postgres.yaml

releases/backend.yaml

При исполь­зо­ва­нии bases: необ­хо­ди­мо обя­за­тель­но исполь­зо­вать yaml раз­де­ли­тель ---, что­бы мож­но было шаб­ло­ни­зи­ро­вать releases (и осталь­ные части, типа helmDefaults) зна­че­ни­я­ми из environments

В таком слу­чае релиз postgres даже не попа­дёт в опи­са­ние для production. Очень удобно!

Переопределяемые глобальные значения для релизов

Конеч­но, здо­ро­во, что мож­но для каж­до­го окру­же­ния зада­вать зна­че­ния для helm чар­тов, но что если у нас опи­са­но несколь­ко окру­же­ний, и мы хотим, допу­стим, задать оди­на­ко­вый для всех affinity, но не хотим настра­и­вать его по-умол­ча­нию в самих чар­тах, кото­рые хра­нят­ся в репах.

В таком слу­чае мы мог­ли бы для каж­до­го рели­за задать 2 фай­ла с values: пер­вый с дефолт­ны­ми зна­че­ни­я­ми, кото­рые будут опре­де­лять зна­че­ния само­го чар­та, а вто­рой со зна­че­ни­я­ми для окру­же­ния, кото­рый в свою оче­редь уже будет пере­опре­де­лять дефолтные.

releases/backend.yaml

envs/default/values/backend.yaml

Определение глобальных значений для helm чартов всех релизов на уровне окружения

 

Допу­стим, у нас в несколь­ких рели­зах созда­ют­ся несколь­ко ingress — мы мог­ли бы вруч­ную для каж­до­го чар­та опре­де­лить hosts:, но в нашем слу­чае домен один и тот же, так поче­му же его не выне­сти в некую гло­баль­ную пере­мен­ную и про­сто под­став­лять её зна­че­ние в чар­ты? Для это­го те фай­лы с values, кото­рые мы хотим пара­мет­ри­зо­вать, долж­ны будут иметь рас­ши­ре­ние .gotmpl, что­бы helmfile знал, что его надо про­гнать через шаблонизатор.

helmfile.yaml

envs/default/values/backend.yaml.gotmpl

envs/default/values/postgres.yaml.gotmpl

Подстановка секретов (secrets) из значений окружения

 

По ана­ло­гии с выше­при­ве­дён­ным при­ме­ром мож­но под­став­лять и зашиф­ро­ван­ные с помо­щью helm secrets зна­че­ния. Вме­сто того, что­бы для каж­до­го рели­за созда­вать свой файл secrets, в кото­ром опре­де­лять для чар­та зашиф­ро­ван­ные зна­че­ния, мы можем про­сто опре­де­лить в релиз­ном default.yaml.gotmpl зна­че­ния, кото­рые будут брать­ся из пере­мен­ных, задан­ных на уровне окру­же­ний. А зна­че­ния, кото­рые нам не надо ни от кого скры­вать, мож­но уже спо­кой­но пере­опре­де­лить в зна­че­ни­ях рели­за в кон­крет­ном окружении.

helmfile.yaml

envs/devel/secrets.yaml

envs/production/secrets.yaml

envs/default/values/backend.yaml.gotmpl

envs/devel/values/backend.yaml

envs/production/values/backend.yaml

getOrNil — спе­ци­аль­ная функ­ция для go шаб­ло­нов в helmfile, кото­рая, даже если .Values.secrets не будет суще­ство­вать, не выки­нет ошиб­ку, а поз­во­лит в резуль­та­те с помо­щью функ­ции default под­ста­вить зна­че­ние по-умолчанию

 

пере­мен­ные для окру­же­ния default мож­но в свою оче­редь пара­мет­ри­зо­вать пере­мен­ны­ми окру­же­ния ОС неко­е­го ран­не­ра, с кото­ро­го будет запус­кать­ся деп­лой, и таким обра­зом полу­чить дина­ми­че­ские окружения

helmfile.yaml