Pular para o conteúdo principal
Versão: 3.19.0

Dicas e Truques de Desenvolvimento de Charts

Este guia cobre algumas das dicas e truques que desenvolvedores de charts do Helm aprenderam enquanto criavam charts de qualidade para produção.

Conheça as Funções de Template

O Helm usa Go templates para criar templates dos seus arquivos de recursos. Embora o Go já inclua várias funções nativas, adicionamos muitas outras.

Primeiro, adicionamos todas as funções da biblioteca Sprig, exceto env e expandenv, por razões de segurança.

Também adicionamos duas funções especiais de template: include e required. A função include permite trazer outro template e então passar os resultados para outras funções de template.

Por exemplo, este trecho de template inclui um template chamado mytpl, então converte o resultado para minúsculas e o envolve em aspas duplas.

value: {{ include "mytpl" . | lower | quote }}

A função required permite declarar uma entrada de valores específica como obrigatória para a renderização do template. Se o valor estiver vazio, a renderização do template falhará com uma mensagem de erro enviada pelo usuário.

O exemplo a seguir da função required declara que uma entrada para .Values.who é obrigatória, e exibirá uma mensagem de erro quando essa entrada estiver ausente:

value: {{ required "A valid .Values.who entry required!" .Values.who }}

Use Aspas em Strings, Não em Inteiros

Quando você está trabalhando com dados de string, é sempre mais seguro colocar aspas nas strings do que deixá-las sem:

name: {{ .Values.MyName | quote }}

Mas quando estiver trabalhando com inteiros, não coloque aspas nos valores. Isso pode, em muitos casos, causar erros de parsing dentro do Kubernetes.

port: {{ .Values.Port }}

Esta observação não se aplica a valores de variáveis de ambiente que são esperados como strings, mesmo quando representam inteiros:

env:
- name: HOST
value: "http://host"
- name: PORT
value: "1234"

Usando a Função 'include'

O Go fornece uma maneira de incluir um template em outro usando a diretiva nativa template. No entanto, a função nativa não pode ser usada em pipelines de template do Go.

Para tornar possível incluir um template e então realizar uma operação na saída desse template, o Helm tem uma função especial include:

{{ include "toYaml" $value | indent 2 }}

O código acima inclui um template chamado toYaml, passa $value para ele, e então passa a saída desse template para a função indent.

Como o YAML atribui significado aos níveis de indentação e espaços em branco, esta é uma ótima maneira de incluir trechos de código, enquanto manipula a indentação em um contexto relevante.

Usando a Função 'required'

O Go fornece uma maneira de definir opções de template para controlar o comportamento quando um mapa é indexado com uma chave que não está presente no mapa. Isso é tipicamente configurado com template.Options("missingkey=option"), onde option pode ser default, zero ou error. Embora definir esta opção como error vá parar a execução com um erro, isso se aplicaria a cada chave ausente no mapa. Pode haver situações em que um desenvolvedor de chart queira impor esse comportamento para valores selecionados no arquivo values.yaml.

A função required dá aos desenvolvedores a capacidade de declarar uma entrada de valor como obrigatória para a renderização do template. Se a entrada estiver vazia em values.yaml, o template não será renderizado e retornará uma mensagem de erro fornecida pelo desenvolvedor.

Por exemplo:

{{ required "A valid foo is required!" .Values.foo }}

O código acima renderizará o template quando .Values.foo estiver definido, mas falhará na renderização e sairá quando .Values.foo estiver indefinido.

Usando a Função 'tpl'

A função tpl permite que desenvolvedores avaliem strings como templates dentro de um template. Isso é útil para passar uma string de template como valor para um chart ou renderizar arquivos de configuração externos. Sintaxe: {{ tpl TEMPLATE_STRING VALUES }}

Exemplos:

# values
template: "{{ .Values.name }}"
name: "Tom"

# template
{{ tpl .Values.template . }}

# output
Tom

Renderizando um arquivo de configuração externo:

# external configuration file conf/app.conf
firstName={{ .Values.firstName }}
lastName={{ .Values.lastName }}

# values
firstName: Peter
lastName: Parker

# template
{{ tpl (.Files.Get "conf/app.conf") . }}

# output
firstName=Peter
lastName=Parker

Criando Image Pull Secrets

Image pull secrets são essencialmente uma combinação de registry, username e password. Você pode precisar deles em uma aplicação que está implantando, mas para criá-los é necessário executar base64 algumas vezes. Podemos escrever um template auxiliar para compor o arquivo de configuração do Docker para uso como payload do Secret. Aqui está um exemplo:

Primeiro, assuma que as credenciais estão definidas no arquivo values.yaml assim:

imageCredentials:
registry: quay.io
username: someone
password: sillyness
email: someone@host.com

Em seguida, definimos nosso template auxiliar assim:

{{- define "imagePullSecret" }}
{{- with .Values.imageCredentials }}
{{- printf "{\"auths\":{\"%s\":{\"username\":\"%s\",\"password\":%s,\"email\":\"%s\",\"auth\":\"%s\"}}}" .registry .username (.password | quote) .email (printf "%s:%s" .username .password | b64enc) | b64enc }}
{{- end }}
{{- end }}

Finalmente, usamos o template auxiliar em um template maior para criar o manifesto do Secret:

apiVersion: v1
kind: Secret
metadata:
name: myregistrykey
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: {{ template "imagePullSecret" . }}

Reiniciando Deployments Automaticamente

Muitas vezes ConfigMaps ou Secrets são injetados como arquivos de configuração em containers ou existem outras mudanças de dependências externas que exigem reiniciar pods. Dependendo da aplicação, uma reinicialização pode ser necessária se eles forem atualizados com um subsequente helm upgrade, mas se a spec do deployment em si não mudou, a aplicação continua executando com a configuração antiga, resultando em um deployment inconsistente.

A função sha256sum pode ser usada para garantir que a seção de annotations de um deployment seja atualizada se outro arquivo mudar:

kind: Deployment
spec:
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
[...]

NOTA: Se você está adicionando isso a um library chart, você não conseguirá acessar seu arquivo em $.Template.BasePath. Em vez disso, você pode referenciar sua definição com {{ include ("mylibchart.configmap") . | sha256sum }}.

No caso de você sempre querer reiniciar seu deployment, você pode usar um passo de annotation similar ao acima, substituindo por uma string aleatória para que ela sempre mude e cause a reinicialização do deployment:

kind: Deployment
spec:
template:
metadata:
annotations:
rollme: {{ randAlphaNum 5 | quote }}
[...]

Cada invocação da função de template gerará uma string aleatória única. Isso significa que se for necessário sincronizar as strings aleatórias usadas por múltiplos recursos, todos os recursos relevantes precisarão estar no mesmo arquivo de template.

Ambos os métodos permitem que seu Deployment aproveite a lógica de estratégia de atualização nativa para evitar downtime.

NOTA: No passado, recomendávamos usar a flag --recreate-pods como outra opção. Esta flag foi marcada como obsoleta no Helm 3 em favor do método declarativo acima.

Instruindo o Helm a Não Desinstalar um Recurso

Às vezes existem recursos que não devem ser desinstalados quando o Helm executa um helm uninstall. Desenvolvedores de chart podem adicionar uma annotation a um recurso para prevenir que ele seja desinstalado.

kind: Secret
metadata:
annotations:
helm.sh/resource-policy: keep
[...]

A annotation helm.sh/resource-policy: keep instrui o Helm a pular a exclusão deste recurso quando uma operação do helm (como helm uninstall, helm upgrade ou helm rollback) resultaria em sua exclusão. No entanto, este recurso se torna órfão. O Helm não o gerenciará mais de forma alguma. Isso pode levar a problemas se usar helm install --replace em uma release que já foi desinstalada, mas manteve recursos.

Usando "Partials" e Template Includes

Às vezes você quer criar algumas partes reutilizáveis em seu chart, sejam elas blocos ou partials de template. E frequentemente, é mais limpo mantê-los em seus próprios arquivos.

No diretório templates/, qualquer arquivo que começa com um underscore (_) não é esperado que produza um arquivo de manifesto Kubernetes. Então, por convenção, templates auxiliares e partials são colocados em um arquivo _helpers.tpl.

Charts Complexos com Muitas Dependências

Muitos dos charts no Artifact Hub da CNCF são "blocos de construção" para criar aplicações mais avançadas. Mas charts podem ser usados para criar instâncias de aplicações em grande escala. Nesses casos, um único chart guarda-chuva pode ter múltiplos subcharts, cada um funcionando como uma peça do todo.

A prática recomendada atual para compor uma aplicação complexa a partir de partes discretas é criar um chart guarda-chuva de nível superior que expõe as configurações globais, e então usar o subdiretório charts/ para incorporar cada um dos componentes.

YAML é um Superset de JSON

De acordo com a especificação YAML, YAML é um superset de JSON. Isso significa que qualquer estrutura JSON válida deveria ser válida em YAML.

Isso tem uma vantagem: Às vezes, desenvolvedores de template podem achar mais fácil expressar uma estrutura de dados com uma sintaxe similar ao JSON em vez de lidar com a sensibilidade de espaços em branco do YAML.

Como melhor prática, templates devem seguir uma sintaxe similar ao YAML a menos que a sintaxe JSON reduza substancialmente o risco de um problema de formatação.

Cuidado ao Gerar Valores Aleatórios

Existem funções no Helm que permitem gerar dados aleatórios, chaves criptográficas, e assim por diante. São boas para usar. Mas esteja ciente de que durante upgrades, os templates são re-executados. Quando uma execução de template gera dados que diferem da última execução, isso acionará uma atualização desse recurso.

Instalar ou Atualizar uma Release com um Único Comando

O Helm fornece uma maneira de realizar um install-or-upgrade como um único comando. Use helm upgrade com o comando --install. Isso fará com que o Helm verifique se a release já está instalada. Se não estiver, ele executará um install. Se estiver, então a release existente será atualizada.

$ helm upgrade --install <release name> --values <values file> <chart directory>