
Al comenzar a trabajar con Kubernetes, a menudo se olvida configurar los recursos de los contenedores. En esta etapa, es suficiente verificar que la imagen de Docker funcione y que se pueda desplegar en el clúster de Kubernetes.
Pero más tarde, se requiere desplegar la aplicación en un clúster de producción junto con otras aplicaciones. Para ello, es necesario asignar recursos al contenedor y asegurarse de que hay suficientes para iniciar y ejecutar la aplicación, sin que surjan problemas en otras aplicaciones en ejecución.
Comando ha traducido un artículo sobre los recursos de los contenedores (CPU y MEM), solicitudes y límites de recursos. Aprenderá qué beneficios ofrecen estas configuraciones y qué sucederá si no se establecen.
Recursos computacionales
Tenemos dos tipos de recursos con las siguientes unidades:
- Unidad Central de Procesamiento (CPU) — núcleos;
- Memoria (MEM) — bytes.
Los recursos se especifican para cada contenedor. En el siguiente archivo YAML del Pod, verá la sección de recursos que contiene los recursos solicitados y límite:
- Recursos solicitados del Pod = suma de los recursos solicitados de todos los contenedores;
- Recursos límite del Pod = suma de los recursos límite de todos los contenedores.
apiVersion: v1
kind: Pod
metadata:
name: backend-pod-name
labels:
application: backend
spec:
containers:
- name: main-container
image: my-backend
tag: v1
ports:
- containerPort: 8080
resources:
requests:
cpu: 0.2 # CPU SOLICITADO: 200m núcleos
memory: "1Gi" # MEM SOLICITADA: 1Gi
limits:
cpu: 1 # USO MÁXIMO DE CPU: 1 núcleo
memory: "1Gi" # USO MÁXIMO DE MEM: 1Gi
- name: other-container
image: other-app
tag: v1
ports:
- containerPort: 8000
resources:
requests:
cpu: "200m" # CPU SOLICITADO: 200m núcleos
memory: "0.5Gi" # MEM SOLICITADA: 0.5Gi
limits:
cpu: 1 # USO MÁXIMO DE CPU: 1 núcleo
memory: "1Gi" # USO MÁXIMO DE MEM: 1GiEjemplo de recursos solicitados y límites
Campo resources.requested de la especificación del Pod — uno de los elementos que se utilizan para encontrar el nodo adecuado. Ya se puede planificar el despliegue del Pod en él. ¿Cómo se busca el nodo adecuado?
Kubernetes se compone de varios componentes, incluidos el nodo maestro o master-nodo (Kubernetes Control Plane). En el nodo maestro hay varios procesos: kube-apiserver, kube-controller-manager y kube-scheduler.
El proceso kube-scheduler se encarga de revisar los nuevos pods creados y buscar posibles nodos de trabajo que cumplan con todas las solicitudes de los pods, incluyendo la cantidad de recursos solicitados. La lista de nodos encontrados por kube-scheduler se clasifica. El pod se programará en el nodo con la puntuación más alta.
¿Dónde se colocará el pod púrpura?
En la imagen se puede ver que kube-scheduler debe programar un nuevo pod púrpura. El clúster de Kubernetes contiene dos nodos: A y B. Como se puede notar, kube-scheduler no puede programar el pod en el nodo A, ya que los recursos disponibles (no solicitados) no cumplen con las solicitudes del pod púrpura. Así, los 1 GB de memoria solicitados por el pod púrpura no caben en el nodo A, ya que el volumen de memoria disponible es de 0.5 GB. Pero el nodo B tiene suficientes recursos. Al final, kube-scheduler decide que el destino del pod púrpura es el nodo B.
Ahora sabemos cómo los recursos solicitados afectan la elección del nodo para ejecutar el pod. Pero, ¿cómo afectan los recursos límites?
Los recursos límites son el umbral que la CPU/MEM no puede sobrepasar. Sin embargo, el recurso de CPU es flexible, por lo que los contenedores que alcanzan los valores límite de CPU no provocarán la finalización del pod. En su lugar, se activará el limitador de CPU. Si se alcanza el límite de uso de MEM, el contenedor se detendrá debido al OOM-Killer y se reiniciará si así lo permite la configuración de RestartPolicy.
Recursos solicitados y límites en detalle
La relación de recursos entre Docker y Kubernetes
La mejor manera de explicar cómo funcionan los recursos solicitados y los límites es mostrar la relación entre Kubernetes y Docker. En la imagen anterior, puedes ver cómo se relacionan los campos de Kubernetes y las banderas de inicio de Docker.
Memoria: solicitud y límite
containers:
...
resources:
requests:
memory: "0.5Gi"
limits:
memory: "1Gi"
Como se mencionó anteriormente, la memoria se mide en bytes. Basado en , podemos especificar la memoria como un número. Por lo general, es un número entero, como 2678, que significa 2678 bytes. También se pueden usar sufijos. G y Gi, lo importante es recordar que no son equivalentes. El primero es decimal, mientras que el segundo es binario. Como ejemplo, se menciona en la documentación de k8s: 128974848, 129e6, 129M, 123Mi — son prácticamente equivalentes.
El parámetro de Kubernetes limits.memory corresponde a la bandera --memory de Docker. En el caso de request.memory La flecha para Docker está ausente porque Docker no utiliza este campo. ¿Puedes preguntar si realmente es necesario? Sí, lo es. Como ya mencioné, el campo es importante para Kubernetes. Basándose en la información de este campo, kube-scheduler decide en qué nodo programar el Pod.
¿Qué ocurre si se solicita insuficiente memoria?
Si el contenedor alcanza los límites de memoria solicitada, el Pod se coloca en un grupo de Pod que se detienen cuando hay falta de memoria en el nodo.
¿Qué sucederá si se establece un límite de memoria demasiado pequeño?
Si el contenedor excede el límite de memoria, se terminará por OOM-Killed. Y se reiniciará, si es posible según la RestartPolicy, donde el valor por defecto es Siempre.
¿Qué pasará si no se especifica la memoria solicitada?
Kubernetes tomará el límite y lo establecerá como el valor por defecto.
¿Qué puede suceder si no se especifica la memoria límite?
El contenedor no tiene restricciones, puede usar tanta memoria como desee. Sin embargo, si comienza a utilizar toda la memoria disponible del nodo, será terminado por OOM. Luego, el contenedor será reiniciado, si es posible según la RestartPolicy.
¿Qué ocurrirá si no se especifican los límites de memoria?
Este es el peor escenario: el programador no sabe cuántos recursos necesita el contenedor, y esto puede causar problemas severos en el nodo. En este caso, sería bueno tener restricciones predeterminadas en el espacio de nombres (establecidas por LimitRange). No hay restricciones predeterminadas: el Pod no tiene límites, puede usar tanta memoria como desee.
Si la memoria solicitada es mayor de lo que puede ofrecer el nodo, el Pod no será programado. Es importante recordar que Requests.memory no es un valor mínimo. Es una descripción de la cantidad de memoria suficiente para que el contenedor funcione de manera continua.
Normalmente se recomienda establecer el mismo valor para request.memory y limit.memory. De este modo, Kubernetes no programará un Pod en un nodo que tenga suficiente memoria para iniciar el Pod, pero no suficiente para su funcionamiento. Tenga en cuenta: al programar el Pod, Kubernetes solo considera requests.memory, y limits.memory no lo tiene en cuenta.
CPU: solicitud y límite
containers:
...
resources:
requests:
cpu: 1
limits:
cpu: "1200m"
Con la CPU, todo es un poco más complicado. Volviendo a la imagen de la correlación entre Kubernetes y Docker, se puede notar que request.cpu corresponde a --cpu-shares, mientras que limit.cpu corresponde a la bandera cpus en Docker.
La CPU solicitada por Kubernetes se multiplica por 1024: la proporción de ciclos de CPU. Si deseas solicitar 1 núcleo completo, debes agregar cpu: 1, como se mostró arriba.
Solicitar un núcleo completo (proporción = 1024) no significa que tu contenedor lo obtenga. Si tu host tiene solo un núcleo y usas más de un contenedor, todos los contenedores deben compartir la CPU disponible entre ellos. ¿Cómo sucede esto? Veamos la imagen.

Solicitud de CPU — sistema de un solo núcleo
Imaginemos que tienes un sistema host con un núcleo, en el que se ejecutan los contenedores. Mamá (Kubernetes) horneó un pastel (CPU) y quiere compartirlo entre los niños (contenedores). Tres niños quieren un pastel completo (proporción = 1024), otro niño quiere la mitad del pastel (512). Mamá quiere ser justa y hace un cálculo sencillo.
# Сколько пирогов хотят дети?
# 3 ребенка хотят по целому пирогу и еще один хочет половину пирога
cakesNumberKidsWant = (3 * 1) + (1 * 0.5) = 3.5
# Выражение получается так:
3 (ребенка/контейнера) * 1 (целый пирог/полное ядро) + 1 (ребенок/контейнер) * 0.5 (половина пирога/половина ядра)
# Сколько пирогов испечено?
availableCakesNumber = 1
# Сколько пирога (максимально) дети реально могут получить?
newMaxRequest = 1 / 3.5 =~ 28%Según el cálculo, los tres niños recibirán el 28% de un núcleo, y no un núcleo completo. Al cuarto niño le tocará el 14% de un núcleo completo, y no la mitad. Pero todo será diferente si tienes un sistema multinúcleo.

Solicitud de CPU — sistema multinúcleo (4)
En la imagen anterior, se puede ver que tres niños quieren un pastel completo y uno quiere la mitad. Dado que mamá horneó cuatro pasteles, cada uno de sus niños obtendrá lo que desee. En un sistema multinúcleo, los recursos de la CPU se distribuyen entre todos los núcleos disponibles. Si un contenedor está limitado a menos de un núcleo completo de CPU, aún puede utilizarlo al 100%.
Los cálculos anteriores se simplificaron para entender cómo se distribuye la CPU entre los contenedores. Por supuesto, además de los contenedores en sí, hay otros procesos que también utilizan recursos de la CPU. Cuando los procesos en un contenedor están inactivos, otros pueden utilizar su recurso. CPU: "200m" corresponde a CPU: 0,2, lo que significa aproximadamente el 20% de un núcleo.
Ahora hablemos de limit.cpu. La CPU que limita Kubernetes se multiplica por 100. El resultado es la cantidad de tiempo que el contenedor puede utilizar cada 100 microsegundos (cpu-period).
limit.cpu corresponde a la bandera de Docker --cpus. Esta es una nueva combinación de viejas --cpu-period y --cpu-quota. Al establecerlo, indicamos cuántos recursos de CPU disponibles puede usar el contenedor como máximo antes de que comience el throttling:
- cpus — combinación
cpu-periodycpu-quota. cpus = 1.5equivale a establecercpu-período = 100000ycpu-cuota = 150000; - cpu-period — período , por defecto 100 microsegundos;
- cpu-cuota — número de microsegundos dentro
cpu-period, que limita el contenedor.
¿Qué sucederá si se solicita una cantidad de CPU insuficiente?
Si el contenedor necesita más de lo que se establece, robará CPU de otros procesos.
¿Qué ocurrirá si se establece un límite de CPU insuficiente?
Dado que el recurso CPU es regulable, se activará el throttling.
¿Qué pasará si no se especifica la solicitud de CPU?
Al igual que con la memoria, el valor de la solicitud es igual al límite.
¿Qué sucederá si no se indica un límite de CPU?
El contenedor usará tanta CPU como necesite. Si se define una política de CPU por defecto (LimitRange) en el espacio de nombres, este límite se utilizará también para el contenedor.
¿Qué pasará si no se especifica ni la solicitud ni el límite de CPU?
Al igual que con la memoria, este es el peor escenario. El planificador no sabe cuántos recursos necesita su contenedor, y esto puede causar problemas graves en el nodo. Para evitarlo, debe establecer límites por defecto para los espacios de nombres (LimitRange).
Recuerde: si solicita más CPU de la que pueden proporcionar los nodos, el Pod no será programado. Requests.cpu — no es un valor mínimo, sino un valor suficiente para iniciar el Pod y funcionar sin fallos. Si la aplicación no realiza cálculos complejos, lo mejor es establecer request.cpu <= 1 y ejecutar tantas réplicas como sean necesarias.
La cantidad ideal de recursos solicitados o límite de recursos
Hemos aprendido sobre la limitación de recursos computacionales. Ahora es el momento de responder a la pregunta: "¿Cuántos recursos necesita mi Pod para ejecutar la aplicación sin problemas? ¿Cuál es la cantidad ideal?".
Desafortunadamente, no hay respuestas claras a estas preguntas. Si no sabe cómo funciona su aplicación, cuánta CPU o memoria necesita, lo mejor es darle a la aplicación mucha memoria y CPU, y luego ejecutar pruebas de rendimiento.
Además de las pruebas de rendimiento, observe el comportamiento de la aplicación durante una semana en la monitorización. Si los gráficos indican que su aplicación consume menos recursos de los que solicitó, puede reducir la cantidad de CPU o memoria solicitada.
Como ejemplo, vea este . Muestra la diferencia entre los recursos solicitados o el límite de recursos y el uso actual de los recursos.
Conclusión
La solicitud y el límite de recursos ayudan a mantener el funcionamiento del clúster de Kubernetes. Una configuración adecuada de los límites minimiza los costos y mantiene constantemente las aplicaciones en funcionamiento.
En resumen, hay que tener en cuenta varios puntos:
- Los recursos solicitados son la configuración que se tiene en cuenta durante el inicio (cuando Kubernetes planifica el despliegue de la aplicación). Por otro lado, el límite de recursos es importante durante la operación, cuando la aplicación ya está en ejecución en el nodo.
- En comparación con la memoria, la CPU es un recurso regulado. En caso de escasez de CPU, tu Pod no se detendrá, se activará el mecanismo de limitación.
- Los recursos solicitados y el límite de recursos no son valores mínimos y máximos. Al definir los recursos solicitados, garantizas que la aplicación funcionará sin problemas.
- Una buena práctica es establecer la solicitud de memoria igual al límite de memoria.
- Es conveniente establecer solicitado
CPU <= 1, si la aplicación no realiza cálculos complejos. - Si solicitas más recursos de los que hay en el nodo, tu Pod nunca será programado en ese nodo.
- Para determinar la cantidad adecuada de recursos solicitados/límites de recursos, utiliza pruebas de carga y monitoreo.
Espero que este artículo te ayude a entender el concepto básico de limitación de recursos. Y que puedas aplicar este conocimiento en tu trabajo.
¡Éxitos!
Qué más leer:
- .
- .
- .
Fuente: habr.com
