Primeros pasos con Kubernetes (VIII): Resource Limits



Anteriormente, hemos visto como una aplicación se puede autoescalar en base a consumos de recursos. Ahora procederemos a ver como podemos asignar un límite de recursos para una aplicación. Imaginemos que tenemos 10 aplicaciones a desplegar, algunas de ellas hacen mucho uso de memoria y poco uso de CPU, pero otras aplicaciones el caso contrario, poca memoria y un uso masivo de CPU. Para poder aprovechar mejor los recursos se puede configurar para cada aplicación el uso de cpu y memoria; de esta manera una aplicación que no use mucha CPU podemos restringir su uso y así permitir que otra aplicación que hace un uso masivo pueda disponer de más CPU en momentos de estres.

Con todo esto también se evita, que si hay un bug en una aplicación y este hace que los tiempos de CPU se disparen (por ejemplo un bucle infinito), la aplicación empiece a consumir el 100% de los recursos y deje al resto con menos CPU de la que necesitan para trabajar. El mismo caso se puede dar si hay un bug de memory leak, si la memoria se encuentra limitada, el bug de memory leak afectará hasta el límite máximo indicado pero no empezará a consumir más memoria que puede ser usada por otras aplicaciones.

Es una buena práctica definir dichos límites para limitar la afectación que pueda ocasionar una aplicación en el resto de aplicaciones que se encuentren en un nodo.

Para ello jugaremos con dos aplicaciones, una de ellas (memory) incrementa su uso de memoria continuamente y sin límite, la otra es el ya conocido Hello World que venimos viendo a lo largo del documento.


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: 8080
        resources:
          limits:
            cpu: 10m


Podemos ver como hemos limitado el uso de CPU a 10m (el 1% de un core de CPU).


apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: memory
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: memory
    spec:
      containers:
      - name: memory
        image: gcr.io/k8slabs-142707/memory:v1
        ports:
        - containerPort: 8080
        resources:
          limits:
            memory: 100Mi



var input = []; // initialise an empty array
do {
  input.push("kkkkkkkkkkkkk");  // the array will dynamically grow
} while (1);


En la aplicación memory podemos ver el incremento de memoria mediante un bucle y la aplicación la hemos limitado a 100Mb de memoria. A continuación lanzaremos las dos aplicaciones y realizaremos las siguientes validaciones.


  1. Observaremos como la aplicación memory se reinicia cada X tiempo, cuando cada Pod supera el límite de 100 Mb de uso. Con esto aseguramos que la aplicación no consuma todos los recursos de memoria de un nodo y afecte al resto de aplicaciones desplegadas en ese nodo. 
  2. Lanzamos llamadas continuas contra la aplicación hello, con lo que haremos que el consumo de CPU se dispare (como ya hemos visto en ejemplos anteriores). Entonces monitorizaremos el consumo de CPU del nodo donde se encuentra desplegado y veremos que no se supera el 1% de consumo de CPU. De hecho veremos que los tiempos de respuesta de la aplicación son más lentos que en pruebas anteriores, dado que ahora el uso de CPU se encuentra restringido.


A continuación podemos ver los scripts que realizan estas pruebas y validaciones.


$ kubectl get pods

NAME                      READY     STATUS             RESTARTS   AGE
hello-2452618930-0h6dt    1/1       Running            0          45s
memory-4172153614-vw388   0/1       CrashLoopBackOff   2          46s

$ kubectl describe po memory-4172153614-vw388

Name:           memory-4172153614-vw388
Namespace:      default
Node:           gke-bs-default-pool-28f5092a-y4wc/10.132.0.4
Start Time:     Tue, 11 Oct 2016 09:51:31 +0000
Labels:         app=memory
                pod-template-hash=4172153614
Status:         Running
IP:             10.64.1.8
Controllers:    ReplicaSet/memory-4172153614
Containers:
  memory:
    Container ID:       docker://d78d9a4cd714ae32084ec9eea2700e6e204d3af5a279f16018b0c4d76028312c
    Image:              gcr.io/k8slabs-142707/memory:v1
    Image ID:           docker://sha256:1728e937497c13d6372e6dad0f995ed8650cea20d08e47eebdd0230ea68baed5
    Port:               8080/TCP
    Limits:
      memory:   100Mi
    Requests:
      cpu:                      100m
      memory:                   100Mi
    State:                      Terminated
      Reason:                   OOMKilled
      Exit Code:                137
      Started:                  Tue, 11 Oct 2016 09:52:58 +0000
      Finished:                 Tue, 11 Oct 2016 09:52:59 +0000
    Last State:                 Terminated
      Reason:                   OOMKilled
      Exit Code:                137
      Started:                  Tue, 11 Oct 2016 09:52:16 +0000
      Finished:                 Tue, 11 Oct 2016 09:52:17 +0000
    Ready:                      False
    Restart Count:              4
    Environment Variables:      
Conditions:
  Type          Status
  Initialized   True
  Ready         False
  PodScheduled  True
Volumes:
  default-token-yeeok:
    Type:       Secret (a volume populated by a Secret)
    SecretName: default-token-yeeok
QoS Tier:       Burstable
Events:
  FirstSeen     LastSeen        Count   From                                            SubobjectPath           Type            Reason          Message
  ---------     --------        -----   ----                                            -------------           --------        ------          -------
  1m            1m              1       {default-scheduler }                                                    Normal          Scheduled       Successfully assigned memory-4172153614-vw388 to gke-bs-default-pool-28f5092a-y4wc
  1m            1m              1       {kubelet gke-bs-default-pool-28f5092a-y4wc}     spec.containers{memory} Normal          Created         Created container with docker id 1c2f549916bc; Security:[seccomp=unconfined]
  1m            1m              1       {kubelet gke-bs-default-pool-28f5092a-y4wc}     spec.containers{memory} Normal          Started         Started container with docker id 1c2f549916bc
  1m            1m              1       {kubelet gke-bs-default-pool-28f5092a-y4wc}     spec.containers{memory} Normal          Created         Created container with docker id 27c37af9dc35; Security:[seccomp=unconfined]
  1m            1m              1       {kubelet gke-bs-default-pool-28f5092a-y4wc}     spec.containers{memory} Normal          Started         Started container with docker id 27c37af9dc35
  1m            1m              2       {kubelet gke-bs-default-pool-28f5092a-y4wc}                             Warning         FailedSync      Error syncing pod, skipping: failed to "StartContainer" for "memory" with CrashLoopBackOff: "Back-off 10s restarting failed container=memory pod=memory-4172153614-vw388_default(49e634c2-8f98-11e6-a4e8-42010a840fc6)"

  1m    1m      1       {kubelet gke-bs-default-pool-28f5092a-y4wc}     spec.containers{memory} Normal  Started         Started container with docker id cf017fe09a72
  1m    1m      1       {kubelet gke-bs-default-pool-28f5092a-y4wc}     spec.containers{memory} Normal  Created         Created container with docker id cf017fe09a72; Security:[seccomp=unconfined]
  1m    1m      2       {kubelet gke-bs-default-pool-28f5092a-y4wc}                             Warning FailedSync      Error syncing pod, skipping: failed to "StartContainer" for "memory" with CrashLoopBackOff: "Back-off 20s restarting failed container=memory pod=memory-4172153614-vw388_default(49e634c2-8f98-11e6-a4e8-42010a840fc6)"

  54s   54s     1       {kubelet gke-bs-default-pool-28f5092a-y4wc}     spec.containers{memory} Normal  Created         Created container with docker id f20653bddc5c; Security:[seccomp=unconfined]
  54s   54s     1       {kubelet gke-bs-default-pool-28f5092a-y4wc}     spec.containers{memory} Normal  Started         Started container with docker id f20653bddc5c
  53s   26s     3       {kubelet gke-bs-default-pool-28f5092a-y4wc}                             Warning FailedSync      Error syncing pod, skipping: failed to "StartContainer" for "memory" with CrashLoopBackOff: "Back-off 40s restarting failed container=memory pod=memory-4172153614-vw388_default(49e634c2-8f98-11e6-a4e8-42010a840fc6)"

  12s   12s     1       {kubelet gke-bs-default-pool-28f5092a-y4wc}     spec.containers{memory} Normal  Created         Created container with docker id d78d9a4cd714; Security:[seccomp=unconfined]
  1m    12s     5       {kubelet gke-bs-default-pool-28f5092a-y4wc}     spec.containers{memory} Normal  Pulled          Container image "gcr.io/k8slabs-142707/memory:v1" already present on machine
  12s   12s     1       {kubelet gke-bs-default-pool-28f5092a-y4wc}     spec.containers{memory} Normal  Started         Started container with docker id d78d9a4cd714
  1m    11s     8       {kubelet gke-bs-default-pool-28f5092a-y4wc}     spec.containers{memory} Warning BackOff         Back-off restarting failed docker container
  11s   11s     1       {kubelet gke-bs-default-pool-28f5092a-y4wc}                             Warning FailedSync      Error syncing pod, skipping: failed to "StartContainer" for "memory" with CrashLoopBackOff: "Back-off 1m20s restarting failed container=memory pod=memory-4172153614-vw388_default(49e634c2-8f98-11e6-a4e8-42010a840fc6)"

$ kubectl get svc

NAME         CLUSTER-IP      EXTERNAL-IP     PORT(S)   AGE
hello        10.67.242.214   104.199.1.118   80/TCP    13m
kubernetes   10.67.240.1               443/TCP   1h
memory       10.67.242.56    130.211.91.75   80/TCP    13m

$ while true; do curl http://104.199.1.118;done

Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!

 Mientras se lanzan las peticiones masivas, en otra consola ejecutamos lo siguiente

$ kubectl get no

NAME                                STATUS    AGE
gke-bs-default-pool-28f5092a-chrz   Ready     1h
gke-bs-default-pool-28f5092a-j9g5   Ready     1h
gke-bs-default-pool-28f5092a-y4wc   Ready     1h

$ kubectl describe no gke-bs-default-pool-28f5092a-y4wc

Name:                   gke-bs-default-pool-28f5092a-y4wc
Labels:                 beta.kubernetes.io/arch=amd64
                        beta.kubernetes.io/instance-type=n1-standard-1
                        beta.kubernetes.io/os=linux
                        cloud.google.com/gke-nodepool=default-pool
                        failure-domain.beta.kubernetes.io/region=europe-west1
                        failure-domain.beta.kubernetes.io/zone=europe-west1-d
                        kubernetes.io/hostname=gke-bs-default-pool-28f5092a-y4wc
Taints:                 
CreationTimestamp:      Tue, 11 Oct 2016 08:33:56 +0000
Phase:
Conditions:
  Type                  Status  LastHeartbeatTime                       LastTransitionTime                      Reason                          Message
  ----                  ------  -----------------                       ------------------                      ------                          -------
  NetworkUnavailable    False   Tue, 11 Oct 2016 10:09:05 +0000         Tue, 11 Oct 2016 10:09:05 +0000         RouteCreated                    RouteController created a route
  OutOfDisk             False   Tue, 11 Oct 2016 10:09:02 +0000         Tue, 11 Oct 2016 08:33:56 +0000         KubeletHasSufficientDisk        kubelet has sufficient disk space available
  MemoryPressure        False   Tue, 11 Oct 2016 10:09:02 +0000         Tue, 11 Oct 2016 08:33:56 +0000         KubeletHasSufficientMemory      kubelet has sufficient memory available
  DiskPressure          False   Tue, 11 Oct 2016 10:09:02 +0000         Tue, 11 Oct 2016 08:33:56 +0000         KubeletHasNoDiskPressure        kubelet has no disk pressure
  Ready                 True    Tue, 11 Oct 2016 10:09:02 +0000         Tue, 11 Oct 2016 08:34:28 +0000         KubeletReady                    kubelet is posting ready status. AppArmor enabled
Addresses:              10.132.0.4,130.211.92.164
Capacity:
 alpha.kubernetes.io/nvidia-gpu:        0
 cpu:                                   1
 memory:                                3788388Ki
 pods:                                  110
Allocatable:
 alpha.kubernetes.io/nvidia-gpu:        0
 cpu:                                   1
 memory:                                3788388Ki
 pods:                                  110
System Info:
 Machine ID:                    ca884066752b8a2220d10f9a57fca3ac
 System UUID:                   7823B355-2309-9DEF-EFBE-2BA9A2554F9C
 Boot ID:                       f6419218-dc9e-4c24-bced-abf3165ddfb6
 Kernel Version:                4.4.14+
 OS Image:                      Google Container-VM Image
 Operating System:              linux
 Architecture:                  amd64
 Container Runtime Version:     docker://1.11.2
 Kubelet Version:               v1.4.0
 Kube-Proxy Version:            v1.4.0
PodCIDR:                        10.64.1.0/24
ExternalID:                     3397695991050214778
Non-terminated Pods:            (4 in total)
  Namespace                     Name                                                            CPU Requests    CPU Limits      Memory Requests Memory Limits
  ---------                     ----                                                            ------------    ----------      --------------- -------------
  default                       hello-2452618930-0h6dt                                          10m (1%)        10m (1%)        0 (0%)          0 (0%)
  default                       memory-4172153614-vw388                                         100m (10%)      0 (0%)          100Mi (2%)      100Mi (2%)
  kube-system                   fluentd-cloud-logging-gke-bs-default-pool-28f5092a-y4wc         80m (8%)        0 (0%)          200Mi (5%)      200Mi (5%)
  kube-system                   kube-proxy-gke-bs-default-pool-28f5092a-y4wc                    100m (10%)      0 (0%)          0 (0%)          0 (0%)
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted. More info: http://releases.k8s.io/HEAD/docs/user-guide/compute-resources.md)
  CPU Requests  CPU Limits      Memory Requests Memory Limits
  ------------  ----------      --------------- -------------
  290m (28%)    10m (1%)        300Mi (8%)      300Mi (8%)
No events.

No hay comentarios