Integrar el flujo de CI con charts de Helm en Argo CD

Hace unos días te conté cómo manipular los manifiestos para Kubernetes con la nueva versión publicada en el registro durante la fase de CI. Sin embargo, cuando se trata de charts de Helm esta tarea puede ser un poco más compleja de gestionar y, al menos yo, no he encontrado una forma elegante de hacerlo al estilo que te mostré en el artículo anterior. Es por ello que con Argo CD he optado por usar la herramienta Argo CD image updater. En este artículo te cuento cómo.

¿Qué es Argo CD image updater?

Se trata de una herramienta que tiene como objetivo actualizar las imágenes de las aplicaciones administradas por Argo CD. Es decir, se configura a nivel del recurso de tipo aplicación de Argo CD y es capaz de monitorizar uno o varios registros y comprobar si hay una nueva versión de tus imágenes y actualizar con ellas la configuración de tu aplicación. Para este ejemplo me he mostrado un flujo como el siguiente:

Argo CD image updater – dev branch

Nota: es importante saber que esta herramienta está todavía en fase de desarrollo, tal y como se indica en su documentación:

Argo CD Image Updater is under active development.
Argo CD Image Updater is under active development.

Por lo que mi propuesta sería usar este en entornos no productivos para agilizar el desarrollo y testing de las nuevas versiones y en el cambio a producción hacer uso de pull request con la versión actualizada en el chart de Helm.

Argo CD – Pull request para el branch de prod

Además aprovecharía estas pull request para validar los manifiestos resultantes con alguna herramienta para este cometido como PSRule, Kubescape, Snyk entre otros. En este otro artículo te cuento cómo.

Ahora que ya tienes claro qué es lo que vamos a conseguir vamos a ver cómo hacerlo.

Instalación del entorno y Argo CD image updater

En este artículo tengo mi entorno montado sobre AKS y ACR. Si no tienes uno puedes crearlo de manera sencilla a través de estos comandos:

# Variables
RESOURCE_GROUP="<YOUR_RESOURCE_GROUP_NAMEZ"
AKS_NAME="<YOUR_AKS_NAME>"
LOCATION="<YOUR_LOCATION>"
ACR_NAME="<YOUR_ACR_NAME>"
# Create a resource group
az group create -n $RESOURCE_GROUP -l $LOCATION
# Create an Azure Container Registry
az acr create \
--resource-group $RESOURCE_GROUP \
--name  $ACR_NAME \
--sku Standard
# Create an AKS cluster
az aks create \
-n $AKS_NAME \
-g $RESOURCE_GROUP \
--generate-ssh-keys \
--node-vm-size Standard_B4ms \
--attach-acr $ACR_NAME 
# Get AKS credentials
az aks get-credentials -n $AKS_NAME -g $RESOURCE_GROUP
# Deploy ArgoCD
kubectl create namespace argocd
# dev
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# or ha
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/master/manifests/ha/install.yaml
# Check everything is running
k get pods -n argocd
# Get the password (user: admin)
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
# Access ArgoCD UI
kubectl port-forward svc/argocd-server -n argocd 8080:443

Una vez que ya tienes el entorno y Argo CD desplegado, lo último que necesitas es desplegar Argo CD image updater. Puedes hacerlo de manera sencilla aplicando el siguiente manifiesto:

# Install Argo CD Image Updater
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj-labs/argocd-image-updater/stable/manifests/install.yaml
# Important: It is not advised to run multiple replicas of the same Argo CD Image Updater instance. Just leave the number of replicas at 1, otherwise weird side effects could occur.

Como puedes ver en el comentario, se aconseja a día de hoy dejar el número de réplicas a uno, ya que de lo contrario podrías experimentar comportamientos anómalos.

Configurar un secreto para el acceso al container registry

Como te decía, en este ejemplo estoy usando AKS y Azure Container Registry como servicios para mi entorno de Kubernetes. Para que Argo CD image updater pueda revisar periódicamente si existe una nueva versión de las imágenes que le digamos, podemos utilizar un secreto con la configuración necesaria, aunque existen otras opciones. En este caso, para solamente asignar los permisos estrictamente necesarios he creado un service principal y he guardado la información en un secreto:

# Create a service principal 
SERVICE_PRINCIPAL_NAME=argocd-acr-sp
# Obtain the full registry ID for subsequent command args
ACR_REGISTRY_ID=$(az acr show --name $ACR_NAME --query "id" --output tsv)
# Create the service principal with rights scoped to the registry.
# Default permissions are for docker pull access. Modify the '--role'
# argument value as desired:
# acrpull:     pull only
# acrpush:     push and pull
# owner:       push, pull, and assign roles
PASSWORD=$(az ad sp create-for-rbac --name $SERVICE_PRINCIPAL_NAME --scopes $ACR_REGISTRY_ID --role acrpull --query "password" --output tsv)
USER_NAME=$(az ad sp list --display-name $SERVICE_PRINCIPAL_NAME --query "[].appId" --output tsv)
# Create a secret with the ACR credentials
kubectl create secret docker-registry acr-credentials \
    --namespace argocd \
    --docker-server=$ACR_NAME.azurecr.io \
    --docker-username=$USER_NAME \
    --docker-password=$PASSWORD

Ahora ya tienes todo listo para configurar tus aplicaciones con Argo CD image updater.

Crear aplicaciones en Argo CD

Si ya tienes experiencia con este operador, sabrás que este utiliza unos recursos personalizados que representan las aplicaciones que va a gestionar con toda la configuración necesaria: donde está el repo que va a utilizar, qué branch, como se va a hacer la sincronización, etcétera. Esta configuración puede hacerse a través del portal de Argo CD, ejecutando manifiestos sobre el cluster donde está este o bien a través de la linea de comandos. Para este ejemplo voy a utilizar esta última, por lo que necesito instalar el CLI de ArgoCD:

# Windows
choco install argocd-cli
# Linux
brew install argocd
# Mac
brew install argocd

En este caso, ya que no tenemos el portal publicado ni interna ni externamente tenemos que asegurarnos que tenemos el port forwarding al puerto 8080 se está ejecutando y hacer login contra él:

# Login to Argo CD
argocd login localhost:8080
argocd version

A partir de este momento podemos usar argocd para interactuar con él. En el caso de la creación de aplicaciones lo habitual sería dar de alta el repositorio que quieres utilizar:

# Add repo with Helm chart
REPO_URL="<YOUR_REPO_URL>"
USER_NAME="<USER_NAME_FOR_THE_REPO>"
PASSWORD="<PASSWORD>"
argocd repo add $REPO_URL \
--name <REPO_NAME> \
--type git \
--username $USER_NAME \
--password $PASSWORD \
--project tour-of-heroes

Esto es útil cuando haces uso de proyectos dentro de Argo CD y otorgas permisos sobre cuáles pueden usar un repositorio u otro. También está bien tener en el apartado Repositories un inventario de todos los que se usan con un nombre descriptivo.

Una vez que tenemos el repositorio ha llegado el momento de crear la aplicación. Para que esta funcione con Argo CD image updater tenemos que asociar a la misma una serie de anotaciones:

# Create an application with the Argo CD Image Updater
argocd app create tour-of-heroes-helm-dev \
--repo $REPO_URL \
--path tour-of-heroes-chart \
--revision dev \
--dest-namespace tour-of-heroes-helm \
--dest-server https://kubernetes.default.svc \
--sync-policy auto \
--sync-option "CreateNamespace=true" \
--annotations "argocd-image-updater.argoproj.io/image-list=api=$ACR_NAME.azurecr.io/tourofheroesapi, web=$ACR_NAME.azurecr.io/tourofheroesweb" \
--annotations "argocd-image-updater.argoproj.io/api.helm.image-name=api.image.repository" \
--annotations "argocd-image-updater.argoproj.io/api.helm.image-tag=api.image.tag" \
--annotations "argocd-image-updater.argoproj.io/api.pull-secret=pullsecret:argocd/acr-credentials" \
--annotations "argocd-image-updater.argoproj.io/api.update-strategy=latest" \
--annotations "argocd-image-updater.argoproj.io/web.helm.image-name=image.repository" \
--annotations "argocd-image-updater.argoproj.io/web.helm.image-tag=image.tag" \
--annotations "argocd-image-updater.argoproj.io/web.pull-secret=pullsecret:argocd/acr-credentials" \
--annotations "argocd-image-updater.argoproj.io/web.update-strategy=latest"

En este ejemplo estoy usando un chart, que tiene dos subcharts, y dos imágenes para actualizar: una web, que es el chart principal, y una API, que es uno de los subcharts. Es por ello que tengo que tener una configuración por cada una de ellas con el listado de las imágenes a actualizar (puedes usar un alias para reutilizarlo en el resto de anotaciones), el nombre de la propiedad en Helm, que hace referencia a cada imagen, cuáles son las propiedades que hacen referencia a los tags, qué estrategia de actualización quiero seguir y por último dónde están las credenciales que permiten el acceso al registro para comprobar las nuevas versiones. Una vez desplegado la aplicación se generará con la versión que tenemos versionada en el archivo values.yaml y cada vez que los desarrolladores provoquen la ejecución de la pipeline de CI, para validar y generar la nueva imagen, esta herramienta se percatará una vez subida la misma al ACR:

Logs de Argo CD image updater

Cuando esto pase lo que ocurrirá es que a nivel de aplicación de Argo CD actualizará los valores correspondientes que se verán de la siguiente manera:

Argo CD image updater actualiza la aplicación gestionada por Argo CD

Por un lado verás la versión actualizada y si te posicionas encima verás el valor original que venía de la configuración.

¡Saludos!