Όρια CPU και επιθετικό throttling στο Kubernetes

Σημείωση. μετάφρ.: Αυτή η εντυπωσιακή ιστορία του Omio—μιας ευρωπαϊκής οργάνωσης ταξιδιών—οδηγεί τους αναγνώστες από τη βασική θεωρία στις συναρπαστικές πρακτικές περιπλοκές της διαμόρφωσης του Kubernetes. Η εξοικείωση με τέτοιες περιπτώσεις βοηθά όχι μόνο να διευρύνετε τους ορίζοντές σας, αλλά και να αποτρέψετε μη ασήμαντα προβλήματα.

Όρια CPU και επιθετικό throttling στο Kubernetes

Σας έχει τύχει ποτέ μια εφαρμογή να κολλήσει στη θέση της, να σταματήσει να ανταποκρίνεται σε ελέγχους υγείας και να μην μπορείτε να καταλάβετε γιατί; Μια πιθανή εξήγηση σχετίζεται με τα όρια ορίων πόρων CPU. Για αυτό θα μιλήσουμε σε αυτό το άρθρο.

TL? DR:
Συνιστούμε ανεπιφύλακτα την απενεργοποίηση των ορίων CPU στο Kubernetes (ή την απενεργοποίηση των ορίων CFS στο Kubelet) εάν χρησιμοποιείτε μια έκδοση του πυρήνα Linux με σφάλμα ορίου CFS. Στον πυρήνα είναι διαθέσιμο σοβαρή και πολύ γνωστό ένα σφάλμα που οδηγεί σε υπερβολικό στραγγαλισμό και καθυστερήσεις
.

Στο Όμιο όλη η υποδομή διαχειρίζεται η Kubernetes. Όλοι οι φόρτοι εργασίας με κατάσταση κατάστασης και χωρίς πολιτεία εκτελούνται αποκλειστικά στο Kubernetes (χρησιμοποιούμε το Google Kubernetes Engine). Τους τελευταίους έξι μήνες, αρχίσαμε να παρατηρούμε τυχαίες επιβραδύνσεις. Οι εφαρμογές παγώνουν ή παύουν να ανταποκρίνονται σε ελέγχους υγείας, χάνουν τη σύνδεση με το δίκτυο κ.λπ. Αυτή η συμπεριφορά μας μπέρδεψε για πολύ καιρό και τελικά αποφασίσαμε να πάρουμε το πρόβλημα στα σοβαρά.

Περίληψη του άρθρου:

  • Λίγα λόγια για τα κοντέινερ και τα Kubernetes.
  • Πώς υλοποιούνται τα αιτήματα και τα όρια της CPU.
  • Πώς λειτουργεί το όριο CPU σε περιβάλλοντα πολλαπλών πυρήνων.
  • Πώς να παρακολουθείτε τον στραγγαλισμό της CPU.
  • Λύση προβλήματος και αποχρώσεις.

Λίγα λόγια για τα κοντέινερ και τα Kubernetes

Το Kubernetes είναι ουσιαστικά το σύγχρονο πρότυπο στον κόσμο των υποδομών. Το κύριο καθήκον του είναι η ενορχήστρωση κοντέινερ.

εμπορευματοκιβώτια

Στο παρελθόν, έπρεπε να δημιουργήσουμε τεχνουργήματα όπως Java JAR/WAR, Python Egg ή εκτελέσιμα για να εκτελούνται σε διακομιστές. Ωστόσο, για να λειτουργήσουν, έπρεπε να γίνει πρόσθετη δουλειά: εγκατάσταση του περιβάλλοντος χρόνου εκτέλεσης (Java/Python), τοποθέτηση των απαραίτητων αρχείων στις σωστές θέσεις, εξασφάλιση συμβατότητας με μια συγκεκριμένη έκδοση του λειτουργικού συστήματος κ.λπ. Με άλλα λόγια, έπρεπε να δοθεί ιδιαίτερη προσοχή στη διαχείριση των ρυθμίσεων (η οποία ήταν συχνά πηγή διαμάχης μεταξύ προγραμματιστών και διαχειριστών συστήματος).

Τα κοντέινερ άλλαξαν τα πάντα. Τώρα το τεχνούργημα είναι μια εικόνα κοντέινερ. Μπορεί να αναπαρασταθεί ως ένα είδος εκτεταμένου εκτελέσιμου αρχείου που περιέχει όχι μόνο το πρόγραμμα, αλλά και ένα πλήρες περιβάλλον εκτέλεσης (Java/Python/...), καθώς και τα απαραίτητα αρχεία/πακέτα, προεγκατεστημένα και έτοιμα για τρέξιμο. Τα κοντέινερ μπορούν να αναπτυχθούν και να εκτελεστούν σε διαφορετικούς διακομιστές χωρίς πρόσθετα βήματα.

Επιπλέον, τα δοχεία λειτουργούν στο δικό τους περιβάλλον sandbox. Έχουν τον δικό τους εικονικό προσαρμογέα δικτύου, το δικό τους σύστημα αρχείων με περιορισμένη πρόσβαση, τη δική τους ιεραρχία διεργασιών, τους δικούς τους περιορισμούς σε CPU και μνήμη κ.λπ. Όλα αυτά υλοποιούνται χάρη σε ένα ειδικό υποσύστημα του πυρήνα του Linux - χώρους ονομάτων.

Kubernetes

Όπως αναφέρθηκε προηγουμένως, ο Kubernetes είναι ενορχηστρωτής κοντέινερ. Λειτουργεί ως εξής: του δίνετε μια δεξαμενή μηχανημάτων και μετά λέτε: "Γεια σου, Kubernetes, ας εκκινήσουμε δέκα περιπτώσεις του κοντέινερ μου με 2 επεξεργαστές και 3 GB μνήμης το καθένα, και ας συνεχίσουν να λειτουργούν!" Η Kubernetes θα φροντίσει για τα υπόλοιπα. Θα βρει δωρεάν χωρητικότητα, θα εκτοξεύσει κοντέινερ και θα τα επανεκκινήσει εάν είναι απαραίτητο, θα κυκλοφορήσει ενημερώσεις κατά την αλλαγή εκδόσεων κ.λπ. Ουσιαστικά, το Kubernetes σάς επιτρέπει να αφαιρέσετε το στοιχείο υλικού και κάνει μια μεγάλη ποικιλία συστημάτων κατάλληλα για την ανάπτυξη και εκτέλεση εφαρμογών.

Όρια CPU και επιθετικό throttling στο Kubernetes
Kubernetes από τη σκοπιά του λαϊκού

Τι είναι τα αιτήματα και τα όρια στο Kubernetes

Εντάξει, καλύψαμε κοντέινερ και Kubernetes. Γνωρίζουμε επίσης ότι πολλά δοχεία μπορούν να βρίσκονται στο ίδιο μηχάνημα.

Μια αναλογία μπορεί να γίνει με ένα κοινόχρηστο διαμέρισμα. Ένας ευρύχωρος χώρος (μηχανήματα/μονάδες) λαμβάνεται και ενοικιάζεται σε πολλούς ενοικιαστές (κοντέινερ). Η Kubernetes λειτουργεί ως μεσίτης. Τίθεται το ερώτημα, πώς να κρατήσουμε τους ενοικιαστές από συγκρούσεις μεταξύ τους; Τι κι αν ένας από αυτούς, ας πούμε, αποφασίσει να δανειστεί το μπάνιο για τη μισή μέρα;

Εδώ μπαίνουν στο παιχνίδι τα αιτήματα και τα όρια. ΕΠΕΞΕΡΓΑΣΤΗΣ Αίτημα απαιτείται αποκλειστικά για σκοπούς σχεδιασμού. Αυτό είναι κάτι σαν μια «λίστα επιθυμιών» του κοντέινερ και χρησιμοποιείται για την επιλογή του καταλληλότερου κόμβου. Ταυτόχρονα η CPU Όριο μπορεί να συγκριθεί με συμφωνία ενοικίασης - μόλις επιλέξουμε μια μονάδα για το κοντέινερ, το δεν μπορώ υπερβαίνουν τα καθορισμένα όρια. Και εδώ δημιουργείται το πρόβλημα...

Πώς υλοποιούνται τα αιτήματα και τα όρια στο Kubernetes

Το Kubernetes χρησιμοποιεί έναν μηχανισμό στραγγαλισμού (παρακάμπτοντας τους κύκλους του ρολογιού) ενσωματωμένο στον πυρήνα για την εφαρμογή ορίων της CPU. Εάν μια εφαρμογή υπερβεί το όριο, ενεργοποιείται το throttling (δηλαδή λαμβάνει λιγότερους κύκλους CPU). Τα αιτήματα και τα όρια για τη μνήμη οργανώνονται διαφορετικά, επομένως είναι πιο εύκολο να εντοπιστούν. Για να το κάνετε αυτό, απλώς ελέγξτε την τελευταία κατάσταση επανεκκίνησης του pod: εάν είναι "OOMKilled". Ο στραγγαλισμός της CPU δεν είναι τόσο απλός, αφού το K8s κάνει διαθέσιμες μετρήσεις μόνο με βάση τη χρήση και όχι από cgroups.

Αίτημα CPU

Όρια CPU και επιθετικό throttling στο Kubernetes
Πώς υλοποιείται το αίτημα CPU

Για απλότητα, ας δούμε τη διαδικασία χρησιμοποιώντας ως παράδειγμα ένα μηχάνημα με CPU 4 πυρήνων.

Το K8s χρησιμοποιεί έναν μηχανισμό ομάδας ελέγχου (cgroups) για τον έλεγχο της κατανομής των πόρων (μνήμη και επεξεργαστής). Ένα ιεραρχικό μοντέλο είναι διαθέσιμο για αυτό: το παιδί κληρονομεί τα όρια της γονικής ομάδας. Οι λεπτομέρειες διανομής αποθηκεύονται σε ένα εικονικό σύστημα αρχείων (/sys/fs/cgroup). Στην περίπτωση ενός επεξεργαστή αυτό είναι /sys/fs/cgroup/cpu,cpuacct/*.

Το K8s χρησιμοποιεί αρχείο cpu.share για την κατανομή πόρων επεξεργαστή. Στην περίπτωσή μας, η ρίζα cgroup λαμβάνει 4096 μερίδια πόρων CPU - το 100% της διαθέσιμης ισχύος επεξεργαστή (1 πυρήνας = 1024, αυτή είναι μια σταθερή τιμή). Η ριζική ομάδα κατανέμει τους πόρους αναλογικά ανάλογα με τα μερίδια των απογόνων που είναι εγγεγραμμένοι cpu.share, και αυτοί με τη σειρά τους κάνουν το ίδιο με τους απογόνους τους κ.λπ. Σε έναν τυπικό κόμβο Kubernetes, η ρίζα cgroup έχει τρία παιδιά: system.slice, user.slice и kubepods. Οι δύο πρώτες υποομάδες χρησιμοποιούνται για τη διανομή πόρων μεταξύ κρίσιμων φορτίων συστήματος και προγραμμάτων χρηστών εκτός των K8. Τελευταίο - kubepods — δημιουργήθηκε από την Kubernetes για τη διανομή πόρων μεταξύ των pods.

Το παραπάνω διάγραμμα δείχνει ότι η πρώτη και η δεύτερη υποομάδα έλαβαν το καθένα 1024 μετοχές, με κατανεμημένη την υποομάδα kuberpod 4096 μερίδια Πώς είναι δυνατό αυτό: τελικά, η ριζική ομάδα έχει πρόσβαση μόνο σε 4096 μετοχές και το άθροισμα των μετοχών των απογόνων της υπερβαίνει σημαντικά αυτόν τον αριθμό (6144)? Το θέμα είναι ότι η τιμή έχει λογική λογική, επομένως ο προγραμματιστής Linux (CFS) τη χρησιμοποιεί για να κατανείμει αναλογικά τους πόρους της CPU. Στην περίπτωσή μας, οι δύο πρώτες ομάδες λαμβάνουν 680 πραγματικές μετοχές (16,6% των 4096) και η kubepod λαμβάνει το υπόλοιπο 2736 μερίδια Σε περίπτωση διακοπής λειτουργίας, οι δύο πρώτες ομάδες δεν θα χρησιμοποιήσουν τους πόρους που έχουν διατεθεί.

Ευτυχώς, ο χρονοπρογραμματιστής έχει έναν μηχανισμό για την αποφυγή σπατάλης αχρησιμοποίητων πόρων CPU. Μεταφέρει την «αδρανής» χωρητικότητα σε μια παγκόσμια δεξαμενή, από την οποία διανέμεται σε ομάδες που χρειάζονται πρόσθετη ισχύ επεξεργαστή (η μεταφορά γίνεται σε παρτίδες για να αποφευχθούν απώλειες στρογγυλοποίησης). Μια παρόμοια μέθοδος εφαρμόζεται σε όλους τους απογόνους των απογόνων.

Αυτός ο μηχανισμός διασφαλίζει μια δίκαιη κατανομή της ισχύος του επεξεργαστή και διασφαλίζει ότι καμία διεργασία δεν «κλέβει» πόρους από άλλους.

Όριο CPU

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

Το K8s εμπλέκεται Μηχανισμός ποσοστώσεων CFS για την εφαρμογή ορίων. Οι ρυθμίσεις τους καθορίζονται σε αρχεία cfs_period_us и cfs_quota_us στον κατάλογο cgroup (το αρχείο βρίσκεται επίσης εκεί cpu.share).

Σε αντίθεση με cpu.share, η ποσόστωση βασίζεται σε χρονική περίοδος, και όχι στη διαθέσιμη ισχύ του επεξεργαστή. cfs_period_us καθορίζει τη διάρκεια της περιόδου (εποχή) - είναι πάντα 100000 μs (100 ms). Υπάρχει μια επιλογή αλλαγής αυτής της τιμής στα K8, αλλά είναι διαθέσιμη μόνο σε alpha προς το παρόν. Ο προγραμματιστής χρησιμοποιεί την εποχή για να επανεκκινήσει τα χρησιμοποιημένα quotas. Δεύτερο αρχείο cfs_quota_us, καθορίζει τον διαθέσιμο χρόνο (όριο) σε κάθε εποχή. Σημειώστε ότι προσδιορίζεται επίσης σε μικροδευτερόλεπτα. Η ποσόστωση μπορεί να υπερβαίνει τη διάρκεια της εποχής. με άλλα λόγια, μπορεί να είναι μεγαλύτερο από 100 ms.

Ας δούμε δύο σενάρια σε μηχανήματα 16 πυρήνων (ο πιο συνηθισμένος τύπος υπολογιστή που έχουμε στο Omio):

Όρια CPU και επιθετικό throttling στο Kubernetes
Σενάριο 1: 2 νήματα και όριο 200 ms. Χωρίς στραγγαλισμό

Όρια CPU και επιθετικό throttling στο Kubernetes
Σενάριο 2: 10 νήματα και όριο 200 ms. Ο περιορισμός ξεκινά μετά από 20 ms, η πρόσβαση στους πόρους του επεξεργαστή επαναλαμβάνεται μετά από άλλα 80 ms

Ας υποθέσουμε ότι έχετε ορίσει το όριο της CPU σε 2 πυρήνες? Το Kubernetes θα μεταφράσει αυτήν την τιμή σε 200 ms. Αυτό σημαίνει ότι το κοντέινερ μπορεί να χρησιμοποιήσει έως και 200 ​​ms χρόνο CPU χωρίς στραγγαλισμό.

Και εδώ αρχίζει η διασκέδαση. Όπως αναφέρθηκε παραπάνω, το διαθέσιμο όριο είναι 200 ​​ms. Εάν εργάζεστε παράλληλα δέκα νήματα σε ένα μηχάνημα 12 πυρήνων (δείτε την εικόνα για το σενάριο 2), ενώ όλα τα άλλα pods είναι σε αδράνεια, το όριο θα εξαντληθεί σε μόλις 20 ms (αφού 10 * 20 ms = 200 ms) και όλα τα νήματα αυτού του pod θα κολλήσουν » (γκάζι) για τα επόμενα 80 ms. Τα ήδη αναφερθέντα σφάλμα προγραμματιστή, λόγω του οποίου συμβαίνει υπερβολικός στραγγαλισμός και το κοντέινερ δεν μπορεί καν να εκπληρώσει την υπάρχουσα ποσόστωση.

Πώς να αξιολογήσετε το throttling σε pods;

Απλώς συνδεθείτε στο pod και εκτελέστε cat /sys/fs/cgroup/cpu/cpu.stat.

  • nr_periods — ο συνολικός αριθμός των περιόδων προγραμματισμού·
  • nr_throttled — αριθμός περιόδων στραγγαλισμού στη σύνθεση nr_periods;
  • throttled_time — αθροιστικός χρόνος στραγγαλισμού σε νανοδευτερόλεπτα.

Όρια CPU και επιθετικό throttling στο Kubernetes

Τι πραγματικά συμβαίνει;

Ως αποτέλεσμα, έχουμε υψηλό γκάζι σε όλες τις εφαρμογές. Μερικές φορές είναι μέσα μιάμιση φορά ισχυρότερο από το υπολογιζόμενο!

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

Απόφαση και συνέπειες

Όλα είναι απλά εδώ. Εγκαταλείψαμε τα όρια της CPU και ξεκινήσαμε την ενημέρωση του πυρήνα του λειτουργικού συστήματος σε συμπλέγματα στην πιο πρόσφατη έκδοση, στην οποία διορθώθηκε το σφάλμα. Ο αριθμός των σφαλμάτων (HTTP 5xx) στις υπηρεσίες μας μειώθηκε άμεσα σημαντικά:

Σφάλματα HTTP 5xx

Όρια CPU και επιθετικό throttling στο Kubernetes
Σφάλματα HTTP 5xx για μία κρίσιμη υπηρεσία

Χρόνος απόκρισης p95

Όρια CPU και επιθετικό throttling στο Kubernetes
Καθυστέρηση αιτήματος κρίσιμης υπηρεσίας, 95ο εκατοστημόριο

Λειτουργικές δαπάνες

Όρια CPU και επιθετικό throttling στο Kubernetes
Αριθμός ωρών που δαπανήθηκαν

Ποια είναι τα αλιεύματα;

Όπως αναφέρθηκε στην αρχή του άρθρου:

Μια αναλογία μπορεί να γίνει με ένα κοινόχρηστο διαμέρισμα... Η Kubernetes λειτουργεί ως μεσίτης. Αλλά πώς να κρατήσετε τους ενοικιαστές από συγκρούσεις μεταξύ τους; Τι κι αν ένας από αυτούς, ας πούμε, αποφασίσει να δανειστεί το μπάνιο για τη μισή μέρα;

Εδώ είναι η σύλληψη. Ένα απρόσεκτο δοχείο μπορεί να καταναλώσει όλους τους διαθέσιμους πόρους της CPU σε ένα μηχάνημα. Εάν έχετε μια έξυπνη στοίβα εφαρμογών (για παράδειγμα, τα JVM, Go, Node VM έχουν ρυθμιστεί σωστά), τότε αυτό δεν είναι πρόβλημα: μπορείτε να εργαστείτε σε τέτοιες συνθήκες για μεγάλο χρονικό διάστημα. Αλλά εάν οι εφαρμογές είναι ελάχιστα βελτιστοποιημένες ή δεν έχουν βελτιστοποιηθεί καθόλου (FROM java:latest), η κατάσταση μπορεί να ξεφύγει από τον έλεγχο. Στο Omio έχουμε αυτοματοποιημένα βασικά Dockerfiles με επαρκείς προεπιλεγμένες ρυθμίσεις για την κύρια στοίβα γλωσσών, επομένως αυτό το ζήτημα δεν υπήρχε.

Συνιστούμε την παρακολούθηση των μετρήσεων ΧΡΗΣΗ (χρήση, κορεσμός και σφάλματα), καθυστερήσεις API και ποσοστά σφαλμάτων. Βεβαιωθείτε ότι τα αποτελέσματα ανταποκρίνονται στις προσδοκίες.

παραπομπές

Αυτή είναι η ιστορία μας. Τα ακόλουθα υλικά βοήθησαν πολύ στην κατανόηση του τι συνέβαινε:

Αναφορές σφαλμάτων Kubernetes:

Έχετε αντιμετωπίσει παρόμοια προβλήματα στο ιατρείο σας ή έχετε εμπειρία σχετικά με το στραγγαλισμό σε περιβάλλοντα παραγωγής με εμπορευματοκιβώτια; Μοιραστείτε την ιστορία σας στα σχόλια!

ΥΓ από τον μεταφραστή

Διαβάστε επίσης στο blog μας:

Πηγή: www.habr.com

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