Los nueve rastrillos de Elasticsearch que pisé

Ilustración de Anton Gudim.


“Una persona entrenada también pisa un rastrillo.
Pero, por otro lado, dónde está la pluma.

Elasticsearch es una gran herramienta, pero cada herramienta requiere no solo ajustes y mantenimiento , sino también atención al detalle. Algunos son insignificantes y yacen en la superficie, mientras que otros están ocultos a tal profundidad que llevará más de un día buscarlos, ni una docena de tazas de café ni un kilómetro de nervios. En este artículo, le contaré acerca de nueve maravillosos rastrillos en la configuración elástica que pisé.

Organizaré el rastrillo en orden descendente de evidencia. Desde aquellos que se pueden prever y eludir en la etapa de configuración y entrada en un clúster en el estado de producción, hasta los muy extraños que brindan la mayor experiencia (y estrellas en los ojos).

Los nodos de datos deben ser iguales


"El clúster se ejecuta a la velocidad del nodo de datos más lento", un axioma agonizante. Pero hay otro punto obvio que no está relacionado con el rendimiento: el elástico no piensa en el espacio del disco, sino en fragmentos, e intenta distribuirlos de manera uniforme entre los nodos de datos. Si algunos de los nodos de datos tienen más espacio que otros, entonces será inútil permanecer inactivo.

Deprecation.log


Puede suceder que alguien no use los medios más modernos para enviar datos al elástico, que no puede establecer el Tipo de contenido al ejecutar consultas. En esta lista, por ejemplo, heka, o cuando los registros salen de los dispositivos por sus medios integrados). En este caso, desaprobación. el registro comienza a crecer a un ritmo alarmante y, para cada solicitud, aparecen las siguientes líneas:

[2018-07-07T14:10:26,659][WARN ][oedrRestController] Content type detection for rest requests is deprecated. Specify the content type using the [Content-Type] header. [2018-07-07T14:10:26,670][WARN ][oedrRestController] Content type detection for rest requests is deprecated. Specify the content type using the [Content-Type] header. [2018-07-07T14:10:26,671][WARN ][oedrRestController] Content type detection for rest requests is deprecated. Specify the content type using the [Content-Type] header. [2018-07-07T14:10:26,673][WARN ][oedrRestController] Content type detection for rest requests is deprecated. Specify the content type using the [Content-Type] header. [2018-07-07T14:10:26,677][WARN ][oedrRestController ] Content type detection for rest requests is deprecated. Specify the content type using the [Content-Type] header. 

Las solicitudes llegan, en promedio, cada 5-10 ms, y cada vez que se agrega una nueva línea al registro. Esto afecta negativamente el rendimiento del subsistema de disco y aumenta iowait. Deprecation.log se puede desactivar, pero no es demasiado razonable. Para recopilar registros elásticos, pero no para arrojar basura, desactivo solo los registros de la clase oedrRestController.

Para hacer esto, agregue la siguiente construcción a logs4j2.properties:

 logger.restcontroller.name = org.elasticsearch.deprecation.rest.RestController logger.restcontroller.level = error 

Levantará los registros de esta clase al nivel de error, y ya no caerán en deprecation.log.

.kibana


¿Cómo se ve un proceso de instalación de clúster típico? Ponemos los nodos, los combinamos en un clúster, colocamos el paquete x (quién lo necesita) y, por supuesto, Kibana. Comenzamos, verificamos que todo funciona y Kibana ve el clúster, y continuamos configurando. El problema es que en un clúster recién instalado, la plantilla predeterminada se ve así:

 { "default": { "order": 0, "template": "*", "settings": { "number_of_shards": "1", "number_of_replicas": "0" } }, "mappings": {}, "aliases": {} } 

Y el índice .kibana, donde se almacenan todas las configuraciones, se crea en una sola copia.

Hubo una vez un caso en que, debido a una falla de hardware, uno de los nodos de datos en el clúster fue eliminado. Rápidamente llegó a un estado consistente, generando réplicas de fragmentos de nodos de datos vecinos, pero, afortunadamente, fue en este nodo de datos donde se ubicó el único fragmento con el índice .kibana. La situación está estancada: el grupo está vivo, en condiciones de trabajo, y Kibana está en estado rojo, y mi teléfono está desgarrado por las llamadas de los empleados que necesitan urgentemente sus registros.

Todo esto se resuelve simplemente. Hasta ahora, nada ha caído:

 XPUT .kibana/_settings { "index": { "number_of_replicas": "<__>" } } 

XMX / XMS


La documentación dice "No más de 32 GB", y con razón. Pero también es correcto que no necesite instalar en la configuración del servicio
 -Xms32g -Xmx32g 

Porque ya tiene más de 32 gigabytes, y aquí nos encontramos con un interesante matiz de Java trabajando con memoria. Por encima de cierto límite, Java deja de usar punteros comprimidos y comienza a consumir una gran cantidad de memoria sin razón. Verificar si los punteros comprimidos usan una máquina Java que ejecuta Elasticsearch es muy simple. Buscamos en el registro de servicio:

 [2018-07-29T15:04:22,041][INFO][oeeNodeEnvironment][log-elastic-hot3] heap size [31.6gb], compressed ordinary object pointers [true] 

La cantidad de memoria que no debe superarse depende, entre otras cosas, de la versión de Java utilizada. Para calcular el volumen exacto en su caso, consulte la documentación .

Ahora he instalado en todos los nodos de datos del elástico:

 -Xms32766m -Xmx32766m 

Parece ser un hecho banal, y la documentación está bien descrita, pero regularmente encuentro instalaciones de Elasticsearch en las que perdí este punto, y Xms / Xmx están configuradas en 32 g.

/ var / lib / elasticsearch


Esta es la ruta predeterminada para almacenar datos en elasticsearch. yml:

 path.data: /var/lib/elasticsearch 

Allí suelo montar una gran matriz RAID, y he aquí por qué: especificamos ES varias formas de almacenar datos, por ejemplo, así:

 path.data: /var/lib/elasticsearch/data1, /var/lib/elasticsearch/data2 

Se montan diferentes discos o matrices de incursiones en data1 y data2. Pero el elástico no se equilibra y no distribuye la carga entre estos caminos. Primero, llena una sección, luego comienza a escribir en otra, por lo que la carga en el almacenamiento será desigual. Sabiendo esto, tomé una decisión inequívoca: combiné todos los discos en RAID0 / 1 y lo monté en la ruta que se especifica en path.data.

procesadores disponibles


Y no, no me refiero a procesadores en nodos de ingesta ahora. Si observa las propiedades de un nodo en ejecución (a través de la API _nodes), puede ver algo como esto:

 "os". { "refresh_interval_in_millis": 1000, "name": "Linux", "arch": "amd64", "version": "4.4.0-87-generic", "available_processors": 28, "allocated_processors": 28 } 

Se puede ver que el nodo se ejecuta en un host con 28 núcleos, y el elástico determinó correctamente su número y comenzó en todos. Pero si hay más de 32 núcleos, a veces sucede así:

 "os": { "refresh_interval_in_millis": 1000, "name": "Linux", "arch": "amd64", "version": "4.4.0-116-generic", "available_processors": 72, "allocated_processors": 32 } 

Debe forzar el número de procesadores disponibles para el servicio; esto tiene un buen efecto en el rendimiento del nodo.

 processors: 72 

thread_pool.bulk.queue_size


En la sección thread_pool.bulk.rejected del último artículo, existía dicha métrica: el recuento de la cantidad de fallas de las solicitudes para agregar datos.

Escribí que el crecimiento de este indicador es una muy mala señal, y los desarrolladores recomiendan no configurar grupos de subprocesos, sino agregar nuevos nodos al clúster; supuestamente, esto resuelve los problemas de rendimiento. Pero las reglas son necesarias para romperlas a veces. Y no siempre es posible "arrojar el problema con hierro", por lo que una de las medidas para combatir las fallas en las solicitudes masivas es aumentar el tamaño de esta cola.

Por defecto, la configuración de la cola se ve así:

 "thread_pool": { "bulk": { "type": "fixed", "min": 28, "max": 28, "queue_size": 200 } } 

El algoritmo es el siguiente:

  1. Recopilamos estadísticas sobre el tamaño promedio de la cola durante el día (el valor instantáneo se almacena en thread_pool.bulk.queue);
  2. Aumente cuidadosamente queue_size a tamaños ligeramente más grandes que el tamaño promedio de la cola activa, porque se produce un error cuando se excede;
  3. Aumentamos el tamaño de la piscina; esto no es necesario, pero sí aceptable.

Para hacer esto, agregue algo como esto a la configuración del host (tendrá, por supuesto, sus propios valores):

 thread_pool.bulk.size: 32 thread_pool.bulk.queue_size: 500 

Y después de reiniciar el nodo, definitivamente controlaremos la carga, E / S, consumo de memoria. y todo lo que es posible revertir la configuración si es necesario.

Importante: esta configuración solo tiene sentido en los nodos que trabajan para recibir datos nuevos.

Creación de índice preliminar


Como dije en el primer artículo de la serie, utilizamos Elasticsearch para almacenar los registros de todos los microservicios. La conclusión es simple: un índice almacena los registros de un componente en un día.

De esto se deduce que cada día se crean nuevos índices por la cantidad de microservicios; por lo tanto, más temprano cada noche, el elástico cayó en el clinch durante aproximadamente 8 minutos, mientras se crearon cien nuevos índices, varios cientos de fragmentos nuevos, el programa de carga de disco fue "al estante", las colas crecieron para enviar registros al elástico en los hosts, y Zabbix floreció con alertas como un árbol de Navidad.

Para evitar esto, era de sentido común escribir un script de Python para crear índices previamente. El script funciona así: encuentra los índices de hoy, extrae sus asignaciones y crea nuevos índices con las mismas asignaciones, pero para el día siguiente. Se ejecuta en cron, se ejecuta durante esas horas cuando Elastic es el menos cargado. El script usa la biblioteca elasticsearch y está disponible en GitHub .

Páginas transparentes para padres enormes


Una vez que encontramos que los nodos elásticos que operan la recepción de datos comenzaron a colgarse bajo carga durante las horas pico. Y con síntomas muy extraños: el uso de todos los núcleos de procesador cae a cero, pero, sin embargo, el servicio se bloquea en la memoria, escucha correctamente el puerto, no hace nada, no responde a las solicitudes y, después de un tiempo, se cae del clúster. El servicio no responde al reinicio systemctl. Solo el viejo y bueno kill −9 ayuda.

Esto no es captado por las herramientas de monitoreo estándar, en los gráficos hasta el momento de la caída, la imagen normal, en los registros de servicio, está vacía. El volcado de memoria de la máquina Java en este punto tampoco fue posible.

Pero, como dicen, "somos profesionales, así que después de un tiempo buscamos en Google la solución". Un problema similar se cubrió en el hilo de discusion.elastic.co y resultó ser un error del kernel relacionado con páginas enormes transparentes. Todo se resolvió desactivando thp en el kernel utilizando el paquete sysfsutils.

Comprobar si tiene habilitadas páginas transparentes enormes es simple:

 cat /sys/kernel/mm/transparent_hugepage/enabled always madvise [never] 

Si [siempre] está allí, está potencialmente en riesgo.

Conclusión


Este es el rastrillo principal (de hecho, hubo, por supuesto, más), que pisé durante un año y medio como administrador del clúster Elasticsearch. Espero que esta información sea útil en el difícil y misterioso viaje al clúster Elasticsearch ideal.

Gracias por la ilustración, Anton Gudim, todavía hay mucho bien en su instagram .

Source: https://habr.com/ru/post/es419041/


All Articles