Fluentd: γιατί είναι σημαντικό να ρυθμίσετε το buffer εξόδου

Fluentd: γιατί είναι σημαντικό να ρυθμίσετε το buffer εξόδου

Σήμερα, είναι αδύνατο να φανταστεί κανείς ένα έργο βασισμένο στο 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 με ένα πρόσθετο buffering εξόδου, αντιμετωπίσαμε μια κατάσταση όπου ορισμένα έγγραφα στο 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 αντιμετωπίζει καθένα από τα προωθημένα τμήματα buffer ως μοναδικό και τους εκχωρεί μοναδικές τιμές πεδίου _id κατά την ευρετηρίαση. Έτσι, εμφανίζονται αντίγραφα μηνυμάτων.

Στο Kibana μοιάζει με αυτό:

Fluentd: γιατί είναι σημαντικό να ρυθμίσετε το buffer εξόδου

Το διάλυμα

Υπάρχουν διάφορες λύσεις σε αυτό το πρόβλημα. Ένα από αυτά είναι ο μηχανισμός που είναι ενσωματωμένος στην προσθήκη fluent-plugin-elasticsearch για τη δημιουργία ενός μοναδικού κατακερματισμού για κάθε έγγραφο. Εάν χρησιμοποιήσετε αυτόν τον μηχανισμό, το ElasticSearch θα αναγνωρίσει τα διπλότυπα στο στάδιο της προώθησης και θα αποτρέψει τα διπλότυπα έγγραφα. Αλλά κανείς δεν μπορεί να αγνοήσει ότι αυτή η μέθοδος επίλυσης του προβλήματος καταπολεμά την έρευνα και δεν εξαλείφει το σφάλμα με την έλλειψη χρόνου, οπότε εγκαταλείψαμε τη χρήση της.

Χρησιμοποιούμε ένα πρόσθετο buffering στην έξοδο Fluentd για να αποτρέψουμε την απώλεια αρχείων καταγραφής σε περίπτωση σύντομων διακοπών δικτύου ή αυξημένης καταγραφής. Εάν, για κάποιο λόγο, το ElasticSearch δεν μπορεί να γράψει αμέσως το έγγραφο στο ευρετήριο, το έγγραφο τοποθετείται σε μια ουρά που αποθηκεύεται στο δίσκο. Επομένως, στην περίπτωσή μας, προκειμένου να εξαλειφθεί η πηγή του προβλήματος που οδηγεί στο σφάλμα που περιγράφηκε παραπάνω, είναι απαραίτητο να ορίσετε τις σωστές τιμές των παραμέτρων buffering, στις οποίες η προσωρινή μνήμη εξόδου Fluentd θα είναι επαρκής μέγεθος και ταυτόχρονα έχουν χρόνο για εκκαθάριση στον καθορισμένο χρόνο.

Θα πρέπει να σημειωθεί ότι οι τιμές των παραμέτρων, που θα συζητηθούν παρακάτω, είναι μεμονωμένες σε κάθε συγκεκριμένη περίπτωση χρήσης buffering σε προσθήκες εξόδου, καθώς εξαρτώνται από πολλούς παράγοντες: την ένταση της καταγραφής μηνυμάτων από τις υπηρεσίες, την απόδοση του το σύστημα του δίσκου, το φορτίο του καναλιού δικτύου και το εύρος ζώνης του. Επομένως, για να γίνετε κατάλληλοι για κάθε μεμονωμένη περίπτωση, αλλά όχι περιττές ρυθμίσεις buffer, αποφεύγοντας μια μακρά τυφλή αναζήτηση, μπορείτε να χρησιμοποιήσετε τις πληροφορίες εντοπισμού σφαλμάτων που γράφει το 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 - το μέγεθος των τμημάτων στα οποία χωρίζονται τα μηνύματα στο buffer.

  • flush_interval - το χρονικό διάστημα μετά το οποίο διαγράφεται η προσωρινή μνήμη.
  • queue_limit_length - ο μέγιστος αριθμός κομματιών στην ουρά.
  • request_timeout είναι ο χρόνος για τον οποίο δημιουργείται μια σύνδεση μεταξύ του Fluentd και του ElasticSearch.

Το συνολικό μέγεθος buffer μπορεί να υπολογιστεί πολλαπλασιάζοντας τις παραμέτρους queue_limit_length και chunk_limit_size, οι οποίες μπορούν να ερμηνευτούν ως "ο μέγιστος αριθμός κομματιών στην ουρά, καθένα από τα οποία έχει ένα δεδομένο μέγεθος." Εάν το μέγεθος του buffer είναι ανεπαρκές, η ακόλουθη προειδοποίηση θα εμφανιστεί στα αρχεία καταγραφής:

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

Σημαίνει ότι το buffer δεν έχει χρόνο για εκκαθάριση στον καθορισμένο χρόνο και τα δεδομένα που εισέρχονται στο πλήρες buffer μπλοκάρονται, γεγονός που θα οδηγήσει σε απώλεια μέρους των αρχείων καταγραφής.

Μπορείτε να αυξήσετε το buffer με δύο τρόπους: είτε αυξάνοντας το μέγεθος κάθε κομματιού στην ουρά είτε αυξάνοντας τον αριθμό των κομματιών που μπορεί να βρίσκονται στην ουρά.

Εάν ορίσετε το μέγεθος κομματιού chunk_limit_size σε περισσότερα από 32 megabyte, τότε το ElasticSeacrh δεν θα το αποδεχτεί, καθώς το εισερχόμενο πακέτο θα είναι πολύ μεγάλο. Επομένως, εάν χρειάζεται να αυξήσετε περαιτέρω το buffer, είναι προτιμότερο να αυξήσετε το μέγιστο μήκος του μήκους ουράς_limit.

Όταν το buffer σταματήσει να ξεχειλίζει και παραμένει μόνο το μήνυμα για την έλλειψη χρονικού ορίου, μπορείτε να αρχίσετε να αυξάνετε την παράμετρο 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" 

Αυτό το μήνυμα δεν επηρεάζει το σύστημα με κανέναν τρόπο και σημαίνει ότι ο χρόνος για την έκπλυση του buffer χρειάστηκε περισσότερο από τον καθορισμένο από την παράμετρο slow_flush_log_threshold. Αυτές είναι πληροφορίες εντοπισμού σφαλμάτων και τις χρησιμοποιούμε όταν επιλέγουμε την τιμή της παραμέτρου request_timeout.

Ο γενικευμένος αλγόριθμος επιλογής έχει ως εξής:

  1. Ορίστε την τιμή request_timeout ώστε να είναι εγγυημένη μεγαλύτερη από την απαραίτητη (εκατοντάδες δευτερόλεπτα). Κατά τη στιγμή της ρύθμισης, το κύριο κριτήριο για τη σωστή ρύθμιση αυτής της παραμέτρου θα είναι η εξαφάνιση των προειδοποιήσεων για έλλειψη χρονικού ορίου.
  2. Περιμένετε μηνύματα σχετικά με την υπέρβαση του ορίου slow_flush_log_threshold. Στο κείμενο της προειδοποίησης, το πεδίο elapsed_time θα περιέχει τον πραγματικό χρόνο εκκαθάρισης του buffer.
  3. Ορίστε την τιμή request_timeout να είναι μεγαλύτερη από τη μέγιστη τιμή lapsed_time που λήφθηκε κατά την περίοδο παρακολούθησης. Υπολογίζουμε την τιμή request_timeout ως elapsed_time + 50%.
  4. Για να αφαιρέσετε τις προειδοποιήσεις σχετικά με ένα μεγάλο flush buffer από το αρχείο καταγραφής, μπορείτε να αυξήσετε την τιμή slow_flush_log_threshold. Υπολογίζουμε αυτήν την τιμή ως elapsed_time + 25%.

Οι τελικές τιμές αυτών των παραμέτρων, όπως σημειώθηκε προηγουμένως, λαμβάνονται ξεχωριστά για κάθε περίπτωση. Ακολουθώντας τον παραπάνω αλγόριθμο, είμαστε εγγυημένοι ότι θα εξαλείψουμε το σφάλμα που οδηγεί σε επαναλαμβανόμενα μηνύματα.

Ο παρακάτω πίνακας δείχνει πώς ο αριθμός των σφαλμάτων ανά ημέρα, που οδηγούν σε διπλά μηνύματα, αλλάζει στη διαδικασία επιλογής των τιμών των παραμέτρων που περιγράφονται παραπάνω:

κόμβος-1
κόμβος-2
κόμβος-3
κόμβος-4

Πριν μετα
Πριν μετα
Πριν μετα
Πριν μετα

απέτυχε να ξεπλύνει το buffer
1749/2
694/2
47/0
1121/2

η επανάληψη πέτυχε
410/2
205/1
24/0
241/2

Θα πρέπει επιπλέον να σημειωθεί ότι οι ρυθμίσεις που λαμβάνονται μπορεί να χάσουν τη συνάφειά τους στη διαδικασία ανάπτυξης του έργου και, κατά συνέπεια, να αυξηθεί ο αριθμός των αρχείων καταγραφής. Η κύρια ένδειξη ότι δεν έχει οριστεί ένα χρονικό όριο είναι η επιστροφή των μεγάλων μηνυμάτων ξεπλύματος buffer στο αρχείο καταγραφής Fluentd, δηλαδή έχει ξεπεραστεί το όριο slow_flush_log_threshold. Από αυτό το σημείο και μετά, υπάρχει ακόμη ένα μικρό περιθώριο πριν την υπέρβαση της παραμέτρου request_timeout, επομένως είναι απαραίτητο να απαντήσετε σε αυτά τα μηνύματα έγκαιρα και να επαναλάβετε τη διαδικασία επιλογής των βέλτιστων ρυθμίσεων που περιγράφονται παραπάνω.

Συμπέρασμα

Η λεπτομέρεια του buffer εξόδου Fluentd είναι ένα από τα κύρια βήματα για τον συντονισμό της στοίβας EFK, τον καθορισμό της σταθερότητας της λειτουργίας της και της σωστής τοποθέτησης των εγγράφων σε ευρετήρια. Εστιάζοντας στον περιγραφόμενο αλγόριθμο διαμόρφωσης, μπορείτε να είστε σίγουροι ότι όλα τα αρχεία καταγραφής θα γραφτούν στο ευρετήριο ElasticSearch με τη σωστή σειρά, χωρίς επανάληψη και απώλεια.

Διαβάστε επίσης άλλα άρθρα στο ιστολόγιό μας:

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο