Puede leer sobre el propio helmfile y ejemplos de su uso en
Nos familiarizaremos con formas no obvias de describir lanzamientos en helmfile.
Digamos que tenemos un paquete de gráficos de helm (por ejemplo, postgres y alguna aplicación de backend) y varios entornos (varios clústeres de Kubernetes, varios espacios de nombres o varios de ambos). Tomamos el helmfile, leemos la documentación y comenzamos a describir nuestros entornos y lanzamientos:
.
├── envs
│ ├── devel
│ │ └── values
│ │ ├── backend.yaml
│ │ └── postgres.yaml
│ └── production
│ └── values
│ ├── backend.yaml
│ └── postgres.yaml
└── helmfile.yaml
helmfile.yaml
environments:
devel:
production:
releases:
- name: postgres
labels:
app: postgres
wait: true
chart: stable/postgresql
version: 8.4.0
values:
- envs/{{ .Environment.Name }}/values/postgres.yaml
- name: backend
labels:
app: backend
wait: true
chart: private-helm-repo/backend
version: 1.0.5
needs:
- postgres
values:
- envs/{{ .Environment.Name }}/values/backend.yaml
Terminamos con 2 ambientes: desarrollo, Production — cada uno contiene sus propios valores para las tablas de liberación del timón. Les implementaremos así:
helmfile -n <namespace> -e <env> apply
Diferentes versiones de cartas de timón en diferentes entornos
¿Qué pasa si necesitamos implementar diferentes versiones del backend en diferentes entornos? ¿Cómo parametrizar la versión de lanzamiento? Los valores ambientales disponibles a través de {{ .Values }}
helmfile.yaml
environments:
devel:
+ values:
+ - charts:
+ versions:
+ backend: 1.1.0
production:
+ values:
+ - charts:
+ versions:
+ backend: 1.0.5
...
- name: backend
labels:
app: backend
wait: true
chart: private-helm-repo/backend
- version: 1.0.5
+ version: {{ .Values.charts.versions.backend }}
...
Diferentes conjuntos de aplicaciones en diferentes entornos.
Genial, pero ¿qué pasa si no es necesario? production
¿Implementar Postgres, porque sabemos que no necesitamos insertar la base de datos en K8 y tenemos a la venta un maravilloso clúster de Postgres separado? Para solucionar este problema tenemos etiquetas.
helmfile -n <namespace> -e devel apply
helmfile -n <namespace> -e production -l app=backend apply
Esto es genial, pero personalmente prefiero describir qué aplicaciones implementar en el entorno no usando argumentos de inicio, sino en la descripción de los propios entornos. ¿Qué hacer? Puede colocar las descripciones de las versiones en una carpeta separada, crear una lista de las versiones necesarias en la descripción del entorno y "recoger" sólo las versiones necesarias, ignorando el resto.
.
├── envs
│ ├── devel
│ │ └── values
│ │ ├── backend.yaml
│ │ └── postgres.yaml
│ └── production
│ └── values
│ ├── backend.yaml
│ └── postgres.yaml
+ ├── releases
+ │ ├── backend.yaml
+ │ └── postgres.yaml
└── helmfile.yaml
helmfile.yaml
environments:
devel:
values:
- charts:
versions:
backend: 1.1.0
- apps:
- postgres
- backend
production:
values:
- charts:
versions:
backend: 1.0.5
- apps:
- backend
- releases:
- - name: postgres
- labels:
- app: postgres
- wait: true
- chart: stable/postgresql
- version: 8.4.0
- values:
- - envs/{{ .Environment.Name }}/values/postgres.yaml
- - name: backend
- labels:
- app: backend
- wait: true
- chart: private-helm-repo/backend
- version: {{ .Values.charts.versions.backend }}
- needs:
- - postgres
- values:
- - envs/{{ .Environment.Name }}/values/backend.yaml
+ ---
+ bases:
+ {{- range .Values.apps }}
+ - releases/{{ . }}.yaml
+ {{- end }}
releases/postgres.yaml
releases:
- name: postgres
labels:
app: postgres
wait: true
chart: stable/postgresql
version: 8.4.0
values:
- envs/{{ .Environment.Name }}/values/postgres.yaml
releases/backend.yaml
releases:
- name: backend
labels:
app: backend
wait: true
chart: private-helm-repo/backend
version: {{ .Values.charts.versions.backend }}
needs:
- postgres
values:
- envs/{{ .Environment.Name }}/values/backend.yaml
Заметка
Cuando se utiliza el bases:
es necesario usar el separador yaml ---
, para que pueda crear plantillas de lanzamientos (y otras partes, como helmDefaults) con valores de entornos
En este caso, la versión de Postgres ni siquiera se incluirá en la descripción de producción. ¡Muy cómodamente!
Valores globales anulables para lanzamientos
Por supuesto, es fantástico poder establecer valores para los gráficos de timón para cada entorno, pero ¿qué pasa si tenemos varios entornos descritos y queremos, por ejemplo, establecer los mismos para todos? affinity
, pero no queremos configurarlo por defecto en los propios gráficos, que se almacenan en los nabos.
En este caso, para cada versión podríamos especificar 2 archivos con valores: el primero con valores predeterminados, que determinarán los valores del propio gráfico, y el segundo con valores para el entorno, que a su vez anulará los los predeterminados.
.
├── envs
+ │ ├── default
+ │ │ └── values
+ │ │ ├── backend.yaml
+ │ │ └── postgres.yaml
│ ├── devel
│ │ └── values
│ │ ├── backend.yaml
│ │ └── postgres.yaml
│ └── production
│ └── values
│ ├── backend.yaml
│ └── postgres.yaml
├── releases
│ ├── backend.yaml
│ └── postgres.yaml
└── helmfile.yaml
releases/backend.yaml
releases:
- name: backend
labels:
app: backend
wait: true
chart: private-helm-repo/backend
version: {{ .Values.charts.versions.backend }}
needs:
- postgres
values:
+ - envs/default/values/backend.yaml
- envs/{{ .Environment.Name }}/values/backend.yaml
envs/default/values/backend.yaml
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- backend
topologyKey: "kubernetes.io/hostname"
Definición de valores globales para gráficos de timón de todas las versiones a nivel ambiental
Digamos que creamos varios ingresos en varias versiones; podríamos definir manualmente para cada gráfico hosts:
, pero en nuestro caso el dominio es el mismo, entonces ¿por qué no ponerlo en alguna variable global y simplemente sustituir su valor en los gráficos? Para ello, aquellos archivos con valores que queramos parametrizar tendrán que tener la extensión .gotmpl
, para que helmfile sepa que debe ejecutarse a través del motor de plantillas.
.
├── envs
│ ├── default
│ │ └── values
- │ │ ├── backend.yaml
- │ │ ├── postgres.yaml
+ │ │ ├── backend.yaml.gotmpl
+ │ │ └── postgres.yaml.gotmpl
│ ├── devel
│ │ └── values
│ │ ├── backend.yaml
│ │ └── postgres.yaml
│ └── production
│ └── values
│ ├── backend.yaml
│ └── postgres.yaml
├── releases
│ ├── backend.yaml
│ └── postgres.yaml
└── helmfile.yaml
helmfile.yaml
environments:
devel:
values:
- charts:
versions:
backend: 1.1.0
- apps:
- postgres
- backend
+ - global:
+ ingressDomain: k8s.devel.domain
production:
values:
- charts:
versions:
backend: 1.0.5
- apps:
- backend
+ - global:
+ ingressDomain: production.domain
---
bases:
{{- range .Values.apps }}
- releases/{{ . }}.yaml
{{- end }}
envs/default/values/backend.yaml.gotmpl
ingress:
enabled: true
paths:
- /api
hosts:
- {{ .Values.global.ingressDomain }}
envs/default/values/postgres.yaml.gotmpl
ingress:
enabled: true
paths:
- /
hosts:
- postgres.{{ .Values.global.ingressDomain }}
Заметка
Obviamente, el ingreso en el gráfico de Postgres es algo extremadamente dudoso, por lo que este artículo se presenta simplemente como un ejemplo esférico en el vacío y para no introducir ninguna nueva versión en el artículo solo por describir el ingreso.
Sustituir secretos por valores ambientales.
Por analogía con el ejemplo anterior, puede sustituir los cifrados usando
.
├── envs
│ ├── default
│ │ └── values
│ │ ├── backend.yaml
│ │ └── postgres.yaml
│ ├── devel
│ │ ├── values
│ │ │ ├── backend.yaml
│ │ │ └── postgres.yaml
+ │ │ └── secrets.yaml
│ └── production
│ ├── values
│ │ ├── backend.yaml
│ │ └── postgres.yaml
+ │ └── secrets.yaml
├── releases
│ ├── backend.yaml
│ └── postgres.yaml
└── helmfile.yaml
helmfile.yaml
environments:
devel:
values:
- charts:
versions:
backend: 1.1.0
- apps:
- postgres
- backend
- global:
ingressDomain: k8s.devel.domain
+ secrets:
+ - envs/devel/secrets.yaml
production:
values:
- charts:
versions:
backend: 1.0.5
- apps:
- backend
- global:
ingressDomain: production.domain
+ secrets:
+ - envs/production/secrets.yaml
---
bases:
{{- range .Values.apps }}
- releases/{{ . }}.yaml
{{- end }}
envs/devel/secrets.yaml
secrets:
elastic:
password: ENC[AES256_GCM,data:hjCB,iv:Z1P6/6xBJgJoKLJ0UUVfqZ80o4L84jvZfM+uH9gBelc=,tag:dGqQlCZnLdRAGoJSj63rBQ==,type:int]
...
envs/production/secrets.yaml
secrets:
elastic:
password: ENC[AES256_GCM,data:ZB/VpTFk8f0=,iv:EA//oT1Cb5wNFigTDOz3nA80qD9UwTjK5cpUwLnEXjs=,tag:hMdIUaqLRA8zuFBd82bz6A==,type:str]
...
envs/default/values/backend.yaml.gotmpl
elasticsearch:
host: elasticsearch
port: 9200
password: {{ .Values | getOrNil "secrets.elastic.password" | default "password" }}
envs/devel/values/backend.yaml
elasticsearch:
host: elastic-0.devel.domain
envs/production/values/backend.yaml
elasticsearch:
host: elastic-0.production.domain
Заметка
Por cierto, getOrNil
- una función especial para las plantillas go en helmfile, que, incluso si .Values.secrets
no existirá, no arrojará un error, pero permitirá el resultado usando la función default
sustituir el valor predeterminado
Conclusión
Las cosas descritas parecen bastante obvias, pero la información sobre una descripción conveniente de la implementación en varios entornos usando helmfile es muy escasa, y me encanta IaC (Infraestructura como código) y quiero tener una descripción clara del estado de implementación.
Para concluir, me gustaría agregar que las variables para el entorno predeterminado pueden, a su vez, parametrizarse con las variables de entorno del SO de un determinado corredor desde el cual se lanzará el despliegue, y así obtener entornos dinámicos.
helmfile.yaml
environments:
default:
values:
- global:
clusterDomain: {{ env "CLUSTER_DOMAIN" | default "cluster.local" }}
ingressDomain: {{ env "INGRESS_DOMAIN" }}
Fuente: habr.com