Precargar imágenes en los nodos de Kubernetes con OpenKruise

A veces nos encontramos en situaciones donde las imágenes que utilizan nuestros pods son tan pesadas que necesitamos que las mismas estén listas antes de que las aplicaciones comiencen a usarlas, con el fin de ganar tiempo en la escalabilidad. Es por ello que muchos recurren a diferentes técnicas para conseguir este objetivo. Hoy quiero contarte una utilidad de OpenKruise que nos facilita este trabajo a través de un custom resource llamado ImagePullJob.

¿Qué es OpenKruise?

Buscando opciones a este escenario me topé con OpenKruise, un proyecto de la Cloud Native Computing Foundation que nos ofrece diferentes extensiones en forma de CRD (Custom Resource Definitions), desde el punto de vista de la automatización. Uno de esas extensiones es ImagePullJob que está pensada para configurar diferentes imágenes, habitualmente más pesadas de lo habitual, que necesitamos que esté en uno o varios conjuntos de nodos de nuestro clúster, con el objetivo de que las mismas estén listas lo antes posible, antes de ser utilizadas por nuestros desarrollos.

Cómo instalar OpenKruise

Las extensiones de OpenKruise pueden instalarse a través de un chart de Helm:

# Install OpenKruise
# Add openkruise charts repository if you haven't do this.
helm repo add openkruise https://openkruise.github.io/charts/
# [Optional]
helm repo update
# Install the latest version.
helm install kruise openkruise/kruise --version 1.1.0

Cómo configurar ImagePullJob

Para mis pruebas he configurado un par de ejemplos, uno para descargar la imagen de SQL Server (1,48GB):

apiVersion: apps.kruise.io/v1alpha1
kind: ImagePullJob
metadata:
  name: always-sqlserver
spec:
  image: mcr.microsoft.com/mssql/server   # [required] image to pull
  parallelism: 10      # [optional] the maximal number of Nodes that pull this image at the same time, defaults to 1
  selector:            # [optional] the names or label selector to assign Nodes (only one of them can be set)
  #   names:
  #   - node-1
  #   - node-2
    matchLabels:
      agentpool: nodepool1
  # podSelector:         # [optional] label selector over pods that should pull image on nodes of these pods. Mutually exclusive with selector.
  #  pod-label: xxx
  completionPolicy:
    type: Always                  # [optional] defaults to Always
    activeDeadlineSeconds: 1200   # [optional] no default, only work for Alway type
    ttlSecondsAfterFinished: 300  # [optional] no default, only work for Alway type
  pullPolicy:                     # [optional] defaults to backoffLimit=3, timeoutSeconds=600
    backoffLimit: 3
    timeoutSeconds: 300

Y otra para mlspark (2,45GB):

apiVersion: apps.kruise.io/v1alpha1
kind: ImagePullJob
metadata:
  name: always-mmlspark
spec:
  image: mcr.microsoft.com/mmlspark/release   # [required] image to pull
  parallelism: 10      # [optional] the maximal number of Nodes that pull this image at the same time, defaults to 1
  selector:            # [optional] the names or label selector to assign Nodes (only one of them can be set)
  #   names:
  #   - node-1
  #   - node-2
    matchLabels:
      agentpool: nodepool1
  # podSelector:         # [optional] label selector over pods that should pull image on nodes of these pods. Mutually exclusive with selector.
  #  pod-label: xxx
  completionPolicy:
    type: Always                  # [optional] defaults to Always
    activeDeadlineSeconds: 1200   # [optional] no default, only work for Alway type
    ttlSecondsAfterFinished: 300  # [optional] no default, only work for Alway type
  pullPolicy:                     # [optional] defaults to backoffLimit=3, timeoutSeconds=600
    backoffLimit: 3
    timeoutSeconds: 300

Como ves, en la definición puedo elegir un conjunto de nodos a través de varios selectores (por nombres de los nodo, por labels o incluso por podSelector), indicar en cuántos nodos queremos hacer pull a la vez, además de el tiempo de espera o el número de intentos de descarga. Una vez desplegadas estas definiciones puedes comprobar el estado con este comando:

kubectl get nodeimage

y tendrás una salida como esta:

kubectl get nodeimage

También puedes ver el estado de cada job que hace la descarga en los diferentes nodos seleccionados:

k get imagepulljob

Para probar que efectivamente se han cargado las imágenes correctamente en un nodo, puedes usar este comando:

kubectl debug node/<YOUR_NODE_NAME> -it --image=mcr.microsoft.com/mmlspark/release

y comprobarás que la ejecución es súper rápida, al tener precargada la imagen de antemano.

¡Saludos!