Escalado rápido y puntual de tus pods con Virtual Kubelet

Existen diferentes escenarios donde puedes necesitar de un escalado rápido de tu clúster de manera puntual que hace que el despliegue de nuevos nodos pueda no resultar todo lo ágil que demandas. Es por ello que Microsoft anunció hace un tiempo una implementación llamada Virtual Kubelet, la cual permite “hacerse pasar por nodos” a otras tecnologías, como si de uno más se tratara, pero que pueden ser mucho más rápidos y ágiles a la hora de escalar. En este artículo quiero mostrarte cómo funciona Virtual Kubelet usando Azure Container Instances.

Crear un clúster de prueba

Para este ejemplo voy a crear un clúster de un solo nodo, para que nos sea fácil quedarnos sin recursos rápidamente:

# Variables
RESOURCE_GROUP="Kubelet-Demo"
AKS_NAME="returngis"
LOCATION="northeurope"

# Create an AKs cluster
az group create -n $RESOURCE_GROUP -l $LOCATION
az aks create --resource-group $RESOURCE_GROUP --name $AKS_NAME --node-count 1 --generate-ssh-keys

# Get AKS context
az aks get-credentials -n $AKS_NAME -g $RESOURCE_GROUP

Una vez creado y asignado el contexto a kubectl puedes comprobar que tienes un único nodo:

➜ kubectl get nodes
NAME                                STATUS   ROLES   AGE     VERSION
aks-nodepool1-14445317-vmss000000   Ready    agent   6m57s   v1.15.11

Desplegar aplicación de prueba

Para ver la utilidad de esto con un ejemplo práctico, voy a lanzar en mi nuevo clúster el siguiente despliegue:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
          ports:
            - containerPort: 80
          resources:
            requests:
              memory: 1.5G
              cpu: 1
      tolerations:
        - key: virtual-kubelet.io/provider
          value: azure
          effect: NoSchedule

Como ves, en él he establecido por un lado los recursos que necesitan los pods para poder funcionar, un poco altos :D, y por otro los taints que tolera. Para el caso de Virtual Kubelet es necesario que tolere la clave virtual-kubelet.io/provider, como verás más adelante.
Si una vez desplegado recuperas los pods comprobarás que algunos se han quedado pendientes de desplegar porque no tienes más espacio en tu clúster de un solo nodo:

➜ kubectl get pods -o wide
NAME                                READY   STATUS    RESTARTS   AGE   IP           NODE                                NOMINATED NODE   READINESS GATES
nginx-deployment-65bbbc895f-dprz4   0/1     Pending   0          12s   <none>       <none>                              <none>           <none>
nginx-deployment-65bbbc895f-gzxsg   0/1     Pending   0          12s   <none>       <none>                              <none>           <none>
nginx-deployment-65bbbc895f-rbmph   1/1     Running   0          12s   10.244.0.8   aks-nodepool1-14445317-vmss000000   <none>           <none>

Como necesito ubicarlos de manera rápida y puntual aquí es donde entra en juego Virtual Kubelet.

Desplegar Virtual Kubelet con el conector para Azure Container Instances

Antes de instalar el conector necesitas tener instalado Helm 2 en el clúster.

#Install Helm 2
brew install [email protected]
brew link --force --overwrite [email protected]

kubectl create serviceaccount tiller --namespace kube-system
kubectl create clusterrolebinding tiller-role-binding --clusterrole cluster-admin --serviceaccount=kube-system:tiller
helm init --service-account tiller --upgrade

Una vez desplegado, a través de este comando puedes instalar el conector de manera sencilla:

#Install ACI Connector for Kubelet
az aks install-connector --resource-group $RESOURCE_GROUP --name $AKS_NAME --connector-name aciconnector

En realidad aks install-connector lo que hace por debajo es desplegar un chart de Helm, de ahí que lo tengas que tener instalado.

Este proceso también se puede hacer durante la creación del clúster:

Instalar Virtual Kubelet durante la creación del clúster

Pero quería que vieras primero la necesidad y luego implantarla 🙂

Si ahora recuperas los nodos verás que tienes dos:

➜ kubectl get nodes
NAME                                             STATUS   ROLES   AGE   VERSION
aks-nodepool1-14445317-vmss000000                Ready    agent   10m   v1.15.11
virtual-kubelet-aciconnector-linux-northeurope   Ready    agent   34s   v1.13.1-vk-v0.9.0-1-g7b92d1ee-dev

Además, como ya te adelante en el apartado anterior, este nuevo nodo tiene un taint asociado, que mis pods ya toleran:

➜ kubectl describe node virtual-kubelet-aciconnector-linux-northeurope
Name:               virtual-kubelet-aciconnector-linux-northeurope
Roles:              agent
Labels:             alpha.service-controller.kubernetes.io/exclude-balancer=true
                    beta.kubernetes.io/os=linux
                    kubernetes.io/hostname=virtual-kubelet-aciconnector-linux-northeurope
                    kubernetes.io/os=linux
                    kubernetes.io/role=agent
                    type=virtual-kubelet
Annotations:        node.alpha.kubernetes.io/ttl: 0
CreationTimestamp:  Fri, 26 Jun 2020 08:36:24 +0200
Taints:             virtual-kubelet.io/provider=azure:NoSchedule

El funcionamiento de Virtual Kubelet es sencillo: todos los pods que se han quedado pendiente de desplegar y que toleran este taint serán gestionados por este nodo virtual que, a su vez, utilizará Azure Container Instances para desplegarlos. Si vuelves a recuperar los pods desplegados, comprobarás que los que estaban pendientes ahora están “ejecutándose” en tu nuevo nodo virtual:

➜ kubectl get pods -o wide
NAME                                                              READY   STATUS    RESTARTS   AGE    IP              NODE                                             NOMINATED NODE   READINESS GATES
aciconnector-linux-northeurope-virtual-kubelet-for-aks-5c6wn2c8   1/1     Running   0          71s    10.244.0.10     aks-nodepool1-14445317-vmss000000                <none>           <none>
nginx-deployment-65bbbc895f-dprz4                                 1/1     Running   0          3m1s   20.191.54.101   virtual-kubelet-aciconnector-linux-northeurope   <none>           <none>
nginx-deployment-65bbbc895f-gzxsg                                 1/1     Running   0          3m1s   20.54.32.175    virtual-kubelet-aciconnector-linux-northeurope   <none>           <none>
nginx-deployment-65bbbc895f-rbmph                                 1/1     Running   0          3m1s   10.244.0.8      aks-nodepool1-14445317-vmss000000                <none>           <none>

De hecho, si accedes al grupo de recursos que AKS crea para desplegar sus recursos, comprobarás que tienes dos del tipo Container Instances que se corresponden con el despliegue nginx-deployment:

Si escalas el deployment hacia arriba o hacia abajo verás que el número de container instances irá acorde a tus necesidades, mucho más rápido que si tuvieras que añadir nuevos nodos que solo necesitas de manera puntual.

¡Saludos!