Skip to main content

Requisitos de Recursos y Límites

Los Pods siempre consumen recursos físicos como memoria y CPU de los Nodos en los que está ejecutándose. El Scheduler no consigue dirigir un Pod a un Nodo que no tiene recurso suficiente para ejecutarlo. Pero más de un Nodo puede tener recurso suficiente para ejecutar un Pod y le corresponde al Scheduler decidir para dónde va.

Para filtrar cuáles son los posibles Nodos, primero se toma en consideración:

    • taints y toleration
    • nodeSelector
    • Affinity
    • resources

Los tres primeros filtrarán los posibles Nodos que podrían recibir los Pods. Entre los posibles, el Scheduler creará un score para cada uno de ellos basado en los recursos disponibles para elegir cuál es mejor para recibir el Pod.

Si ningún Nodo tiene recursos para recibir el Pod, este quedará con el status de Pending.

Alt text

Una observación importante es que cada Contenedor dentro de un Pod puede tener sus recursos definidos individualmente. Los recursos no son atribuidos al Pod como un todo, sino a cada Contenedor que es ejecutado dentro del Pod. Al sumar los recursos de todos los Contenedores dentro de un Pod obtenemos el total de recursos del Pod. Sin embargo es el Scheduler de Kubernetes quien realiza esa atribución de recursos, considerando las necesidades de cada Contenedor.

De hecho, no tendría sentido atribuir recursos directamente al Pod, pues el Scheduler necesita entender y distribuir los recursos de forma adecuada entre los diferentes Contenedores que componen el Pod.

Por defecto un Contenedor no tiene ningún límite definido, o sea, PUEDE CONSUMIR UN NODO ENTERO.

apiVersion: v1
kind: Pod
...
spec:
containers:
- name: busybox
image: busybox
resources:
## requests son los recursos garantizados para el contenedor
requests:
memory: "32Mi"
cpu: "100m"
## Limits es hasta dónde un contenedor puede extenderse
limits:
memory: "128Mi"
cpu: "500m"
- name: nginx
image: nginx
resources:
# Si no se define el requests será igual al limits
limits:
memory: "128Mi"
cpu: "500m"

Entendiendo los valores.

El menor valor para cpu es 0.1 = 100m (m=mili) 1 representaría 1 vcpu o sea, un core de cpu.

Para memoria podemos ver la tabla para conocimiento, pero lo que nos interesa es M (Megabytes) y G (Gigabyte) Usando G estamos usando la base decimal mientras Gi usa la base binaria.

Alt text

1.074G sería igual a 1Gi 1.048M sería igual a 1Mi

Un aspecto a considerar es que un contenedor no opera constantemente en su capacidad máxima. La demanda por recursos, como memoria y CPU, puede variar a lo largo del tiempo conforme el proceso ejecutado por el contenedor necesite más o menos recursos.

Cuando recursos adicionales están disponibles, el proceso puede aprovecharlos para optimizar su desempeño. Sin embargo, este enfoque puede llevar a una situación en que otros procesos en el nodo, incluyendo aquellos del sistema operativo del propio nodo y contenedores de otros pods, acaban siendo perjudicados debido a la asignación excesiva de recursos por parte del contenedor en cuestión.

Para evitar esto podemos definir los límites para frenar ese crecimiento.

En el caso de la CPU, el nodo es capaz de limitar efectivamente el consumo del contenedor para no exceder la cantidad necesaria. Sin embargo, en lo que se refiere a la memoria, esa imposición de límite no es tan eficaz. Si un contenedor intenta exceder el límite de memoria establecido, la acción generalmente es finalizar el pod para evitar que continúe creciendo y afecte el desempeño de otros recursos, resultando en el evento "Out of Memory" (OOM).

Escenarios:

  • Ejecutar sin requests y sin limits no es buena práctica y permite el ahogamiento.
  • Ejecutar con limits sin request sería lo mismo que sub utilizar recursos disponibles si existen.
  • Ejecutar con requests y limits te da un control pero todavía vamos a sub utilizar recursos si están disponibles.
  • Ejecutar con requests garantiza el mínimo para el pod y permite que crezca. Pero en ese caso si todos los pods tienen el request definido, tienen garantía mínima de sus recursos. Este es el escenario ideal.

Generalmente usamos limits para evitar el mal uso de la infraestructura.

Alt text

¿Cómo definir un valor por defecto para todos los contenedores de todos los pods dentro de un namespace?

Un LimitRange proporciona restricciones que pueden:

  • Imponer el uso mínimo y máximo de recursos de computación por pod o contenedor en un namespace.
  • Aplicar solicitud de almacenamiento mínimo y máximo por PersistentVolumeClaim en un espacio de nombres.
  • Imponer una proporción entre request y limits para un recurso en un namespace.
  • Definir request/limits por defecto para recursos de computación en un namespace e inyectarlos automáticamente en contenedores en tiempo de ejecución.

Alt text

Si pods ya están ejecutándose y un LimitRange es creado después, solamente nuevos pods tendrán esos valores configurados automáticamente.

Si quisiéramos limitar todo el recurso de un namespace ante el cluster usaríamos ResourcesQuotas.

Alt text