Fluentd: Por que é importante configurar o búfer de saída

Fluentd: Por que é importante configurar o búfer de saída

Hoxe en día, é imposible imaxinar un proxecto baseado en Kubernetes sen a pila ELK, que garda rexistros das aplicacións e dos compoñentes do sistema do clúster. Na nosa práctica, usamos a pila EFK con Fluentd en lugar de Logstash.

Fluentd é un colector de rexistros moderno e universal que está gañando cada vez máis popularidade e uniuse á Cloud Native Computing Foundation, polo que o seu vector de desenvolvemento se centra no uso en conxunto con Kubernetes.

O feito de usar Fluentd en lugar de Logstash non cambia a esencia xeral do paquete de software, porén, Fluentd caracterízase polos seus propios matices específicos derivados da súa versatilidade.

Por exemplo, cando comezamos a usar EFK nun proxecto ocupado cunha alta intensidade de rexistro, enfrontámonos ao feito de que en Kibana algunhas mensaxes mostráronse repetidamente varias veces. Neste artigo dirémosche por que ocorre este fenómeno e como resolver o problema.

O problema da duplicación de documentos

Nos nosos proxectos, Fluentd está implantado como un DaemonSet (iniciado automaticamente nunha instancia en cada nodo do clúster de Kubernetes) e supervisa os rexistros de contedores stdout en /var/log/containers. Despois da recollida e procesamento, os rexistros en forma de documentos JSON envíanse a ElasticSearch, en clúster ou en forma autónoma, dependendo da escala do proxecto e dos requisitos de rendemento e tolerancia a fallos. Kibana úsase como interface gráfica.

Ao usar Fluentd cun complemento de almacenamento no búfer de saída, atopamos unha situación na que algúns documentos en ElasticSearch tiñan exactamente o mesmo contido e só diferían no identificador. Podes verificar que se trata dunha repetición de mensaxes usando o rexistro de Nginx como exemplo. No ficheiro de rexistro, esta mensaxe existe nunha única copia:

127.0.0.1 192.168.0.1 - [28/Feb/2013:12:00:00 +0900] "GET / HTTP/1.1" 200 777 "-" "Opera/12.0" -

Non obstante, hai varios documentos en ElasticSearch que conteñen esta mensaxe:

{
  "_index": "test-custom-prod-example-2020.01.02",
  "_type": "_doc",
  "_id": "HgGl_nIBR8C-2_33RlQV",
  "_version": 1,
  "_score": 0,
  "_source": {
    "service": "test-custom-prod-example",
    "container_name": "nginx",
    "namespace": "test-prod",
    "@timestamp": "2020-01-14T05:29:47.599052886 00:00",
    "log": "127.0.0.1 192.168.0.1 - [28/Feb/2013:12:00:00  0900] "GET / HTTP/1.1" 200 777 "-" "Opera/12.0" -",
    "tag": "custom-log"
  }
}

{
  "_index": "test-custom-prod-example-2020.01.02",
  "_type": "_doc",
  "_id": "IgGm_nIBR8C-2_33e2ST",
  "_version": 1,
  "_score": 0,
  "_source": {
    "service": "test-custom-prod-example",
    "container_name": "nginx",
    "namespace": "test-prod",
    "@timestamp": "2020-01-14T05:29:47.599052886 00:00",
    "log": "127.0.0.1 192.168.0.1 - [28/Feb/2013:12:00:00  0900] "GET / HTTP/1.1" 200 777 "-" "Opera/12.0" -",
    "tag": "custom-log"
  }
}

Ademais, pode haber máis de dúas repeticións.

Mentres solucionas este problema nos rexistros de Fluentd, podes ver unha gran cantidade de avisos co seguinte contido:

2020-01-16 01:46:46 +0000 [warn]: [test-prod] failed to flush the buffer. retry_time=4 next_retry_seconds=2020-01-16 01:46:53 +0000 chunk="59c37fc3fb320608692c352802b973ce" error_class=Fluent::Plugin::ElasticsearchOutput::RecoverableRequestFailure error="could not push logs to Elasticsearch cluster ({:host=>"elasticsearch", :port=>9200, :scheme=>"http", :user=>"elastic", :password=>"obfuscated"}): read timeout reached"

Estes avisos prodúcense cando ElasticSearch non pode devolver unha resposta a unha solicitude dentro do tempo especificado polo parámetro request_timeout, polo que non se pode borrar o fragmento do búfer reenviado. Despois diso, Fluentd tenta enviar o fragmento do búfer a ElasticSearch de novo e despois dun número arbitrario de intentos, a operación finaliza con éxito:

2020-01-16 01:47:05 +0000 [warn]: [test-prod] retry succeeded. chunk_id="59c37fc3fb320608692c352802b973ce" 
2020-01-16 01:47:05 +0000 [warn]: [test-prod] retry succeeded. chunk_id="59c37fad241ab300518b936e27200747" 
2020-01-16 01:47:05 +0000 [warn]: [test-dev] retry succeeded. chunk_id="59c37fc11f7ab707ca5de72a88321cc2" 
2020-01-16 01:47:05 +0000 [warn]: [test-dev] retry succeeded. chunk_id="59c37fb5adb70c06e649d8c108318c9b" 
2020-01-16 01:47:15 +0000 [warn]: [kube-system] retry succeeded. chunk_id="59c37f63a9046e6dff7e9987729be66f"

Non obstante, ElasticSearch trata cada un dos fragmentos de búfer transferidos como únicos e asígnalles valores de campo _id únicos durante a indexación. Así aparecen as copias das mensaxes.

En Kibana vese así:

Fluentd: Por que é importante configurar o búfer de saída

A solución

Hai varias opcións para resolver este problema. Un deles é o mecanismo integrado no complemento fluent-plugin-elasticsearch para xerar un hash único para cada documento. Se utiliza este mecanismo, ElasticSearch recoñecerá as repeticións na fase de reenvío e evitará documentos duplicados. Pero debemos ter en conta que este método de resolución do problema loita coa investigación e non elimina o erro coa falta de tempo de espera, polo que abandonamos o seu uso.

Usamos un complemento de almacenamento en búfer na saída de Fluentd para evitar a perda de rexistro en caso de problemas de rede a curto prazo ou aumento da intensidade de rexistro. Se por algún motivo ElasticSearch non pode escribir un documento de xeito instantáneo no índice, o documento queda en cola e almacénase no disco. Polo tanto, no noso caso, para eliminar a orixe do problema que leva ao erro descrito anteriormente, é necesario establecer os valores correctos para os parámetros de almacenamento en búfer, nos que o búfer de saída Fluentd terá un tamaño suficiente e ao mesmo tempo conseguen ser despexados no tempo previsto.

Cómpre sinalar que os valores dos parámetros que se comentan a continuación son individuais en cada caso específico de uso do buffer nos complementos de saída, xa que dependen de moitos factores: a intensidade de escribir mensaxes no rexistro por servizos, o rendemento do sistema de disco, a rede. carga da canle e o seu ancho de banda. Polo tanto, para obter unha configuración do búfer axeitada para cada caso individual, pero non redundante, evitando as buscas longas a cegas, pode utilizar a información de depuración que Fluentd escribe no seu rexistro durante a operación e obter os valores correctos con relativa rapidez.

No momento no que se rexistrou o problema, a configuración tiña este aspecto:

 <buffer>
        @type file
        path /var/log/fluentd-buffers/kubernetes.test.buffer
        flush_mode interval
        retry_type exponential_backoff
        flush_thread_count 2
        flush_interval 5s
        retry_forever
        retry_max_interval 30
        chunk_limit_size 8M
        queue_limit_length 8
        overflow_action block
      </buffer>

Ao resolver o problema, seleccionáronse manualmente os valores dos seguintes parámetros:
chunk_limit_size — o tamaño dos anacos nos que se dividen as mensaxes do búfer.

  • flush_interval — intervalo de tempo despois do cal se borra o búfer.
  • queue_limit_length — o número máximo de anacos na cola.
  • request_timeout é o tempo durante o que se establece a conexión entre Fluentd e ElasticSearch.

O tamaño total do búfer pódese calcular multiplicando os parámetros queue_limit_length e chunk_limit_size, que se poden interpretar como "o número máximo de anacos na cola, cada un dos cales ten un tamaño determinado". Se o tamaño do búfer é insuficiente, aparecerá o seguinte aviso nos rexistros:

2020-01-21 10:22:57 +0000 [warn]: [test-prod] failed to write data into buffer by buffer overflow action=:block

Significa que o búfer non ten tempo para borrarse no tempo asignado e os datos que entran no búfer completo están bloqueados, o que levará á perda de parte dos rexistros.

Podes aumentar o búfer de dúas formas: aumentando o tamaño de cada anaco da cola ou o número de anacos que poden estar na cola.

Se estableces o tamaño do fragmento chunk_limit_size en máis de 32 megabytes, ElasticSeacrh non o aceptará, xa que o paquete entrante será demasiado grande. Polo tanto, se precisa aumentar aínda máis o búfer, é mellor aumentar a lonxitude máxima da cola queue_limit_length.

Cando o búfer deixa de desbordarse e só queda a mensaxe de tempo de espera insuficiente, podes comezar a aumentar o parámetro request_timeout. Non obstante, se estableces o valor en máis de 20 segundos, os seguintes avisos comezarán a aparecer nos rexistros de Fluentd:

2020-01-21 09:55:33 +0000 [warn]: [test-dev] buffer flush took longer time than slow_flush_log_threshold: elapsed_time=20.85753920301795 slow_flush_log_threshold=20.0 plugin_id="postgresql-dev" 

Esta mensaxe non afecta de ningún xeito ao funcionamento do sistema e significa que o tempo de vaciado do búfer tardou máis que o establecido polo parámetro slow_flush_log_threshold. Esta é información de depuración e usámola ao elixir o valor do parámetro request_timeout.

O algoritmo de selección xeralizado é o seguinte:

  1. Establece request_timeout cun valor garantido maior do necesario (centos de segundos). Durante a configuración, o principal criterio para a configuración correcta deste parámetro será a desaparición dos avisos por falta de tempo de espera.
  2. Agarda as mensaxes sobre a superación do limiar slow_flush_log_threshold. O texto de aviso no campo elapsed_time mostrará a hora real na que se limpou o búfer.
  3. Establece request_timeout nun valor superior ao valor máximo de elapsed_time obtido durante o período de observación. Calculamos o valor request_timeout como elapsed_time + 50%.
  4. Para eliminar os avisos sobre baleirados longos do búfer do rexistro, pode aumentar o valor de slow_flush_log_threshold. Calculamos este valor como elapsed_time + 25%.

Os valores finais destes parámetros, como se indicou anteriormente, obtéñense individualmente para cada caso. Seguindo o algoritmo anterior, temos a garantía de eliminar o erro que leva a mensaxes repetidas.

A seguinte táboa mostra como cambia o número de erros ao día, que leva á duplicación de mensaxes, no proceso de selección dos valores dos parámetros descritos anteriormente:

nodo-1
nodo-2
nodo-3
nodo-4

Antes despois
Antes despois
Antes despois
Antes despois

non se puido limpar o búfer
1749/2
694/2
47/0
1121/2

reintento con éxito
410/2
205/1
24/0
241/2

Paga a pena sinalar ademais que a configuración resultante pode perder a súa relevancia a medida que o proxecto crece e, en consecuencia, aumenta o número de rexistros. O principal sinal de tempo de espera insuficiente é a devolución de mensaxes sobre un longo vaciado do búfer ao rexistro de Fluentd, é dicir, superando o limiar slow_flush_log_threshold. A partir deste momento, aínda queda unha pequena marxe antes de que se supere o parámetro request_timeout, polo que é necesario responder a estas mensaxes de forma oportuna e repetir o proceso de selección da configuración óptima descrita anteriormente.

Conclusión

O axuste fino do búfer de saída de Fluentd é unha das principais etapas da configuración da pila EFK, determinando a estabilidade do seu funcionamento e a correcta colocación dos documentos nos índices. Segundo o algoritmo de configuración descrito, pode estar seguro de que todos os rexistros se escribirán no índice ElasticSearch na orde correcta, sen repeticións nin perdas.

Lea tamén outros artigos no noso blog:

Fonte: www.habr.com

Engadir un comentario