Diez errores comunes al usar Kubernetes

Nota. traducir: Los autores de este artículo son ingenieros de una pequeña empresa checa, Pipetail. Se las arreglaron para reunir una lista maravillosa de problemas e ideas erróneas [a veces banales, pero aún así] muy urgentes relacionados con el funcionamiento de los clústeres de Kubernetes.

Diez errores comunes al usar Kubernetes

A lo largo de los años de uso de Kubernetes, hemos trabajado con una gran cantidad de clústeres (tanto administrados como no administrados, en GCP, AWS y Azure). Con el tiempo, empezamos a notar que algunos errores se repetían constantemente. Sin embargo, no hay por qué avergonzarse de ello: ¡la mayoría de ellos los hemos hecho nosotros mismos!

El artículo contiene los errores más comunes y también menciona cómo corregirlos.

1. Recursos: peticiones y límites

Este artículo definitivamente merece la mayor atención y el primer lugar en la lista.

Solicitud de CPU generalmente o no se especifica en absoluto o tiene un valor muy bajo (para colocar tantos pods en cada nodo como sea posible). Por tanto, los nodos se sobrecargan. Durante momentos de alta carga, la potencia de procesamiento del nodo se utiliza por completo y una carga de trabajo particular recibe sólo lo que "solicitó" aceleración de la CPU. Esto conduce a una mayor latencia de la aplicación, tiempos de espera y otras consecuencias desagradables. (Lea más sobre esto en nuestra otra traducción reciente: “Límites de CPU y aceleración agresiva en Kubernetes" - aprox. trad.)

Mejor esfuerzo (extremadamente no recomendado):

resources: {}

Solicitud de CPU extremadamente baja (extremadamente no recomendado):

   resources:
      Requests:
        cpu: "1m"

Por otro lado, la presencia de un límite de CPU puede provocar saltos irrazonables de ciclos de reloj por parte de los pods, incluso si el procesador del nodo no está completamente cargado. Una vez más, esto puede provocar mayores retrasos. Continúa la controversia en torno al parámetro Cuota CFS de CPU en el kernel de Linux y la aceleración de la CPU según los límites establecidos, además de deshabilitar la cuota CFS... Lamentablemente, los límites de la CPU pueden causar más problemas de los que pueden resolver. Puede encontrar más información sobre esto en el siguiente enlace.

Selección excesiva (comprometerse demasiado) Los problemas de memoria pueden conducir a problemas mayores. Alcanzar el límite de la CPU implica omitir ciclos de reloj, mientras que alcanzar el límite de memoria implica matar el pod. ¿Alguna vez has observado OOMkill? Sí, eso es exactamente de lo que estamos hablando.

¿Quiere minimizar la probabilidad de que esto suceda? No sobreasigne memoria y utilice QoS (calidad de servicio) garantizada estableciendo la solicitud de memoria al límite (como en el ejemplo siguiente). Lea más sobre esto en Presentaciones de Henning Jacobs (Ingeniero jefe en Zalando).

rompible (mayor probabilidad de que maten a OOM):

   resources:
      requests:
        memory: "128Mi"
        cpu: "500m"
      limits:
        memory: "256Mi"
        cpu: 2

Garantizado:

   resources:
      requests:
        memory: "128Mi"
        cpu: 2
      limits:
        memory: "128Mi"
        cpu: 2

¿Qué ayudará potencialmente a la hora de configurar recursos?

Con servidor de métricas puede ver el consumo actual de recursos de CPU y el uso de memoria por pods (y contenedores dentro de ellos). Lo más probable es que ya lo estés usando. Simplemente ejecute los siguientes comandos:

kubectl top pods
kubectl top pods --containers
kubectl top nodes

Sin embargo, sólo muestran el uso actual. Puede darle una idea aproximada del orden de magnitud, pero en última instancia necesitará Historial de cambios en las métricas a lo largo del tiempo. (para responder preguntas como: "¿Cuál fue la carga máxima de la CPU?", "¿Cuál fue la carga ayer por la mañana?", etc.). Para esto puedes usar Prometeo, DataDog y otras herramientas. Simplemente obtienen métricas del servidor de métricas y las almacenan, y el usuario puede consultarlas y trazarlas en consecuencia.

VerticalPodAutoescalador permite automatizar este proceso. Realiza un seguimiento del historial de uso de la CPU y la memoria y configura nuevas solicitudes y límites en función de esta información.

Usar la potencia informática de manera eficiente no es una tarea fácil. Es como jugar al Tetris todo el tiempo. Si está pagando demasiado por la potencia informática con un consumo promedio bajo (digamos ~10%), le recomendamos buscar productos basados ​​en AWS Fargate o Virtual Kubelet. Se basan en un modelo de facturación sin servidor/pago por uso, que puede resultar más económico en tales condiciones.

2. Sondas de vida y preparación.

De forma predeterminada, las comprobaciones de actividad y preparación no están habilitadas en Kubernetes. Y a veces se olvidan de encenderlos...

Pero, ¿de qué otra manera se puede iniciar un reinicio del servicio en caso de un error fatal? ¿Y cómo sabe el equilibrador de carga que un pod está listo para aceptar tráfico? ¿O que puede manejar más tráfico?

Estas pruebas suelen confundirse entre sí:

  • Vivacidad — verificación de “supervivencia”, que reinicia la cápsula si falla;
  • Preparación — verificación de preparación, si falla, desconecta el pod del servicio Kubernetes (esto se puede verificar usando kubectl get endpoints) y el tráfico no llega a él hasta que la siguiente verificación se complete con éxito.

Ambos controles REALIZADO DURANTE TODO EL CICLO DE VIDA DEL POD. Es muy importante.

Un error común es pensar que las sondas de preparación solo se ejecutan al inicio para que el equilibrador pueda saber que el pod está listo (Ready) y puede comenzar a procesar el tráfico. Sin embargo, esta es sólo una de las opciones para su uso.

Otra es la posibilidad de descubrir que el tráfico en el pod es excesivo y lo sobrecarga (o el pod realiza cálculos que consumen muchos recursos). En este caso, la verificación de preparación ayuda reduzca la carga en la cápsula y “enfríela”. La finalización exitosa de una verificación de preparación en el futuro permite aumentar la carga en la cápsula nuevamente. En este caso (si la prueba de preparación falla), fallar la prueba de vida sería muy contraproducente. ¿Por qué reiniciar un grupo que está sano y trabajando duro?

Por lo tanto, en algunos casos, es mejor no realizar ninguna verificación que habilitarlas con parámetros configurados incorrectamente. Como se indicó anteriormente, si verificación de vida copias verificación de preparación, entonces estás en un gran problema. La opción posible es configurar solo prueba de preparaciónY vida peligrosa dejar de lado.

Ambos tipos de comprobaciones no deberían fallar cuando fallan las dependencias comunes; de lo contrario, esto provocará una falla en cascada (tipo avalancha) de todos los pods. En otras palabras, no te hagas daño.

3. LoadBalancer para cada servicio HTTP

Lo más probable es que tenga servicios HTTP en su clúster que le gustaría reenviar al mundo exterior.

Si abre el servicio como type: LoadBalancer, su controlador (dependiendo del proveedor de servicios) proporcionará y negociará un LoadBalancer externo (no necesariamente ejecutándose en L7, sino incluso en L4), y esto puede afectar el costo (dirección IPv4 estática externa, potencia de procesamiento, facturación por segundo). ) debido a la necesidad de crear una gran cantidad de dichos recursos.

En este caso, es mucho más lógico utilizar un equilibrador de carga externo, abriendo servicios como type: NodePort. O mejor aún, expanda algo como controlador de entrada nginx (o traefik), quien será el único Puerto de nodo punto final asociado con el equilibrador de carga externo y enrutará el tráfico en el clúster utilizando preámbulo-Recursos de Kubernetes.

Otros (micro)servicios dentro del clúster que interactúan entre sí pueden "comunicarse" utilizando servicios como IP de clúster y un mecanismo de descubrimiento de servicios integrado a través de DNS. Simplemente no utilice su DNS/IP público, ya que esto puede afectar la latencia y aumentar el costo de los servicios en la nube.

4. Autoescalar un cluster sin tener en cuenta sus características

Al agregar y eliminar nodos de un clúster, no debe confiar en algunas métricas básicas como el uso de CPU en esos nodos. La planificación de pods debe tener en cuenta muchos restricciones, como afinidad de pod/nodo, contaminación y tolerancias, solicitudes de recursos, QoS, etc. El uso de un escalador automático externo que no tenga en cuenta estos matices puede generar problemas.

Imagine que se debe programar un determinado pod, pero se solicita/desmonta toda la potencia de CPU disponible y el pod se queda atrapado en un estado Pending. El escalador automático externo ve la carga promedio actual de la CPU (no la solicitada) y no inicia la expansión (poner a escala) - no agrega otro nodo. Como resultado, este grupo no se programará.

En este caso, escala inversa. (ampliación) — eliminar un nodo de un clúster siempre es más difícil de implementar. Imagine que tiene un pod con estado (con almacenamiento persistente conectado). Volúmenes persistentes normalmente pertenecen a zona de disponibilidad específica y no se replican en la región. Por lo tanto, si un escalador automático externo elimina un nodo con este pod, el programador no podrá programar este pod en otro nodo, ya que esto solo se puede hacer en la zona de disponibilidad donde se encuentra el almacenamiento persistente. Pod quedará atascado en el estado Pending.

Muy popular en la comunidad de Kubernetes. escalador automático de clúster. Se ejecuta en un clúster, admite API de los principales proveedores de nube, tiene en cuenta todas las restricciones y puede escalar en los casos anteriores. También es capaz de escalar manteniendo todos los límites establecidos, ahorrando así dinero (que de otro modo se gastaría en capacidad no utilizada).

5. Descuidar las capacidades de IAM/RBAC

Tenga cuidado al utilizar usuarios de IAM con secretos persistentes para maquinas y aplicaciones. Organizar el acceso temporal mediante roles y cuentas de servicio (cuentas de servicio).

A menudo nos encontramos con el hecho de que las claves de acceso (y los secretos) están codificadas en la configuración de la aplicación, además de descuidar la rotación de los secretos a pesar de tener acceso a Cloud IAM. Utilice roles de IAM y cuentas de servicio en lugar de usuarios cuando corresponda.

Diez errores comunes al usar Kubernetes

Olvídese de kube2iam y vaya directamente a los roles de IAM para cuentas de servicio (como se describe en nota del mismo nombre Štěpán Vraný):

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/my-app-role
  name: my-serviceaccount
  namespace: default

Una anotación. No es tan difícil, ¿verdad?

Además, no otorgue privilegios a cuentas de servicio ni a perfiles de instancia. admin и cluster-adminsi no lo necesitan. Esto es un poco más difícil de implementar, especialmente en los RBAC K8, pero definitivamente vale la pena el esfuerzo.

6. No confíes en la antiafinidad automática para los pods

Imagine que tiene tres réplicas de alguna implementación en un nodo. El nodo cae, y con él todas las réplicas. Situación desagradable, ¿verdad? Pero ¿por qué estaban todas las réplicas en el mismo nodo? ¿No se supone que Kubernetes proporciona alta disponibilidad (HA)?

Desafortunadamente, el programador de Kubernetes, por iniciativa propia, no cumple con las reglas de existencia separada. (antiafinidad) para vainas. Deberán indicarse explícitamente:

// опущено для краткости
      labels:
        app: zk
// опущено для краткости
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: "app"
                    operator: In
                    values:
                    - zk
              topologyKey: "kubernetes.io/hostname"

Eso es todo. Ahora los pods se programarán en diferentes nodos (esta condición se verifica solo durante la programación, pero no durante su operación; por lo tanto, requiredDuringSchedulingIgnoredDuringExecution).

Aquí estamos hablando de podAntiAffinity en diferentes nodos: topologyKey: "kubernetes.io/hostname", - y no sobre diferentes zonas de disponibilidad. Para implementar una HA completa, deberá profundizar en este tema.

7. Ignorar los presupuestos de PodDisruption

Imagine que tiene una carga de producción en un clúster de Kubernetes. Periódicamente, los nodos y el propio clúster deben actualizarse (o retirarse del servicio). Un PodDisruptionBudget (PDB) es algo así como un acuerdo de garantía de servicio entre los administradores y usuarios del clúster.

PDB le permite evitar interrupciones del servicio provocadas por la falta de nodos:

apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: zookeeper

En este ejemplo, usted, como usuario del clúster, les dice a los administradores: "Oye, tengo un servicio de cuidador del zoológico y, sin importar lo que hagas, me gustaría tener al menos 2 réplicas de este servicio siempre disponibles".

Puedes leer más sobre esto. aquí.

8. Múltiples usuarios o entornos en un clúster común

Espacios de nombres de Kubernetes (espacios de nombres) no proporcione un aislamiento fuerte.

Un error común es que si implementa una carga que no es de producción en un espacio de nombres y una carga de producción en otro, entonces no se influirán mutuamente de ninguna manera... Sin embargo, se puede lograr un cierto nivel de aislamiento utilizando solicitudes/limitaciones de recursos, estableciendo cuotas y estableciendo clases de prioridad. Cierto aislamiento “físico” en el plano de datos lo proporcionan afinidades, tolerancias, contaminaciones (o deseleccionadores de nodos), pero dicha separación es bastante dificil implementar.

Aquellos que necesiten combinar ambos tipos de cargas de trabajo en el mismo clúster tendrán que lidiar con la complejidad. Si no existe tal necesidad y usted puede permitirse el lujo de tener uno un grupo más (digamos, en una nube pública), entonces es mejor hacerlo. Esto logrará un nivel de aislamiento mucho mayor.

9. Política de tráfico externa: clúster

Muy a menudo vemos que todo el tráfico dentro del clúster llega a través de un servicio como NodePort, para el cual está configurada la política predeterminada. externalTrafficPolicy: Cluster. Esto significa que Puerto de nodo está abierto en todos los nodos del clúster y puede utilizar cualquiera de ellos para interactuar con el servicio deseado (conjunto de pods).

Diez errores comunes al usar Kubernetes

Al mismo tiempo, los pods reales asociados con el servicio NodePort mencionado anteriormente generalmente solo están disponibles en un determinado subconjunto de estos nodos. En otras palabras, si me conecto a un nodo que no tiene el pod requerido, reenviará el tráfico a otro nodo. agregando un salto y aumento de la latencia (si los nodos están ubicados en diferentes zonas de disponibilidad/centros de datos, la latencia puede ser bastante alta; además, los costos del tráfico de salida aumentarán).

Por otro lado, si un determinado servicio de Kubernetes tiene una política establecida externalTrafficPolicy: Local, entonces NodePort se abre solo en aquellos nodos donde realmente se están ejecutando los pods necesarios. Cuando se utiliza un balanceador de carga externo que verifica el estado (control de salud) puntos finales (¿cómo lo hace? AWSELB), Él enviará tráfico sólo a los nodos necesarios, lo que tendrá un efecto beneficioso en los retrasos, las necesidades informáticas y las facturas de salida (y el sentido común dicta lo mismo).

Existe una alta probabilidad de que ya estés usando algo como traefik o controlador de entrada nginx como punto final de NodePort (o LoadBalancer, que también usa NodePort) para enrutar el tráfico de entrada HTTP, y configurar esta opción puede reducir significativamente la latencia de dichas solicitudes.

В esta publicacion Puede obtener más información sobre externalTrafficPolicy, sus ventajas y desventajas.

10. No te quedes atado a clusters y no abuses del plano de control.

Anteriormente, era costumbre llamar a los servidores por sus nombres propios: Anton, HAL9000 y Colossus... Hoy han sido reemplazados por identificadores generados aleatoriamente. Sin embargo, la costumbre se mantuvo y ahora los nombres propios se agrupan.

Una historia típica (basada en hechos reales): todo comenzó con una prueba de concepto, por lo que el clúster tenía un nombre orgulloso las pruebas … Han pasado los años y TODAVÍA se utiliza en producción, y todo el mundo tiene miedo de tocarlo.

No hay nada divertido en que los grupos se conviertan en mascotas, por lo que recomendamos eliminarlos periódicamente mientras practicas. recuperación de desastres (esto ayudará ingeniería del caos - aprox. trad.). Además, no estaría de más trabajar en la capa de control. (plano de control). Tener miedo de tocarlo no es buena señal. Etc. ¿muerto? ¡Chicos, están realmente en problemas!

Por otro lado, no debes dejarte llevar por su manipulación. Con tiempo la capa de control puede volverse lenta. Lo más probable es que esto se deba a que se crea una gran cantidad de objetos sin rotarlos (una situación común cuando se usa Helm con la configuración predeterminada, razón por la cual su estado en mapas de configuración/secretos no se actualiza; como resultado, miles de objetos se acumulan en la capa de control) o con edición constante de objetos kube-api (para escalado automático, para CI/CD, para monitoreo, registros de eventos, controladores, etc.).

Además, recomendamos consultar los acuerdos SLA/SLO con el proveedor de Kubernetes gestionado y prestar atención a las garantías. El vendedor puede garantizar disponibilidad de la capa de control (o sus subcomponentes), pero no el retraso p99 de las solicitudes que le envía. En otras palabras, puedes ingresar kubectl get nodesy recibirá una respuesta solo después de 10 minutos, y esto no constituirá una violación de los términos del acuerdo de servicio.

11. Bonificación: usar la última etiqueta

Pero esto ya es un clásico. Últimamente nos hemos encontrado con esta técnica con menos frecuencia, ya que muchos, habiendo aprendido de una amarga experiencia, han dejado de usar la etiqueta. :latest y comencé a fijar versiones. ¡Hurra!

ECR mantiene la inmutabilidad de las etiquetas de imágenes; Le recomendamos que se familiarice con esta notable característica.

Resumen

No espere que todo funcione de la noche a la mañana: Kubernetes no es una panacea. Mala aplicación permanecerá así incluso en Kubernetes (y probablemente empeore). El descuido conducirá a una complejidad excesiva, un trabajo lento y estresante de la capa de control. Además, corre el riesgo de quedarse sin una estrategia de recuperación ante desastres. No espere que Kubernetes proporcione aislamiento y alta disponibilidad desde el primer momento. Dedique algo de tiempo a hacer que su aplicación sea verdaderamente nativa de la nube.

Puede familiarizarse con las experiencias fallidas de varios equipos en esta colección de historias por Henning Jacobs.

Aquellos que deseen agregar errores a la lista de este artículo pueden contactarnos en Twitter (@MarekBartik, @MstrsObserver).

PD del traductor

Lea también en nuestro blog:

Fuente: habr.com

Añadir un comentario