Primeros pasos con Kubernetes (VII): Autoscaling

Hemos visto anteriormente como podemos configurar el número de instancias de una aplicación y modificarla según nuestras necesidades. Esta característica nos permite evitar dejar sin servicio ante la caída de una máquina, pero una aplicación que se encuentra replicada continuamente y siempre en el mismo número de instancias tiene los siguientes problemas:


  • Se están gastando recursos innecesarios si la carga del sistema es nula o muy baja en un momento dado.
  • No puede cubrir la demanda de peticiones si hay un pico imprevisto de peticiones en un momento dado.


Ante estos escenarios, veremos como podemos configurar un elemento de Kubernetes llamado “Horizontal Pod Autoscaling” que va replicando instancias de un Pod según la carga del sistema. Se define un rango mínimo y máximo de instancias y el % de uso de CPU a partir de la cual empieza a incrementar el número de replicas si lo supera o reducirlas si el % se encuentra por debajo.

Para poder realizar todo esto, lo primero que deberemos hacer es asignar una cantidad de CPU (de toda la que tiene el nodo donde está corriendo nuestra aplicación) a nuestro Pod, tal como vemos en nuestro fichero YAML de Deployment.



apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: hello
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
      - name: hello
        image: gcr.io/k8slabs-142707/hello:v1
        ports:
        - containerPort: 8081
        resources:
          requests:
            cpu: 10m
        readinessProbe:
          httpGet:
            path: /
            port: 8080
          periodSeconds: 2
          timeoutSeconds: 1
          successThreshold: 2
          initialDelaySeconds: 1
        livenessProbe:
          httpGet:
            path: /
            port: 8080
          timeoutSeconds: 1
          initialDelaySeconds: 10


En nuestro caso, los nodos del cluster son de 1 CPU (1000m) y configuramos el Pod indicando que para funcionar el requiere 10m de CPU (un 1% de la CPU disponible).

Ahora definiremos el Horizontal Pod Autoscaling.


apiVersion: extensions/v1beta1
kind: HorizontalPodAutoscaler
metadata:
  name: hello
spec:
  scaleRef:
    kind: Deployment
    name: hello
    subresource: scale
  minReplicas: 1
  maxReplicas: 10
  cpuUtilization:
    targetPercentage: 10


En la configuración le hemos indicado que tiene que monitorizar la CPU del Deployment llamado “hello”, es decir, todos los Pods que este se encargue de desplegar. Durante la monitorización le hemos indicado que cuando se detecte que la media de uso de CPU de todos los Pods (del Deployment hello) sea superior al 10% de toda la CPU que requiere para funcionar (1% - 10m) entonces se asume que la carga del sistema está empezando a crecer y que podría darse el caso que la calidad del servicio se viera afectada, por lo que se iniciará el proceso de incrementar las replicas hasta un máximo de 10 instancias.

Si la media de uso de CPU de todos los Pods, se encuentra por debajo del umbral del 10%, entonces se asume que la carga del sistema está bajando y por lo tanto ya no se requiere de tantas instancias en el sistema, por lo que el Horizontal Pod Autoscaling procederá a reducir el número de instancias hasta un mínimo de 1.

El tiempo de muestreo para calcular la media de CPU es configurable pero por defecto tarda alrededor de un minuto para recalcular la media de uso. Estos tiempos implican que el incremento/decremento no es instantáneo por lo que quizás se tarda unos 2-3 minutos en que el sistema reaccione.

La asignación de % y usos de CPU son muy subjetivos por los que cada aplicación requerirá de pruebas para detectar los mínimos de CPU necesarios para que el servicio de buenos tiempos, así como los máximos a partir de los cuales se está desperdiciando CPU. Una aplicación que únicamente redirecciona peticiones presumiblemente haga un uso muy limitado de CPU mientras que otra aplicación que se dedica a parsear XML es casi seguro que deberá tener asignaciones de CPU más elevadas.

La mejor manera de tunear estos valores es testear cada aplicación por separado y empezar dándole una pequeña cantidad de CPU y verificar los tiempos de respuesta. Entonces ir incrementando la cantidad de CPU hasta que los tiempos de respuesta son iguales que en la anterior prueba. Llegados ese punto podemos decir que por más CPU que le demos a la aplicación no mejorará sus tiempos de respuesta y por lo tanto se estará desaprovechando CPU que podría usar otras aplicaciones.

Para verificar el funcionamiento, desplegaremos la aplicación y en otra consola iremos lanzando un proceso que vaya haciendo peticiones continuadamente sobre nuestra aplicación hasta que se incremente el número de peticiones y a continuación lo pararemos para ver como se decrementa.


$ kubectl create -f ./apps/hello/service.yml

service "hello" created

$ kubectl create -f ./apps/hello/deployment.yml

deployment "hello" created

$ kubectl create -f ./apps/hello/autoscaler.yml

horizontalpodautoscaler "hello" created

$ kubectl get svc
NAME         CLUSTER-IP     EXTERNAL-IP      PORT(S)   AGE
hello        10.67.252.28   104.199.23.193   80/TCP    1m
kubernetes   10.67.240.1               443/TCP   59m

$ kubectl get pods

NAME                     READY     STATUS    RESTARTS   AGE
hello-3182237304-7brf7   1/1       Running   0          1m

$ kubectl get hpa -w
NAME      REFERENCE          TARGET    CURRENT     MINPODS   MAXPODS   AGE
hello     Deployment/hello   10%          1         10        1m
NAME      REFERENCE          TARGET    CURRENT   MINPODS   MAXPODS   AGE
hello     Deployment/hello   10%       0%        1         10        1m
hello     Deployment/hello   10%       40%       1         10        3m
hello     Deployment/hello   10%       200%      1         10        3m

Ctrl+C

$ kubectl get pods

NAME                     READY     STATUS    RESTARTS   AGE
hello-3182237304-15tm6   1/1       Running   0          48s
hello-3182237304-7brf7   1/1       Running   0          4m
hello-3182237304-q0ak3   1/1       Running   0          48s
hello-3182237304-tqyuk   1/1       Running   0          48s

$ kubectl get hpa -w

NAME      REFERENCE          TARGET    CURRENT   MINPODS   MAXPODS   AGE
hello     Deployment/hello   10%       200%      1         10        5m
NAME      REFERENCE          TARGET    CURRENT   MINPODS   MAXPODS   AGE
hello     Deployment/hello   10%       0%        1         10        5m

Ctrl+C

$ kubectl get pods

NAME                     READY     STATUS        RESTARTS   AGE
hello-3182237304-15tm6   1/1       Terminating   0          5m
hello-3182237304-7brf7   1/1       Running       0          9m
hello-3182237304-q0ak3   1/1       Terminating   0          5m
hello-3182237304-tqyuk   1/1       Terminating   0          5m

$ kubectl get pods

NAME                     READY     STATUS    RESTARTS   AGE
hello-3182237304-7brf7   1/1       Running   0          9m

$ while true; do curl 104.199.23.193; done

!Hello World!Hello World!Hello World…………………………….

Ctrl+C pasados un par o tres de minutos


Todo este proceso como hemos comentado puede llevar un tiempo en que tengamos el uso de CPU al 200% o al 0% y todavía no se hayan reescalado las instancias de los Pods, por lo que iremos lanzando los comandos cada 30 segundos y entonces si que veremos los cambios.

No hay comentarios