Artículo

Renovate <3 GitOps: automatizando las actualizaciones del software desplegado en Kubernetes

Retrato de Anaís Solís
Anaís Solís
1
1
18 minutos de lectura
3 de junio de 2025
Restaurante futurista con ambiente minimalista en tonos blancos, donde robots humanoides con pantallas táctiles atienden a los clientes. Personas elegantemente vestidas disfrutan de sus comidas o trabajan en laptops. Al fondo, grandes ventanales muestran una ciudad moderna con rascacielos.

Índice

  1. La dificultad de actualizar el software alojado en nuestro clúster
  2. GitOps
  3. Renovate
  4. Conclusión

La dificultad de actualizar el software alojado en nuestro clúster

Administrar un clúster de Kubernetes (k8s) implica mantener el software que alojamos en él. Uno de los mayores desafíos para personas y organizaciones es mantenerse al día con las últimas versiones de software. El problema se agrava si, en vez de utilizar un clúster k8s en la nube de manera gestionada, utilizamos máquinas virtuales o incluso servidores físicos. En esos casos, además de las actualizaciones del software alojado, debemos encargarnos de actualizar k8s y del sistema operativo.

Hoy en día, la seguridad es una preocupación para muchas empresas por lo que es importante mantenerse al día con las actualizaciones de software. Gestionarlas manualmente no es factible, sobre todo porque el problema escala: cuanto más software alojemos, más difícil será mantenerse al día. Básicamente, existen dos maneras de hacerlo manualmente:

  • Una es comprobar a diario si hay nuevas actualizaciones de todo el software y configurar manualmente la nueva versión. Esto en mi opinión es tedioso, lento y desmotivador.
  • La otra es, por ejemplo, reducir este intervalo y hacerlo semanalmente. Puede ser menos tedioso, pero aumenta la posibilidad de que aparezcan una o más vulnerabilidades en cualquier momento, lo que incrementa las posibilidades de que tu aplicación o incluso tu clúster se vean comprometidos.

Afortunadamente, existen maneras de automatizar estas tareas. En este artículo, explicaré cómo podemos lograrlo combinando GitOps y Renovate. Solo explicaré GitOps de forma general, ya que partiré de un repositorio de GitOps ya existente y me centraré más en el uso de Renovate a partir de ahí.

GitOps

GitOps es una metodología que consiste en tener un repositorio Git como única fuente de la verdad para la infraestructura y el despliegue de las aplicaciones, con el objetivo de que la configuración definida en el repositorio sea equivalente a la configuración desplegada realmente.

¿Y cómo se aplica esto a k8s? La idea es tener tus manifiestos de k8s dentro de un repositorio Git, de forma que eso sea lo que se vaya a desplegar en tu clúster. ¡Es como si pudieras ver el estado de tu clúster a través del repositorio!

Para que esto funcione, necesitamos un controlador que compruebe constantemente que los manifiestos definidos en el repositorio coincidan con los desplegados en el clúster. Más concretamente: si hay un cambio en el repositorio, este se modificará en el clúster en consecuencia, y si hay un cambio en el clúster que difiere de lo definido en el repositorio, el controlador aplicará lo que esté en el repositorio, eliminando el cambio en el clúster.

Existen varias herramientas para lograr este comportamiento, pero una de las más populares, y la que usaré en este artículo, es ArgoCD. Esta herramienta es muy útil porque, además de funcionar como un controlador de GitOps, incluye una interfaz web donde puedes ver el estado de tus despliegues y hacer un seguimiento de los cambios en los recursos en tiempo real.

Como quiero centrarme en Renovate, pasaré directamente a la parte donde tenemos ArgoCD instalado y conectado a un clúster y a un repositorio. Para seguir las explicaciones, puedes consultar el repositorio de ejemplo que he creado :).

Renovate

Tenemos entonces un repositorio de manifiestos de GitOps que actúa como la única fuente de la verdad. Nuestro objetivo era mantenernos al día con las actualizaciones de software, ¿verdad? Al usar GitOps sin nada más, no estamos automatizando las actualizaciones de nuestro software. De hecho, como hemos instalado un controlador de GitOps, ¡tenemos otro software que debemos actualizar para estar al día con las últimas versiones!

Pero el primer paso de esta estrategia era contar con un entorno GitOps para configurar nuestra próxima herramienta: Renovate. Renovate es un bot que resuelve nuestro objetivo directamente: gestionar nuestras dependencias automáticamente. Probablemente conozcas Dependabot, un bot que también resuelve este problema. Sin embargo, tiene algunos problemas, por ejemplo, que solo se puede usar en Github. Además, Renovate se ha anunciado en el Thoughtworks Tech Radar de abril de 2025 como una herramienta recomendada, más completa y personalizable que Dependabot.

Renovate tiene muchos casos de uso, el más popular es la actualización automática de dependencias en un proyecto de software. Pero aquí aprenderemos a usarlo también en un repositorio GitOps para actualizar automáticamente el software alojado en un clúster k8s.

No voy a explicar completamente cómo funciona Renovate, ya que ofrece muchas opciones de configuración y uso. Pero daré una breve explicación general de su funcionamiento y luego veremos de forma más práctica cómo implementar algunos casos de uso para actualizar el software alojado.

Renovate funciona como un operador que actualiza cada paquete paso a paso:

  • Renovate comienza clonando un repositorio Git para poder empezar a trabajar en él.
  • Utiliza uno o más managers para extraer dependencias y versiones, por ejemplo, el Kubernetes manager. Los managers detectan dependencias de terceros.
  • Después, utiliza un datasource para comprobar las versiones disponibles de las dependencias. Por ejemplo, el Kubernetes manager utiliza el datasource de Docker para obtener las versiones de las imágenes. Los datasources indican a Renovate cómo buscar nuevas versiones de las dependencias detectadas previamente.
  • Renovate intenta detectar el esquema de versiones correcto para cada tipo de dependencia, lo que le permite identificar la versión más reciente entre todas las opciones disponibles. Esto no suele ser un problema, pero si lo es, se puede configurar manualmente el esquema de versiones para una dependencia específica.
  • Finalmente, Renovate envía una pull request (PR) para cada dependencia que tenga una actualización disponible. Se puede configurar Renovate para que realice un merge automático de algunas PR, por ejemplo, si las actualizaciones representan versiones de tipo patch que no deberían alterar el comportamiento habitual de la aplicación.

Configuración en el repositorio

Ahora ya podemos empezar a configurar Renovate en nuestro repositorio GitOps. ¡Recuerda consultar el repositorio de ejemplo que he mencionado anteriormente!

Empezamos creando el siguiente fichero renovate.json en el directorio raíz del repositorio:

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
}

Después necesitaremos configurar Renovate para que se ejecute en nuestro repositorio. Existen múltiples maneras de hacerlo, pero en este caso lo ejecuto usando GitHub Actions con un token de acceso personal (PAT):

name: renovate

on:
  workflow_dispatch:
  repository_dispatch:
  push:
    branches:
      - main

jobs:
  renovate:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
      - name: Renovate
        uses: renovatebot/github-action@6312f35b96c47b1e3cd516f42616ce96145a0982 # v41.0.15
        with:
          token: ${{ secrets.RENOVATE_TOKEN }}
        env:
          RENOVATE_REPOSITORIES: ${{ github.repository }}

Casos de uso con Renovate

En este punto, Renovate será ejecutado por cada commit realizado en el repositorio. Ahora ya podemos empezar a describir diferentes casos de uso para actualizar las dependencias en nuestro clúster automáticamente.

Actualizando las tags de las imágenes

Este es uno de los casos de uso más comunes. En el repositorio de ejemplo, tenemos el fichero manifests/nginx/deployment.yaml con la imagen nginx:1.27.5-alpine-slim:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    spec:
      containers:
      - name: nginx
        image: nginx:1.27.5-alpine-slim  # The image tag
        ...

Lo que necesitamos es configurar Renovate para que detecte y actualice las tags de las imágenes en nuestros manifiestos de k8s. Sin embargo, los nombres de fichero de los manifiestos de Kubernetes no tienen una nomenclatura estandarizada. Solo sabemos que son archivos YAML y nada más. Por lo tanto, debemos indicarle específicamente a Renovate dónde puede encontrarlos.

Para que esto funcione, tendremos que modificar el fichero renovate.json. Técnicamente, necesitaremos sobrescribir la propiedad fileMatch del manager de kubernetes (que es una lista vacía por defecto) para que localice los manifiestos en la carpeta manifests:

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
 
  "kubernetes": {
    "fileMatch": [
      "manifests/.+\\.yaml$"
    ]
  }
}

La propiedad fileMatch consiste en una lista de expresiones regulares, donde todas se tienen en cuenta para elegir las rutas en el repositorio que contienen los manifiestos de k8s. El hecho de que se trate de una lista de expresiones regulares en lugar de una sola nos proporciona mucha flexibilidad para configuraciones más complejas (¡usaremos varias expresiones regulares para el último caso!).

Después de hacer commit de este cambio, Renovate buscará imágenes que se puedan actualizar en los manifiestos. El Deployment anterior de nginx tenía una versión desactualizada de la imagen. Renovate lo ha detectado y abre una PR para actualizarla a la última versión:

Algo que cabe destacar es que Renovate seleccionó automáticamente la tag correcta para la imagen. No seleccionó la tag nginx:1.28.0, sino la nginx:1.28.0-alpine-slim.

Esto se debe a que el componente de versionado de Docker de Renovate intenta seguir las convenciones comunes para las tags de imagen de Docker. En este caso, Renovate trata el texto después del primer guion de la tag como un indicador de plataforma o compatibilidad, por lo que cada actualización de tag válida para esa imagen debe tener el prefijo -alpine-slim.

Actualizando aplicaciones desplegadas con Helm

Los Helm charts son una forma popular de desplegar aplicaciones. Proporcionan una capa de abstracción donde no es necesario gestionar directamente los manifiestos de k8s. Simplemente se parametriza el chart y todos los recursos se desplegarán automáticamente.

En ArgoCD, para desplegar una aplicación mediante un Helm chart, solo se necesita un recurso Application. Por ejemplo, para desplegar ingress-controller mediante su Helm chart oficial, podemos usar el fichero apps/ingress-nginx.yaml:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: ingress-nginx
  namespace: argocd
spec:
  project: default
  source:
    chart: ingress-nginx
    repoURL: https://kubernetes.github.io/ingress-nginx
    targetRevision: 4.11.6  # The chart version
  destination:
    server: https://kubernetes.default.svc
    namespace: ingress-nginx
  syncPolicy:
    automated:
      selfHeal: true
    syncOptions:
    - CreateNamespace=true

Sin embargo, no podemos usar el manager kubernetes de Renovate para esto, ya que se trata de una Custom Resource Definition (CRD) de ArgoCD. ¡Pero hay solución a esto! Renovate cuenta con un manager argocd que permite gestionar las actualizaciones de estas CRD.

Pero, de nuevo, tenemos el mismo problema que en el primer caso. Las CRD de ArgoCD, al igual que los manifiestos de Kubernetes, no tienen una nomenclatura estandarizada, por lo que debemos especificar a Renovate dónde encontrarlas. En este caso, como las almacenamos en la carpeta apps/, podemos configurar el atributo fileMatch del manager argocd como corresponde:

{
  "argocd": {
    "fileMatch": [
      "apps/.+\\.yaml$"
    ]
  }
}

Con este cambio Renovate detectará la versión del chart y realizará una PR si hay una nueva:

Actualizando la versión de manifiestos externos

En este caso hemos incluido los manifiestos de ArgoCD en nuestro repositorio de GitOps haciendo referencia a ellos mediante Kustomize. Los manifiestos se encuentran en un repositorio Git externo. Podría parecer un poco hacky, pero este es un método de instalación oficial de ArgoCD. El fichero en cuestión es manifests/argocd/kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: argocd
resources:
  - https://raw.githubusercontent.com/argoproj/argo-cd/v2.14.10/manifests/install.yaml

La versión de ArgoCD se identifica con v2.14.10 en la URL. Esta es la que Renovate actualizará. Sin embargo, automatizar esto no es tan fácil, ya que no existe un manager de Renovate predefinido que pueda detectar una versión definida en una URL, como hacemos aquí.

Aquí es donde brilla Renovate, ya que nos permite crear managers personalizados para buscar dependencias que otros managers no pueden. Los managers personalizados pueden usar expresiones regulares para buscar el número de versión de la dependencia. Así es como se puede definir un manager personalizado para detectar las actualizaciones en los manifiestos de ArgoCD:

"customManagers": [
    {
      "customType": "regex",
      "fileMatch": [
        "manifests/argocd/kustomization\\.yaml$"
      ],
      "matchStrings": [
        "https://raw\\.githubusercontent\\.com/argoproj/argo-cd/v(?<currentValue>\\S+)/manifests/install\\.yaml"
      ],
      "datasourceTemplate": "github-tags",
      "depNameTemplate": "argoproj/argo-cd",
      "versioningTemplate": "semver"
    }
  ]

Tenemos la propiedad fileMatch que hemos definido en los casos anteriores, pero en este caso especificamos solo el archivo que queremos gestionar con este manager: manifests/argocd/kustomization.yaml.

También definimos datasourceTemplate y depNameTemplate para indicar a Renovate que busque una nueva versión en las tags del repositorio de GitHub argoproj/argo-cd, utilizando el datasource github-tags.

Con estos cambios, Renovate puede detectar la versión correctamente y actualizar la URL de los manifiestos si hay una nueva versión de ArgoCD. ¿A que mola?

Actualizando el agente y el servidor de Kubernetes

Este último caso es interesante para clústers que utilizan k3s. k3s es una distribución de Kubernetes ligera, enfocada en entornos de IoT o con recursos limitados. La recomiendo personalmente para homelabbing 🙂

K3s proporciona un método para actualizar el agente y el servidor de k8s automáticamente, usando un controlador y una CRD. Sin embargo, tenemos control limitado al respecto. Puedes, como máximo, especificar un intervalo de tiempo para que se realice la actualización. Pero no sabemos cuándo se lanzará una nueva versión de k3s ni a qué versión específica se actualizará.

¿Y si pudiéramos gestionar las actualizaciones de k3s con Renovate? Podríamos recibir una notificación mediante una PR cuando haya una actualización disponible y aplicarla en cualquier momento, además de tener registros de las actualizaciones aplicadas previamente, al igual que con otras actualizaciones de software de Renovate.

¡Pues también podemos usar Renovate para esto! Pero requerirá todavía más trabajo que en los casos anteriores. Estos son los manifiestos que necesitamos para las actualizaciones automáticas de k3s:

  • El system-upgrade-controller, que gestiona la actualización de k3s en los nodos.
  • La CRD del Plan, que se utiliza para especificar las condiciones de la actualización.
  • El Plan para el agente de k3s.
  • El Plan para el servidor de k3s.

Los dos primeros manifiestos no los gestionamos nosotros, sino el equipo de Rancher, y queremos que se actualicen si se producen cambios, ¿verdad? Así que, en lugar de descargarlos y aplicarlos, podemos gestionarlos como hicimos con los manifiestos de ArgoCD, incluyéndolos como fuentes externas. Podemos usar Kustomize para esto, como se define en el archivo manifests/k3s/kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/rancher/system-upgrade-controller/releases/download/v0.15.2/system-upgrade-controller.yaml
- https://github.com/rancher/system-upgrade-controller/releases/download/v0.15.2/crd.yaml
- server-plan.yaml
- agent-plan.yaml

Debemos tener en cuenta que también debemos agregar el Plan para el agente y el Plan para el servidor a los recursos de Kustomize. Para ellos, usaremos el siguiente fichero manifests/k3s/agent-plan.yaml:

apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
  name: agent-plan
  namespace: system-upgrade
spec:
  concurrency: 1
  cordon: true
  nodeSelector:
    matchExpressions:
    - key: node-role.kubernetes.io/control-plane
      operator: DoesNotExist
  prepare:
    args:
    - prepare
    - server-plan
    image: rancher/k3s-upgrade
  serviceAccountName: system-upgrade
  upgrade:
    image: rancher/k3s-upgrade
  version: "v1.32.3+k3s1"

Y el siguiente fichero manifests/k3s/server-plan.yaml:

apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
  name: server-plan
  namespace: system-upgrade
spec:
  concurrency: 1
  cordon: true
  nodeSelector:
    matchExpressions:
    - key: node-role.kubernetes.io/control-plane
      operator: In
      values:
      - "true"
  tolerations:
  - effect: NoSchedule
    key: node-role.kubernetes.io/master
    operator: Exists
  serviceAccountName: system-upgrade
  upgrade:
    image: rancher/k3s-upgrade
  version: "v1.32.3+k3s1"

Esto no es suficiente para las actualizaciones automáticas. Hemos especificado la versión de k3s en el campo version del plan para indicar que queremos actualizar a esa versión. Sin embargo, cuando exista una nueva actualización, el campo debería cambiar según corresponda. También queremos que la versión del controlador y de la CRD de Plan cambien si hay una nueva versión para ellos. Vamos a ver cómo podemos usar Renovate para actualizarlos.

Podemos configurar fácilmente las actualizaciones para los manifiestos de Rancher, porque ya sabemos cómo gestionarlas. Este manager personalizado, basado en el manager de ArgoCD que creamos anteriormente, puede usarse para realizar el seguimiento de las actualizaciones:

"customManagers": [
    ...
    {
      "customType": "regex",
      "fileMatch": [
        "^k3s/kustomization\\.yaml$"
      ],
      "matchStrings": [
        "https://github\\.com/rancher/system-upgrade-controller/releases/download/v(?<currentValue>\\S+)/.*\\.yaml"
      ],
      "depNameTemplate": "rancher/system-upgrade-controller",
      "datasourceTemplate": "github-tags",
      "versioningTemplate": "semver"
    },
]

Para las actualizaciones de la versión de k3s, el proceso es un poco más complejo. Además de usar un manager personalizado, usaremos también un datasource personalizado. Se pueden crear datasources personalizados especificando un endpoint HTTP y una query JSONata para indicar a Renovate cómo extraer información del contenido de la respuesta (para más información sobre el formato de esta plantilla, consulta los enlaces en este párrafo).

Para localizar y extraer la versión de k3s, usaremos el siguiente recurso JSON. En este recurso se especifican todos los canales de actualización disponibles, junto con la última versión de k3s de cada uno. Para crear un datasource que analice y seleccione la última versión en el canal “stable”, podemos usar el siguiente código:

"customDatasources": [
        "k3s": {
      "defaultRegistryUrlTemplate": "https://update.k3s.io/v1-release/channels",
      "transformTemplates": [
        "{\"releases\":[{\"version\": $$.(data[id = 'stable'].latest),\"sourceUrl\":\"https://github.com/k3s-io/k3s\",\"changelogUrl\":$join([\"https://github.com/k3s-io/k3s/releases/tag/\",data[id = 'stable'].latest])}],\"sourceUrl\": \"https://github.com/k3s-io/k3s\",\"homepage\": \"https://k3s.io/\"}"
      ]
]

El último paso es crear un manager personalizado para encontrar el campo version en los planes de actualización y permitir que use nuestro datasource personalizado para buscar actualizaciones. En lugar de usar expresiones regulares, podemos usar una consulta JSONata para localizar al campo version en el manifiesto:

"customManagers": [
    ...
    {
      "customType": "jsonata",
      "fileFormat": "yaml",
      "fileMatch": [
        "manifests/k3s/agent-plan\\.yaml$",
        "manifests/k3s/server-plan\\.yaml$"
      ],
      "matchStrings": [
        "spec.{ \"depName\": \"k3s\", \"currentValue\": version }"
      ],
      "depNameTemplate": "k3s",
      "versioningTemplate": "semver-coerced",
      "datasourceTemplate": "custom.k3s"
    },
]

Si commiteamos esto, ¡podemos ver que Renovate ha detectado actualizaciones en estas dependencias, realizando dos commits diferentes para actualizarlas!

Conclusión

Renovate es una herramienta excelente, no solo para gestionar actualizaciones de software, sino también actualizaciones de software alojado en nuestra infraestructura, como hemos visto. Puede reducir significativamente la carga de trabajo que conlleva la actualización de dependencias, impactando positivamente en la seguridad.

Su amplia compatibilidad con diversas plataformas de alojamiento Git puede ayudar a las empresas a reducir el vendorlocking con proveedores como GitHub, que Dependabot puede causar, además de ofrecer mejor compatibilidad con tecnologías poco comunes. Además, algunas características, como los managers y datasources personalizados, pueden ayudar a gestionar casos específicos, comunes en proyectos grandes y empresariales, que de otro modo se gestionarían manualmente y podrían pasar por alto muchas actualizaciones importantes.

Personalmente, considero que el uso de Renovate para la infraestructura GitOps es muy interesante y fácil de configurar. Espero que esta publicación ayude a difundir su uso.

Retrato de Anaís Solís
Anaís Solís
He programando previamente en múltiples lenguajes y entornos, pero hoy en día mi rol ha dado un giro hacia la cloud y la automatización, especialmente con Google Cloud y Kubernetes. Conocer ambos mundos me convierte en una persona que se puede integrar fácilmente en un entorno DevOps. Siempre pienso que las cosas se pueden hacer mejor de lo que están ahora, no solo en el trabajo, sino en la vida en general.

Otros artículos que te pueden interesar

EuroAgile Song Contest
Si Agile fuera una canción, ¿qué estilo tendría? ¿Jazz, por su improvisación y colaboración constante?; ¿rock, por su energía y capacidad de adaptación?; ¿electrónica, por su innovación y…
Compartir en LinkedIn
Compartir en X

Copiar el link