En esta página se describen las prácticas recomendadas generales para diseñar clústeres de GKE escalables. Puede aplicar estas recomendaciones en todos los clústeres y cargas de trabajo para conseguir un rendimiento óptimo. Estas recomendaciones son especialmente importantes para los clústeres que tienes previsto escalar en gran medida. Las prácticas recomendadas están dirigidas a los administradores responsables de aprovisionar la infraestructura y a los desarrolladores que preparan los componentes y las cargas de trabajo de Kubernetes.
¿Qué es la escalabilidad?
En un clúster de Kubernetes, la escalabilidad se refiere a la capacidad del clúster para crecer sin salirse de sus objetivos de nivel de servicio (SLOs). Kubernetes también tiene su propio conjunto de SLOs.
Kubernetes es un sistema complejo y su capacidad de escalado depende de varios factores. Algunos de estos factores son el tipo y el número de nodos de un pool de nodos, los tipos y el número de pools de nodos, el número de pods disponibles, cómo se asignan los recursos a los pods y el número de servicios o back-ends que hay detrás de un servicio.
Prácticas recomendadas para la disponibilidad
Elegir un plano de control regional o zonal
Debido a las diferencias arquitectónicas, los clústeres regionales son más adecuados para la alta disponibilidad. Los clústeres regionales tienen varios planos de control en varias zonas de cálculo de una región, mientras que los clústeres zonales tienen un plano de control en una sola zona de cálculo.
Si se actualiza un clúster zonal, la VM del plano de control experimenta un tiempo de inactividad durante el cual la API de Kubernetes no está disponible hasta que se completa la actualización.
En los clústeres regionales, el plano de control sigue estando disponible durante el mantenimiento del clúster, como la rotación de IPs, la actualización de las VMs del plano de control o el cambio de tamaño de los clústeres o los grupos de nodos. Al actualizar un clúster regional, al menos una de las varias VMs del plano de control siempre está en ejecución durante la actualización gradual, por lo que la API de Kubernetes sigue estando disponible. Del mismo modo, una interrupción en una sola zona no provocará ningún tiempo de inactividad en el plano de control regional.
Sin embargo, los clústeres regionales de alta disponibilidad tienen ciertas desventajas:
Los cambios en la configuración del clúster tardan más porque deben propagarse por todos los planos de control de un clúster regional en lugar de por el único plano de control de los clústeres zonales.
Es posible que no puedas crear o actualizar clústeres regionales con la misma frecuencia que los zonales. Si no se pueden crear VMs en una de las zonas, ya sea por falta de capacidad o por otro problema transitorio, no se podrán crear ni actualizar clústeres.
Debido a estas ventajas e inconvenientes, los clústeres zonales y regionales tienen diferentes casos prácticos:
- Usa clústeres zonales para crear o actualizar clústeres rápidamente cuando la disponibilidad no sea un problema.
- Utiliza clústeres regionales cuando la disponibilidad sea más importante que la flexibilidad.
Selecciona cuidadosamente el tipo de clúster al crear uno, ya que no podrás cambiarlo después. En su lugar, debe crear un clúster y, a continuación, migrar el tráfico a él. Migrar el tráfico de producción entre clústeres es posible, pero difícil a gran escala.
Elegir grupos de nodos multizonales o zonales
Para conseguir una alta disponibilidad, el plano de control de Kubernetes y sus nodos deben distribuirse en diferentes zonas. GKE ofrece dos tipos de grupos de nodos: de una sola zona y multizona.
Para desplegar una aplicación de alta disponibilidad, distribuye tu carga de trabajo entre varias zonas de computación de una región mediante el uso de grupos de nodos multizonales, que distribuyen los nodos de forma uniforme entre las zonas.
Si todos tus nodos están en la misma zona, no podrás programar pods si esa zona deja de estar disponible. Usar grupos de nodos multizonales tiene ciertas ventajas e inconvenientes:
Las GPUs solo están disponibles en zonas concretas. Es posible que no se puedan obtener en todas las zonas de la región.
La latencia de ida y vuelta entre zonas de una misma región puede ser mayor que la que se produce entre recursos de una misma zona. La diferencia debería ser irrelevante para la mayoría de las cargas de trabajo.
El precio del tráfico de salida entre zonas de la misma región está disponible en la página de precios de Compute Engine.
Prácticas recomendadas para la escalabilidad
Infraestructura base
Las cargas de trabajo de Kubernetes requieren redes, computación y almacenamiento. Debes proporcionar suficiente CPU y memoria para ejecutar pods. Sin embargo, hay más parámetros de la infraestructura subyacente que pueden influir en el rendimiento y la escalabilidad de un clúster de GKE.
Redes de clúster
Usar un clúster nativo de VPC es la opción predeterminada de redes y la recomendada para configurar nuevos clústeres de GKE. Los clústeres nativos de VPC permiten cargas de trabajo más grandes, un mayor número de nodos y otras ventajas.
En este modo, la red de VPC tiene un intervalo secundario para todas las direcciones IP de los pods. A cada nodo se le asigna una porción del intervalo secundario para sus propias direcciones IP de pods. De esta forma, la red VPC puede entender de forma nativa cómo enrutar el tráfico a los pods sin depender de rutas personalizadas. Una sola red de VPC puede tener hasta 15.000 VMs.
Otro enfoque, que está obsoleto y admite un máximo de 1500 nodos, es usar un clúster basado en rutas. Un clúster basado en rutas no es adecuado para cargas de trabajo grandes. Consume cuota de rutas de VPC y no tiene otras ventajas de las redes nativas de VPC. Para ello, se añade una nueva ruta personalizada a la tabla de enrutamiento de la red de VPC de cada nodo nuevo.
Tamaño de la agrupación
Los clústeres de más de 5000 nodos deben usar Private Service Connect. Private Service Connect conecta de forma privada los nodos y el plano de control, y ofrece una mejor seguridad y rendimiento de la red.
Balanceo de carga de clústeres
Ingress de GKE y Cloud Load Balancing configuran y despliegan balanceadores de carga para exponer cargas de trabajo de Kubernetes fuera del clúster y también a Internet público. Los controladores de Ingress y de servicio de GKE implementan objetos como reglas de reenvío, mapas de URLs, servicios de backend y grupos de puntos finales de red, entre otros, en nombre de las cargas de trabajo de GKE. Cada uno de estos recursos tiene cuotas y límites inherentes, y estos límites también se aplican en GKE. Cuando un recurso de Cloud Load Balancing concreto alcance su cuota, impedirá que un Ingress o un Service determinado se despliegue correctamente y aparecerán errores en los eventos del recurso.
En la siguiente tabla se describen los límites de escalado al usar Ingress y Services de GKE:
Balanceador de carga | Límite de nodos por clúster |
---|---|
Balanceador de carga de red de paso a través interno |
|
Balanceador de carga de red de paso a través externo | 1000 nodos por zona |
Balanceador de carga de aplicación externo |
|
Balanceador de carga de aplicación interno | Sin límite de nodos |
Si necesitas aumentar este límite, ponte en contacto con tu Google Cloud equipo de Ventas.
DNS
El descubrimiento de servicios en GKE se proporciona a través de kube-dns, que es un recurso centralizado para proporcionar resolución de DNS a los pods que se ejecutan en el clúster. Esto puede convertirse en un cuello de botella en clústeres muy grandes o en cargas de trabajo que tengan una carga de solicitudes elevada. GKE autoescala automáticamente kube-dns en función del tamaño del clúster para aumentar su capacidad. Si esta capacidad sigue siendo insuficiente, GKE ofrece una resolución local y distribuida de las consultas de DNS en cada nodo con NodeLocal DNSCache. De esta forma, se proporciona una caché DNS local en cada nodo de GKE que responde a las consultas de forma local, lo que distribuye la carga y ofrece tiempos de respuesta más rápidos.
Gestionar direcciones IP en clústeres nativos de VPC
Un clúster nativo de VPC usa tres intervalos de direcciones IP:
- Intervalo principal de la subred de nodos: el valor predeterminado es /20 (4092 direcciones IP).
- Intervalo secundario de la subred de pods: el valor predeterminado es /14 (262.144 direcciones IP). Sin embargo, puedes configurar la subred del pod.
- Intervalo secundario de la subred de servicio: el valor predeterminado es /20 (4096 direcciones). Sin embargo, no puedes cambiar este intervalo después de crear la subred de servicio.
Para obtener más información, consulta Intervalos de direcciones IP para clústeres nativos de VPC.
Limitaciones y recomendaciones de direcciones IP:
- Límite de nodos: el límite de nodos se determina en función de las direcciones IP principales y de los pods por nodo. Debe haber suficientes direcciones en los intervalos de direcciones IP de nodos y pods para aprovisionar un nuevo nodo. De forma predeterminada, solo puedes crear 1024 nodos debido a las limitaciones de las direcciones IP de los pods.
- Límite de pods por nodo: de forma predeterminada, el límite de pods por nodo es de 110 pods. Sin embargo, puedes configurar CIDRs de pods más pequeños para usarlos de forma eficiente con menos pods por nodo.
- Escalar más allá de RFC 1918: si necesitas más direcciones IP de las que hay disponibles en el espacio privado definido por RFC 1918, te recomendamos que uses direcciones privadas que no sean RFC 1918 o PUPIs para tener más flexibilidad.
- Intervalo de direcciones IP secundarias para servicios y pods: de forma predeterminada, puedes configurar 4096 servicios. Sin embargo, puedes configurar más servicios eligiendo el intervalo de subred de servicio. No puedes modificar los intervalos secundarios después de crearlos. Cuando crees un clúster, asegúrate de elegir intervalos lo suficientemente grandes como para dar cabida al crecimiento previsto. Sin embargo, puedes añadir más direcciones IP para los pods más adelante mediante CIDR de varios pods no contiguos.
Para obtener más información, consulta las siguientes secciones:
- No hay suficiente espacio de direcciones IP libres para los pods
- Intervalos de limitación de nodos
- Planificar las direcciones IP al migrar a GKE
Configurar nodos para mejorar el rendimiento
Los nodos de GKE son máquinas virtuales normales. Google Cloud Algunos de sus parámetros, como el número de núcleos o el tamaño del disco, pueden influir en el rendimiento de los clústeres de GKE.
Reducir los tiempos de inicialización de los pods
Puedes usar streaming de imágenes para transmitir datos de imágenes de contenedor aptas a medida que tus cargas de trabajo los soliciten, lo que conlleva tiempos de inicialización más rápidos.
Tráfico de salida
En Google Cloud, el tipo de máquina y el número de núcleos asignados a la instancia determinan su capacidad de red. El ancho de banda de salida máximo varía de 1 a 32 Gbps, mientras que el ancho de banda de salida máximo de las máquinas e2-medium-2 predeterminadas es de 2 Gbps. Para obtener información detallada sobre los límites de ancho de banda, consulta Tipos de máquinas de núcleo compartido.
IOPS y rendimiento del disco
En Google Cloud, el tamaño de los discos persistentes determina las IOPS y el rendimiento del disco. GKE suele usar Persistent Disks como discos de arranque y para crear copias de seguridad de los volúmenes persistentes de Kubernetes. Al aumentar el tamaño del disco, aumentan tanto las IOPS como el rendimiento, hasta alcanzar ciertos límites.
Cada operación de escritura en un disco persistente contribuye al límite de salida de red acumulativo de la instancia de la máquina virtual. Por lo tanto, el rendimiento de IOPS de los discos, especialmente de las SSD, también depende del número de vCPUs de la instancia, además del tamaño del disco. Las VMs con menos núcleos tienen límites de IOPS de escritura más bajos debido a las limitaciones de salida de red en el rendimiento de escritura.
Si tu instancia de máquina virtual no tiene suficientes CPUs, tu aplicación no podrá acercarse al límite de IOPS. Por lo general, deberías tener una CPU disponible por cada 2000-2500 IOPS de tráfico previsto.
Las cargas de trabajo que requieren una gran capacidad o un gran número de discos deben tener en cuenta los límites de cuántos discos persistentes se pueden adjuntar a una sola VM. En el caso de las VMs normales, el límite es de 128 discos con un tamaño total de 64 TB, mientras que las VMs de núcleo compartido tienen un límite de 16 discos persistentes con un tamaño total de 3 TB. Google Cloud aplica este límite, no Kubernetes.
Monitorizar métricas del plano de control
Usa las métricas del plano de control disponibles para configurar tus paneles de control de monitorización. Puedes usar las métricas del plano de control para observar el estado del clúster, los resultados de los cambios en la configuración del clúster (por ejemplo, al implementar cargas de trabajo adicionales o componentes de terceros) o cuando resuelvas problemas.
Una de las métricas más importantes que se deben monitorizar es la latencia de la API de Kubernetes. Los aumentos de latencia indican que el sistema está sobrecargado. Ten en cuenta que las llamadas LIST que transfieren grandes cantidades de datos tienen una latencia mucho mayor que las solicitudes más pequeñas.
El aumento de la latencia de la API de Kubernetes también puede deberse a respuestas lentas de webhooks de admisión de terceros. Puedes usar métricas para medir la latencia de los webhooks y detectar este tipo de problemas habituales.
El límite superior sugerido para una llamada de un solo recurso, como GET, POST o PATCH, es de un segundo. El límite superior sugerido para las llamadas LIST con ámbito de espacio de nombres y de clúster es de 30 segundos. Los límites superiores se definen mediante SLOs definidos por la comunidad de Kubernetes de código abierto. Para obtener más información, consulta Detalles de los SLIs y SLOs de latencia de llamadas a la API.
Prácticas recomendadas para desarrolladores de Kubernetes
Usar la lista y el patrón de vigilancia en lugar de la lista periódica
Como desarrollador de Kubernetes, es posible que tengas que crear un componente que cumpla los siguientes requisitos:
- Tu componente debe recuperar la lista de algunos objetos de Kubernetes periódicamente.
- Tu componente debe ejecutarse en varias instancias (en el caso de DaemonSet, incluso en cada nodo).
Este componente puede generar picos de carga en kube-apiserver, aunque el estado de los objetos recuperados periódicamente no cambie.
El método más sencillo es usar llamadas LIST periódicas. Sin embargo, este enfoque es ineficiente y caro tanto para el llamante como para el servidor, ya que todos los objetos deben cargarse en la memoria, serializarse y transferirse cada vez. El uso excesivo de solicitudes LIST puede sobrecargar el plano de control o generar una limitación excesiva de dichas solicitudes.
Puedes mejorar tu componente configurando el parámetro resourceVersion=0 en las llamadas LIST. De esta forma, kube-apiserver puede usar la caché de objetos en memoria y se reduce la frecuencia con la que el servidor de la API de Kubernetes interactúa con la API de etcd, lo que también reduce el procesamiento relacionado.
Te recomendamos que evites las llamadas LIST repetibles y que las sustituyas por el patrón de lista y de observación. Lista los objetos una vez y, a continuación, usa la API Watch para obtener los cambios incrementales del estado. Este método reduce el tiempo de procesamiento y minimiza el tráfico en comparación con las llamadas LIST periódicas. Cuando los objetos no cambian, no se genera ninguna carga adicional.
Si usas el lenguaje Go, consulta SharedInformer y SharedInformerFactory para ver los paquetes de Go que implementan este patrón.
Limitar el tráfico innecesario generado por relojes y listas
Kubernetes usa internamente watches para enviar notificaciones sobre las actualizaciones de objetos. Aunque los relojes requieren muchos menos recursos que las llamadas LIST periódicas, el procesamiento de relojes en clústeres grandes puede consumir una parte importante de los recursos del clúster y afectar al rendimiento del clúster. El mayor impacto negativo se genera al crear relojes que observan objetos que cambian con frecuencia desde varios lugares. Por ejemplo, observando datos sobre todos los pods de un componente que se ejecuta en todos los nodos. Si instalas código o extensiones de terceros en tu clúster, estos pueden crear este tipo de relojes de forma oculta.
Te recomendamos que sigas estas prácticas recomendadas:
- Reduce el procesamiento innecesario y el tráfico generado por los relojes y las llamadas LIST.
- Evita crear comprobaciones que observen objetos que cambian con frecuencia desde varios lugares (por ejemplo, DaemonSets).
- (Muy recomendable) Crea un controlador central que monitorice y procese los datos necesarios en un solo nodo.
- Solo se monitoriza un subconjunto de los objetos. Por ejemplo, kubelet de cada nodo solo observa los pods programados en el mismo nodo.
- Evita implementar componentes o extensiones de terceros que puedan afectar al rendimiento del clúster haciendo un gran volumen de llamadas de tipo watch o LIST.
Usar actualizaciones continuas con DaemonSets para evitar aumentos repentinos del tráfico
Cuando se crea un DaemonSet en un clúster, programa un nuevo pod en cada nodo inmediatamente. Si todos esos pods nuevos se conectan al mismo endpoint de red, esas solicitudes se producen simultáneamente, lo que supone una carga elevada para el host de destino. Para evitarlo, configura tus DaemonSets con actualizaciones continuas con un número limitado de maxSurge
pods.
Limitar el tamaño del manifiesto de objetos de Kubernetes
Si necesitas operaciones rápidas que requieran un alto rendimiento de los pods, como cambiar el tamaño o actualizar cargas de trabajo grandes, asegúrate de que el tamaño de los manifiestos de los pods sea mínimo (lo ideal es que sea inferior a 10 KiB).
Kubernetes almacena los manifiestos de recursos en una base de datos que es un almacén de clave-valor. Todo el manifiesto se envía cada vez que se recupera el recurso, incluso cuando se usa el patrón de lista y de observación.
El tamaño del manifiesto tiene las siguientes limitaciones:
- Tamaño máximo de cada manifiesto de objeto: aproximadamente 1,5 MiB.
- Cuota total de todos los objetos de la API de Kubernetes del clúster: el tamaño de la cuota preconfigurada es de 6 GiB. Esto incluye un registro de cambios con todas las actualizaciones de todos los objetos de los últimos 150 segundos del historial del clúster.
- Rendimiento del plano de control durante periodos de tráfico elevado: los manifiestos de mayor tamaño aumentan la carga del servidor de la API.
En el caso de los objetos únicos que se procesan con poca frecuencia, el tamaño del archivo de manifiesto no suele ser un problema, siempre que sea inferior a 1,5 MiB. Sin embargo, si los tamaños de los manifiestos son superiores a 10 KiB para numerosos objetos procesados con frecuencia (como los pods en cargas de trabajo muy grandes), se puede producir un aumento de la latencia de las llamadas a la API y una disminución del rendimiento general. Las listas y los relojes, en concreto, pueden verse afectados significativamente por los manifiestos de gran tamaño. También puede tener problemas con la cuota de la base de datos de estado del clúster, ya que la cantidad de revisiones de los últimos 150 segundos puede acumularse rápidamente durante los periodos de mucho tráfico del servidor de la API.
Para reducir el tamaño del manifiesto de un pod, se pueden usar ConfigMaps de Kubernetes para almacenar parte de la configuración, en concreto la parte que comparten varios pods del clúster. Por ejemplo, las variables de entorno suelen compartirse entre todos los pods de una carga de trabajo.
Ten en cuenta que también es posible que se produzcan problemas similares con los objetos ConfigMap si son tan numerosos, grandes y se procesan con tanta frecuencia como los pods. Extraer parte de la configuración es más útil cuando se reduce el tráfico general.
Inhabilitar el montaje automático de la cuenta de servicio predeterminada
Si la lógica que se ejecuta en tus pods no necesita acceder a la API de Kubernetes, debes inhabilitar el montaje automático predeterminado de la cuenta de servicio para evitar la creación de secretos y monitorizaciones relacionados.
Cuando creas un pod sin especificar una cuenta de servicio, Kubernetes realiza automáticamente las siguientes acciones:
- Asigna la cuenta de servicio predeterminada al pod.
- Monta las credenciales de la cuenta de servicio como un secreto para el pod.
- Por cada secreto montado, kubelet crea un elemento de observación para monitorizar los cambios que se produzcan en ese secreto en cada nodo.
En los clústeres grandes, estas acciones representan miles de observadores innecesarios, lo que puede suponer una carga significativa para kube-apiserver.
Usar Protocol Buffers en lugar de JSON para las solicitudes de API
Usa buffers de protocolo al implementar componentes altamente escalables, tal como se describe en Conceptos de la API de Kubernetes.
La API REST de Kubernetes admite JSON y búferes de protocolo como formato de serialización de objetos. JSON se usa de forma predeterminada, pero los búferes de protocolo son más eficientes para el rendimiento a gran escala, ya que requieren un procesamiento menos intensivo de la CPU y envían menos datos a través de la red. La sobrecarga relacionada con el procesamiento de JSON puede provocar tiempos de espera al enumerar datos de gran tamaño.