Descomplicando Helm
O que iremos ver hoje?
Hoje é dia de falar sobre uma peça super importante quando estamos falando sobre como gerenciar aplicações no Kubernetes. O Helm é um gerenciador de pacotes para Kubernetes que facilita a definição, instalação e atualização de aplicações complexas no Kubernetes.
Durante o dia de hoje, nós iremos descomplicar de uma vez por todas as suas dúvidas no momento de utilizar o Helm para gerencia de aplicações no Kubernetes.
Bora!
O que é o Helm?
O Helm é um gerenciador de pacotes para Kubernetes que facilita a definição, instalação e atualização de aplicações complexas no Kubernetes. Com ele você pode definir no detalhe como a sua aplicação será instalada, quais configurações serão utilizadas e como será feita a atualização da aplicação.
Mas para que você possa utilizar o Helm, a primeira coisa é fazer a sua instalação. Vamos ver como realizar a instalação do Helm na sua máquina Linux.
Lembrando que o Helm é um projeto da CNCF e é mantido pela comunidade, ele funciona em máquinas Linux, Windows e MacOS.
Para realizar a instalação no Linux, podemos utilizar diferentes formas, como baixar o binário e instalar manualmente, utilizar o gerenciador de pacotes da sua distribuição ou utilizar o script de instalação preparado pela comunidade.
Vamos ver como realizar a instalação do Helm no Linux utilizando o script de instalação, mas fique a vontade de utilizar a forma que você achar mais confortável, e tire todas as suas dúvidas no site da documentação oficial do Helm.
Para fazer a instalação, vamos fazer o seguinte:
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh
Com o comando acima, você irá baixar o script de instalação utilizando o curl, dar permissão de execução para o script e executar o script para realizar a instalação do Helm na sua máquina.
A saída será algo assim:
Downloading https://get.helm.sh/helm-v3.14.0-linux-amd64.tar.gz
Verifying checksum... Done.
Preparing to install helm into /usr/local/bin
helm installed into /usr/local/bin/helm
Vamos ver se a instalação foi realizada com sucesso, executando o comando helm version
:
helm version
No meu caso, no momento de criação desse material eu tenho a seguinte saída:
version.BuildInfo{Version:"v3.14.0", GitCommit:"3fc9f4b2638e76f26739cd77c7017139be81d0ea", GitTreeState:"clean", GoVersion:"go1.21.5"}
Pronto, agora que já temos o Helm instalado, já podemos começar a nossa brincadeira.
Durante o dia de hoje, eu quero ensinar o Helm de uma maneira diferente do que estamos acostumados a ver por aí. Vamos focar inicialmente na criação de Charts, e depois vamos ver como instalar e atualizar aplicações utilizando o Helm, de uma maneira mais natural e prática.
O que é um Chart?
Um Chart é um pacote que contém informações necessárias para criar instâncias de aplicações Kubernetes. É com ele que iremos definir como a nossa aplicação será instalada, quais configurações serão utilizadas e como será feita a atualização da aplicação.
Um Chart, normalmente é composto por um conjunto de arquivos que definem a aplicação, e um conjunto de templates que definem como a aplicação será instalada no Kubernetes.
Vamos parar de falar e vamos criar o nosso primeiro Chart, acho que ficará mais fácil de entender.
Criando o nosso primeiro Chart
Para o nosso exemplo, vamos usar novamente a aplicação de exemplo chamada Giropops-Senhas, que é uma aplicação que gera senhas aleatórias que criamos durante uma live no canal da LINUXtips.
Ela é uma aplicação simples, é uma aplicação em Python, mas especificamente uma aplicação Flask, que gera senhas aleatórias e exibe na tela. Ela utiliza um Redis para armazenar temporariamente as senhas geradas.
Simples como voar!
A primeira coisa que temos que fazer é clonar o repositório da aplicação, para isso, execute o comando abaixo:
git clone git@github.com:badtuxx/giropops-senhas.git
Com isso temos um diretório chamado giropops-senhas
com o código da aplicação, vamos acessa-lo:
cd giropops-senhas
O conteúdo do diretório é o seguinte:
app.py LICENSE requirements.txt static tailwind.config.js templates
Pronto, o nosso repo já está clonado, agora vamos começar com o nosso Chart.
A primeira coisa que iremos fazer, somente para facilitar o nosso entendimento, é criar os manifestos do Kubernetes para a nossa aplicação. Vamos criar um Deployment e um Service para o Giropops-Senhas e para o Redis.
Vamos começar com o Deployment do Redis, para isso, crie um arquivo chamado redis-deployment.yaml
com o seguinte conteúdo:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: redis
name: redis-deployment
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- image: redis
name: redis
ports:
- containerPort: 6379
resources:
limits:
memory: "256Mi"
cpu: "500m"
requests:
memory: "128Mi"
cpu: "250m"
Agora vamos criar o Service do Redis, para isso, crie um arquivo chamado redis-service.yaml
com o seguinte conteúdo:
apiVersion: v1
kind: Service
metadata:
name: redis-service
spec:
selector:
app: redis
ports:
- protocol: TCP
port: 6379
targetPort: 6379
type: ClusterIP
Agora vamos criar o Deployment do Giropops-Senhas, para isso, crie um arquivo chamado giropops-senhas-deployment.yaml
com o seguinte conteúdo:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: giropops-senhas
name: giropops-senhas
spec:
replicas: 2
selector:
matchLabels:
app: giropops-senhas
template:
metadata:
labels:
app: giropops-senhas
spec:
containers:
- image: linuxtips/giropops-senhas:1.0
name: giropops-senhas
ports:
- containerPort: 5000
imagePullPolicy: Always
resources:
limits:
memory: "256Mi"
cpu: "500m"
requests:
memory: "128Mi"
cpu: "250m"
E finalmente, vamos criar o Service do Giropops-Senhas, para isso, crie um arquivo chamado giropops-senhas-service.yaml
com o seguinte conteúdo:
apiVersion: v1
kind: Service
metadata:
name: giropops-senhas
labels:
app: giropops-senhas
spec:
selector:
app: giropops-senhas
ports:
- protocol: TCP
port: 5000
nodePort: 32500
targetPort: 5000
name: tcp-app
type: NodePort
Manifesos criados! Perceba que não temos nada de novo até agora, somente criamos os manifestos do Kubernetes para a nossa aplicação como já fizemos anteriormente.
Mas o pq eu fiz isso? Simples, para que você possa entender que um Chart é basicamente isso, um conjunto de manifestos do Kubernetes que definem como a sua aplicação será instalada no Kubernetes.
E você deve estar falando: Esse Jeferson está de brincadeira, pois eu já sei como fazer isso, cadê a novidade? Cadê o Helm? Calma calabrezo! 😄
Bem, a ideia de criar os manifestos é somente para nos guiar durante a criação do nosso Chart.
Com os arquivos para nos ajudar, vamos criar o nosso Chart.
Para criar o nosso Chart, poderiamos utilizar o comando helm create
, mas eu quero fazer de uma maneira diferente, quero criar o nosso Chart na mão, para que você possa entender como ele é composto, e depois voltamos para o helm create
para criar os nossos próximos Charts.
Bem, a primeira coisa que temos que fazer é criar um diretório para o nosso Chart, vamos criar um diretório chamado giropops-senhas-chart
:
mkdir giropops-senhas-chart
Agora vamos acessar o diretório:
cd giropops-senhas-chart
Bem, agora vamos começar a criar a nossa estrutura de diretórios para o nosso Chart, e o primeiro cara que iremos criar é o Chart.yaml, que é o arquivo que contém as informações sobre o nosso Chart, como o nome, a versão, a descrição, etc.
Vamos criar o arquivo Chart.yaml com o seguinte conteúdo:
apiVersion: v2
name: giropops-senhas
description: Esse é o chart do Giropops-Senhas, utilizados nos laboratórios de Kubernetes.
version: 0.1.0
appVersion: 0.1.0
sources:
- https://github.com/badtuxx/giropops-senhas
Nada de novo até aqui, somente criamos o arquivo Chart.yaml com as informações sobre o nosso Chart. Agora vamos para o nosso próximo passo, criar o diretório templates
que é onde ficarão os nossos manifestos do Kubernetes.
Vamos criar o diretório templates
:
mkdir templates
Vamos mover os manifestos que criamos anteriormente para o diretório templates
:
mv ../redis-deployment.yaml templates/
mv ../redis-service.yaml templates/
mv ../giropops-senhas-deployment.yaml templates/
mv ../giropops-senhas-service.yaml templates/
Vamos deixar eles quietinhos lá por enquanto, e vamos criar o próximo arquivo que é o values.yaml
. Esse é uma peça super importante do nosso Chart, pois é nele que iremos definir as variáveis que serão utilizadas nos nossos manifestos do Kubernetes, é nele que o Helm irá se basear para criar os manifestos do Kubernetes, ou melhor, para renderizar os manifestos do Kubernetes.
Quando criamos os manifestos para a nossa App, nós deixamos ele da mesma forma como usamos para criar os manifestos do Kubernetes, mas agora, com o Helm, nós podemos utilizar variáveis para definir os valores que serão utilizados nos manifestos, e é isso que iremos fazer, e é isso que é uma das mágicas do Helm.
Vamos criar o arquivo values.yaml
com o seguinte conteúdo:
giropops-senhas:
name: "giropops-senhas"
image: "linuxtips/giropops-senhas:1.0"
replicas: "3"
port: 5000
labels:
app: "giropops-senhas"
env: "labs"
live: "true"
service:
type: "NodePort"
port: 5000
targetPort: 5000
name: "giropops-senhas-port"
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "500m"
redis:
image: "redis"
replicas: 1
port: 6379
labels:
app: "redis"
env: "labs"
live: "true"
service:
type: "ClusterIP"
port: 6379
targetPort: 6379
name: "redis-port"
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "500m"
Não confunda o arquivo acima com os manifestos do Kubernetes, o arquivo acima é apenas algumas definições que iremos usar no lugar das variáveis que defineremos nos manifestos do Kubernetes.
Precisamos entender como ler o arquivo acima, e é bem simples, o arquivo acima é um arquivo YAML, e nele temos duas chaves, giropops-senhas
e redis
, e dentro de cada chave temos as definições que iremos utilizar, por exemplo:
giropops-senhas
:image
: A imagem que iremos utilizar para o nosso Deploymentreplicas
: A quantidade de réplicas que iremos utilizar para o nosso Deploymentport
: A porta que iremos utilizar para o nosso Servicelabels
: As labels que iremos utilizar para o nosso Deploymentservice
: As definições que iremos utilizar para o nosso Serviceresources
: As definições de recursos que iremos utilizar para o nosso Deployment
redis
:image
: A imagem que iremos utilizar para o nosso Deploymentreplicas
: A quantidade de réplicas que iremos utilizar para o nosso Deploymentport
: A porta que iremos utilizar para o nosso Servicelabels
: As labels que iremos utilizar para o nosso Deploymentservice
: As definições que iremos utilizar para o nosso Serviceresources
: As definições de recursos que iremos utilizar para o nosso Deployment
E nesse caso, caso eu queira usar o valor que está definido para image
, eu posso utilizar a variável no meu manifesto do Kubernetes, onde:
- : É a variável que o Helm utiliza para acessar as variáveis que estão definidas no arquivo
values.yaml
, e o resto é a chave que estamos acessando.
Entendeu? Eu sei que é meu confuso no começo, mas treinando irá ficar mais fácil.
Vamos fazer um teste rápido, como eu vejo o valor da porta que está definida para o Service do Redis?
Pensou?
Já sabemos que temos que começar com .Values, para representar o arquivo values.yaml
, e depois temos que acessar a chave redis
, e depois a chave service
, e depois a chave port
, então, o valor que está definido para a porta que iremos utilizar para o Service do Redis é .
Sempre você tem que respeitar a indentação do arquivo values.yaml
, pois é ela que define como você irá acessar as chaves, certo?
Dito isso, já podemos começar a substituir os valores do que está definido nos manifestos do Kubernetes pelos valores que estão definidos no arquivo values.yaml
. Iremos sair da forma estática para a forma dinâmica, é o Helm em ação!
Vamos começar com o arquivo redis-deployment.yaml
, e vamos substituir o que está definido por variáveis, e para isso, vamos utilizar o seguinte conteúdo:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: redis
name: redis-deployment
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- image: {{ .Values.redis.image }}
name: redis
ports:
- containerPort: {{ .Values.redis.port }}
resources:
limits:
memory: {{ .Values.redis.resources.limits.memory }}
cpu: {{ .Values.redis.resources.limits.cpu }}
requests:
memory: {{ .Values.redis.resources.requests.memory }}
cpu: {{ .Values.redis.resources.requests.cpu }}
Veja que estamos usando e abusando das variáveis que estão definidas no arquivo values.yaml
, agora vou explicar o que estamos fazendo:
- `image`: Estamos utilizando a variável `{{ .Values.redis.image }}` para definir a imagem que iremos utilizar para o nosso Deployment
- `name`: Estamos utilizando a variável `{{ .Values.redis.name }}` para definir o nome que iremos utilizar para o nosso Deployment
- `replicas`: Estamos utilizando a variável `{{ .Values.redis.replicas }}` para definir a quantidade de réplicas que iremos utilizar para o nosso Deployment
- `resources`: Estamos utilizando as variáveis `{{ .Values.redis.resources.limits.memory }}`, `{{ .Values.redis.resources.limits.cpu }}`, `{{ .Values.redis.resources.requests.memory }}` e `{{ .Values.redis.resources.requests.cpu }}` para definir as definições de recursos que iremos utilizar para o nosso Deployment.
Com isso, ele irá utilizar os valores que estão definidos no arquivo values.yaml
para renderizar o nosso manifesto do Kubernetes, logo, quando precisar alterar alguma configuração, basta alterar o arquivo values.yaml
, e o Helm irá renderizar os manifestos do Kubernetes com os valores definidos.
Vamos fazer o mesmo para os outros manifestos do Kubernetes, e depois vamos ver como instalar a nossa aplicação utilizando o Helm.
Vamos fazer o mesmo para o arquivo redis-service.yaml
:
apiVersion: v1
kind: Service
metadata:
name: redis-service
spec:
selector:
app: redis
ports:
- protocol: TCP
port: {{ .Values.redis.service.port }}
targetPort: {{ .Values.redis.service.port }}
type: {{ .Values.redis.service.type }}
E para o arquivo giropops-senhas-deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: giropops-senhas
name: giropops-senhas
spec:
replicas: {{ .Values.giropops-senhas.replicas }}
selector:
matchLabels:
app: giropops-senhas
template:
metadata:
labels:
app: giropops-senhas
spec:
containers:
- image: {{ .Values.giropops-senhas.image }}
name: giropops-senhas
ports:
- containerPort: {{ .Values.giropops-senhas.service.port }}
imagePullPolicy: Always
resources:
limits:
memory: {{ .Values.giropops-senhas.resources.limits.memory }}
cpu: {{ .Values.giropops-senhas.resources.limits.cpu }}
requests:
memory: {{ .Values.giropops-senhas.resources.requests.memory }}
cpu: {{ .Values.giropops-senhas.resources.requests.cpu }}
E para o arquivo giropops-senhas-service.yaml
:
apiVersion: v1
kind: Service
metadata:
name: giropops-senhas
labels:
app: giropops-senhas
spec:
selector:
app: giropops-senhas
ports:
- protocol: TCP
port: {{ .Values.giropops-senhas.service.port }}
nodePort: {{ .Values.giropops-senhas.service.nodePort }}
targetPort: {{ .Values.giropops-senhas.service.port }}
type: {{ .Values.giropops-senhas.service.type }}
Agora já temos todos os nosso manifestos mais dinâmicos, e portanto já podemos chama-los de Templates, que é o nome que o Helm utiliza para os manifestos do Kubernetes que são renderizados utilizando as variáveis.
Ahhh, temos que criar um diretório chamado charts
para que o Helm possa gerenciar as dependências do nosso Chart, mas como não temos dependências, podemos criar um diretório vazio, vamos fazer isso:
mkdir charts
Pronto, já temos o nosso Chart criado!
Agora vamos testa-lo para ver se tudo está funcionando como esperamos.
Instalando o nosso Chart
Para que possamos utilizar o nosso Chart, precisamos utilizar o comando helm install
, que é o comando que utilizamos para instalar um Chart no Kubernetes.
Vamos instalar o nosso Chart, para isso, execute o comando abaixo:
helm install giropops-senhas ./
Se tudo ocorrer bem, você verá a seguinte saída:
NAME: giropops-senhas
LAST DEPLOYED: Thu Feb 8 16:37:58 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
O nosso Chart foi instalado com sucesso!
Vamos listar os Pods para ver se eles estão rodando, execute o comando abaixo:
kubectl get pods
A saída será algo assim:
NAME READY STATUS RESTARTS AGE
giropops-senhas-7d4fddc49f-9zfj9 1/1 Running 0 42s
giropops-senhas-7d4fddc49f-dn996 1/1 Running 0 42s
giropops-senhas-7d4fddc49f-fpvh6 1/1 Running 0 42s
redis-deployment-76c5cdb57b-wf87q 1/1 Running 0 42s
Perceba que temos 3 Pods rodando para a nossa aplicação Giropops-Senhas, e 1 Pod rodando para o Redis, conforme definimos no arquivo values.yaml
.
Agora vamos listar os Charts que estão instalados no nosso Kubernetes, para isso, execute o comando abaixo:
helm list
Se você quiser ver de alguma namespace específica, você pode utilizar o comando helm list -n <namespace>
, mas no nosso caso, como estamos utilizando a namespace default, não precisamos especificar a namespace.
Para ver mais detalhes do Chart que instalamos, você pode utilizar o comando helm get
, para isso, execute o comando abaixo:
helm get all giropops-senhas
A saída será os detalhes do Chart e os manifestos que foram renderizados pelo Helm.
Vamos fazer uma alteração no nosso Chart, e vamos ver como atualizar a nossa aplicação utilizando o Helm.
Atualizando o nosso Chart
Vamos fazer uma alteração no nosso Chart, e vamos ver como atualizar a nossa aplicação utilizando o Helm.
Vamos editar o values.yaml
e alterar a quantidade de réplicas que estamos utilizando para a nossa aplicação Giropops-Senhas, para isso, edite o arquivo values.yaml
e altere a quantidade de réplicas para 5:
giropops-senhas:
name: "giropops-senhas"
image: "linuxtips/giropops-senhas:1.0"
replicas: "5"
port: 5000
labels:
app: "giropops-senhas"
env: "labs"
live: "true"
service:
type: "NodePort"
port: 5000
targetPort: 5000
name: "giropops-senhas-port"
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "500m"
redis:
image: "redis"
replicas: 1
port: 6379
labels:
app: "redis"
env: "labs"
live: "true"
service:
type: "ClusterIP"
port: 6379
targetPort: 6379
name: "redis-port"
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "500m"
A única coisa que alteramos foi a quantidade de réplicas que estamos utilizando para a nossa aplicação Giropops-Senhas, de 3 para 5.
Vamos agora pedir para o Helm atualizar a nossa aplicação, para isso, execute o comando abaixo:
helm upgrade giropops-senhas ./
Se tudo ocorrer bem, você verá a seguinte saída:
Release "giropops-senhas" has been upgraded. Happy Helming!
NAME: giropops-senhas
LAST DEPLOYED: Thu Feb 8 16:46:26 2024
NAMESPACE: default
STATUS: deployed
REVISION: 2
TEST SUITE: None
Agora vamos ver se o número de réplicas foi alterado, para isso, execute o comando abaixo:
giropops-senhas-7d4fddc49f-9zfj9 1/1 Running 0 82s
giropops-senhas-7d4fddc49f-dn996 1/1 Running 0 82s
giropops-senhas-7d4fddc49f-fpvh6 1/1 Running 0 82s
redis-deployment-76c5cdb57b-wf87q 1/1 Running 0 82s
giropops-senhas-7d4fddc49f-ll25z 1/1 Running 0 18s
giropops-senhas-7d4fddc49f-w8p7r 1/1 Running 0 18s
Agora temos mais dois Pods em execução, da mesma forma que definimos no arquivo values.yaml
.
Agora vamos remover a nossa aplicação:
helm uninstall giropops-senhas
A saída será algo assim:
release "giropops-senhas" uninstalled
Já era, a nossa aplicação foi removida com sucesso!
Como eu falei, nesse caso criamos tudo na mão, mas eu poderia ter usado o comando helm create
para criar o nosso Chart, e ele teria criado a estrutura de diretórios e os arquivos que precisamos para o nosso Chart, e depois teríamos que fazer as alterações que fizemos manualmente.
A estrutura de diretórios que o helm create
cria é a seguinte:
giropops-senhas-chart/
├── charts
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── NOTES.txt
│ ├── serviceaccount.yaml
│ ├── service.yaml
│ └── tests
│ └── test-connection.yaml
└── values.yaml
Eu não criei dessa forma, pois acho que iria complicar um pouco o nosso entendimento inicial, pois ele iria criar mais coisas que iriamos utilizar, mas em contrapartida, podemos utiliza-lo para nos basear para os nossos próximos Charts.
Ele é o nosso amigo, e sim, ele pode nos ajudar! hahaha
Agora eu preciso que você pratique o máximo possível, brincando com as diversas opções que temos disponíveis no Helm, e o mais importante, use e abuse da documentação oficial do Helm, ela é muito rica e tem muitos exemplos que podem te ajudar.
Bora deixar o nosso Chart ainda mais legal?
Utilizando range
e o if
no Helm
O Helm tem uma funcionalidade muito legal que é o range
, que é uma estrutura de controle que nos permite iterar sobre uma lista de itens, e isso é muito útil quando estamos trabalhando com listas de itens, como por exemplo, quando estamos trabalhando com os manifestos do Kubernetes.
Para que você consiga utilizar o range
, precisamos antes entender sua estrutura básica, por exemplo, temos um arquivo com 4 frutas, e queremos listar todas elas, como eu faria isso?
Primeiro, vamos criar um arquivo chamado frutas.yaml
com o seguinte conteúdo:
frutas:
- banana
- maçã
- uva
- morango
Agora vamos pegar fruta por fruta, e colocando a seguinte frase antes de cada uma delas: "Eu gosto de".
Para isso, vamos criar um arquivo chamado eu-gosto-frutas.yaml
com o seguinte conteúdo:
{{- range .Values.frutas }}
Eu gosto de {{ . }}
{{- end }}
O resultado será:
Eu gosto de banana
Eu gosto de maçã
Eu gosto de uva
Eu gosto de morango
Ficou fácil, certo?
O range
percorreu toda a lista e ainda adicionou a frase que queríamos.
Vamos imaginar que eu tenho uma lista de portas que eu quero expor para a minha aplicação, e eu quero criar um Service para cada porta que eu tenho na minha lista, como eu faria isso?
Por exemplo, a nossa aplicação Giropops-Senhas, ela tem 2 portas que ela expõe, a porta 5000 e a porta 8088. A porta 5000 é a porta que a aplicação escuta, e a porta 8088 é a porta que a aplicação expõe as métricas para o Prometheus.
Outra função super interessante e que é muito útil é o if
, que é uma estrutura de controle que nos permite fazer uma verificação se uma condição é verdadeira ou falsa, e baseado nisso, podemos fazer alguma coisa ou não.
a Estrutura básica do if
é a seguinte:
{{- if eq .Values.giropopsSenhas.service.type "NodePort" }}
nodePort: {{ .Values.giropopsSenhas.service.nodePort }}
targetPort: {{ .Values.giropopsSenhas.service.targetPort }}
{{- else }}
targetPort: {{ .Values.giropopsSenhas.service.targetPort }}
{{- end }}
Onde:
- `{{- if eq .Values.giropopsSenhas.service.type "NodePort" }}`: Verifica se o valor que está definido para a chave `type` é igual a `NodePort`
- `nodePort: {{ .Values.giropopsSenhas.service.nodePort }}`: Se a condição for verdadeira, ele irá renderizar o valor que está definido para a chave `nodePort`
- `targetPort: {{ .Values.giropopsSenhas.service.targetPort }}`: Se a condição for verdadeira, ele irá renderizar o valor que está definido para a chave `targetPort`
- `{{- else }}`: Se a condição for falsa, ele irá renderizar o valor que está definido para a chave `targetPort`
- `{{- end }}`: Finaliza a estrutura de controle
Simples como voar! Bora lá utilizar essas duas fun´ções para deixar o nosso Chart ainda mais legal.
Vamos começar criando um arquivo chamado giropops-senhas-service.yaml
com o seguinte conteúdo:
{{- range .Values.giropops-senhas.ports }}
apiVersion: v1
kind: Service
metadata:
name: {{ .name }}
labels:
app: {{ .name }}
spec:
type: {{ .serviceType }}
ports:
- name: {{ .name }}
port: {{ .port }}
{{- if eq .serviceType "NodePort" }}
nodePort: {{ .NodePort }}
{{- end }}
targetPort: {{ .targetPort }}
selector:
app: giropops-senhas
---
{{- end }}
No arquivo acima, estamos utilizando a função range
para iterar sobre a lista de portas que queremos expor para a nossa aplicação, e estamos utilizando a função if
para verificar se a porta que estamos expondo é do tipo NodePort
, e baseado nisso, estamos renderizando o valor que está definido para a chave nodePort
.
Agora vamos alterar o arquivo values.yaml
e adicionar a lista de portas que queremos expor para a nossa aplicação, para isso, edite o arquivo values.yaml
e adicione a lista de portas que queremos expor para a nossa aplicação:
giropops-senhas:
name: "giropops-senhas"
image: "linuxtips/giropops-senhas:1.0"
replicas: "3"
ports:
- port: 5000
targetPort: 5000
name: "giropops-senhas-port"
serviceType: NodePort
NodePort: 32500
- port: 8088
targetPort: 8088
name: "giropops-senhas-metrics"
serviceType: ClusterIP
labels:
app: "giropops-senhas"
env: "labs"
live: "true"
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "500m"
redis:
image: "redis"
replicas: 1
port: 6379
labels:
app: "redis"
env: "labs"
live: "true"
service:
type: "ClusterIP"
port: 6379
targetPort: 6379
name: "redis-port"
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "500m"
Temos algumas coisas novas no arquivo values.yaml
. O objetivo da mudança é deixar o arquivo ainda mais dinâmico, e para isso, adicionamos adicionamos mais informações sobre as portas que iremos utilizar. Deixamos as informações mais organizadas para facilitar a dinâmica criada no arquivo giropops-senhas-service.yaml
.
Poderiamos ainda criar um único template para realizar o deploy do Redis e do Giropops-Senhas, somente para que possamos gastar um pouquinho mais do nosso conhecimento, ou seja, isso aqui é muito mais para fins didáticos do que para a vida real, mas vamos lá, vamos criar um arquivo chamado giropops-senhas-deployment.yaml
com o seguinte conteúdo:
{{- range $component, $config := .Values.deployments }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $component }}
labels:
app: {{ $config.labels.app }}
spec:
replicas: {{ $config.replicas }}
selector:
matchLabels:
app: {{ $config.labels.app }}
template:
metadata:
labels:
app: {{ $config.labels.app }}
spec:
containers:
- name: {{ $component }}
image: {{ $config.image }}
ports:
{{- range $config.ports }}
- containerPort: {{ .port }}
{{- end }}
resources:
requests:
memory: {{ $config.resources.requests.memory }}
cpu: {{ $config.resources.requests.cpu }}
limits:
memory: {{ $config.resources.limits.memory }}
cpu: {{ $config.resources.limits.cpu }}
{{- if $config.env }}
env:
{{- range $config.env }}
- name: {{ .name }}
value: {{ .value }}
{{- end }}
{{- end }}
---
{{- end }}
Estamos utilizando a função range
logo no inicio do arquivo, e com ele estamos interando sobre a lista de componentes que temos no nosso arquivo values.yaml
, ou seja, o Redis e o Giropops-Senhas. Mas também estamos utilizando o range
para interar sobre a lista de outras configurações que temos no nosso arquivo values.yaml
, como por exemplo, as portas que queremos expor para a nossa aplicação e o limite de recursos que queremos utilizar. Ele definiu duas variáveis, $component
e $config
, onde $component
é o nome do componente que estamos interando, e $config
é a configuração que estamos interando, fácil!
Agora vamos instalar a nossa aplicação com o comando abaixo:
helm install giropops-senhas ./
A saída será algo assim:
Error: INSTALLATION FAILED: parse error at (giropops-senhas/templates/services.yaml:1): bad character U+002D '-'
Parece que alguma coisa de errado não está certo, certo? hahaha
O que aconteceu foi o seguinte:
Quando estamos utilizando o nome do componente com um hífen, e tentamos passar na utilização do range
, o Helm não entende que aquele é o nome do recurso que estamos utilizando, e retorna o erro de bad character U+002D '-'
.
Para resolver isso, vamos utilizar mais uma função do Helm, que é a função index
.
A função index
nos permite acessar um valor de um mapa baseado na chave que estamos passando, nesse caso seria o .Values
, e ainda buscar um valor baseado na chave que estamos passando, que é o nome do componente que estamos interando. Vamos ver como ficaria o nosso services.yaml
com a utilização da função index
:
{{- range (index .Values "giropops-senhas").ports }}
apiVersion: v1
kind: Service
metadata:
name: {{ .name }}
labels:
app: {{ .name }}
spec:
type: {{ .serviceType }}
ports:
- name: {{ .name }}
port: {{ .port }}
{{- if eq .serviceType "NodePort" }}
nodePort: {{ .NodePort }}
{{- end }}
targetPort: {{ .targetPort }}
selector:
app: giropops-senhas
---
{{- end }}
Pronto, agora acredito que tudo terá um final feliz, para ter certeza, vamos instalar a nossa aplicação com o comando abaixo:
helm install giropops-senhas ./
Se tudo ocorrer bem, você verá a seguinte saída:
NAME: giropops-senhas
LAST DEPLOYED: Sat Feb 10 12:19:27 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
Vamos ver se deu bom!
kubectl get deployment
Temos a saída abaixo:
NAME READY UP-TO-DATE AVAILABLE AGE
giropops-senhas-deployment 3/3 3 3 4m1s
redis-deployment 1/1 1 1 4m1s
Agora os Pods:
kubectl get pods
A saída:
NAME READY STATUS RESTARTS AGE
giropops-senhas-deployment-5c547c9cf-979vp 1/1 Running 0 4m40s
giropops-senhas-deployment-5c547c9cf-s5k9x 1/1 Running 0 4m39s
giropops-senhas-deployment-5c547c9cf-zp4s4 1/1 Running 0 4m39s
redis-deployment-69c5869684-cxslb 1/1 Running 0 4m40s
Vamos ver os Services:
kubectl get svc
Se a sua saída trouxe os dois serviços, com os nomes giropops-senhas-port
e giropops-senhas-metrics
, e com os tipos NodePort
e ClusterIP
, respectivamente, é um sinal de que deu super bom!
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
giropops-senhas-app NodePort 10.96.185.6 <none> 5000:32500/TCP 5m1s
giropops-senhas-metrics ClusterIP 10.96.107.37 <none> 8088/TCP 5m1s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d21h
Parece que deu ruim, certo?
Ficou faltando o Service do Redis. 😕
Vamos resolver, mas antes, vamos mudar um pouco a organização em nosso values.yaml
.
deployments:
giropops-senhas:
name: "giropops-senhas"
image: "linuxtips/giropops-senhas:1.0"
replicas: "3"
labels:
app: "giropops-senhas"
env: "labs"
live: "true"
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "500m"
redis:
image: "redis"
replicas: 1
port: 6379
labels:
app: "redis"
env: "labs"
live: "true"
service:
type: "ClusterIP"
port: 6379
targetPort: 6379
name: "redis-port"
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "500m"
services:
giropops-senhas:
ports:
- port: 5000
targetPort: 5000
name: "giropops-senhas-app"
serviceType: NodePort
NodePort: 32500
- port: 8088
targetPort: 8088
name: "giropops-senhas-metrics"
serviceType: ClusterIP
labels:
app: "giropops-senhas"
env: "labs"
live: "true"
redis:
ports:
- port: 6379
targetPort: 6378
name: "redis-port"
serviceType: ClusterIP
labels:
app: "redis"
env: "labs"
live: "true"
Precisamos agora atualizar os nossos templates para que eles possam utilizar as novas chaves que criamos no arquivo values.yaml
.
Vamos atualizar o services.yaml
para que ele possa utilizar as novas chaves que criamos no arquivo values.yaml
:
{{- range $component, $config := .Values.services }}
{{ range $port := $config.ports }}
apiVersion: v1
kind: Service
metadata:
name: {{ $component }}-{{ $port.name }}
labels:
app: {{ $config.labels.app }}
spec:
type: {{ $port.serviceType }}
ports:
- port: {{ $port.port }}
targetPort: {{ $port.targetPort }}
protocol: TCP
name: {{ $port.name }}
{{ if eq $port.serviceType "NodePort" }}
nodePort: {{ $port.nodePort }}
{{ end }}
selector:
app: {{ $config.labels.app }}
---
{{ end }}
{{- end }}
Adicionamos um novo range
para interar sobre a lista de portas que queremos expor para a nossa aplicação, e utilizamos a função index
para acessar o valor que está definido para a chave services
no arquivo values.yaml
.
Como o nosso deployments.yaml
já está atualizado, não precisamos fazer nenhuma alteração nele, o que precisamos é deployar o nosso Chart novamente e ver se as nossas mondificações funcionaram.
Temos duas opções, ou realizamos o uninstall
e o install
novamente, ou realizamos o upgrade
da nossa aplicação.
Vou realizar o uninstall
e o install
novamente, para isso, execute os comandos abaixo:
helm uninstall giropops-senhas
E agora:
helm install giropops-senhas ./
Caso eu queira fazer o upgrade
, eu poderia utilizar o comando abaixo:
helm upgrade giropops-senhas ./
Pronto, se tudo estiver certinho, temos uma saída parecida com a seguinte:
NAME: giropops-senhas
LAST DEPLOYED: Sat Feb 10 14:05:37 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
Vamos listar os recursos:
kubectl get deployments,pods,svc
Assim ele trará todos os nossos recursos utilizados pela nossa aplicação.
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/giropops-senhas 3/3 3 3 69s
deployment.apps/redis 1/1 1 1 69s
NAME READY STATUS RESTARTS AGE
pod/giropops-senhas-8598bc5699-68sn6 1/1 Running 0 69s
pod/giropops-senhas-8598bc5699-wgnxj 1/1 Running 0 69s
pod/giropops-senhas-8598bc5699-xqssx 1/1 Running 0 69s
pod/redis-69c5869684-62d2h 1/1 Running 0 69s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/giropops-senhas-app NodePort 10.96.119.23 <none> 5000:30032/TCP 69s
service/giropops-senhas-metrics ClusterIP 10.96.110.83 <none> 8088/TCP 69s
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d22h
service/redis-service ClusterIP 10.96.77.187 <none> 6379/TCP 69s
Pronto! Tudo criado com sucesso!
Agora você já sabe como utilizar o range
, index
e o if
no Helm, e já sabe como criar um Chart do zero, e já sabe como instalar, atualizar e remover a sua aplicação utilizando o Helm.
Utilizando default
, toYaml
e toJson
no Helm
Vamos comecer mais algumas funções do Helm, que são o default
, toYaml
e toJson
, que nos ajudarão a deixar o nosso Chart ainda mais dinâmico e customizável.
Suponhamos que queremos garantir que sempre haja um valor padrão para o número de réplicas nos nossos deployments, mesmo que esse valor não tenha sido especificamente definido no values.yaml
. Podemos modificar o nosso giropops-senhas-deployment.yaml
para incluir a função default
:
replicas: {{ .Values.giropopsSenhas.replicas | default 3 }}
Agora vamos adicionar a configuração necessária para a observabilidade da nossa aplicação "Giropops-Senhas", que inclui diversos parâmetros de configuração, e precisamos passá-la como uma string JSON para um ConfigMap. E o toJson
irá nos salvar:
No values.yaml
, adicionamos uma configuração complexa:
observability:
giropops-senhas:
logging: true
metrics:
enabled: true
path: "/metrics"
Agora vamos criar um ConfigMap que inclui essa configuração como uma string JSON:
{{- range $component, $config := .Values.observability }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ $component }}-observability-config
data:
app-config.json: |
{{- end }}
Dessa forma, transformamos a configuração definida no values.yaml
em uma string JSON formatada, que é injetada diretamente no ConfigMap. A função nindent 4
garante que iremos usar com 4 espaços de indentação a cada linha do conteúdo injetado.
{
"logging": true,
"metrics": {
"enabled": true,
"path": "/metrics"
}
}
Fácil!
E por fim, vamos adicionar o endereço de um banco de dados como uma configuração YAML, e precisamos passá-la como uma string YAML para um ConfigMap. E o toYaml
é a função que irá garantir que a configuração seja injetada corretamente:
A configuração no values.yaml
:
databases:
giropops-senhas:
type: "MySQL"
host: "mysql.svc.cluster.local"
port: 3306
name: "MyDB"
Com isso, já podemos criar um ConfigMap que inclui essa configuração como uma string YAML:
{{- range $component, $config := .Values.databases }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ $component }}-db-config
data:
app-config.yaml: |
{{- end }}
Dessa forma, transformamos a configuração definida no values.yaml
em uma string YAML formatada, que é injetada diretamente no ConfigMap. A função nindent 2
garante que o conteúdo injetado esteja corretamente indentado, pois ela adiciona 2 espaços de indentação a cada linha do conteúdo injetado.
Para que possamos aplicar essas modificações, precisamos realizar o upgrade
da nossa aplicação, para isso, execute o comando abaixo:
helm upgrade giropops-senhas ./
Agora já temos além dos deployments e services, também os ConfigMaps para a nossa aplicação.
Para ver os ConfigMaps, execute o comando abaixo:
kubectl get configmaps
Para ver os detalhes de cada ConfigMap, execute o comando abaixo:
kubectl get configmap <configmap-name> -o yaml
Chega a ser lacrimejante de tão lindo! 😄
O Que São Helpers no Helm?
Helpers no Helm são funções definidas em arquivos _helpers.tpl
dentro do diretório templates
de um gráfico Helm. Eles permitem a reutilização de código e lógicas complexas em seus templates, promovendo práticas de codificação DRY (Don't Repeat Yourself). Isso significa que, em vez de repetir o mesmo código em vários lugares, você pode definir uma função helper e chamá-la sempre que precisar.
Por Que Usar Helpers?
- Reutilização de Código: Evita duplicação e facilita a manutenção.
- Abstração de Complexidade: Encapsula lógicas complexas, tornando os templates mais limpos e fáceis de entender.
- Personalização e Flexibilidade: Permite a criação de templates mais dinâmicos e adaptáveis às necessidades específicas do usuário.
Criando o Nosso Primeiro Helper
Para ilustrar a criação e o uso de helpers, vamos começar com um exemplo prático. Imagine que você precisa incluir o nome padrão do seu aplicativo em vários recursos Kubernetes no seu chart Helm. Em vez de escrever manualmente o nome em cada recurso, você pode definir um helper para isso.
- Definindo um Helper:
No diretóriotemplates
, crie um arquivo chamado_helpers.tpl
e adicione o seguinte conteúdo:
{{/*
Define um helper para o nome do aplicativo.
*/}}
{{- define "meuapp.name" -}}
{{- default .Chart.Name .Values.appName | trunc 63 | trimSuffix "-" -}}
{{- end -}}
Esta função define um nome padrão para o seu aplicativo, usando o nome do gráfico (Chart.Name
) ou um nome personalizado definido em Values.appName
, limitando-o a 63 caracteres e removendo quaisquer hífens no final.
- Usando o Helper:
Agora, você pode usar este helper em seus templates para garantir que o nome do aplicativo seja consistente em todos os recursos. Por exemplo, em um template de Deployment, você pode usar:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "meuapp.name" . }}
labels:
app: {{ include "meuapp.name" . }}
À medida que você se familiariza com os helpers, pode começar a explorar usos mais avançados. Aqui estão alguns exemplos que demonstram a flexibilidade e o poder dos helpers no Helm.
Controlando a Complexidade
Imagine que você tenha múltiplos serviços que precisam ser configurados de maneira ligeiramente diferente com base em certos valores de entrada. Você pode criar um helper complexo que gera a configuração apropriada para cada serviço.
{{/*
Gerar configuração específica do serviço.
*/}}
{{- define "meuapp.serviceConfig" -}}
{{- if eq .Values.serviceType "frontend" -}}
# Configuração específica do frontend
{{- else if eq .Values.serviceType "backend" -}}
# Configuração específica do backend
{{- end -}}
{{- end -}}
Exemplo 2: Personalização Baseada em Ambiente
Em ambientes de desenvolvimento, você pode querer configurar seus serviços de maneira diferente do que em produção. Um helper pode ajudar a injetar essas configurações com base no ambiente.
{{/*
Ajustar configurações com base no ambiente.
*/}}
{{- define "meuapp.envConfig" -}}
{{- if eq .Values.environment "prod" -}}
# Configurações de produção
{{- else -}}
# Configurações de desenvolvimento
{{- end
-}}
{{- end -}}
Melhores Práticas ao Usar Helpers
- Mantenha os Helpers Simples: Funções muito complexas podem ser difíceis de manter e entender.
- Nomeie os Helpers de Forma Clara: Os nomes devem refletir o propósito da função para facilitar a compreensão e o uso.
- Documente Seus Helpers: Comentários claros sobre o que cada helper faz ajudam outros desenvolvedores a entender seu código mais rapidamente.
- Use Helpers para Lógicas Recorrentes: Aproveite os helpers para evitar a repetição de lógicas complexas ou padrões comuns em seus templates.
Criando o _helpers.tpl
da nossa App
Chegou o momento de chamar os Helpers do Helm para nos ajudar a dimunir a repetição de códigos e também para reduzir a complexidade de nossos Templates.
Vamos dividir em algumas etapas para ficar fácil o entendimento sobre o que estamos fazendo em cada passo. 😃
Passo 1: Criando o arquivo _helpers.tpl
Como já vimos, o arquivo _helpers.tpl
contém definições de templates que podem ser reutilizadas em vários lugares. Aqui estão alguns templates úteis para o nosso caso:
Labels
Para reutilizar as labels de aplicativos em seus deployments e services:
{{/*
Generate application labels
*/}}
{{- define "app.labels" -}}
app: {{ .labels.app }}
env: {{ .labels.env }}
live: {{ .labels.live }}
{{- end }}
No arquivo acima estamos definindo um helper que gera as labels do aplicativo com base nas configurações fornecidas. Isso nos permite reutilizar as mesmas labels em vários recursos, garantindo consistência e facilitando a manutenção.
Resources
Template para definir os requests e limits de CPU e memória:
{{/*
Generate container resources
*/}}
{{- define "app.resources" -}}
requests:
memory: {{ .resources.requests.memory }}
cpu: {{ .resources.requests.cpu }}
limits:
memory: {{ .resources.limits.memory }}
cpu: {{ .resources.limits.cpu }}
{{- end }}
Aqui estamos definindo um helper que gera as configurações de recursos para um contêiner com base nas configurações fornecidas.
Ports
Template para a definição de portas no deployment:
{{/*
Generate container ports
*/}}
{{- define "app.ports" -}}
{{- range .ports }}
- containerPort: {{ .port }}
{{- end }}
{{- end }}
Passo 2: Refatorando Deployments.yaml
e Services.yaml
Com os helpers definidos, já podemos refatorar nossos arquivos Deployments.yaml
e Services.yaml
para utilizar esses templates.
O nosso Deployments.yaml
{{- range $component, $config := .Values.deployments }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $component }}
labels:
{{- include "app.labels" $config | nindent 4 }}
spec:
replicas: {{ $config.replicas | default 3 }}
selector:
matchLabels:
app: {{ $config.labels.app }}
template:
metadata:
labels:
{{- include "app.labels" $config | nindent 8 }}
spec:
containers:
- name: {{ $component }}
image: {{ $config.image }}
ports:
{{- include "app.ports" $config | nindent 10 }}
resources:
{{- include "app.resources" $config | nindent 12 }}
{{- if $config.env }}
env:
{{- range $config.env }}
- name: {{ .name }}
value: {{ .value }}
{{- end }}
{{- end }}
---
{{- end }}
O nosso Services.yaml
{{- range $component, $config := .Values.services }}
{{- range $port := $config.ports }}
apiVersion: v1
kind: Service
metadata:
name: {{ $component }}-{{ $port.name }}
labels:
{{- include "app.labels" $config | nindent 4 }}
spec:
type: {{ $port.serviceType }}
ports:
- port: {{ $port.port }}
targetPort: {{ $port.targetPort }}
protocol: TCP
name: {{ $port.name }}
{{- if eq $port.serviceType "NodePort" }}
nodePort: {{ $port.nodePort }}
{{- end }}
selector:
app: {{ $config.labels.app }}
---
{{- end }}
{{- end }}
Pronto! Agora já temos o _helpers.tpl
criado e os templates atualizados!
Caso queira testar, basta instalar ou fazer o upgrade do nosso Chart. Não vou fazer aqui novamente pois já executamos mais de 1 milhão de vezes, você já sabe como fazer isso com os pés nas costas. 😃
Passo 3: Refatorando os ConfigMaps
Ainda precisamos trabalhar com os nosso ConfigMaps, e para isso eu pensei em executar algo um pouco mais complexo, somente para que possamos gastar todo o nosso conhecimento. hahaha
Para tornar os arquivos config-map-dp.yaml
e config-map-obs.yaml
mais inteligentes e menos complexos com a ajuda do arquivo _helpers.tpl
, podemos adicionar mais definições de template que facilitam a criação de ConfigMaps para bases de dados e configurações de observabilidade. Vou adicionar templates específicos para esses dois tipos de ConfigMap no arquivo _helpers.tpl
e, em seguida, refatorar os arquivos de ConfigMap para utilizar esses templates.
Atualizando o _helpers.tpl
Adicionaremos templates para gerar ConfigMaps de bancos de dados e observabilidade:
{{/*
Generate database config map
*/}}
{{- define "database.configmap" -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .component }}-db-config
data:
app-config.yaml: |
{{- end }}
{{/*
Generate observability config map
*/}}
{{- define "observability.configmap" -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .component }}-observability-config
data:
app-config.json: |
{{- end }}
Agora estamos praticamente colocando todo o conteúdo dos ConfigMaps aqui, isso fará com que os nossos arquivos fiquem bem pequenos e somente utilizando a combinação do values.yaml
e o _helpers.tpl
.
Refatorando config-map-dp.yaml
Para utilizar o template do _helpers.tpl
, bora modificar o arquivo config-map-dp.yaml
da seguinte forma:
{{- range $component, $config := .Values.databases }}
{{- $data := dict "component" $component "config" $config }}
{{- include "database.configmap" $data | nindent 0 }}
{{- end }}
Isso irá percorrer todos os componentes definidos em .Values.databases
e aplicar o template definido para criar um ConfigMap para cada banco de dados.
Refatorando config-map-obs.yaml
Da mesma forma, modifique o arquivo config-map-obs.yaml
para usar o template de observabilidade:
{{- range $component, $config := .Values.observability }}
{{- $data := dict "component" $component "config" $config }}
{{- include "observability.configmap" $data | nindent 0 }}
{{- end }}
Isso irá iterar sobre os componentes definidos em .Values.observability
e aplicar o template para criar um ConfigMap de observabilidade para cada componente.
Ahhh, o nosso arquivo _helpers.tpl
ficou da seguinte maneira:
{{/* Define a base para reutilização de labels */}}
{{- define "app.labels" -}}
app: {{ .labels.app }}
env: {{ .labels.env }}
live: {{ .labels.live }}
{{- end }}
{{/* Template para especificações de recursos de containers */}}
{{- define "app.resources" -}}
requests:
memory: {{ .resources.requests.memory }}
cpu: {{ .resources.requests.cpu }}
limits:
memory: {{ .resources.limits.memory }}
cpu: {{ .resources.limits.cpu }}
{{- end }}
{{/* Template para definição de portas em containers */}}
{{- define "app.ports" -}}
{{- range .ports }}
- containerPort: {{ .port }}
{{- end }}
{{- end }}
{{/* Template para gerar um ConfigMap para configurações de banco de dados */}}
{{- define "database.configmap" -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .component }}-db-config
data:
app-config.yaml: |
{{- end }}
{{/* Template para gerar um ConfigMap para configurações de observabilidade */}}
{{- define "observability.configmap" -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .component }}-observability-config
data:
app-config.json: |
{{- end }}
Veja o quanto conseguimos modificar os nossos Templates utilizando o nosso _helpers.tpl
, isso é mágico demais! Porém é importante lembrar que não devemos usar os helpers para deixar as coisas mais complexas, e sim, facilitar e diminuir a nossa carga cognitiva e a repetição de códigos. Aqui estamos trabalhando de forma que fique mais didática, porém isso não quer dizer que você deva repetir isso em sua produção. Tudo depende, e dito isso, agora que você já conhece bem o que são os Helm Charts, acho que já podemos conhecer como criar o nosso repositório de Helm Charts!
Criando um repositório de Helm Charts
É bem comum que você tenha um repositório interno para armazenar os seus Helm Charts, para que outras pessoas consigam utilizar e ajudar no gerenciamento do repositório.
A criação de um repositório é bastante simples, e para o nosso exemplo vamos utilizar o Github para ser o nosso repo de Charts. Esse repositório pode ser público ou privado, depende da sua necessidade.
Vou colocar alguns passos para que você possa criar o seu repositório no Github, antes da gente começar a começar a trabalhar com o nosso repositório de Helm Charts.
Criando o repositório no Github
- Acesse o Github e faça o login na sua conta.
- Clique no ícone de "+" no canto superior direito e selecione "New repository".
- Nomeie o seu repositório (por exemplo, meu-repo-charts).
- Deixe o repositório público ou privado, conforme sua necessidade.
- Clique em "Create repository".
Pronto, repo criado!
Agora vamos clona-lo para a nossa máquina e começar a trabalhar com ele.
git clone < endereço do seu repo >
Agora acesse o diretório do seu repositório e vamos começar a brincadeira do lado do Helm.
Inicializando o repositório
Primeira coisa, vamos criar o diretórios charts e acessa-lo:
mkdir charts
cd charts
Agora vamos copiar o nosso Chart para o diretório charts
:
cp -r <diretório do seu Chart> ./
O conteúdo será algo assim:
.
├── charts
│ └── giropops-senhas
│ ├── charts
│ ├── Chart.yaml
│ ├── templates
│ │ ├── configmap-db.yaml
│ │ ├── configmap-observability.yaml
│ │ ├── deployments.yaml
│ │ ├── _helpers.tpl
│ │ └── services.yaml
│ └── values.yaml
└── README.md
Vamos aproveitar e conhecer dois comandos que irão nos ajudar a ter certeza que está tudo certo com o nosso Chart.
O primeiro é o lint
, usado para ver como está a qualidade do nosso Chart:
helm lint giropops-senhas
Com isso, se tudo estiver bem você verá a seguinte saída:
==> Linting charts/giropops-senhas
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, 0 chart(s) failed
Temos um aviso, mas isso não é um problema, é apenas uma recomendação.
Outro comando que nos ajuda a ter certeza que está tudo certo com o nosso Chart é o template
, que irá renderizar o nosso Chart e verificar se está tudo certo:
helm template charts/giropops-senhas
Com isso você verá a saída do seu Chart renderizado, e assim você consegue conferir os manifestos gerados e ver se está tudo certo. 😃
O próximo passo é criar um pacote do nosso Chart, que nada mais é que um .tgz que contém o nosso Chart, e para isso, execute o comando abaixo:
helm package charts/giropops-senhas
A saída:
Successfully packaged chart and saved it to: /home/LINUXtips/meu-repo/giropops-senhas-0.1.0.tgz
Agora que já temosk o tarball do nosso Chart, vamos adicionar ele ao nosso repositório, e para que isso seja possível vamos conhecer um comando que irá nos ajudar nessa tarefa, que é o repo index
.
O repo index
irá criar um arquivo index.yaml que contém as informações sobre os pacotes disponíveis no repositório, e para isso, execute o comando abaixo:
helm repo index --url <URL do seu repo no github> .
Perceba que ele criou um arquivo chamado index.yaml
, e nesse arquivo temos informções sobre o nosso Chart, como o nome, a versão, a descrição, o tipo de aplicação, e o endereço do Chart.
Vamos dar um cat
nesse arquivo para ver o que temos:
cat index.yaml
apiVersion: v1
entries:
giropops-senhas:
- apiVersion: v2
appVersion: 0.1.0
created: "2024-02-13T11:40:40.803868957+01:00"
description: Esse é o chart do Giropops-Senhas, utilizados nos laboratórios de
Kubernetes.
digest: 05bc20f073f5e7824ok43o4k3okfdfac1be5c46e4bdc0ac3a8a45eec
name: giropops-senhas
sources:
- https://github.com/seu-user/seu-repo
urls:
- https://github.com/seu-user/seu-repo/giropops-senhas-0.1.0.tgz
version: 0.1.0
generated: "2024-02-13T11:40:40.803383504+01:00"
Com o index.yaml, precisamos ir para o próximo passo, que é fazer o commit e o push do nosso repositório.
git add .
git commit -m "Adicionando o Chart do Giropops-Ssenhas"
git push origin main
Pronto, o nosso código está no repo lá no GitHub. \o/
Mas o nosso trabalho não para por aqui, precisamos configurar o GitHub Pages para o nosso repositório de Helm Charts.
Configurando o GitHub Pages
Siga os passos abaixo para configurar o seu GitHub Pages:
- Acesse o repositório no Github.
- Clique na aba "Settings".
- Role a página até a seção "Pages".
- Selecione a branch
main
e a pastaroot
e clique em "Save". - Aguarde alguns minutos e copie o link que aparecerá na seção "Pages".
Algo parecido com a imagem abaixo:
Pronto, o nosso repositorio de Helm Charts está configurado e pronto para ser utilizado!
Um coisa importante é alterar o index.yaml
para que ele aponte para o endereço do GitHub Pages, e para isso, execute o comando abaixo:
helm repo index --url <URL do seu repo no github> .
Com isso, o seu index.yaml
estará apontando para o endereço do GitHub Pages, do contrário o seu repositório não funcionará.
Agora que já temos o nosso repositório de Helm Charts, vamos ver como utilizá-lo.
Utilizando o nosso repositório de Helm Charts
Deu trabalho, mas agora já temos o nosso repo de Helm Charts criado com sucesso, porém ainda não sabemos qual o seu gostinho, afinal ainda não utilizamos ele.
Vamos resolver isso!
Primeira coisa, para que possamos utilizar o Chart de um determinado repositório, precisamos adicionar esse repositório ao Helm, e para isso, execute o comando abaixo:
helm repo add meu-repo <URL do seu repo no github>
Vamos listar os repositórios que temos no Helm:
helm repo list
A minha saída:
NAME URL
metrics-server https://kubernetes-sigs.github.io/metrics-server/
kyverno https://kyverno.github.io/kyverno/
meurepo https://badtuxx.github.io/charts-example/
Veja que o nosso repo está lá! Além do nosso repo, temos mais dois outros repos adicionados por mim anteriomente, que são o metrics-server
e o kyverno
. É com esse comando que conseguimos ver todos os repositórios que temos no Helm.
Podemos listar os Charts que temos no nosso repositório da seguinte maneira:
helm search repo meu-repo
A saída:
NAME CHART VERSION APP VERSION DESCRIPTION
meure2po/giropops-senhas 0.1.0 0.1.0 Esse é o chart do Giropops-Senhas, utilizados n...
O nosso está lá, e o melhor, pronto para ser utilizado!
Agora, caso você queria instalar o Chart do Giropops-Senhas, basta executar o comando abaixo:
helm install giropops-senhas meu-repo/giropops-senhas
E pronto, o seu Chart já está instalado e funcionando!
Caso você queira personalizar o seu Chart, basta criar um arquivo values.yaml
e passar as configurações que você deseja, e depois passar esse arquivo para o Helm, da seguinte maneira:
helm install giropops-senhas meu-repo/giropops-senhas -f values.yaml
E para atualizar um Chart que já está instalado, basta executar o comando abaixo:
helm upgrade giropops-senhas meu-repo/giropops-senhas
Quer ver os detalhes do seu Chart? Execute o comando abaixo:
helm show all meu-repo/giropops-senhas
O que teremos será uma descrição do nosso Chart, com todas as informações que definimos no Chart.yaml
e no values.yaml
, algo como a saida abaixo:
apiVersion: v2
appVersion: 0.1.0
description: Esse é o chart do Giropops-Senhas, utilizados nos laboratórios de Kubernetes.
name: giropops-senhas
sources:
- https://github.com/badtuxx/giropops-senhas
version: 0.1.0
---
giropopsSenhas:
name: "giropops-senhas"
image: "linuxtips/giropops-senhas:1.0"
replicas: "3"
port: 5000
labels:
app: "giropops-senhas"
env: "labs"
live: "true"
service:
type: "NodePort"
port: 5000
targetPort: 5000
name: "giropops-senhas-port"
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "500m"
redis:
image: "redis"
replicas: 1
port: 6379
labels:
app: "redis"
env: "labs"
live: "true"
service:
type: "ClusterIP"
port: 6379
targetPort: 6379
name: "redis-port"
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "500m"
E por fim, caso você queira remover o seu Chart, basta executar o comando abaixo:
helm uninstall giropops-senhas
Simples como voar! Sim, eu sei, chega a ser lacrimejante, mas agora eu já posso dizer que você domina o Helm!
O que vimos no dia de hoje
Hoje o dia foi intenso, pois conseguimos descomplicar como usar o Helm para deixar a nossa vida ainda melhor. Aprendemos a criar um Chart do zero, utilizando diversas técnicas e deixando muito conhecimento para que você possa utilizar em sua vida!
Agora eu quero que você me manda algum Chart que você criou com o que você aprendeu por aqui! Deixa o tio Jefinho feliz! hahahha
E por hoje é só! #VAIIII
Acesse
Copyright © 2024