Cómo escalar con KEDA agentes de Azure DevOps en un clúster de AKS con autoescalado

En el vídeo de esta semana compartí contigo cómo puedes utilizar AKS para hospedar los agentes que se encarguen de los trabajos solicitados en nuestras pipelines de Azure DevOps.

En la prueba de concepto, y siguiendo la documentación de KEDA, podíamos ver cómo según íbamos pidiendo ejecutarse las pipelines a través de la interfaz de Azure DevOps iban aumentando el número de pods que actúan como agentes de nuestra organización. Sin embargo, con la configuración proporcionada en el vídeo, y la que podemos encontrar en la documentación de KEDA, si te lías a pedirle que ejecute pipelines llega un momento en el que los pods comienzan a dar error.

Esto es así porque no se han establecido al menos las requests necesarias para el contenedor que aloja este Pod, de tal forma que nuestro clúster se asegure de que no mueren por falta de recursos. Por suerte esto tiene fácil solución 😙

Añadir los request al despliegue de los agentes

El script número tres del vídeo, el cuál se encarga de aplicar los manifiestos con la configuración tanto para el deployment de los agentes como de KEDA, necesita una ligera modificación:

kubectl create secret generic azdevops-pat --from-literal=personalAccessToken=$PAT
cat <<EOF | kubectl apply -f -
# cat <<EOF > azdevops-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: azdevops-deployment
  labels:
    app: azdevops-agent
spec:
  replicas: 1
  selector:
    matchLabels:
      app: azdevops-agent
  template:
    metadata:
      labels:
        app: azdevops-agent
    spec:
      containers:
      - name: azdevops-agent
        image: $ACR_NAME.azurecr.io/ado-agent:$IMAGE_ID
        env:
          - name: AZP_URL
            value: "https://dev.azure.com/$ORGANIZATION_NAME"
          - name: AZP_POOL
            value: "$AGENT_POOL_NAME"
          - name: AZP_TOKEN
            valueFrom:
              secretKeyRef:
                name: azdevops-pat
                key: personalAccessToken
        resources:
          requests:
            cpu: 100m
            memory: 1Gi
        volumeMounts:
        - mountPath: /var/run/docker.sock
          name: docker-volume
      volumes:
      - name: docker-volume
        hostPath:
          path: /var/run/docker.sock
EOF
# Let create KEDA configuration to scale the agents
# cat <<EOF > keda-config.yaml
cat <<EOF | kubectl apply -f -
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: pipeline-trigger-auth
spec:
  secretTargetRef:
    - parameter: personalAccessToken
      name: azdevops-pat
      key: personalAccessToken
---
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: azure-pipelines-scaledobject
spec:
  scaleTargetRef:
    name: azdevops-deployment
  minReplicaCount: 1
  maxReplicaCount: 5 
  triggers:
  - type: azure-pipelines
    metadata:
      poolName: "$AGENT_POOL_NAME"
      organizationURLFromEnv: "AZP_URL"
    authenticationRef:
     name: pipeline-trigger-auth
EOF

En este lo único que he hecho ha sido añadirle al despliegue el apartado resources donde le especifico las requests que pienso que necesitan mis agentes. Con ello me aseguro de que mi clúster de Kubernetes se va a preocupar de estos pods y no solo va a hacer un Best Effort.

Determinar la cantidad de recursos que necesitan mis agentes con Vertical Pod Autoscaler

Por otro lado, para asegurarnos de que no nos quedamos cortos, ni tampoco nos pasamos con las requests, lo ideal sería también habilitar VPA en nuestro clúster de AKS, lo que podemos hacer de forma sencilla tanto en la creación:

# Create AKS cluster with KEDA and VPA enabled
az aks create \
--resource-group $RESOURCE_GROUP \
--name $AKS_NAME \
--enable-keda \
--generate-ssh-keys \
--attach-acr $ACR_NAME \
--node-vm-size Standard_B2ms \
--node-count 1 \
--enable-cluster-autoscaler \
--min-count 1 --max-count 3 \
--enable-vpa

Como actualizando el que ya tenemos:

az aks update -n $AKS_NAME -g $RESOURCE_GROUP --enable-vpa

Y luego por último crear un recurso del tipo Vertical Pod Autoscaler para el despliegue de los agentes:

apiVersion: "autoscaling.k8s.io/v1"
kind: VerticalPodAutoscaler
metadata:
  name: agents-vpa
spec:
  updatePolicy:
    updateMode: "Off"
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: azdevops-deployment
  resourcePolicy:
    containerPolicies:
      - containerName: '*'
        # minAllowed:
        #   cpu: 100m
        #   memory: 50Mi
        maxAllowed:
          cpu: 1
          memory: 3Gi
        controlledResources: ["cpu", "memory"]

¡Saludos!