Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.
Monitorización del plano de control
Servidor de API
Al analizar nuestro servidor de API, es importante recordar que una de sus funciones es limitar las solicitudes entrantes para evitar sobrecargar el plano de control. Lo que puede parecer un cuello de botella a nivel de servidor de API, en realidad podría estar protegiéndolo de problemas más graves. Debemos tener en cuenta las ventajas y desventajas de aumentar el volumen de solicitudes que circulan por el sistema. Para determinar si los valores del servidor de API deben aumentarse, he aquí una pequeña muestra de las cosas que debemos tener en cuenta:
-
¿Cuál es la latencia de las solicitudes que pasan por el sistema?
-
¿Esa latencia se debe al propio servidor de API o es algo «posterior», como etcd?
-
¿La profundidad de la cola del servidor API es un factor en esta latencia?
-
¿Las colas de prioridad e imparcialidad de la API (APF) están configuradas correctamente para los patrones de llamadas a la API que queremos?
¿Dónde está el problema?
Para empezar, podemos usar la métrica de latencia de la API para saber cuánto tarda el servidor de API en atender las solicitudes. Utilicemos el siguiente mapa de calor de PromQL y Grafana para mostrar estos datos.
max(increase(apiserver_request_duration_seconds_bucket{subresource!="status",subresource!="token",subresource!="scale",subresource!="/healthz",subresource!="binding",subresource!="proxy",verb!="WATCH"}[$__rate_interval])) by (le)
nota
Para obtener información detallada sobre cómo supervisar el servidor de API con el panel de API utilizado en este artículo, consulte el siguiente blog https://aws.amazon.com/blogs/containers/troubleshooting-amazon-eks-api-servers-with-prometheus/
Todas estas solicitudes están por debajo de la marca de un segundo, lo que es un buen indicador de que el plano de control está gestionando las solicitudes de manera oportuna. Pero, ¿y si ese no fuera el caso?
El formato que utilizamos en la duración de la solicitud de API anterior es un mapa de calor. Lo bueno del formato de mapa de calor es que nos indica el valor de tiempo de espera de la API de forma predeterminada (60 segundos). Sin embargo, lo que realmente necesitamos saber es en qué umbral debería preocuparnos este valor antes de alcanzar el límite de tiempo de espera. Para obtener una guía aproximada sobre cuáles son los umbrales aceptables, podemos utilizar el SLO de Kubernetes anterior, que se puede encontrar aquí https://github.com/kubernetes/community/blob/master/sig-scalability/slos/slos.md#steady-state-slisslos
nota
¿Observa la función máxima en esta afirmación? Cuando se utilizan métricas que agregan varios servidores (de forma predeterminada, dos servidores de API en EKS), es importante no hacer un promedio de esos servidores juntos.
Patrones de tráfico asimétricos
¿Qué pasaría si un servidor de API [pod] tuviera poca carga y el otro, mucho? Si calculamos el promedio de esos dos números juntos, podríamos malinterpretar lo que está sucediendo. Por ejemplo, aquí tenemos tres servidores de API, pero toda la carga está en uno de estos servidores de API. Por regla general, todo lo que tenga varios servidores, como etcd y servidores de API, debe desglosarse si se invierte por problemas de escala y rendimiento.
Con el paso a la prioridad y equidad de las API, el número total de solicitudes en el sistema es solo un factor para comprobar si el servidor de API tiene un exceso de suscripciones. Como el sistema ahora funciona con una serie de colas, debemos comprobar si alguna de estas colas está llena y si el tráfico de esa cola está disminuyendo.
Analicemos estas colas con la siguiente consulta:
max without(instance)(apiserver_flowcontrol_nominal_limit_seats{})
nota
Para obtener más información sobre cómo funciona la API A&F, consulta la siguiente guía de mejores prácticas
Aquí vemos los siete grupos de prioridades diferentes que vienen de forma predeterminada en el clúster
A continuación, queremos ver qué porcentaje de ese grupo de prioridades se está utilizando, de modo que podamos entender si un determinado nivel de prioridad se está saturando. Podría ser conveniente limitar las solicitudes cuando el nivel de carga de trabajo es bajo; sin embargo, reducir el nivel de elección de un líder no lo sería.
El sistema API Priority and Fairness (APF) tiene una serie de opciones complejas, algunas de las cuales pueden tener consecuencias imprevistas. Un problema común que vemos sobre el terreno es aumentar la profundidad de las colas hasta el punto de que empieza a añadir una latencia innecesaria. Podemos controlar este problema mediante la apiserver_flowcontrol_current_inqueue_request métrica. Podemos comprobar si hay caídas utilizando laapiserver_flowcontrol_rejected_requests_total. Estas métricas tendrán un valor distinto de cero si algún segmento supera su concurrencia.
Aumentar la profundidad de las colas puede convertir al servidor de API en una fuente importante de latencia y debe hacerse con cuidado. Recomendamos ser prudentes con el número de colas que se crean. Por ejemplo, el número de acciones en un sistema EKS es de 600. Si creamos demasiadas colas, esto puede reducir el número de veces que se comparte en colas importantes que necesitan ese rendimiento, como la cola para la elección del líder o la cola del sistema. Crear demasiadas colas adicionales puede dificultar el tamaño correcto de estas colas.
Para centrarnos en un cambio sencillo e impactante que puedas hacer en APF, simplemente tomamos acciones de grupos infrautilizados y aumentamos el tamaño de los grupos que están al máximo de su uso. Si redistribuyes las acciones de forma inteligente entre estos grupos, puedes reducir la probabilidad de que caigan.
Para obtener más información, consulte los ajustes de prioridad e imparcialidad de la API en la Guía de mejores prácticas de EKS.
Latencia entre API y etcd
¿Cómo podemos usar la metrics/logs del servidor API para determinar si hay un problema con el servidor API, un problema relacionado con el servidor API o una combinación de ambos? upstream/downstream Para entender mejor esto, veamos cómo se pueden relacionar el servidor de API y etcd, y qué tan fácil puede ser solucionar problemas en un sistema incorrecto.
En el siguiente gráfico vemos la latencia del servidor API, pero también vemos que gran parte de esta latencia está correlacionada con el servidor etcd, debido a que las barras del gráfico muestran la mayor parte de la latencia a nivel etcd. Si hay 15 segundos de latencia de etcd al mismo tiempo, hay 20 segundos de latencia del servidor de API, entonces la mayor parte de la latencia se encuentra realmente en el nivel de etcd.
Al analizar todo el flujo, vemos que es aconsejable no centrarse únicamente en el servidor de API, sino también buscar señales que indiquen que etcd está bajo presión (es decir, que los contadores de aplicaciones lentas aumentan). La capacidad de pasar rápidamente al área problemática correcta con solo un vistazo es lo que hace que un panel de control sea poderoso.
nota
El panel de control de la sección se encuentra en https://github.com/RiskyAdventure/Troubleshooting-Dashboards/blob/main/api-troubleshooter.json
Problemas entre el plano de control y el lado del cliente
En este gráfico, buscamos las llamadas a la API que tardaron más tiempo en completarse durante ese período. En este caso, vemos que un recurso personalizado (CRD) llama a una función APPLY, que es la llamada más latente durante el período de las 05:40.
Con estos datos, podemos usar una consulta de Ad-Hoc PromQL o CloudWatch Insights para extraer las solicitudes LIST del registro de auditoría durante ese período y ver de qué aplicación se trata.
Encontrando la fuente con CloudWatch
Las métricas se utilizan mejor para encontrar el área problemática que queremos analizar y reducir tanto el período como los parámetros de búsqueda del problema. Una vez que tengamos estos datos, queremos hacer la transición a los registros para obtener información más detallada sobre los tiempos y los errores. Para ello, convertiremos nuestros registros en métricas mediante CloudWatch Logs Insights.
Por ejemplo, para investigar el problema anterior, utilizaremos la siguiente consulta de CloudWatch Logs Insights para obtener el UserAgent y el RequestURI y así poder determinar qué aplicación está causando esta latencia.
nota
Es necesario utilizar un recuento adecuado para no determinar el List/Resync comportamiento normal de un reloj.
fields @timestamp, @message | filter @logStream like "kube-apiserver-audit" | filter ispresent(requestURI) | filter verb = "list" | parse requestReceivedTimestamp /\d+-\d+-(?<StartDay>\d+)T(?<StartHour>\d+):(?<StartMinute>\d+):(?<StartSec>\d+).(?<StartMsec>\d+)Z/ | parse stageTimestamp /\d+-\d+-(?<EndDay>\d+)T(?<EndHour>\d+):(?<EndMinute>\d+):(?<EndSec>\d+).(?<EndMsec>\d+)Z/ | fields (StartHour * 3600 + StartMinute * 60 + StartSec + StartMsec / 1000000) as StartTime, (EndHour * 3600 + EndMinute * 60 + EndSec + EndMsec / 1000000) as EndTime, (EndTime - StartTime) as DeltaTime | stats avg(DeltaTime) as AverageDeltaTime, count(*) as CountTime by requestURI, userAgent | filter CountTime >=50 | sort AverageDeltaTime desc
Al usar esta consulta, encontramos dos agentes diferentes que ejecutaban una gran cantidad de operaciones de listas de alta latencia. Splunk y CloudWatch agente. Con los datos, podemos tomar la decisión de eliminar, actualizar o reemplazar este controlador por otro proyecto.
nota
Para obtener más información sobre este tema, consulta el siguiente blog
Programador
Como las instancias del plano de control de EKS se ejecutan en una cuenta de AWS independiente, no podremos extraer esos componentes para obtener métricas (el servidor de API es la excepción). Sin embargo, dado que tenemos acceso a los registros de auditoría de estos componentes, podemos convertir esos registros en métricas para ver si alguno de los subsistemas está provocando un cuello de botella en la escalabilidad. Utilicemos CloudWatch Logs Insights para ver cuántos pods no programados hay en la cola del programador.
Los pods no programados aparecen en el registro del planificador
Si tuviéramos la posibilidad de recopilar las métricas del programador directamente en un Kubernetes autogestionado (como Kops), utilizaríamos el siguiente PromQL para entender el retraso acumulado en el programador.
max without(instance)(scheduler_pending_pods)
Como no tenemos acceso a la métrica anterior en EKS, utilizaremos la siguiente consulta de CloudWatch Logs Insights para ver el número de pods acumulados y comprobar cuántos pods no se pudieron desprogramar durante un período determinado. Después, podríamos profundizar en los mensajes de las horas punta para entender la naturaleza del embotellamiento. Por ejemplo, los nodos no funcionan lo suficientemente rápido o el limitador de velocidad del propio planificador.
fields timestamp, pod, err, @message
| filter @logStream like "scheduler"
| filter @message like "Unable to schedule pod"
| parse @message /^.(?<date>\d{4})\s+(?<timestamp>\d+:\d+:\d+\.\d+)\s+\S*\s+\S+\]\s\"(.*?)\"\s+pod=(?<pod>\"(.*?)\")\s+err=(?<err>\"(.*?)\")/
| count(*) as count by pod, err
| sort count desc
Aquí vemos los errores del planificador que indican que el pod no se implementó porque el PVC de almacenamiento no estaba disponible.
nota
El registro de auditoría debe estar activado en el plano de control para habilitar esta función. También se recomienda limitar la retención de registros para no aumentar innecesariamente los costos con el tiempo. A continuación, se muestra un ejemplo de cómo activar todas las funciones de registro con la herramienta EKSCTL.
cloudWatch: clusterLogging: enableTypes: ["*"] logRetentionInDays: 10
Kube Controller Manager
Kube Controller Manager, como todos los demás controladores, tiene límites en cuanto al número de operaciones que puede realizar a la vez. Revisemos cuáles son algunos de esos indicadores consultando una configuración de KOPS en la que podemos establecer estos parámetros.
kubeControllerManager: concurrentEndpointSyncs: 5 concurrentReplicasetSyncs: 5 concurrentNamespaceSyncs: 10 concurrentServiceaccountTokenSyncs: 5 concurrentServiceSyncs: 5 concurrentResourceQuotaSyncs: 5 concurrentGcSyncs: 20 kubeAPIBurst: 30 kubeAPIQPS: "20"
Estas controladoras tienen colas que se llenan en momentos de alta rotación en un clúster. En este caso, vemos que el controlador del conjunto de réplicas tiene una gran cantidad de atrasos en la cola.
Tenemos dos maneras diferentes de abordar esta situación. Si funcionamos de forma autogestionada, podríamos simplemente aumentar las rutinas simultáneas; sin embargo, esto tendría un impacto en el etcd al procesar más datos en el KCM. La otra opción sería reducir la cantidad de objetos de conjuntos de réplicas que se utilizan .spec.revisionHistoryLimit en la implementación para reducir la cantidad de objetos de conjuntos de réplicas que podemos deshacer y, por lo tanto, reducir la presión sobre este controlador.
spec: revisionHistoryLimit: 2
Otras funciones de Kubernetes se pueden ajustar o desactivar para reducir la presión en los sistemas de alta tasa de abandono. Por ejemplo, si la aplicación de nuestros pods no necesita comunicarse directamente con la API de k8s, desactivar el secreto proyectado en esos pods disminuiría la carga de trabajo. ServiceaccountTokenSyncs Si es posible, esta es la forma más conveniente de abordar estos problemas.
kind: Pod spec: automountServiceAccountToken: false
En los sistemas en los que no podemos acceder a las métricas, podemos volver a consultar los registros para detectar cualquier controversia. Si quisiéramos ver el número de solicitudes que se están procesando por controlador o a nivel agregado, usaríamos la siguiente consulta de CloudWatch Logs Insights.
Volumen total procesado por el KCM
# Query to count API qps coming from kube-controller-manager, split by controller type. # If you're seeing values close to 20/sec for any particular controller, it's most likely seeing client-side API throttling. fields @timestamp, @logStream, @message | filter @logStream like /kube-apiserver-audit/ | filter userAgent like /kube-controller-manager/ # Exclude lease-related calls (not counted under kcm qps) | filter requestURI not like "apis/coordination.k8s.io/v1/namespaces/kube-system/leases/kube-controller-manager" # Exclude API discovery calls (not counted under kcm qps) | filter requestURI not like "?timeout=32s" # Exclude watch calls (not counted under kcm qps) | filter verb != "watch" # If you want to get counts of API calls coming from a specific controller, uncomment the appropriate line below: # | filter user.username like "system:serviceaccount:kube-system:job-controller" # | filter user.username like "system:serviceaccount:kube-system:cronjob-controller" # | filter user.username like "system:serviceaccount:kube-system:deployment-controller" # | filter user.username like "system:serviceaccount:kube-system:replicaset-controller" # | filter user.username like "system:serviceaccount:kube-system:horizontal-pod-autoscaler" # | filter user.username like "system:serviceaccount:kube-system:persistent-volume-binder" # | filter user.username like "system:serviceaccount:kube-system:endpointslice-controller" # | filter user.username like "system:serviceaccount:kube-system:endpoint-controller" # | filter user.username like "system:serviceaccount:kube-system:generic-garbage-controller" | stats count(*) as count by user.username | sort count desc
Al analizar los problemas de escalabilidad, lo más importante es analizar cada paso del proceso (API, programador, KCM, etc.) antes de pasar a la fase detallada de solución de problemas. A menudo, en la fase de producción se dan cuenta de que es necesario realizar ajustes en más de una parte de Kubernetes para que el sistema funcione al máximo rendimiento. Es fácil solucionar de forma inadvertida lo que es solo un síntoma (por ejemplo, el tiempo de espera de un nodo) de un cuello de botella mucho mayor.
ETC.D
etcd usa un archivo mapeado en memoria para almacenar los pares clave-valor de manera eficiente. Existe un mecanismo de protección para establecer el tamaño de este espacio de memoria disponible, que normalmente se establece en los límites de 2, 4 y 8 GB. Un menor número de objetos en la base de datos implica menos tareas de limpieza, etc. cuando se actualizan los objetos y es necesario limpiar las versiones anteriores. Este proceso de limpieza de las versiones antiguas de un objeto se denomina compactación. Tras varias operaciones de compactación, hay un proceso posterior que recupera el espacio utilizable, denominado desfragmentación, que se produce por encima de un determinado umbral o según un cronograma fijo.
Hay un par de medidas relacionadas con los usuarios que podemos hacer para limitar la cantidad de objetos en Kubernetes y así reducir el impacto del proceso de compactación y desfragmentación. Por ejemplo, Helm se mantiene en un nivel alto. revisionHistoryLimit Esto mantiene los objetos más antiguos, como ReplicaSets los del sistema, para poder revertirlos. Al establecer los límites del historial en 2, podemos reducir la cantidad de objetos (por ejemplo ReplicaSets) de diez a dos, lo que a su vez reduciría la carga del sistema.
apiVersion: apps/v1 kind: Deployment spec: revisionHistoryLimit: 2
Desde el punto de vista de la supervisión, si los picos de latencia del sistema se producen siguiendo un patrón establecido separados por horas, puede resultar útil comprobar si este proceso de desfragmentación es el origen. Esto lo podemos comprobar mediante el uso de los registros. CloudWatch
Si quieres ver los start/end tiempos de desfragmentación, usa la siguiente consulta:
fields @timestamp, @message | filter @logStream like /etcd-manager/ | filter @message like /defraging|defraged/ | sort @timestamp asc