— uma camada para , que permite descrever múltiplos lançamentos helm em um só lugar, parametrizar seus charts para vários ambientes, além de definir a ordem de seu deploy.
Você pode ler sobre o helmfile e exemplos de uso na e .
Vamos explorar maneiras menos óbvias de descrever lançamentos no helmfile.
Suponha que temos um conjunto de charts helm (por exemplo, postgres e uma aplicação backend) e vários ambientes (vários clusters Kubernetes, vários namespaces ou ambos). Pegamos o helmfile, lemos a documentação e começamos a descrever nossos ambientes e lançamentos:
.
├── envs
│ ├── devel
│ │ └── values
│ │ ├── backend.yaml
│ │ └── postgres.yaml
│ └── production
│ └── values
│ ├── backend.yaml
│ └── postgres.yaml
└── helmfile.yamlhelmfile.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.yamlTemos 2 ambientes: devel, production — cada um contém seus próprios valores para os charts helm dos lançamentos. Vamos realizar o deploy deles assim:
helmfile -n -e applyVersões diferentes dos charts helm em diferentes ambientes
O que fazer se precisarmos lançar versões diferentes do backend em diferentes ambientes? Como parametrizar a versão do lançamento? Os valores do ambiente, disponíveis atravé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 }}
...Conjunto variado de aplicações em diferentes ambientes
Ótimo, mas e se não precisarmos production implantar o postgres, porque sabemos que não devemos colocar um banco de dados no k8s e para produção temos um belo cluster postgres separado? Para resolver esse problema, temos labels (etiquetas)
helmfile -n -e devel apply
helmfile -n -e production -l app=backend applyÉ ótimo, mas pessoalmente prefiro descrever quais aplicativos implantar no ambiente não por meio de argumentos de execução, mas na descrição dos próprios ambientes. O que fazer? Podemos colocar a descrição das versões em uma pasta separada, criar uma lista das versões necessárias na descrição do ambiente e "conectar" apenas as versões necessárias, ignorando as demais.
.
├── 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.yamlreleases/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.yamlNota
Ao usar bases: é necessário usar obrigatoriamente o delimitador yaml ---, para que seja possível usar templates para releases (e outras partes, como helmDefaults) com valores dos environments
Nesse caso, a release postgres nem será incluída na descrição para produção. Muito conveniente!
Valores globais substituíveis para releases
Claro, é ótimo que podemos definir valores para os charts helm em cada ambiente, mas e se tivermos vários ambientes descritos e quisermos, por exemplo, definir um valor idêntico para todos afinidade, mas não queremos configurá-lo por padrão nos próprios charts, que estão armazenados nos repositórios.
Nesse caso, poderíamos especificar 2 arquivos com values para cada release: o primeiro com os valores padrão, que determinarão os valores do próprio chart, e o segundo com os valores para o ambiente, que, por sua vez, substituirá os padrões.
.
├── envs
+ │ ├── padrão
+ │ │ └── valores
+ │ │ ├── backend.yaml
+ │ │ └── postgres.yaml
│ ├── desenvolvimento
│ │ └── valores
│ │ ├── backend.yaml
│ │ └── postgres.yaml
│ └── produção
│ └── valores
│ ├── backend.yaml
│ └── postgres.yaml
├── releases
│ ├── backend.yaml
│ └── postgres.yaml
└── helmfile.yamlreleases/backend.yaml
releases:
- nome: backend
etiquetas:
app: backend
wait: true
chart: private-helm-repo/backend
version: {{ .Values.charts.versions.backend }}
needs:
- postgres
values:
+ - envs/padrão/valores/backend.yaml
- envs/{{ .Environment.Name }}/valores/backend.yamlenvs/padrão/valores/backend.yaml
afinidade:
podAntiAffinity:
preferidoDuranteAgendamentoIgnoradoDuranteExecução:
- peso: 1
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/nome
operator: In
valores:
- backend
topologyKey: "kubernetes.io/hostname"Definição de valores globais para helm charts de todas as releases a nível de ambiente
Suponha que temos várias releases gerando vários ingress — poderíamos definir manualmente para cada chart. hosts:, mas no nosso caso o domínio é o mesmo, por que não transformá-lo numa variável global e simplesmente inserir seu valor nos charts? Para isso, os arquivos com valores que queremos parametrizar devem ter a extensão .gotmpl, para que o helmfile saiba que ele deve ser processado pelo template.
.
├── envs
│ ├── padrão
│ │ └── valores
- │ │ ├── backend.yaml
- │ │ ├── postgres.yaml
+ │ │ ├── backend.yaml.gotmpl
+ │ │ └── postgres.yaml.gotmpl
│ ├── desenvolvimento
│ │ └── valores
│ │ ├── backend.yaml
│ │ └── postgres.yaml
│ └── produção
│ └── valores
│ ├── backend.yaml
│ └── postgres.yaml
├── releases
│ ├── backend.yaml
│ └── postgres.yaml
└── helmfile.yamlhelmfile.yaml
ambientes:
desenvolvimento:
valores:
- charts:
versões:
backend: 1.1.0
- apps:
- postgres
- backend
+ - global:
+ ingressDomain: k8s.devel.domain
produção:
valores:
- charts:
versões:
backend: 1.0.5
- apps:
- backend
+ - global:
+ ingressDomain: production.domain
---
bases:
{{- range .Values.apps }}
- releases/{{ . }}.yaml
{{- end }}envs/padrão/valores/backend.yaml.gotmpl
ingress:
ativado: true
caminhos:
- /api
hosts:
- {{ .Values.global.ingressDomain }}envs/padrão/valores/postgres.yaml.gotmpl
ingress:
ativado: true
caminhos:
- /
hosts:
- postgres.{{ .Values.global.ingressDomain }}Nota
É óbvio que o ingress no chart postgres é algo extremamente duvidoso, então no artigo isso é apresentado apenas como um exemplo esférico em um vácuo e para não introduzir um novo lançamento apenas para descrever o ingress.
Substituição de segredos (secrets) a partir de variáveis de ambiente.
Analogamente ao exemplo acima, podemos substituir e também usar os valores criptografados com . Ao invés de criar um arquivo secrets para cada lançamento, onde definimos os valores criptografados para o chart, podemos simplesmente definir no arquivo default.yaml.gotmpl os valores que serão extraídos das variáveis definidas a nível de ambientes. E os valores que não precisamos esconder de ninguém podem ser facilmente sobrescritos nos valores do lançamento em um ambiente específico.
.
├── 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.yamlhelmfile.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/padrão/valores/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.domainenvs/production/values/backend.yaml
elasticsearch:
host: elastic-0.production.domainNota
A propósito, getOrNil — uma função especial para templates go no helmfile, que mesmo se .Values.secrets não existir, não lançará um erro, mas permitirá usar a função padrão para substituir o valor padrão.
Conclusão
As coisas descritas parecem bastante óbvias, mas as informações sobre uma descrição conveniente de deployment em múltiplos ambientes usando helmfile são escassas, e eu adoro IaC (Infrastructure-as-Code) e quero ter uma descrição clara do estado do deployment.
Por fim, gostaria de adicionar que as variáveis para o ambiente padrão podem, por sua vez, ser parametrizadas por variáveis de ambiente do sistema operacional de um runner, a partir do qual o deployment será iniciado, e assim obter ambientes dinâmicos.
helmfile.yaml
environments:
default:
values:
- global:
clusterDomain: {{ env "CLUSTER_DOMAIN" | default "cluster.local" }}
ingressDomain: {{ env "INGRESS_DOMAIN" }}Fonte: habr.com
