Helm Charts – From Zero To Hero
Como usar os Helm Charts em pipelines de CI/CD com análise estática do código, uso avançado do Helm Charts com criação e configuração dinâmica de alguns objetos do Kubernetes.
Introdução
Na primeira parte desse artigo, foi apresentado como o Helm Charts vai além de um simples gerenciador de pacotes. O objetivo principal do Helm é descrever uma estrutura de aplicação através dos Helm Charts, facilitando a instalação e gestão dos pacotes e suas dependências. Também falamos sobre como criar uma estrutura de Helm Charts básica, seus objetos e algumas possibilidades de personalização.
O objetivo desse artigo é demostrar como automatizar um fluxo que começa com a codificação de uma aplicação e termina no deploy da mesma em um cluster Kubernetes utilizando e integrando ferramentas como Azure Devops, GKE e principalmente Helm Chart. Será abordado como usar os Helm Charts em pipelines de CI/CD com análise estática do código, uso avançado do Helm Chart com criação e configuração dinâmica de alguns objetos do Kubernetes.
Pré-requisitos
Com base nos pilares de DevOps, as esteiras de CI/CD compõem uma etapa muito importante da automação, garantindo a entrega de software de maneira ágil, segura, padronizada e resiliente. Os Helm Charts também contribuem com o pilar de automação de DevOps e podem trabalhar em conjunto com as esteiras no deploy das aplicações em clusters Kubernetes.
Para a demonstração vamos utilizar as ferramentas Azure Devops e um Cluster Kubernetes na Google Cloud Platform, tendo como premissa os seguintes itens:
- Criação e acesso ao Cluster Kubernetes GKE;
- Conta, Projeto e Permissão administrativa no Azure Devops;
- Task do Helm instalada no Azure Devops;
Helm Chart Personalizado
Será criado o Helm Chart básico para a aplicação hello_world em python, em seguida a customização com o template para configmap, adequação do template de deployment e o teste do Auto-Scaling Horizontal nativo do Kubernetes.
Criação do Helm Chart básico:
‘helm create pyhelloworld’

Criação do configmap template:
O configmap template pode ser habilitado ou não pela key/value no arquivo de configuração values.yaml, nele é utilizado a biblioteca Glob patterns, essa biblioteca permite “importar” os arquivos que estão no diretório files do Helm Chart “pyhelloworld”.

No diretório files será criado o arquivo configmap-test.yaml. Para simularmos uma configuração de prometheus.
As modificações no template de deployment permitem que o container receba a mesma porta de serviço, configuração dinâmica de variáveis de ambiente no container, montagem do configmap(caso ativo) como um volume.

O arquivo values.yaml precisa conter os blocos de configuração para cada funcionalidade que adicionamos no Helm Chart, como ativação do configmap, variáveis de ambiente no container, montagem do configmap como volume no container e configuração de porta pro container.

Com essas configurações no Helm Chart, há maior flexibilização na utilização, permitindo que aplicações com perfis diferentes.
Preparação dos Ambientes
Para criar a integração entre Azure Devops e outros serviços externos, é utilizado um recurso no próprio Azure Devops chamado Service Connections. Ele permite que configuremos o método de conexão com cada tecnologia externa e usemos como um recurso nos pipelines.
Para fazermos a integração com o Cluster GKE, vamos começar com a criação de uma service account no Kubernetes para deploy das aplicações via pipeline.
Para a criação de service account no GCP é muito simples, vamos usar o comando ‘kubectl’ para executar os passos:
Criação de namespace:
‘kubectl create namespace teste’
Criação da service-account:
‘kubectl create serviceaccount application-deploy -ns teste’
Atribuição de permissão para a service account criada no passo anterior:
‘kubectl create clusterrolebinding application-deploy –clusterrole=edit –serviceaccount=teste:application-deploy’
Com a service account criada e permissão atribuída, vamos seguir com a integração do Azure Devops e GKE. Nas Configurações do Projeto no Azure Devops, clicar em “service connections” e em seguida em “Criar Service Connection”. Agora, basta seguir as recomendações da tela.


Autentication method: Método de autenticação no cluster. Vamos utilizar “Service Account”
Server URL: URL do Cluster.
Ex: https://35.45.69.32.
Comando para extrair essa informação direto do cluster.
kubectl config view –minify -o jsonpath={.clusters[0].cluster.server}
Secret: Token da Service Account que criamos anteriormente.
Comandos para obter essa informação:
kubectl get serviceAccounts <service-account-name> -n <namespace> -o=jsonpath={.secrets[*].name}
kubectl get secret <service-account-secret-name> -n <namespace> -o json
Service Connection Name: Nome da service connection
Marcar: Grant access permission to all pipelines.
Dessa forma finalizamos a integração entre o projeto do Azure DevOps e o GKE.
Construção das esteiras de CI/CD
O pipeline de CI(Continuous Integration) para essa demonstração será bem simples, apenas com verificação estática do código da aplicação e do Helm Chart. O pipeline será baseado em YAML através do arquivo azure-pipelines.yaml.
Para a análise estática do código da aplicação em python será usado o flake8, que permite fazer uma análise de estilo de codificação, complexidade de código, erros de sintaxe, etc. Vamos configurar para ignorar o limite de caracteres por linha.
Na análise estática do código do Helm Chart vamos usar o lint do próprio Helm.
azure-pipelines.yaml
variables:
– name: appPath
value: ‘app’
– name: pythonVersion
value: ‘3.11’
– name: helmChart
value: ‘./pyhelloworld’
– name: dockerfile
value: ‘Dockerfile’
– name: registry
value: ‘dockerHub’
– name: repositoryId
value: ‘joaovicaria/pyhelmhw’
stages:
– stage: Analyze
displayName: Analyze
condition: always()
jobs:
– job: Preflight
steps:
– script: |
curl https://baltocdn.com/helm/signing.asc | gpg –dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
sudo apt-get install apt-transport-https –yes
echo “deb [arch=$(dpkg –print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main” | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm
displayName: Install Helm
#Define versao do python
– task: UsePythonVersion@0
displayName: Use Python ${{ variables.pythonVersion }}
inputs:
versionSpec: ${{ variables.pythonVersion }}
# Instala Pip
– bash: python -m ensurepip –upgrade
displayName: Install pip
# Instala Dependencias
– bash: pip install pytest-azurepipelines flake8 –upgrade
displayName: Install Dependencies
#Instala requirements
– bash: pip install -r requirements.txt
workingDirectory: ${{ variables.appPath }}
displayName: Install packages
# Lint via flake8.
– bash: flake8 –extend-ignore E501,W292
displayName: “Linting: flake8”
workingDirectory: ${{ variables.appPath }}
continueOnError: true
– script: helm lint ${{ variables.helmChart }}
displayName: “Linting: HelmChart”
– stage: PublishArtifact
displayName: PublishArtifact
dependsOn: Analyze
condition: in(variables[‘Build.SourceBranch’], ‘refs/heads/main’)
jobs:
– job: Publish_Container
steps:
– task: Docker@2
displayName: ‘Login to Container Registry’
inputs:
command: login
containerRegistry: ${{ variables.registry }}
– task: Docker@2
displayName: ‘Build and push image’
inputs:
dockerfile: ‘${{ variables.dockerfile }}’
command: buildAndPush
containerRegistry: ${{ variables.registry }}
repository: ‘${{ variables.repositoryId }}’
tags: |
$(Build.BuildId)
Após a criação do arquivo azure-pipelines.yaml, criamos o pipeline a partir dele. Na execução do pipeline, foram encontrados erros no código da aplicação e do Helm Chart.
Na primeira tela o flake8 identificou problemas com tamanho de linha de código e uma linha a mais.
Na segunda tela o lint do Helm identificou um erro de parse na linha 42 do arquivo deployment.yaml, nesse caso a declaração correta é “{{- end }}” e na linha 42 está “{{ – end }}”.


Após os ajustes nos códigos, o pipeline executou com sucesso. No caso da análise na aplicação, foi criado uma ‘exception’ na análise do flake8, ignorando o tamanho de linhas superior a 79 caracteres e ajustado a linha a mais no final do arquivo.

Após o pipeline de CI criado e funcionando, vamos construir o Pipeline Release que fará deploy da aplicação no cluster Kubernetes usando a task do Helm Chart.
O Azure Devops Pipeline Release no modo clássico permite que aplicações sejam “deployadas” em múltiplos ambientes de forma visual, segura e fácil. Para criá-lo, clicar em Pipeline/Release/New Release Pipeline.
Na tela de configuração do novo Pipeline Release precisamos criar dois artefatos, um do tipo “Build” para a Branch main nesse é preciso ativar a trigger para que quando o pipeline de CI finalizar disparar esse pipeline Release. O outro artefato é o repositório da própria aplicação para usarmos o Helm Chart que criamos. Observe o nome do “source alias”, vamos utilizá-lo na referência do values.yaml.

Em “Stages” vamos criar um stage “Teste” de exemplo. A trigger desse stage vai ser o build na Branch main e será adicionado um job com uma task em Agent Job configuramos o agente que executará as tasks e em seguida adicionamos uma task do Helm Chart. Na task precisamos definir algumas configurações, cada configuração fará referência a uma variável que terá definido seu nome e valor em um grupo de variáveis criado na “Library” do Azure Devops.

namespace: teste
chart_path: Caminho do helm chart que criamos. Obs.: Quando definimos os artefatos para o Pipeline Release, um desses artefatos foi o código do repositório da aplicação. Vamos precisar do valor do campo “source alias”, pois é a partir dessa referência que ficará a árvore de diretório com o código da aplicação e helm chart.
release_name: Nome da release.
value_path: À partir do “source alias” do artefato do repositório, passamos o caminho do arquivo values.yaml que contém todas as configurações para essa release da aplicação.
Após salvar o grupo de variáveis, associamos ele no stage do Pipeline Release:

Após a associação do grupo de variáveis com o Pipeline Release, vamos adicionar a task do Helm e preencher os campos de acordo com a imagem.

Display Name: Nome da Task.
Connection type: Tipo de conexão com o cluster Kubernetes. Nesse caso vamos usar a Kubernetes Service Connection que criamos anteriormente.
Kubernetes Service Connection: Service connection do GKE.
Namespace: Namespace onde será feito o “deployment” da aplicação.
Command: Selecionar o upgrade, caso a release da aplicação não exista no namespace do kubernetes, será instalado, caso exista será feito uma atualização caso alguma configuração no helm chart tenha mudado.
Chart Type: Pode ser por nome ou por caminho do chart.
Chart Name: Caminho do helm chart.
Release Name: Nome da Release.
Set Values: Alteração de alguma configuração no momento do deployment. Usamos o BuildId da pipeline na tag da imagem do container, foi o mesmo usado no momento do build.
Values Files: Caminho do values.yaml com as configurações personalizadas.
No começo do artigo fizemos a criação e personalização do HelmChart, nesse ponto vamos fazer a configuração do Helm Chart para atender as necessidades dessa aplicação em questão. Há duas formas de fazer isso, a primeira é configurando os campos do arquivo “values.yaml” e a segunda é usando a sessão “Set Values” da Task do Helm no Pipeline Release. Segue o que foi modificado no arquivo “values.yaml” para atender as necessidades da aplicação.
image:
repository: joaovicaria/pyhelmhw
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: “latest”
imagePullSecrets:
– name: myregistrykey
configMap:
enabled: true
pod:
env:
– name: ENV_VAR_EXAMPLE
value: helm_hello_world_env_var
volumeMount:
– name: configvolume
mountPath: /config
volume:
– name: configvolume
configMapName: pyhelloworld
service:
type: ClusterIP
port: 5000
ingress:
enabled: true
className: “”
annotations:
kubernetes.io/ingress.class: nginx
hosts:
– host: pyhelloworld-teste.labgft.com.br
paths:
– path: /
pathType: Prefix
– path: /metrics
pathType: Prefix
tls: []
resources:
limits:
cpu: 100m
memory: 128Mi
autoscaling:
enabled: true
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 20
Passando pelas etapas de CI, a trigger configurada no Piepeline Release vai disparar e o deploy da aplicação no cluster Kubernetes será executado.

Nesse exemplo a aplicação vai consumir o valor de uma secret no Secret Manager do GCP, para isso precisamos associar a service account da Release no Cluster com uma service account da cloud e estabelecer a permissão via IAM. Com essa associação entre service accounts podemos dar permissão para qualquer serviço ou recurso da cloud para aplicações no cluster.
Vincule a conta de serviço do IAM à conta de serviço do kubernetes:
‘gcloud iam service–accounts add–iam–policy–binding <service_account_gcp>@@PROJECT_ID_GCP.iam.gserviceaccount.com \
–member=serviceAccount:PROJECT_ID.svc.id.goog[<namespace>/<service_account_k8s>] \
–role=’roles/iam.workloadIdentityUser’’
Anote a conta de serviço do Kubernetes com a conta de serviço do IAM:
‘kubectl annotate serviceaccount <service_account_k8s> \
–namespace=namespace \
iam.gke.io/gcp-service-account=<service_account_gcp>@PROJECT_ID_GCP.iam.gserviceaccount.com’
Essa aplicação exibirá no navegador o valor da última versão de uma secret no Secret Manager da GCP, o conteúdo do arquivo criado no configmap e o valor da variável de ambiente que foi configurado no values.yaml.
Apresenta mensagem da página principal:

Apresenta valor da variável de ambiente:

Apresenta valor da secret no SecretManager:

Apresenta valor do arquivo no configMap:

Funcionamento do autoscalling horizontal do Kubernetes, no caso foi configurado para criar um novo POD sempre que a utilização da CPU passar de 20% e pelo JMeter enviaremos várias requisições simultâneas.
Conclusão
Pudemos ver como o Helm Chart além de uma ferramenta poderosa de gerenciamento de pacotes e suas dependências também é muito flexível em atender as necessidades de um projeto. Associado com ferramentas de CI/CD garantimos a qualidade, segurança e garantia em entrega contínua de software.
Com o controle de qualidade e entrega contínua estabelecidos podemos explorar outros aspectos dos ambientes como desenvolver Helm Charts mais robustos ou até mesmo Charts com propriedades específicas que podem se correlacionar usando as dependências entre eles.
Referências:
InfoQ: How to Use Open Source Prometheus to Monitor Applications at Scale
https://www.infoq.com/articles/prometheus-monitor-applications-at-scale/
Helm
https://helm.sh/docs/
Medium: Connecting Azure DevOps to GCR and GKE
https://johnclarke73.medium.com/connecting-vsts-to-gcr-and-gke-7a2e03d54d96
Azure Devops – Pipelines
https://learn.microsoft.com/en-us/azure/devops/pipelines/?view=azure-devops
Flake8
https://flake8.pycqa.org/en/latest/
Aplicação de Exemplo
https://github.com/joao-vicaria/py-helmchart-helloworld