Helmfile: superpoderes para Helm

Miguel Fontanilla
Computación en la nube
Helm es sin duda una de las herramientas más utilizadas en el mundo de Kubernetes. Es especialmente útil ya que ....

Helmfile: superpoderes para Helm ☸️🔧

Helm es sin duda una de las herramientas más utilizadas en el mundo de Kubernetes. Es especialmente útil ya que es a la vez, una herramienta de despliegue, un gestor de paquetes y una herramienta de templatizado que permite customizar el comportamiento de las aplicaciones en función de los valores utilizados. Además, proporciona características ideales para integrarlo en entornos de CI/CD modernos, convirtiéndola en una herramienta esencial para tu 'caja de herramientas' de DevOps.

Helm se basa en charts, que funcionan como paquetes de software independientes, conteniendo los manifiestos de los objetos de Kubernetes a desplegar. No obstante, para entornos más complejos, en los que deben desplegarse varios charts, no existe una manera sencilla de gestionar estos despliegues de forma centralizada. Basándome en mi experiencia, se pueden desarrollar plugins de Helm que permitan orquestar multiples charts y valores en varios entornos. No obstante, esta estrategia puede acabar siendo poco práctica, ya que es necesario mantener los plugins. Además, el empleo de dichos plugins impide utilizar algunas herramientas que proporcionan integraciones nativas con Helm, como herramientas de CD o GitOps. Pero no te preocupes, a continuación conocerás una herramienta que puede ayudarte a solucionar estos problemas: Helmfile.

Helmfile es un CLI desarrollado por Robert Boll que permite orquestar multiples releases de Helm de forma declarativa usando código (YAML+go templating). Proporciona una gran flexibilidad y modularidad para la gestión tanto de charts como de valores, así como una forma de desplegar los cambios en los clústers muy 'Terraformiana'. A parte de esto, también soporta kustomize en caso de que no quieras seguir la 'senda de Helm' 🌚.

Al igual que en artículos anteriores, se ha desarrollado un repo con ejemplos. Puedes encontrarlos aquí.

Instalando Helmfile ⬇️

Helmfile utiliza Helm para poder desplegar las releases de los charts, por lo que en primer lugar, es necesario instalar Helm en tu máquina local. El siguiente comando descargará el script de instalación de Helm 3 en tu máquina (no te recomiendo que utilices Helm 2 😂).



curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3chmod 700 get_helm.sh
./get_helm.sh

Si prefieres instalar el binario manualmente, puedes encontrarlo en la página de releases de Helm.

Lo siguiente es instalar Helmfile. Puedes hacerlo descargando el binario de la página de releases de Helmfile. Ejecuta los siguientes comandos añadiendo tu SO/plataforma a la URL.



curl -fsSL -o helmfile https://github.com/roboll/helmfile/releases/download/v0.135.0/helmfile_chmod 700 helmfile 
mv helmfile  /usr/local/bin

Finalmente tendrás que instalar el plugin diff de Helm, ya que es necesario para que Helmfile pueda detectar las diferencias entre las releases presentes en los clústers y los cambios a desplegar.



helm plugin install https://github.com/databus23/helm-diff

Estructura 📂

Una vez que todos los binarios y plugins se encuentren instalados, es el momento de presentar la estructura utilizada por Helmfile para organizar las releases/charts como código. Para disponer de un Helmfile de ejemplo, clona el repositorio de ejemplo.



git clone https://github.com/mifonpe/helmfile-examplecd helmfile-example

Es importante tener en cuenta que la estructura de Helmfile es altamente customizable, y que los ficheros pueden ser referenciados desde otros ficheros, lo que se conoce como 'layering', y ofrece infinitas posibilidades para organizar el código. La siguiente imagen muestra el contenido del repositorio de ejemplo, que es sencillamente una forma particular de organizar el código que encuentro bastante útil.

  • En el directorio raíz puedes encontrar helmfile.yaml, que es el nombre por defecto para el fichero de configuración. Funciona como un 'punto de entrada' y referencia al resto de ficheros.
  • El directorio bases contiene los repositorios de Helm que serán utilizados, así como los valores para los diferentes entornos.
  • El directorio releases contiene releases.yaml, que especifica las releases de Helm a instalar, así como metadatos específicos de las releases.
  • Bajo la ruta releases/values se encuentran los diferentes ficheros de valores a utilizar. Helmfile fusiona dichos valores con los presentes en los charts referenciados en releases.yaml.

helmfile.yaml define los ficheros base, los helmfiles anidados y los valores por defecto para Helm. Los ficheros base se fusionan entre ellos y junto con helmfile.yaml antes de procesar el resto de ficheros. El código que verás a continuación muestra el contenido de helmfile.yaml.



bases:  
    - "bases/repos.yaml"  
    - "bases/environments.yaml"helmfiles:  
    - "releases/releases.yaml"‍

helmDefaults:  
    atomic: true   
    createNamespace: true          
    cleanupOnFail: true    
    verify: true  
    timeout: 300  
    wait: true

Bajo la directiva helmfiles es posible especificar el fichero o conjunto de ficheros en los que se definen las releases de Helm. Para mantener este ejemplo lo más sencillo posible, todas las releases se han agrupado en el mismo fichero (releases/releases.yaml). La directiva helmDefaults permite customizar el funcionamiento de Helm, con parámetros similares a los flags nativos del CLI de Helm.


repos.yaml es una colección de repositorios de Helm que utiliza Helmfile para descargar las releases que se le indican. Al ser parte de los ficheros base, se fusiona con helmfile.yaml (también puede especificarse este bloque directamente dentro de helmfile.yaml). Puedes ver sus contenidos a continuación.

repositories: - name: stable url: https://charts.helm.sh/stable - name: ingress-nginx url: https://kubernetes.github.io/ingress-nginx - name: bitnami url: https://charts.bitnami.com/bitnami - name: kubernetes-dashboard url: https://kubernetes.github.io/dashboard/

El siguiente fragmento de código forma parte del fichero releases.yaml. Bajo la directiva releases se especifican las diferentes releases de Helm junto con algunos parámetros que permiten customizarlas. La ruta del fichero o ficheros de valores a utilizar, que se combinan con los valores por defecto del chart también se especifica aquí. Fíjate en que se pueden utilizar valores como el nombre del entorno para renderizar las rutas, y además Helmfile soporta el empleo de templating de go para definir releases condicionales.

En este caso particular, el autoscaler solo se desplegará en el entorno productivo, teniendo en cuenta los valores de entorno definidos en /bases/environments.yaml.



bases:
  - "../bases/environments.yaml"
  - "../bases/repos.yaml"

releases:
{{- if .Environment.Values.autoscaler.enabled | default false }}
- name: "cluster-autoscaler-{{ .Environment.Name }}"
  namespace: "autoscaler"
  chart: "stable/cluster-autoscaler"
  labels:
    chart: "cluster-autoscaler"
    repo: "stable"
    namespace: "kube-system"
  version: 8.0.0
  values:
    - "./values/cluster-autoscaler.values.yaml"

{{ end }}

- name: "nginx-ingress-{{ .Environment.Name }}"
  namespace: "nginx-ingress"
  createNamespace: true
  chart: "ingress-nginx/ingress-nginx"
  labels:
    chart: "ingress-nginx"
    repo: "ingress-nginx"
    component: "ingress"
    namespace: "kube-ingress"
  version: 3.11.0
  values:
    - "./values/common/nginx-ingress-values.yaml"
    - "./values/{{ .Environment.Name }}/nginx-ingress-values.yaml"

NOTA: Es importante mencionar aquí que Helmfile trata los ficheros de valores de forma diferente dependiendo de su extensión. Si necesitas renderizar expresiones dentro de los ficheros, añade al extensión .gotmpl , ya que si no, los ficheros se procesarán sin evaluar las expresiones. El siguiente código muestra el contenido de cluster-autoscaler-values.yaml.gotmpl, el único fichero de valores en este ejemplo que realmente utiliza expresiones. En este caso, se emplean para determinar el nombre del cluster en función del entorno.



rbac:
  create: true
cloudProvider: aws
awsRegion: eu-west-1
replicaCount: 1
extraArgs:
  v: 4
  balance-similar-node-groups: true
autoDiscovery:
  clusterName: "{{ .Environment.Name }}-cluster"
  enabled: true

Desplegando Charts ⚙️

Para probar Helmfile, desplegaremos releases en dos entornos diferentes: producción y desarrollo. El Helmfile de ejemplo contiene cuatro releases:

  • Nginx Ingress Controller
  • Kubernetes Dashboard
  • Cluster Autoscaler
  • Apache server

Como se ha comentado anteriormente, la release del autoscaler será sólo desplegada en prod utilizando la configuración avanzada de Helmfile. Además, las releases de Kubernetes dashboard y Apache utilizarán valores diferentes en función del entorno (host del ingress, número de réplicas, etc)

Los entornos consistirán en clústers de AWS EKS que desplegaremos utilizando eksctl. Si no has leído el artículo anterior sobre eksctl, y quieres conocer más detalles a cerca de esta herramienta, échale un ojo.

No obstante, también puedes probar este ejemplo en local con minikube o docker desktop (o incluso en tu clúster propio), pero ten en cuenta que no podrás emular dos clústers separados con este setup.

Para empezar, descarga el binario de eksctl y colócalo bajo tu PATH.



curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv /tmp/eksctl /usr/local/bin

Antes de desplegar, necesitarás configurar tus credenciales de AWS, utilizando el AWS CLI. Para ello, necesitarás un par de access key y secret key de AWS. Si no dispones de uno, puedes generarlo en la consola de IAM de AWS. Consulta la documentación oficial como referencia si lo necesitas.



aws configure

Ten en cuenta que el usuario al que se le asignen dichas claves de acceso debe tener permisos para crear la infraestructura. Para este ejemplo puedes utilizar la policy gestionada AdministratorAccess . Guarda bien el par de claves, y una vez que termines de probar estos ejemplos y este usuario no se vaya a utilizar de nuevo, elimínala, para evitar riesgos innecesarios.

Entorno de Desarrollo 🏗️

Una vez que eksctl esté listo para usar, ejecuta los siguientes comandos para crear el entorno de desarrollo en tu cuenta de AWS, y espera a eksctl termine su labor. Sé paciente, ya que puede tardar un rato ⏳.



cd clusters
eksctl create cluster -f dev.yaml

Para cuando eksctl haya acabado, deberías tener un clúster con un sólo nodo. Eksctl se encarga de apuntar tu kubeconfig local y tu contexto de Kubernetes al clúster que acaba de crear, por lo que no es necesario configurar nada más.

Una vez que el clúster esté listo, empezaremos a desplegar los charts. En primer lugar, ejecuta el siguiente comando para añadir y actualizar los repos (es equivalente a helm repo add && helm repo update).



cd ..
helmfile --environment  dev repos

Helmfile puede utilizarse también para comprobar los templates que componen los charts. Para hacerlo, ejecuta el siguiente comando. Al añadir el flag –skip-deps Helmfile omite los pasos repo add y update , así como la construcción de las dependencias del chart (helm dependency build).



helmfile --environment dev lint --skip-deps

El siguiente comando despliega los charts adecuados en el entorno de desarrollo. El flag –suppress-secrets indica a Helmfile que no debe mostrar los secretos en la salida del terminal. Esto es especialmente útil para las herramientas de CI/CD, en las que los logs de los ejecutores suelen ser explícitos.



helmfile --environment dev apply --suppress-secrets --skip-deps

Como se comentó anteriormente, Helmfile tiene una forma de mostrar y aplicar los cambios 'a lo Terraform'. En este caso, todos los recursos se muestran en verdes ya que son nuevos recursos a añadir. Fíjate en como las definiciones de los secretos no se muestran en el terminal.

Ahora deberías poder ver tres releases desplegadas en el clúster. Puedes comprobarlo ejecutando el siguiente comando de Helm.



helm ls --all-namespaces

Como apache y Kubernetes dashboard se exponen a través de un ingress, ambas aplicaciones se pueden alcanzar con un navegador web.

NOTA: Seguramente te has fijado en que he podido alcanzar ambos ingress sin ninguna configuración extra de DNS. ¿Cómo es esto posible? No, no es magia negra 🧙‍♂️, es un sencillo truco que consiste en crear una entrada de DNS en el fichero /etc/hosts que contenga la IP del balanceador del ingress y el nombre del ingress. Si quieres probarlo, puedes obtener la IP del balanceador ejecutando el siguiente comando.



kubectl get ingress | grep dashboard | awk '{print $3}'| xargs -I {} nslookup {} | grep Address | tail -1 | cut -f 2 -d ":"

Una vez que hayas obtenido la IP, añade las entradas correspondientes a /etc/hosts .



sudo -- sh -c 'echo " apache-dev.kubesandclouds.com" >> /etc/hosts'
sudo -- sh -c 'echo " kubernetes-dashboard.dev.com" >> /etc/hosts'

Helmfile incluye el comando sync, que permite sincronizar los contenidos de los 'ficheros de estado' (repos, releases y dependencias). Es recomendable ejecutar este comando de forma periódica para asegurarse de que las releases están actualizadas. La principal diferencia entre helmfile apply y helmfile sync es que el primero solo despliega los cambios si se encuentran diferencias, mientras que el segundo sincroniza todos los recursos.



helmfile --environment dev sync  --skip-deps

Ahora, modifica algunos de los valores para el entorno de desarrollo. Por ejemplo, modifica el número de réplicas del deployment de apache y luego ejecuta el siguiente comando. Como verás, muestra los campos de los objetos de Kubernetes que serán modificados en las releases de Helm presentes en el clúster. Este comando es bastante útil para entornos de CI/CD en los que se necesita aprobación manual para desplegar cambios en base a un plan de despliegue. En estos casos, el plan de despliegue se puede generar con helmfile diff y una vez aprobados, se aplican con helmfile apply.



helmfile --environment dev diff --suppress-secrets --skip-deps

El comando diff utiliza el plugin diff de Helm, y es por eso que tuvimos que instalarlo al comienzo.

Entorno de producción 🏭

Ahora, desplegaremos el clúster de producción y sus aplicaciones utilizando el mismo procedimiento que hemos empleado para el entorno de desarrollo.



cd clusters
eksctl create cluster -f prod.yaml
cd ..
helmfile --environment  prod repos
helmfile --environment prod apply --suppress-secrets --skip-deps

Si todo funciona como debe, tendrás un clúster desplegado con una serie de aplicaciones que puedes ver en la siguiente imagen. En este caso, aparece una nueva release, cluster autoscaler, ya que los valores condicionales solo permitían que se desplegase en el entorno de producción.

El autoscaler es completamente funcional, y puedes probarlo escalando el número de réplicas del deployment de apache con el siguiente comando.



kubectl scale deploy apache-prod --replicas=20

Si le das un par de minutos al cluster autoscaler, verás que aparecen nuevos nodos para poder servir el incremento de carga generado por las nuevas réplicas. En este ejemplo, el número máximo de nodos configurado para el clúster es 3.

Desmontando el chiringuito🎪

Una vez que hayas terminado de jugar con tus entornos y Helmfile, puedes destruir directamente los clústers utilizando eksctl 💥.



cd clusters/
eksctl delete cluster -f dev.yaml
eksctl delete cluster -f prod.yaml

Si estás probando este ejemplo en tu propio clúster, puedes eliminar todas las releases con Helmfile. Para ello, ejecuta los siguientes comandos.



helmfile --environment dev destroy
helmfile --environment prod destroy

Sigue aprendiendo👩‍💻👨‍💻

Si te ha gustado Helmfile, tómate tu tiempo para investigar su repo en GitHub, ya que te ayudará a entender mejor todas las configuraciones avanzadas y opciones para customizar las releases y el propio comportamiento de Helm.

Recuerda que el ejemplo que se ha presentado en este artículo es solo una de las posibles formas de organizar Helmfiles, puedes hacer una prueba y organizarlo como mejor se adapte a tu escenario particular: colocando todas las releases y la configuración en un solo fichero, utilizando un fichero por release, etc. Si quieres indagar un poco más en cómo organizar tus Helmfiles, échale un ojo a la Guía de mejores prácticas de Helmfile.

Miguel Fontanilla

DevOps/Cloud Engineer at Orange. Cloud Architecture, Virtualization, Services Orchestration, Automation and Optimization. Always learning.

Related Posts

Únete a nuestra Newsletter

Lidera la Conversación en la Nube