Você pode ler sobre o próprio helmfile e exemplos de seu uso em
Conheceremos maneiras não óbvias de descrever lançamentos no helmfile
Digamos que temos um pacote de gráficos de leme (por exemplo, postgres e algum aplicativo de back-end) e vários ambientes (vários clusters Kubernetes, vários namespaces ou vários de ambos). Pegamos o helmfile, lemos a documentação e começamos a descrever nossos ambientes e releases:
.
├── 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
Acabamos com 2 ambientes: d, produção — cada um contém seus próprios valores para os gráficos de liberação do leme. Iremos implantar para eles assim:
helmfile -n <namespace> -e <env> apply
Diferentes versões de gráficos de leme em diferentes ambientes
E se precisarmos implementar versões diferentes do back-end em ambientes diferentes? Como parametrizar a versão de lançamento? Os valores ambientais disponíveis através {{ .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 diferente de aplicativos em ambientes diferentes
Ótimo, mas e se não precisarmos production
implementar o postgres, porque sabemos que não precisamos enviar o banco de dados para o k8s e temos um maravilhoso cluster postgres separado para venda? Para resolver este problema temos rótulos
helmfile -n <namespace> -e devel apply
helmfile -n <namespace> -e production -l app=backend apply
Isso é ótimo, mas pessoalmente prefiro descrever quais aplicativos implantar no ambiente não usando argumentos de inicialização, mas na descrição dos próprios ambientes. O que fazer? Você pode colocar as descrições dos lançamentos em uma pasta separada, criar uma lista dos lançamentos necessários na descrição do ambiente e “pegar” apenas os lançamentos necessários, ignorando o 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
Заметка
Quando se utiliza o bases:
é necessário usar o separador yaml ---
, para que você possa modelar versões (e outras partes, como helmDefaults) com valores de ambientes
Neste caso, o release do postgres nem será incluído na descrição para produção. Muito confortavelmente!
Valores globais substituíveis para lançamentos
Claro, é ótimo que você possa definir valores para gráficos de leme para cada ambiente, mas e se tivermos vários ambientes descritos e quisermos, por exemplo, definir o mesmo para todos? affinity
, mas não queremos configurá-lo por padrão nos próprios gráficos, que são armazenados nos nabos.
Neste caso, para cada versão poderíamos especificar 2 arquivos com valores: o primeiro com valores padrão, que determinará os valores do próprio gráfico, e o segundo com valores para o ambiente, que por sua vez substituirá o os padrão.
.
├── 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"
Definindo valores globais para gráficos de leme de todas as versões no nível do ambiente
Digamos que criamos vários ingressos em vários lançamentos - poderíamos definir manualmente para cada gráfico hosts:
, mas no nosso caso o domínio é o mesmo, então por que não colocá-lo em alguma variável global e simplesmente substituir seu valor nos gráficos? Para isso, os arquivos com valores que queremos parametrizar deverão ter a extensão .gotmpl
, para que o helmfile saiba que precisa ser executado por meio do mecanismo de modelo.
.
├── 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, o ingresso no gráfico do postgres é algo extremamente duvidoso, então este artigo é dado simplesmente como um exemplo esférico no vácuo e para não introduzir alguma nova versão no artigo apenas para descrever o ingresso
Substituindo segredos de valores ambientais
Por analogia com o exemplo acima, você pode substituir os criptografados 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
Заметка
By the way, getOrNil
- uma função especial para modelos go no helmfile, que, mesmo que .Values.secrets
não existirá, não gerará erro, mas permitirá o resultado usando a função default
substituir valor padrão
Conclusão
As coisas descritas parecem bastante óbvias, mas as informações sobre uma descrição conveniente da implantação em vários ambientes usando helmfile são muito escassas, e eu adoro IaC (Infraestrutura como Código) e quero ter uma descrição clara do estado de implantação.
Concluindo, gostaria de acrescentar que as variáveis do ambiente padrão podem, por sua vez, ser parametrizadas com as variáveis de ambiente do SO de um determinado executor a partir do qual será lançada a implantação, 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