Artigo original: https://www.freecodecamp.org/news/what-is-a-helm-chart-tutorial-for-kubernetes-beginners/

O Kubernetes  é uma ferramenta muito útil para desenvolvedores nativos da nuvem. No entanto, por si só, ele não abrange todos os aspectos – existem algumas coisas que o Kubernetes não consegue resolver ou que estão fora de seu escopo.

Essa é uma das razões pelas quais os projetos de código aberto são tão bons. Eles ajudam ferramentas incríveis a se tornarem ainda mais incríveis quando as combinamos com outras ferramentas incríveis de código aberto. Muitas vezes, essas ferramentas foram desenvolvidas com o único propósito de preencher lacunas. Uma dessas ferramentas é o Helm.

O que é o Helm?

O Helm é amplamente conhecido como o "gerenciador de pacotes do Kubernetes". Embora se apresente assim, seu funcionamento vai muito além de um simples gerenciador de pacotes. No entanto, vamos começar do início.

Helm é um projeto de código aberto que foi originalmente criado pela DeisLabs e doado à CNCF (texto em inglês), que agora o mantém. O objetivo original do Helm era proporcionar aos usuários uma maneira melhor de gerenciar todos os arquivos YAML do Kubernetes que criamos em projetos no Kubernetes.

O caminho que o Helm seguiu para resolver esse problema foi criar Helm Charts. Cada chart (em português, algo como "gráfico") é um pacote com um ou mais arquivos de configuração do Kubernetes – um chart pode ter charts filhos e charts dependentes também.

Isso significa que o Helm instala toda a árvore de dependências de um projeto se você executar o comando de instalação para o chart de nível superior. Você precisa apenas de um único comando para instalar toda a sua aplicação, em vez de listar os arquivos para instalação via kubectl.

Os charts também permitem que você controle a versão dos arquivos de configuração, assim como fazemos com o Node.js ou qualquer outro pacote. Isso permite que você instale versões específicas do chart, o que significa manter configurações específicas para sua infraestrutura na forma de código.

O Helm também mantém todo o histórico de lançamento de todos os charts, para que você possa voltar para uma versão anterior se algo der errado.

O Helm oferece suporte nativo ao Kubernetes. Isso significa que você não precisa escrever nenhum arquivo de sintaxe complexa ou algo do tipo para começar a usar o Helm. Basta colocar seus arquivos de modelo em um novo chart e você está pronto para começar.

Porém, você pode se perguntar o porquê devemos usá-lo. Gerenciar manifestos de aplicações pode ser facilmente feito com algumas combinações de comandos.

Por que você deve usar o Helm?

O Helm realmente se destaca onde o Kubernetes não consegue. Por exemplo, na criação de templates.
O escopo do projeto Kubernetes é lidar com seus contêineres para você, não com seus arquivos de template (em português, modelos).

Isso torna muito difícil a criação de arquivos verdadeiramente genéricos para serem usados por meio de uma grande equipe ou organização, com diferentes parâmetros que precisam ser definidos para cada arquivo.

Além disso, como você versiona informações sensíveis usando o Git quando os arquivos de modelo são texto simples?

A resposta: com os templates. O Helm permite que você adicione variáveis e use funções dentro de seus arquivos de modelo. Isso o torna perfeito para aplicações escaláveis que, em algum momento, precisarão ter seus parâmetros alterados. Vamos ver um exemplo:

Eu tenho um projeto de código aberto chamado Zaqar, um microsserviço de e-mail simples para Node.js, que se comunica com o SendGrid. O projeto é basicamente composto por um serviço, um deployment e um escalonamento automático.

Vamos pegar o arquivo de deployment como exemplo. Eu teria algo assim:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: zaqar
  namespace: default
  labels:
    app: zaqar
    version: v1.0.0
    env: production
spec:
  replicas: 1
  selector:
    matchLabels:
      app: zaqar
      env: production
  template:
    metadata:
      labels:
        app: zaqar
        version: v1.0.0
        env: production
    spec:
      containers:
        - name: zaqar
          image: "khaosdoctor/zaqar:v1.0.0"
          imagePullPolicy: IfNotPresent
          env:
            - name: SENDGRID_APIKEY
              value: "MY_SECRET_KEY"
            - name: DEFAULT_FROM_ADDRESS
              value: "my@email.com"
            - name: DEFAULT_FROM_NAME
              value: "Lucas Santos"
          ports:
            - name: http
              containerPort: 3000
              protocol: TCP
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 250m
              memory: 256Mi

Se eu quiser usar esse modelo em um pipeline de integração contínua (CI) ou publicá-lo no meu GitHub, precisarei substituir as partes variáveis por marcadores de posição. Assim, podemos substituir esses textos pelas informações necessárias.

Nesse caso, tanto a tag da versão, a etiqueta de ambiente e as variáveis de ambiente seriam substituídas por marcadores de posição, como mostrado abaixo:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: zaqar
  namespace: default
  labels:
    app: zaqar
    version: #!VERSION!#
    env: #!ENV!#
spec:
  replicas: 1
  selector:
    matchLabels:
      app: zaqar
      env: #!ENV!#
  template:
    metadata:
      labels:
        app: zaqar
        version: #!VERSION!#
        env: #!ENV!#
    spec:
      containers:
        - name: zaqar
          image: "khaosdoctor/zaqar:#!VERSION!#"
          imagePullPolicy: IfNotPresent
          env:
            - name: SENDGRID_APIKEY
              value: "#!SENDGRID_KEY!#"
            - name: DEFAULT_FROM_ADDRESS
              value: "#!FROM_ADDR!#"
            - name: DEFAULT_FROM_NAME
              value: "#!FROM_NAME!#"
          ports:
            - name: http
              containerPort: 3000
              protocol: TCP
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 250m
              memory: 256Mi

Agora podemos executar nosso pipeline de CI. Antes de fazer isso, contudo, precisamos substituir nossos marcadores de posição pelos valores reais.

Para isso, podemos usar o sed e sua sintaxe "super fácil"  sed  s/#!PLACEHOLDER!#/substituicao/g e aplicá-la até que tenhamos substituído todos os marcadores de posição. O comando final seria algo assim:

cat deploy.yaml | \
    sed 's/#!ENV!#/production/g' | \
    sed 's/#!VERSION!#/v1.0.0/g' | \
    sed 's/#!SENDGRID_KEY!#/MyKey/g' | \
    sed 's/#!FROM_ADDR!#/my@email.com/g' | \
    sed 's/#!FROM_NAME!#/Lucas Santos/g'

Por padrão, o sed imprime tudo no stdout. Então, podemos adicionar outro I para o kubectl -f, como <todo o comando anterior> | kubectl -f -. Desse modo, teremos nossa implantação no lugar. O único problema é que precisamos fazer o mesmo para todos os outros arquivos.

Agora, imagine um projeto maior, com muitas outras variáveis e marcadores de posição. Você provavelmente escreveria um script para fazer isso por você, não é mesmo? Esse script é o Helm.

Quando você cria um Chart (mais sobre isso posteriormente), temos uma árvore de diretórios específica que devemos seguir para que o Helm entenda o que queremos fazer. Dentro do diretório de modelos templates, podemos adicionar nossos arquivos de manifesto, com go templating nativo, assim

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.name }}
  namespace: {{ default .Release.Namespace .Values.namespace }}
  labels:
    app: {{ .Values.name }}
    version: {{ .Values.image.tag }}
    env: {{ .Values.env }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Values.name }}
      env: {{ .Values.env }}
  template:
    metadata:
      labels:
        app: {{ .Values.name }}
        version: {{ .Values.image.tag }}
        env: {{ .Values.env }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "khaosdoctor/zaqar:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          env:
            - name: SENDGRID_APIKEY
              value: {{ required "You must set a valid Sendgrid API key" .Values.environment.SENDGRID_APIKEY | quote }}
            - name: DEFAULT_FROM_ADDRESS
              value: {{ required "You must set a default from address" .Values.environment.DEFAULT_FROM_ADDRESS | quote }}
            - name: DEFAULT_FROM_NAME
              value: {{ required "You must set a default from name" .Values.environment.DEFAULT_FROM_NAME | quote }}
          ports:
            - name: http
              containerPort: 3000
              protocol: TCP
          resources:
            {{- toYaml .Values.resources | nindent 12 }}

Todos esses valores podem ser obtidos de um arquivo Values.yaml (para valores padrão), ou você pode defini-los na linha de comando usando a flag --set <caminho> valor.

Se queremos instalar nosso chart, podemos emitir o seguinte comando

helm upgrade --install --create-namespace myChart ./path/to/my/chart \
  --set image.tag=v1.0.0 \
  --set env=production \
  --set environment.SENDGRID_APIKEY=myKey \
  --set environment.DEFAULT_FROM_ADDRESS="my@email.com" \
  --set environment.DEFAULT_FROM_NAME="Lucas Santos"

O Helm também nos permite usar funções dentro de nossos deployments. Assim, podemos ter funções padrões para recorrer a valores padrões se não forem preenchidos, como o namespace. Também podemos ter a função required, que exibe uma mensagem e falha na instalação do Chart se o valor não for fornecido, como é o caso de nossas variáveis de ambiente.

Há muitas outras funções úteis na documentação deles – confira.

Agora, somos capazes de gerenciar não apenas de modo mais eficiente os recursos de nossa aplicação, mas também de publicar esses recursos em um sistema de controle de versão de código aberto sem nenhum problema ou questão de segurança.

Como criar um Helm Chart

É bem fácil criar um chart no Helm. Primeiro, você precisa ter o Helm instalado. Em seguida, basta digitar helm create <nome do chart> e ele criará um diretório preenchido com arquivos e outros diretórios. Esses arquivos são necessários para que o Helm crie um chart.

Vamos dar uma olhada mais detalhada em como é essa árvore de diretórios e quais são os arquivos dentro dela:

  • chart.yaml: aqui é onde você colocará as informações relacionadas ao seu chart. Isso inclui a versão do chart, o nome e a descrição, para que você possa encontrá-lo se o publicar em um repositório aberto. Nesse arquivo, você também poderá definir dependências externas usando a chave dependencies (documentação em inglês).
  • values.yaml: como vimos antes, este é o arquivo que contém os valores padrão para as variáveis.
  • templates (dir): esse é o local onde você colocará todos os seus arquivos de manifesto. Tudo aqui será passado e criado no Kubernetes.
  • charts: se o seu chart depender de outro chart que você possui, ou se você não quiser depender da biblioteca padrão do Helm (o registro padrão de onde o Helm baixa os charts), você pode trazer essa mesma estrutura dentro desse diretório. As dependências do chart são instaladas de baixo para cima, o que significa que se o chart A depende do chart B, e B depende do C, a ordem de instalação será C -> B -> A.

Há outros campos, mas estes são os mais comuns e são os obrigatórios. Você pode dar uma olhada rápida no repositório do Zaqar para ver como podemos publicar charts de código aberto.

Um aviso rápido: ao instalar o Helm, certifique-se de estar instalando a versão 3. A versão 2 ainda funciona, mas precisa de um componente do lado do servidor chamado Tiller, que vincula sua instalação do Helm a um único cluster. O Helm 3 removeu essa necessidade com a adição de vários CRDs, mas não é suportado em todas as versões do Kubernetes.

Como hospedar um Helm Chart

Ok, você criou seu chart, e agora? Precisamos baixar o repositório inteiro para instalar esses charts? Não! O Helm tem uma biblioteca pública para os charts mais usados, que funciona de modo semelhante ao Docker Hub.

Você também pode criar seu próprio repositório de charts e hospedá-lo on-line (documentação em inglês). O Helm bebe da mesma fonte que o HomeBrew, ou o Linux. Você pode acessar esses repositórios para baixar os charts contidos neles.

Uma vez que um repositório de charts é basicamente um arquivo index.yaml servido a partir de um servidor da web estático, você pode praticamente criar um repositório de charts de qualquer lugar.

Vamos pegar o Zaqar, por exemplo – ele está hospedado no GitHub Pages e é acessível por meio do meu domínio. Quando o Helm procura por um arquivo index.yaml, na verdade está procurando a lista de versões disponíveis desse chart, seus resumos SHA256 e a localização do arquivo empacotado .tgz para baixar o próprio chart. Isso é mais ou menos o que o NPM faz nos bastidores (de modo bastante simplificado).

Isso significa que você não precisa ter seu repositório clonado para sempre, e seus charts podem ser privados também. Você só precisa criar um repositório de charts.

Você pode até mesmo usar serviços hospedados como o Azure CR para fazer o trabalho, ou você pode ter uma solução completa chamada Chart Museum, que permite armazenar seus charts e oferece uma interface de usuário organizada.

Conclusão

O Helm veio para ficar. Ele ajudou e continuará a ajudar muitos desenvolvedores do Kubernetes por muito tempo.

Se você quer saber como usar o Helm, pode consultar a documentação deles, ou pode fazer este módulo de aprendizagem gratuito sobre como implantar suas aplicações no Kubernetes de maneira fácil com o Helm.