Helm: reusable чарт – named templates, и общий чарт для нескольких приложений

Автор: | 18/10/2020

Проект активно развивается, Kubernetes прижился, и всё больше наших сайтов запускается в нём.

И со временем возникла вполне ожидаемая проблема, которая уже озвучивалась в самом начале нашего “путешествия” в Helm: пошаговое создание чарта и деплоймента из Jenkins – как быть с манифестами Kubernetes и шаблонами Helm для нескольких приложений?

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

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

В результате чарты сейчас выглядят так:

[simterm]

$ tree -d k8s/
k8s/
├── app1-chart
│   ├── charts
│   ├── env
│   │   ├── dev
│   │   ├── dev-2
│   │   └── prod
│   └── templates
└── app2-chart
    ├── charts
    ├── env
    │   ├── dev
    │   ├── prod
    │   └── stage
    └── templates

[/simterm]

И теперь копировать сюда тот же самый чарт третий раз?

Ну совсем не хочется.

А потому – посмотрим внимательнее на возможности шаблонизатора Helm-а, и попробуем сделать один чарт для трёх похожих приложений.

Со временем его можно будет расширить, и включить сюда все остальные.

Deployment – структура файла

Что бы продумать структуру общего шаблона – посмотрим, какие блоки общие, а какие будут различаться, и как мы их можем максимально гибко подключать.

Будем рассматривать только файл deployment.yaml – остальные шаблоны потом сделаем по его подобию.

Итак:

  • файл deployment.yaml:
    • metadata:
      • name: общий, значение будем брать из файла values.yaml конкретного проекта
      • annotations: общий, тут у нас только reloader.stakater.com/auto, и у всех он будет один и всегда true (хотя возможны варианты – см. Kubernetes: ConfigMap и Secrets — auto-reload данных в подах)
      • labels: общий, и будет использоваться в нескольких местах, например в деплойменте ниже – spec.template.metadata.labels, и в файле с Kubernetes Cronjobs – вынесем лейблы в _helpers.yaml
    • spec:
      • replicas: общий, значение будем брать из файла values.yaml конкретного проекта
      • strategy:
        • type: общий, значение будем брать из файла values.yaml конкретного проекта
      • selector:
        • matchLabels: общий, и будет использоваться в нескольких местах, например в Cronjobs и Services – вынесем в _helpers.yaml
      • template:
        • metadata:
          • labels: берём из _helpers.yaml
      • spec:
        • containers:
          • name: общий, значение будем брать из файла values.yaml конкретного проекта
          • image: общий, значение будем брать из файла values.yaml конкретного проекта
          • env: вот тут самое интересное:
            1. можем вынести целым блоком в values.yaml каждого проекта
              1. плюсы: не надо будет отдельно описывать сами значения – можем их указать прямо тут, в поле value самих переменных
              2. минусы:
                1. практически у всех одинаковые переменные – дублируем код между проектами
                2. раздутый файл values.yaml, т.к. переменных много
                3. не все переменные идут в виде КЛЮЧ:ЗНАЧЕНИЕ – некоторые будут брать значение через valueFrom.secretKeyRef.<KUBE_SECRET_NAME>, значит значение в values.yaml задать не выйдет
            2. можем включить в общий _helpers.yaml, и будет общий набор переменных, каждая заключена в свой if – если в values.yaml значение найдено, то переменная создаётся в общем шаблоне
            3. и на случай, если у проекта совершенно отдельный набор переменных – то в общем шаблоне перед блоком env можно добавить условие типа if {{ .Values.chartSettings.customEnvs == true }} – будем пропускать включение переменных из _helpers.yaml или values.yaml, и подключать файл шаблона через tpl .File.Get project1/envs.yaml
          • volumeMounts: тоже может различаться, посмотрим потом
          • ports:
          • livenessProbe, readinessProbe: в целом тоже будет общий, с httpGet.path и httpGet.port – можно вынести в _helpers.yaml, но добавить тоже условие customProbes, и при необходимости – подключать через .File.Get
          • resources:
          • volumes: – тоже может различаться, посмотрим потом
          • imagePullSecrets: одинаковый у всех
  • hpa.yaml – описываем HPA
  • network.yaml – Service, Ingress
  • secrets.yaml – Kubernetes Secrets
  • rbac.yaml – подключение пользовательских групп к создаваемым неймспейсам
  • cronjobs.yaml – Kubernetes Cronjobs
  • _helpers.tpl – и наш “помошник” – сюда ключим Helm Named Templates

Собственно, основная идея такая:

  • общий чарт с шаблонами, которые лежат в templates
    • в templates храним файлы deployment.yaml, hpa.yaml, _helpers.tpl, etc
  • рядом с templates создадим каталог projects
    • и внутри него – каталоги по имени проектов
      • project1
      • project2
      • project3
        • внутри каждого создадим каталог env
          • внутри которого будет dev, stage, prod
            • внутри которых будут отдельные values.yaml и secrets.yaml

Helm Named templates

Отличный пост на тему именованных шаблонов – How To Reduce Helm Chart Boilerplate With Named Templates (больше ссылок – в конце поста).

Официальная документация – тут>>>.

Первым в нашем шаблоне нам встретится блок labels, который мы хотим вынести в _helpers.yaml, и из него подключать в деплоймент и другие шаблоны.

Идея именованных шаблонов в том, что мы один раз пишем какой-то блок, который потом можем включать везде в нашем чарте. Плюс – они позволяют убрать из основного шаблона лишний код, что бы сделать его более красивым и лаконичным.

Файлы именованных шаблонов начинаются с _, и заканчиваются расширением .tpl.

Самый известный пример – _helpers.tpl, который и будем использовать.

Описание каждого шаблона в файле _helpers.tpl начинается с define, заканчивается end.

Имя именованного шаблона как правило включает в себя имя чарта и блока, который этот шаблон добавляет, но никто не мешает использовать любое другое, например в данном случае общее имя у них будет helpers.

Из неудобства – тут в именах нельзя явно использовать значения типа .Chart.Name – но можно создать переменную. Посмотрим – будем ли это делать, пока просто захардкодим имя helpers.

Общие labels

Какие вообще полезные лейблы можно добавить, которые были бы общими?

  • environmentDev, Stage, Prod – будет подставляться из values.yaml
  • appversion – задаётся при билде в Jenkins через --set

Дальше моя фантазия иссякла, а потому спросим Google – “kubernetes recommended labels“, и находим первой же ссылкой Recommended Labels.

Плюс, см. Labels and Annotations в документации самого Helm.

Кроме того, можно посмотреть чарты каких-то готовых приложений, например – sonarqube/templates/deployment.yaml.

Значит, нам надо добавить пока четыре наших кастомных лейблы:

  • application: {{ .Values.appConfig.appName }}, будем задавать в values.yaml каждого приложения
  • version: {{ .Chart.Version }}-{{ .Chart.AppVersion }}, задаются из Jenkins
  • environment: {{ .Values.appConfig.appEnv }},будем задавать в values.yaml каждого приложения
  • managed-by: {{ .Release.Service }}

А если захотим добавить ещё – можно будет это сделать в одном месте, а не обновлять все файлы, в которых используются labels.

define

Возвращаемся к _helpers.tpl, и описываем наши лейблы:

{{- define "helpers.labels" -}}
application: {{ .Values.appConfig.appName }}
version: {{ .Chart.Version }}-{{ .Chart.AppVersion }}
environment: {{ .Values.appConfig.appEnv }}
managed-by: {{ .Release.Service }}
{{- end }}

include

Далее, с помощью include – указываем, куда именно мы хотим включить эти лейблы в нашем шаблоне деплоймента:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.appConfig.appName }}-deployment
  labels: {{- include "helpers.labels" . | nindent 4 }}
...

indent vs nindent

  • indent – просто задаёт кол-во отступов
  • nindent – начинает новую строку

Т.е. вместо того, что бы писать:

...
  labels: 
    {{- include "helpers.labels" . | indent 4 }}
...

Мы можем сделать всё в одну строку и обойтись без пробелов в самом шаблоне:

...
labels: {{- include "helpers.labels" . | nindent 4 }}
...

Кроме того, играет роль при вставке блоков YAML в общий шаблон – увидим ниже.

Вернёмся к именованным шаблонам – аналогично поступаем со spec.selector.matchLabels – вынесем его в отдельный шаблон, что бы потом переиспользовать в, например, Services.

В _helpers.tpl создаём:

...
{{- define "helpers.selectorLabels" -}}
application: {{ .Values.appConfig.appName }}
{{- end }}

Пока делаем выборку только по приложению, потом можно будет для каждого отдельного приложения изменить селекторы.

Добавляем в деплоймент:

...
  selector:
    matchLabels:
      {{- include "helpers.selectorLabels" . | nindent 6 }}
  template:
...

Шаблоны в шаблоне

Ну и приступим к самому интересному – вставке блоков шаблонов в шаблон.

Итак, у нас в текущем шаблоне есть блок env:

...
        env:
        - name: APP_SECRET
          valueFrom:
            secretKeyRef:
              name: app-backend-secrets
              key: backend-app-secret
        - name: AUTH_SECRET
          valueFrom:
            secretKeyRef:
              name: app-backend-secrets
              key: backend-auth-secret
        - name: CLIENT_HOST
          value: {{ .Values.appConfig.clientHost }}
        - name: DATABASE_HOST
          value: {{ .Values.appConfig.db.host }}
        - name: DATABASE_SLAVE_HOST
          value: {{ .Values.appConfig.db.slave }}
        - name: DB_USERNAME
          value: {{ .Values.appConfig.db.user }}
        - name: INSTANA_AGENT_HOST
          valueFrom:
            fieldRef:
              fieldPath: status.hostIP
...

Который мы хотим вынести отдельный блок в другом файле, и вставлять оттуда в общий шаблон, который пишем сейчас.

Блок env из values.yaml проекта

Посмотрим, какие варианты у нас есть.

Сначала – набросаем сами переменные в values.yaml.

Создаём дерево каталогов:

[simterm]

$ mkdir -p projects/newapp/env/dev

[/simterm]

И внутри него – файлы values.yaml и secrets.yaml:

[simterm]

$ touch projects/newapp/env/dev/{values.yaml,secrets.yaml}

[/simterm]

Далее, в projects/newapp/env/dev/values.yaml создаём список environments и в него добавим пока одну переменную:

environments:    
  - name: 'DATABASE_HOST'
    value: 'dev.aurora.web.example.com'

Что бы иметь возможно проверить работу шаблона с helm --debug --dry-run – добавляем все остальные значения типа {{ .Values.deployment.replicaCount }}.

Цикл range

Документация – Flow Control.

Хорошие примеры – Helm Template range.

Итак, у нас есть переменная для подов DATABASE_HOST со значением dev.aurora.web.example.com, которая описана в values.yaml в виде элемента списка с двумя парами ключ:значение:

...
  <LIST_NAME>:
    - <VAR_NAME>: <VAR_VALUE>
      <VAR_NAME>: <VAR_VALUE>
...

Которые мы можем вставить в шаблон следующим образом:

...
    spec:
      containers:
      - name: {{ .Values.appConfig.appName }}-pod
        image: {{ .Values.deployment.image.repository }}/{{ .Values.deployment.image.name }}:{{ .Values.deployment.image.tag }}
        env:
        {{- range .Values.deployment.environments }}
        - name: {{ .name }}                        
          value: {{ .value }}
        {{- end }}
...

тут:

  • перебираем список environments, разделённый символом – и содержащий строки с ключ:значение
  • перебираем элементы списка, находим VAR_NAME .name – подставляем в name: {{ .name }}
  • перебираем элементы списка, находим VAR_NAME .value – подставляем в value: {{ .value }}

Проверям:

[simterm]

$ helm upgrade --install eks-dev-1-newapp-backend --namespace eks-dev-1-newapp-backend-ns --create-namespace -f projects/newapp/env/dev/values.yaml --debug --dry-run .
...
spec:
  replicas: 2
  strategy:
    type: 
  selector:
    matchLabels:
      application: newapp
  template:
    metadata:
      labels:
        application: newapp
        version: 0.1.0-1.16.0
        environment: dev
        managed-by: Helm
    spec:
      containers:
      - name: newapp-pod
        image: projectname/projectname:latest
        env:
        - name: DATABASE_HOST       
          value: dev.aurora.web.example.com
        ports:
          - containerPort: 3001
...

[/simterm]

Кстати – уже видим и наши лейблы.

А вот и наши переменные:

[simterm]

...
        env:
        - name: DATABASE_HOST       
          value: dev.aurora.web.example.com
...

[/simterm]

Цикл range с переменными

Другой вариант – с использованием переменных, например $key и $value – в таком случае мы не привязываемся к конкретным именам переменных в самом файле values.yaml, а просто переберём их все:

...
        {{- range $key, $value := .Values.deployment.environments }}
        env:
        - name: {{ $key }}
          value: {{ $value }}
        {{- end }}
...

Обновляем values.yaml – теперь environments описываем как словарь, и задаём наши переменные просто как ключ:значение, для наглядности – добавим ещё одну переменную, DB_USERNAME:

...
  environments:  
    DATABASE_HOST: 'dev.aurora.web.example.com'
    DB_USERNAME: 'dbuser'
...

Проверяем:

[simterm]

$ helm upgrade --install eks-dev-1-newapp-backend --namespace eks-dev-1-newapp-backend-ns --create-namespace -f projects/newapp/env/dev/values.yaml --debug --dry-run .
...
    spec:
      containers:
      - name: newapp-pod
        image: projectname/projectname:latest
        env:
        - name: DATABASE_HOST
          value: dev.aurora.web.example.com
        - name: DB_USERNAME
         value: dbuser
        ports:
          - containerPort: 3001
...

[/simterm]

toYaml

Но вспомним наши переменные в нынешнем файле деплоймента:

...
        - name: DATABASE_HOST
          value: {{ .Values.backendConfig.db.host }}
        - name: DB_USERNAME
          value: {{ .Values.backendConfig.db.user }}
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: projectname-backend-secrets
              key: backend-db-password
...

Простым циклом тут пройтись уже не получится, а потому третий вариант – описывать в values.yaml весь блок, а потом вставлять его в шаблон, используя toYaml .

Для этого в самом values.yaml описываем весь блок:

environments:
  - name: DATABASE_HOST
    value: 'dev.aurora.web.example.com'
  - name: DB_USERNAME
    value: 'dbuser'
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: projectname-backend-secrets
        key: backend-db-password

А затем вставляем в env деплоймента:

...
      containers:
      - name: {{ .Values.appConfig.appName }}-pod
        image: {{ .Values.deployment.image.repository }}/{{ .Values.deployment.image.name }}:{{ .Values.deployment.image.tag }}
        env: 
        {{- toYaml .Values.deployment.environments | nindent 8 }}
...

Пробелы

Обратите внимание, что перед toYaml после {{ имеется символ тире “-“:

{{- toYaml .Values.deployment.environments | nindent 8 }}

Который используется для удаления символов пробела перед вставляемым блоком, при этом новая строка также считается пробелом.

Если его убрать – то в результате мы получим лишнюю новую строку:

[simterm]

...
        env:

        - name: DATABASE_HOST
          value: dev.aurora.web.example.com
        - name: DB_USERNAME
...

[/simterm]

См. Controlling Whitespace во Flow Control и примеры на странице Directives and Whitespace Handling.

Кроме того – мы тут снова используем nindent, что бы добавить символ новой строки для каждой строки, полученной из .Values.deployment.environments.

Helm tpl

И ещё один вариант – использование tpl функции.

В отличии от других способов – тут мы можем в values.yaml использовать директивы типа {{ .Release.Name }}, т.к. подставляемый блок будет обработан шаблонизатором, как часть самого шаблона.

Обновим values.yaml:

environments: |-
  - name: RELEASE_NAME
    value: {{ .Release.Name }}
  - name: DATABASE_HOST
    value: 'dev.aurora.web.example.com'
  - name: DB_USERNAME
    value: 'dbuser'
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: projectname-backend-secrets
        key: backend-db-password

Обратите внимание на |- – описываем блок с переменными как string, и убираем newline в конце.

Теперь добавляем в шаблон:

...
      containers:
      - name: {{ .Values.appConfig.appName }}-pod
        image: {{ .Values.deployment.image.repository }}/{{ .Values.deployment.image.name }}:{{ .Values.deployment.image.tag }}
        env: 
        {{- tpl .Values.deployment.environments . | nindent 8 }}
...

Тут мы в функцию tml передаём содержимое .Values.deployment.environments, и указываем включить его в текущий шаблон.

Проверяем:

[simterm]

$ helm upgrade --install eks-dev-1-newapp-backend --namespace eks-dev-1-newapp-backend-ns --create-namespace -f projects/newapp/env/dev/values.yaml --debug --dry-run .
...
    spec:
      containers:
      - name: newapp-pod
        image: projectname/projectname:latest
        env:
        - name: RELEASE_NAME
          value: eks-dev-1-newapp-backend
        - name: DATABASE_HOST
          value: 'dev.aurora.web.example.com'
        - name: DB_USERNAME
          value: 'dbuser'
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: projectname-backend-secrets
              key: backend-db-password
        ports:
          - containerPort: 3001
...

[/simterm]

Блок env из _helpers.tpl

Тут всё более-менее нам уже известно, но добавим проверку – есть ли значение для переменной в values.yaml.

if/else — flow control

В _helpers.yaml описываем шаблон:

{{- define "helpers.environments" -}}
- name: RELEASE_NAME
  value: {{ .Release.Name }}
{{- if .Values.appConfig.db.host }}
- name: DATABASE_HOST
  value: 'dev.aurora.web.example.com'
{{- end }}
{{- if .Values.appConfig.db.user }}
- name: DB_USERNAME
  value: 'dbuser'
{{- end }}
{{- if .Values.appConfig.db.password }}
- name: DB_PASSWORD
  valueFrom: 
    secretKeyRef:
      name: projectname-backend-secrets
      key: backend-db-password
{{- end }}
{{- end }}

Тут с помощью if для каждой переменной мы проверяем – имеется ли значение, и если оно будет найдено – то переменная и значение будут добавлены в общий шаблон.

Таким образом – можем спокойно расширять список переменных в helpers.environments, не волнуясь о том, что в каком-то проекте не будет значения для новой переменной из подключаемого шаблона, и изменение этого общего шаблона поломает деплой этого приложения.

Включаем его в шаблон:

...
      containers:
      - name: {{ .Values.appConfig.appName }}-pod
        image: {{ .Values.deployment.image.repository }}/{{ .Values.deployment.image.name }}:{{ .Values.deployment.image.tag }}
        env: {{- include "helpers.environments" . | nindent 8 }}
...

Проверяем:

[simterm]

$ helm upgrade --install eks-dev-1-newapp-backend --namespace eks-dev-1-newapp-backend-ns --create-namespace -f projects/newapp/env/dev/values.yaml --debug --dry-run .
...
    spec:
      containers:
      - name: newapp-pod
        image: projectname/projectname:latest
        env:
        - name: RELEASE_NAME
          value: eks-dev-1-newapp-backend
        - name: DATABASE_HOST
          value: 'dev.aurora.web.example.com'
        - name: DB_USERNAME
          value: 'dbuser'
        ports:
          - containerPort: 3001
...

[/simterm]

Обратите внимание, что DB_PASSWORD не создана, т.к. {{ .Release.Name }} есть по-умолчанию, .Values.appConfig.db.host и .Values.appConfig.db.user – есть в values.yaml, а .Values.appConfig.db.password у нас хранится в Helm Secrets и файле secrets.yaml, который мы сейчас не используем вообще.

Соответсвенно, при генерации манифеста – Helm пропускает создание этой переменной.

Блок env из файла

Ну и третий вариант, который придумался – подключать содержимое блока env в общий шаблон из файла проекта.

Добавим в values.yaml параметр, который будет отключать include шаблона из _helpers.tpl, назовём его customEnvs, и заодно параметр, который позволит передавать путь к файлу с переменными – customEnvsFile:

...
deployment:

  customEnvs: true
  customEnvsFile: 'projects/newapp/templates/environments.yaml'
....

Теперь в деплоймент добавляем проверку условия, если оно не срабатывает – то через {{ else }} выполняем подстановку из _helpers.yaml:

...
        env:
        {{- if .Values.deployment.customEnvs  }}
          {{- .Files.Get .Values.deployment.customEnvsFile | nindent 8 }}
        {{- else }}
          {{- include "helpers.environments" . | nindent 8 }}
        {{ end -}}
        ports:
...

И всё хорошо, но только потому, что сейчас из projects/newapp/templates/environments.yaml убран вызов .Release.Name:

- name: DATABASE_HOST
  value: 'dev.aurora.web.example.com'
- name: DB_USERNAME
  value: 'dbuser'
- name: DB_PASSWORD
  valueFrom:
    secretKeyRef:
      name: projectname-backend-secrets
      key: backend-db-password

Решаем с помощью той жу функции tpl, которую уже использовали.

Возвращаемся к projects/newapp/templates/environments.yaml – добавляем .Release.Name:

- name: RELEASE_NAME
  value: {{ .Release.Name }}
- name: DATABASE_HOST
  value: 'dev.aurora.web.example.com'
...

В деплойменте меняем вставку – добавляем tpl,  а сам .Files.Get и его аргумент заворачиваем в скобки:

...
        {{- if .Values.deployment.customEnvs  }}
          {{- tpl ( .Files.Get .Values.deployment.customEnvsFile ) . | nindent 8 }}
        {{- else }} 
          {{- include "helpers.environments" . | nindent 8 }}
        {{ end -}}
...

Проверяем:

[simterm]

$ helm upgrade --install eks-dev-1-newapp-backend --namespace eks-dev-1-newapp-backend-ns --create-namespace -f projects/newapp/env/dev/values.yaml --debug --dry-run .
...
    spec:
      containers:
      - name: newapp-pod
        image: projectname/projectname:latest
        env:
        - name: RELEASE_NAME
          value: eks-dev-1-newapp-backend
        - name: DATABASE_HOST
          value: 'dev.aurora.web.example.com'
        - name: DB_USERNAME
          value: 'dbuser'
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: projectname-backend-secrets
              key: backend-db-password
        ports:
          - containerPort: 3001
...

[/simterm]

С volumes, volumesMounts постуаем аналогично.

В целом схема с использованием _helpers.yaml выглядит вполне рабочей – попробуем применить на одном проекте, и будем посмотреть.

Поностью шаблон деплоймента теперь выглядит так:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.appConfig.appName }}-deployment
  labels: {{- include "helpers.labels" . | nindent 4 }}
  annotations:
    {{- if .Values.deployment.deploymentAnnotations }}
      {{- toYaml .Values.deployment.deploymentAnnotations | nindent 6 }}
    {{- end }}
    reloader.stakater.com/auto: "true"
spec:
  replicas: {{ .Values.deployment.replicaCount }}
  strategy:
    type: {{ .Values.deployment.delpoyStrategy }}
  selector:
    matchLabels:
      {{- include "helpers.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels: {{- include "helpers.labels" . | nindent 8 }}
    spec:
      containers:
      - name: {{ .Values.appConfig.appName }}-pod
        image: {{ .Values.deployment.image.repository }}/{{ .Values.deployment.image.name }}:{{ .Values.deployment.image.tag }}
        env: 
        {{- if .Values.deployment.customEnvs  }}
          {{- tpl ( .Files.Get .Values.deployment.customEnvsFile ) . | nindent 8 }}
        {{- else }}
          {{- include "helpers.environments" . | nindent 8 }}
        {{ end -}}
        ports:
          - containerPort: {{ .Values.appConfig.port }}
        {{- with .Values.deployment.livenessProbe }}
        livenessProbe:
          httpGet:
            path: {{ .path }}
            port: {{ .port }}
          initialDelaySeconds: {{ .initDelay }}
        {{- end }}
        {{- with .Values.deployment.readinessProbe }}
        readinessProbe:
          httpGet:
            path: {{ .path }}
            port: {{ .port }}
          initialDelaySeconds: {{ .initDelay }}
        {{- end }}
        resources:
          requests:
            cpu: {{ .Values.deployment.resources.requests.cpu | quote }}
      imagePullSecrets:
        - name: bttrm-docker-secret

Исключение шаблона из чарта

И напоследок – пример того, как можно исключить шаблон из чарта вообще.

Например, возьмём файл cronjobs.yaml:

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: {{ .Values.appConfig.appName }}-cron
  labels: {{- include "helpers.labels" . | nindent 4 }}
spec: 
  schedule: {{ .Values.cronjobs.schedule | quote }}
  startingDeadlineSeconds:  {{ .Values.cronjobs.startingDeadline }}
  concurrencyPolicy: {{ .Values.cronjobs.concurrencyPolicy }}
  jobTemplate:
    spec:
      template:
        metadata:
          labels: {{- include "helpers.labels" . | nindent 12 }}
        spec:
          containers:
            - name: {{ .Values.appConfig.appName }}-cron
              image: {{ .Values.deployment.image.repository }}/{{ .Values.deployment.image.name }}:{{ .Values.deployment.image.tag }}
              env:
              {{- if .Values.deployment.customEnvs  }}
                {{- tpl ( .Files.Get .Values.deployment.customEnvsFile ) . | nindent 14 }}
              {{- else }}
                {{- include "helpers.environments" . | nindent 14 }}
              {{ end -}}
             command: ["npm"]
              args: ["run", "cron:app"]
          restartPolicy: Never
          imagePullSecrets:
              - name: bttrm-docker-secret

Обратите внимание, что мы тут используем наш _helpers.yaml, что облечает жизнь – те же лейблы, те же переменные.

Но – не каждый проект использует кроны. Как исключить из чарта?

В values.yaml добавляем параметр cronjobs.enabled:

...
################
### Cronjobs ###
################ 
             
cronjobs:     
          
  enabled: false
  schedule: '*/15 * * * *'
  startingDeadline: 10
  concurrencyPolicy: 'Forbid'
...

А затем – оборачиваем весь шаблон в единый блок if:

{{- if .Values.cronjobs.enabled }}
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: {{ .Values.appConfig.appName }}-cron
  labels: {{- include "helpers.labels" . | nindent 4 }}
...
          imagePullSecrets:
              - name: bttrm-docker-secret
{{- end }}

Проверяем:

[simterm]

$ helm upgrade --install eks-dev-1-newapp-backend --namespace eks-dev-1-newapp-backend-ns --create-namespace -f projects/newapp/env/dev/values.yaml --debug --dry-run .
...
---
# Source: project-backend/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: newapp-deployment
...
      imagePullSecrets:
        - name: bttrm-docker-secret

[/simterm]

Кронов нет.

Добавляем --set cronjobs.enabled=true – и они будут добавлены в релиз:

[simterm]

$ helm upgrade --install eks-dev-1-newapp-backend --namespace eks-dev-1-newapp-backend-ns --create-namespace -f projects/newapp/env/dev/values.yaml --debug --dry-run . --set cronjobs.enabled=true
...
      imagePullSecrets:
        - name: bttrm-docker-secret
---
# Source: project-backend/templates/cronjobs.yaml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: newapp-cron
...
          imagePullSecrets:
              - name: bttrm-docker-secret

[/simterm]

Готово.

Ссылки по теме