Fluentd: Защо е важно да конфигурирате изходния буфер

Fluentd: Защо е важно да конфигурирате изходния буфер

В днешно време е невъзможно да си представим базиран на Kubernetes проект без стека ELK, който записва регистрационни файлове както на приложенията, така и на системните компоненти на клъстера. В нашата практика ние използваме стека EFK с Fluentd вместо Logstash.

Fluentd е модерен, универсален колектор на журнали, който набира все по-голяма популярност и се присъедини към Cloud Native Computing Foundation, поради което неговият вектор за развитие е фокусиран върху използване във връзка с Kubernetes.

Фактът, че се използва Fluentd вместо Logstash, не променя общата същност на софтуерния пакет, но Fluentd се характеризира със свои собствени специфични нюанси, произтичащи от неговата гъвкавост.

Например, когато започнахме да използваме EFK в натоварен проект с висок интензитет на регистриране, се сблъскахме с факта, че в Kibana някои съобщения се показват многократно няколко пъти. В тази статия ще ви разкажем защо възниква това явление и как да разрешите проблема.

Проблемът с дублирането на документи

В нашите проекти Fluentd се внедрява като DaemonSet (автоматично стартиран в един екземпляр на всеки възел на клъстера Kubernetes) и наблюдава регистрационните файлове на контейнера stdout в /var/log/containers. След събиране и обработка, регистрационните файлове под формата на JSON документи се изпращат до ElasticSearch, повдигнати в клъстерна или самостоятелна форма, в зависимост от мащаба на проекта и изискванията за производителност и устойчивост на грешки. Kibana се използва като графичен интерфейс.

Когато използвахме Fluentd с приставка за буфериране на изхода, срещнахме ситуация, при която някои документи в ElasticSearch имаха точно същото съдържание и се различаваха само по идентификатора. Можете да проверите дали това е повторение на съобщението, като използвате регистрационния файл на Nginx като пример. В регистрационния файл това съобщение съществува в едно копие:

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

Има обаче няколко документа в ElasticSearch, които съдържат това съобщение:

{
  "_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"
  }
}

Освен това може да има повече от две повторения.

Докато коригирате този проблем в регистрационните файлове на Fluentd, можете да видите голям брой предупреждения със следното съдържание:

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"

Тези предупреждения възникват, когато ElasticSearch не може да върне отговор на заявка в рамките на времето, определено от параметъра request_timeout, поради което препратеният буферен фрагмент не може да бъде изчистен. След това Fluentd се опитва отново да изпрати буферния фрагмент на ElasticSearch и след произволен брой опити операцията завършва успешно:

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"

ElasticSearch обаче третира всеки от прехвърлените буферни фрагменти като уникален и им присвоява уникални стойности на полето _id по време на индексирането. Ето как се появяват копията на съобщенията.

В Kibana изглежда така:

Fluentd: Защо е важно да конфигурирате изходния буфер

Решението

Има няколко варианта за решаване на този проблем. Един от тях е механизмът, вграден в плъгина fluent-plugin-elasticsearch за генериране на уникален хеш за всеки документ. Ако използвате този механизъм, ElasticSearch ще разпознае повторенията на етапа на препращане и ще предотврати дублиране на документи. Но трябва да вземем предвид, че този метод за решаване на проблема се бори с разследването и не елиминира грешката с липса на таймаут, така че изоставихме използването му.

Използваме приставка за буфериране на изхода на Fluentd, за да предотвратим загуба на регистрационни файлове в случай на краткотрайни мрежови проблеми или повишена интензивност на регистриране. Ако по някаква причина ElasticSearch не може незабавно да запише документ в индекса, документът се поставя на опашка и се съхранява на диска. Следователно, в нашия случай, за да се елиминира източникът на проблема, който води до описаната по-горе грешка, е необходимо да се зададат правилните стойности за параметрите на буфериране, при които изходният буфер на Fluentd ще бъде с достатъчен размер и в същото време успяват да бъдат изчистени в определеното време.

Струва си да се отбележи, че стойностите на параметрите, обсъдени по-долу, са индивидуални във всеки конкретен случай на използване на буфериране в изходни добавки, тъй като те зависят от много фактори: интензивността на писане на съобщения в дневника от услуги, производителност на дисковата система, мрежа натоварването на канала и неговата честотна лента. Следователно, за да получите настройки на буфера, които са подходящи за всеки отделен случай, но не и излишни, като избягвате сляпо дълги търсения, можете да използвате информацията за отстраняване на грешки, която Fluentd записва в своя журнал по време на работа и сравнително бързо да получите правилните стойности.

По времето, когато проблемът беше записан, конфигурацията изглеждаше така:

 <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>

При решаването на проблема ръчно са избрани стойностите на следните параметри:
chunk_limit_size — размерът на парчетата, на които са разделени съобщенията в буфера.

  • flush_interval — интервал от време, след който буферът се изчиства.
  • queue_limit_length — максималният брой парчета в опашката.
  • request_timeout е времето, за което се установява връзката между Fluentd и ElasticSearch.

Общият размер на буфера може да бъде изчислен чрез умножаване на параметрите queue_limit_length и chunk_limit_size, които могат да се тълкуват като „максималния брой парчета в опашката, всяка от които има даден размер.“ Ако размерът на буфера е недостатъчен, в регистрационните файлове ще се появи следното предупреждение:

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

Това означава, че буферът няма време да бъде изчистен за определеното време и данните, които влизат в пълния буфер, се блокират, което ще доведе до загуба на част от регистрационните файлове.

Можете да увеличите буфера по два начина: чрез увеличаване или на размера на всяка част в опашката, или на броя на частите, които могат да бъдат в опашката.

Ако зададете размера на парчето chunk_limit_size на повече от 32 мегабайта, тогава ElasticSeacrh няма да го приеме, тъй като входящият пакет ще бъде твърде голям. Следователно, ако трябва да увеличите допълнително буфера, по-добре е да увеличите максималната дължина на опашката queue_limit_length.

Когато буферът спре да се препълва и остане само съобщението за недостатъчно изчакване, можете да започнете да увеличавате параметъра request_timeout. Ако обаче зададете стойността на повече от 20 секунди, следните предупреждения ще започнат да се появяват в регистрационните файлове на 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" 

Това съобщение не засяга работата на системата по никакъв начин и означава, че времето за промиване на буфера е отнело повече от зададеното от параметъра slow_flush_log_threshold. Това е информация за отстраняване на грешки и ние я използваме, когато избираме стойността на параметъра request_timeout.

Обобщеният алгоритъм за избор е както следва:

  1. Задайте request_timeout на стойност, гарантирано по-голяма от необходимото (стотици секунди). По време на настройката основният критерий за правилната настройка на този параметър ще бъде изчезването на предупрежденията за липса на таймаут.
  2. Изчакайте съобщения за превишаване на прага slow_flush_log_threshold. Предупредителният текст в полето elapsed_time ще покаже реалното време, когато буферът е бил изчистен.
  3. Задайте request_timeout на стойност, по-голяма от максималната стойност на elapsed_time, получена по време на периода на наблюдение. Ние изчисляваме стойността на request_timeout като elapsed_time + 50%.
  4. За да премахнете предупрежденията за дълги промивания на буфери от регистрационния файл, можете да повишите стойността на slow_flush_log_threshold. Ние изчисляваме тази стойност като elapsed_time + 25%.

Крайните стойности на тези параметри, както беше отбелязано по-рано, се получават индивидуално за всеки случай. Като следваме горния алгоритъм, гарантирано ще елиминираме грешката, която води до повтарящи се съобщения.

Таблицата по-долу показва как броят на грешките на ден, водещи до дублиране на съобщения, се променя в процеса на избор на стойностите на описаните по-горе параметри:

възел-1
възел-2
възел-3
възел-4

Преди след
Преди след
Преди след
Преди след

не успя да изчисти буфера
1749/2
694/2
47/0
1121/2

повторният опит е успешен
410/2
205/1
24/0
241/2

Струва си допълнително да се отбележи, че получените настройки може да загубят своята релевантност, тъй като проектът расте и съответно броят на регистрационните файлове се увеличава. Основният признак за недостатъчно изчакване е връщането на съобщения за дълго промиване на буфера към журнала на Fluentd, тоест превишаване на прага slow_flush_log_threshold. От този момент нататък все още има малък марж преди превишаването на параметъра request_timeout, така че е необходимо да отговорите на тези съобщения своевременно и да повторите процеса на избор на оптималните настройки, описани по-горе.

Заключение

Фината настройка на изходния буфер на Fluentd е един от основните етапи на конфигуриране на стека EFK, определящ стабилността на неговата работа и правилното поставяне на документи в индекси. Въз основа на описания алгоритъм за конфигурация можете да сте сигурни, че всички регистрационни файлове ще бъдат записани в индекса ElasticSearch в правилния ред, без повторения или загуби.

Прочетете и други статии в нашия блог:

Източник: www.habr.com

Добавяне на нов коментар