Como crear un Helm Chart para kubernetes
Gran parte de mi día lo solía gastar creando diferentes objetos de kubernetes, deployment, configmaps, secrets, services, etc… . Esta es una tarea algo tediosa y a veces repetitiva en la que te puedes equivocar fácilmente. Es en este momento y para este tipo de casos donde debemos hacer uso y crear un Helm Chart para Kubernetes. Ya que estos nos ayudarán y coordinarán la descarga, instalación y deployment de nuestras aplicaciones. En los siguientes puntos iremos viendo los pasos necesarios que nos ayudarán a ver como crear un Helm Chart para kubernetes.
¿Por que se debería usar Helm?
Sobre por qué deberíamos usar Helm, la respuesta es bastante fácil, por simplicidad y porque nos va a facilitar mucho el trabajo. Helm va a realizar la gestión de los deployment en kubernetes a través de templates. Helm nos da una estructura muy flexibe para que podamos ejecutar cualquier tipo de aplicación en nuestro clúster de Kubernetes. Incluso va a permitir realizar versionado de nuestros templates, de manera que podamos tener diferentes deployments de una manera sencilla y eficaz.
En el mundo del desarrollo y la infraestructura lo que hay que llevar a cabo es evitar realizar tareas manuales siempre y cuando sean posible, ya que estas nos podrán llevar a errores. Es por eso que, aplicando Helm chart podremos eliminar errores y realizar despliegues de una manera sencilla y segura.
Hands on
En este ejemplo vamos a crear un ejemplo de Helm con minikube. Pulsa aquí para instalar minikube y aquí para configurar e instalar Helm.
Si tenemos todo instalado y configurado vamos a comenzar arrancando nuestro minikube.
minikube start
Y a continuación vamos a inicializar la creación de un nuevo Helm chart con nombre chartexample:
helm create chartexample
Una vez creado podemos verificar los ficheros y carpetas creadas:
$ ls chartexample charts Chart.yaml templates values.yaml
Estructura de Helm Chart
Una vez hemos creado nuestro Helm con el comando anterior, veremos dos ficheros y dos carpetas. Vamos a ir viendo uno por uno.
Si abrimos Chart.yaml veremos lo siguiente:
apiVersion: v2 name: chartexample description: A Helm chart for Kubernetes # A chart can be either an 'application' or a 'library' chart. # # Application charts are a collection of templates that can be packaged into versioned archives # to be deployed. # # Library charts provide useful utilities or functions for the chart developer. They're included as # a dependency of application charts to inject those utilities and functions into the rendering # pipeline. Library charts do not define any templates and therefore cannot be deployed. type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) version: 0.1.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. appVersion: "1.16.0"
En el fichero vemos una serie de atributos, empezando por la versión del API, el nombre del gráfico y una descripción. A continuación veremos el tipo de chart que tendremos que es application por defecto, y la versión del chart y de la aplicación.
Ahora pasamos a la carpeta /templates, que se podría decir que es en donde reside toda la lógica para poder realizar un despliegue de nuestra aplicación. Es la que contiene los ficheros de los objetos de kubernetes, si echas un vistazo,verás un deployment, ingress, un service, un service account, un horizontal pod autoscaler y una carpeta de tests.
Si continuamos explorando veremos una carpeta vacía que se llama charts. Esta carpeta nos será de gran utilidad para añadir charts que serán necesarios o que nuestra aplicación dependerá de ellos.
A continuación vamos a analizar donde reside la lógica y la configuración que hay que aplicar para generar un template, el fichero values.yaml.
Analizar fichero values.yaml en Helm Chart
El archivo values.yaml nos trae un formato de plantilla en el que podremos realizar la configuración de nuestros objetos de kubernetes.
# Default values for chartexample. # This is a YAML-formatted file. # Declare variables to be passed into your templates. replicaCount: 1 image: repository: nginx pullPolicy: IfNotPresent # Overrides the image tag whose default is the chart appVersion. tag: "" imagePullSecrets: [] nameOverride: "" fullnameOverride: "" serviceAccount: # Specifies whether a service account should be created create: true # Annotations to add to the service account annotations: {} # The name of the service account to use. # If not set and create is true, a name is generated using the fullname template name: "" podAnnotations: {} podSecurityContext: {} # fsGroup: 2000 securityContext: {} # capabilities: # drop: # - ALL # readOnlyRootFilesystem: true # runAsNonRoot: true # runAsUser: 1000 service: type: ClusterIP port: 80 ingress: enabled: false className: "" annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" hosts: - host: chart-example.local paths: - path: / pathType: ImplementationSpecific tls: [] # - secretName: chart-example-tls # hosts: # - chart-example.local resources: {} # We usually recommend not to specify default resources and to leave this as a conscious # choice for the user. This also increases chances charts run on environments with little # resources, such as Minikube. If you do want to specify resources, uncomment the following # lines, adjust them as necessary, and remove the curly braces after 'resources:'. # limits: # cpu: 100m # memory: 128Mi # requests: # cpu: 100m # memory: 128Mi autoscaling: enabled: false minReplicas: 1 maxReplicas: 100 targetCPUUtilizationPercentage: 80 # targetMemoryUtilizationPercentage: 80 nodeSelector: {} tolerations: [] affinity: {}
En el fichero de configuración anterior podemos ver que se ha estructurado para poder añadir un servicio, un ingress, autoscaling, afinidad etc… .Vamos a ir analizando y definiendo cada parte de nuestro fichero values.yaml.
Configuración del Pod
La primera parte de nuestro fichero values contiene información referida a configuración del Pod.
El primer argumento que nos encontramos es replicaCount, el cual nos indica el número de réplicas que existirán del pod, por defecto lo establece a 1.
A continuación nos encontramos con la definición de la imagen, en la que por defecto define la imagen de nginx y la pullPolicy a IfNotPresent, la cual cambiaremos a always para nuestro ejemplo.
Secretos
A continuación nos encontramos con imagePullSecrets, en esta sección se podrá definir cualquier password o tokens.
Nombres
La siguiente sección que nos encontramos es la referida a nameOverride y fullnameOverride. En esta sección podremos cambiar el nombre del chart que hemos creado con el comando helm create.
Service Account
En la creación por defecto generará un service account basado en el nombre completo que hemos definido. Lo mejor y más recomendable es generar uno propio y definir los permisos que desees.
Seguridad
En la sección de securityContext y podSecurityContext, se van a definir límites sobre usuarios, o que grupos de archivos se pueden usar o no.
Red
La siguiente parte que vamos a ver es service e ingress. En cuanto a service, vamos a poder definir ClusterIP o NodePort. Este último es el que vamos a usar en nuestro ejemplo, ya que va a exponer el servicio en la dirección IP de cada nodo de kubernetes con un puerto fijo.
Y en cuanto a ingress, lo vamos a dejar por defecto a false.
Recurso
En la de sección de resources, vamos a definir los límites de los recursos a emplear. Por defecto viene vacío, pero lo recomendable es rellenarlo. En nuestro ejemplo vamos a definir 100m de CPU y 128Mi de memoria.
Autoescalado
En siguiente parámetro que nos encontramos es autoscaling, el cual se encuentra desactivado por defecto y una vez activado funcionará en función de unos parámetros.
Tolerations, node selectors, y affinities
A continuación veremos los últimos tres apartados que nos quedan, los cuales por defecto vienen vacíos.
Tal y como ya explícamos en algún artículo anterior, nodeSelector se utiliza cuando se va a asignar nuestra aplicación o pods a nodos específicos en nuestro clúster de kubernetes.
Tolerations y affinities, van a asegurarnos que nuestros pods se están ejecutando en diferentes nodos.
En nuestro ejemplo por simplicidad vamos a dejarlo con la configuración por defecto.
Hacer deploy con Helm
A continuación vamos a hacer deploy de un nginx con las modificaciones que hemos indicado anteriormente:
# Default values for chartexample. # This is a YAML-formatted file. # Declare variables to be passed into your templates. replicaCount: 1 image: repository: nginx pullPolicy: Always # Overrides the image tag whose default is the chart appVersion. tag: "" imagePullSecrets: [] nameOverride: "" fullnameOverride: "" serviceAccount: # Specifies whether a service account should be created create: true # Annotations to add to the service account annotations: {} # The name of the service account to use. # If not set and create is true, a name is generated using the fullname template name: "" podAnnotations: {} podSecurityContext: {} # fsGroup: 2000 securityContext: {} # capabilities: # drop: # - ALL # readOnlyRootFilesystem: true # runAsNonRoot: true # runAsUser: 1000 service: type: NodePort port: 80 ingress: enabled: false className: "" annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" hosts: - host: chart-example.local paths: - path: / pathType: ImplementationSpecific tls: [] # - secretName: chart-example-tls # hosts: # - chart-example.local resources: # We usually recommend not to specify default resources and to leave this as a conscious # choice for the user. This also increases chances charts run on environments with little # resources, such as Minikube. If you do want to specify resources, uncomment the following # lines, adjust them as necessary, and remove the curly braces after 'resources:'. limits: cpu: 100m memory: 128Mi requests: cpu: 100m memory: 128Mi autoscaling: enabled: false minReplicas: 1 maxReplicas: 100 targetCPUUtilizationPercentage: 80 # targetMemoryUtilizationPercentage: 80 nodeSelector: {} tolerations: [] affinity: {}
Para hacer un install con Helm usaremos el siguiente comando:
helm install helm-example chartexample/ --values chartexample/values.yaml
y si todo va bien debería mostrar la siguiente salida:
NAME: helm-example LAST DEPLOYED: Fri Jun 11 13:17:17 2021 NAMESPACE: default STATUS: deployed REVISION: 1 NOTES: 1. Get the application URL by running these commands: export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services helm-example-chartexample) export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}") echo http://$NODE_IP:$NODE_PORT
Una vez ejecutados los comandos anteriores deberíamos obtener la IP y el puerto de ejecución para ver la página de nginx:
Conclusión
A lo largo de este artículo sobre como crear un Helm Chart para kubernetes, hemos visto como podemos desplegar una aplicación haciendo uso de Helm y sus templates. Esto nos ayudará para tareas repetitivas o cuando tengamos que realizar despliegues de aplicaciones con características similares.
Otro ejemplo en el que hemos usado Helm es en el autoescalado con Spring Boot y Prometheus.