A veces tenemos escenarios donde el escalado de AKS requiere que las máquinas estén disponibles lo antes posible, y me refiero no solo su creación sino también las imágenes que necesitamos ya precargadas. Hasta ahora, cada vez que escalábamos nuestro clúster, y luego volvía a desescalar, las máquinas restantes se eliminaban, haciendo con ello que no tuvieramos ningún tipo de coste por estas, pero también que todo lo descargado desapareciera. Es por ello que ahora tenemos un modo llamado Deallocate para nuestros node pools, que nos permite apagarlas en lugar de eliminarlas. En este artículo te comparto mis pruebas.
Crear un clúster de prueba
Si todavía no tienes un entorno de AKS, puedes generar uno de manera sencilla con estos pasos:
#Variables
RESOURCE_GROUP="scale-down-mode-deallocate"
LOCATION="northeurope"
AKS_NAME="deallocate-mode-aks"
# Create Resource Group
az group create --name $RESOURCE_GROUP --location $LOCATION
# Create AKS
az aks create \
--resource-group $RESOURCE_GROUP \
--name $AKS_NAME \
--node-vm-size Standard_B4ms \
--node-count 1 \
--generate-ssh-keys
# Get credentials
az aks get-credentials \
--resource-group $RESOURCE_GROUP \
--name $AKS_NAME
Crear nodepools de prueba
Hasta ahora, cuando creábamos un nuevo node pool lo hacíamos de la siguiente forma:
SCALE_DOWN_DELETE_NODE_POOL="delete"
# Create a nodepool with delete mode
az aks nodepool add \
--node-count 10 \
--node-osdisk-type Managed \
--max-pods 10 \
--name $SCALE_DOWN_DELETE_NODE_POOL \
--cluster-name $AKS_NAME \
--resource-group $RESOURCE_GROUP
Cuando finaliza la creación tenemos un Virtual Machine Scale Set con diez máquinas ejecutándose:
Si desescalamos el mismo:
# Scale down to 2
az aks nodepool scale \
--node-count 2 \
--name $SCALE_DOWN_DELETE_NODE_POOL \
--cluster-name $AKS_NAME \
--resource-group $RESOURCE_GROUP
Este eliminará las máquinas sobrantes en el proceso, desapareciendo estas del apartado Nodes o Instances, en el VMSS:
Ahora vamos a crear otro pool con el modo Deallocate:
SCALE_DOWN_DEALLOCATE_NODE_POOL="deallocate"
# Create a nodepool with deallocate mode
az aks nodepool add \
--node-count 10 \
--scale-down-mode Deallocate \
--node-osdisk-type Managed \
--name $SCALE_DOWN_DEALLOCATE_NODE_POOL \
--cluster-name $AKS_NAME \
--resource-group $RESOURCE_GROUP
Como ves, en este ejemplo lo que hemos hecho ha sido añadir un nuevo parámetro llamado –scale-down-mode Deallocate (necesitas tener la última versión de Azure CLI), que nos va a permitir justamente eso. El comportamiento durante el escalado la primera vez será exactamente igual, es decir que creará las máquinas, pero lo que cambia es el desescalado:
# Scale down to 2
az aks nodepool scale \
--node-count 2 \
--name $SCALE_DOWN_DEALLOCATE_NODE_POOL \
--cluster-name $AKS_NAME \
--resource-group $RESOURCE_GROUP
En este caso te darás cuenta de que efectivamente las máquinas no desaparecen sino que se apagan, haciendo que todo lo que hubiera en ellas, como las imágenes descargadas previamente, permanezcan ahí.
Y la próxima vez que escales en lugar de crear máquinas nuevas utilizará las previamente apagadas.

Para un escenario como el que comentaba con OpenKruise y la precarga de imágenes pesadas esta aproximación nos puede ayudar a tener máquinas siempre preparadas antes de ser necesarias, agilizando en este sentido el tiempo de escalado. Una configuración para el ImagePullJob podría ser esta:
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)
matchLabels:
agentpool: deallocate
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
De esta forma, nos aseguramos que el nodepool llamado deallocate tendrá la imagen mmlspark cuando creamos los 10 nodos y cuando apagamos 8 de ellos los mismos seguirán manteniendo esta para cuando necesitemos escalar, sin necesidad de descargarla de nuevo como ocurría hasta ahora. Incluso si la imagen se actualiza, si los Dockerfile están correctamente diseñados muchas de las capas se podrán reaprovechar y solamente se descargará aquellas que hayan cambiado.
Puedes probar con qué rapidez se ejecuta un pod en una máquina del nodepool deallocate vs. el nodepool delete ejecutando estos dos comandos en paralelo:
# Test in a node from deallocate nodepool
kubectl debug node/aks-deallocate-<NODE_NAME> -it --image=mcr.microsoft.com/mmlspark/release
# Test in a node from delete nodepool
kubectl debug node/aks-delete-<NODE_NAME> -it --image=mcr.microsoft.com/mmlspark/release
Tiempos en el escalado
Otra prueba que he hecho ha sido el tiempo de escalado entre un node pool en modo deallocate y otro en modo delete y estos han sido los resultados de la petición:
Como ves el tiempo de escalado de las máquinas en sí solo es de medio minuto de diferencia aproximadamente, pero si le sumamos la carga de las imágenes para los pods que requieren de estas el modo deallocate será mucho más eficiente en este sentido.
Importante: En este modo debes tener en cuenta que tendrás un coste adicional por los discos de las máquinas virtuales desalocadas.
¡Saludos!