
Καλό για όλους!
Ονομάζομαι Νικήτα και είμαι ο επικεφαλής της ομάδας μηχανικών της Cian. Μία από τις αρμοδιότητές μου στην εταιρεία είναι να μειώσω στο μηδέν τον αριθμό των συμβάντων που σχετίζονται με την υποδομή παραγωγής.
Ό,τι ακολουθεί μας έχει προκαλέσει πολύ πόνο και ο σκοπός αυτού του άρθρου είναι να αποτρέψει άλλους από το να επαναλάβουν τα λάθη μας ή τουλάχιστον να ελαχιστοποιήσει τον αντίκτυπό τους.
Προοίμιο
Πριν από πολύ καιρό, όταν το Cian αποτελούνταν από μονόλιθους και δεν υπήρχαν ίχνη μικροϋπηρεσιών, μετρούσαμε τη διαθεσιμότητα πόρων ελέγχοντας 3-5 σελίδες.
Αν απαντήσουν, όλα είναι εντάξει. Αν δεν απαντήσουν για μεγάλο χρονικό διάστημα, υπάρχει μια ειδοποίηση. Οι άνθρωποι αποφάσιζαν σε συναντήσεις για πόσο καιρό θα έπρεπε να είναι εκτός λειτουργίας ώστε να θεωρηθεί περιστατικό. Η ομάδα μηχανικών συμμετείχε πάντα στην έρευνα του περιστατικού. Όταν τελείωνε η έρευνα, έγραφαν μια νεκροψία - ένα είδος έκθεσης στο email με την ακόλουθη μορφή: τι συνέβη, πόσο διήρκεσε, τι κάναμε εκείνη τη στιγμή, τι θα κάνουμε στο μέλλον.
Κύριες σελίδες του ιστότοπου ή πώς καταλαβαίνουμε ότι έχουμε φτάσει στον πάτο
Για να κατανοήσουμε με κάποιο τρόπο την προτεραιότητα του σφάλματος, έχουμε εντοπίσει τις πιο κρίσιμες σελίδες για την επιχειρηματική λειτουργικότητα του ιστότοπου. Τις χρησιμοποιούμε για να μετρήσουμε τον αριθμό των επιτυχημένων/ανεπιτυχών αιτημάτων και των χρονικών ορίων λήξης. Έτσι μετράμε τον χρόνο λειτουργίας.
Ας υποθέσουμε ότι έχουμε ανακαλύψει ότι υπάρχουν ορισμένα εξαιρετικά σημαντικά τμήματα του ιστότοπου που είναι υπεύθυνα για την κύρια υπηρεσία - αναζήτηση και δημοσίευση διαφημίσεων. Εάν ο αριθμός των αιτημάτων που κατέληξαν σε σφάλμα υπερβαίνει το 1%, πρόκειται για κρίσιμο περιστατικό. Εάν το ποσοστό των σφαλμάτων υπερβαίνει το 15% κατά τη διάρκεια 0,1 λεπτών κατά τη διάρκεια του prime time, θεωρείται επίσης κρίσιμο περιστατικό. Αυτά τα κριτήρια καλύπτουν την πλειονότητα των περιστατικών, τα υπόλοιπα είναι πέρα από το πεδίο εφαρμογής αυτού του άρθρου.

Κορυφαία Καλύτερα Περιστατικά Κυανής Ακτίνας
Έτσι, έχουμε σίγουρα μάθει να προσδιορίζουμε το γεγονός ότι έχει συμβεί ένα περιστατικό.
Τώρα κάθε περιστατικό περιγράφεται λεπτομερώς και αντικατοπτρίζεται σε ένα έπος Jira. Παρεμπιπτόντως: για αυτό, δημιουργήσαμε ένα ξεχωριστό έργο, που ονομάζεται FAIL - μόνο έπη μπορούν να δημιουργηθούν σε αυτό.
Αν συγκεντρώσετε όλες τις αποτυχίες των τελευταίων ετών, οι κορυφαίοι είναι:
- συμβάντα που σχετίζονται με το mssql;
- συμβάντα που προκαλούνται από εξωτερικούς παράγοντες·
- σφάλματα διαχειριστή.
Ας ρίξουμε μια πιο προσεκτική ματιά στα λάθη των διαχειριστών, καθώς και σε μερικές άλλες ενδιαφέρουσες αποτυχίες.
Πέμπτη θέση - "Ας βάλουμε τα πράγματα σε τάξη στο DNS"
Ήταν μια ζοφερή Τρίτη. Αποφασίσαμε να καθαρίσουμε το σύμπλεγμα DNS.
Ήθελα να μεταφέρω εσωτερικούς διακομιστές DNS από το bind στο powerdns, διαθέτοντας εντελώς ξεχωριστούς διακομιστές για αυτό, όπου δεν υπάρχει τίποτα άλλο παρά DNS.
Τοποθετήσαμε έναν διακομιστή DNS σε κάθε τοποθεσία του DC μας και ήρθε η ώρα να μετακινήσουμε ζώνες από το bind στο powerdns και να αλλάξουμε την υποδομή σε νέους διακομιστές.
Εν μέσω μετακόμισης, όλων των ανθρώπων διακομιστέςΑπό τους διακομιστές που καθορίστηκαν σε τοπικές συνδέσεις προσωρινής αποθήκευσης, παρέμεινε μόνο ένας—αυτός στο κέντρο δεδομένων της Αγίας Πετρούπολης. Αυτό το κέντρο δεδομένων αρχικά δηλώθηκε ως μη κρίσιμο για εμάς, αλλά ξαφνικά έγινε σημείο αποτυχίας.
Ήταν κατά τη διάρκεια αυτής της μεταβατικής περιόδου που το κανάλι μεταξύ Μόσχας και Αγίας Πετρούπολης διακόπηκε. Ουσιαστικά μείναμε χωρίς DNS για πέντε λεπτά και ανακτήσαμε την ισχύ μας όταν... φιλοξενών διόρθωσε τα προβλήματα.
Συμπεράσματα:
Αν νωρίτερα παραμελούσαμε τους εξωτερικούς παράγοντες κατά την προετοιμασία για την εργασία, τώρα περιλαμβάνονται και αυτοί στη λίστα με αυτά που προετοιμάζουμε. Και τώρα προσπαθούμε να διασφαλίσουμε ότι όλα τα στοιχεία είναι δεσμευμένα n-2 και κατά τη διάρκεια της εργασίας μπορούμε να μειώσουμε αυτό το επίπεδο σε n-1.
- Κατά τη δημιουργία ενός σχεδίου δράσης, σημειώστε τα σημεία όπου η υπηρεσία ενδέχεται να αποτύχει και σκεφτείτε εκ των προτέρων ένα σενάριο όπου τα πράγματα θα πάνε «χειρότερα».
- Κατανείμετε εσωτερικούς διακομιστές DNS σε διαφορετικές γεωγραφικές τοποθεσίες/κέντρα δεδομένων/ράφια/διακόπτες/είσοδους.
- Σε κάθε διακομιστή, εγκαταστήστε έναν τοπικό διακομιστή DNS προσωρινής αποθήκευσης που ανακατευθύνει τα αιτήματα στους κύριους διακομιστές DNS και, εάν δεν είναι διαθέσιμος, θα απαντήσει από την προσωρινή μνήμη.
Τέταρτη θέση - "Καθαρίζοντας το Nginx"
Μια μέρα, η ομάδα μας αποφάσισε ότι "φτάνει πια" και ξεκίνησε η διαδικασία αναδιαμόρφωσης των ρυθμίσεων nginx. Ο κύριος στόχος είναι να φέρουμε τις ρυθμίσεις σε μια διαισθητική δομή. Προηγουμένως, όλα ήταν "ιστορικά καθιερωμένα" και δεν είχαν καμία λογική. Τώρα κάθε server_name μετακινήθηκε σε ένα αρχείο με το ίδιο όνομα και όλες οι ρυθμίσεις διανεμήθηκαν σε φακέλους. Παρεμπιπτόντως, η ρύθμιση περιέχει 253949 γραμμές ή 7836520 χαρακτήρες και καταλαμβάνει σχεδόν 7 megabyte. Το ανώτερο επίπεδο της δομής:
Δομή Nginx
├── access
│ ├── allow.list
...
│ └── whitelist.conf
├── geobase
│ ├── exclude.conf
...
│ └── geo_ip_to_region_id.conf
├── geodb
│ ├── GeoIP.dat
│ ├── GeoIP2-Country.mmdb
│ └── GeoLiteCity.dat
├── inc
│ ├── error.inc
...
│ └── proxy.inc
├── lists.d
│ ├── bot.conf
...
│ ├── dynamic
│ └── geo.conf
├── lua
│ ├── cookie.lua
│ ├── log
│ │ └── log.lua
│ ├── logics
│ │ ├── include.lua
│ │ ├── ...
│ │ └── utils.lua
│ └── prom
│ ├── stats.lua
│ └── stats_prometheus.lua
├── map.d
│ ├── access.conf
│ ├── ..
│ └── zones.conf
├── nginx.conf
├── robots.txt
├── server.d
│ ├── cian.ru
│ │ ├── cian.ru.conf
│ │ ├── ...
│ │ └── my.cian.ru.conf
├── service.d
│ ├── ...
│ └── status.conf
└── upstream.d
├── cian-mcs.conf
├── ...
└── wafserver.confΈγινε πολύ καλύτερο, αλλά κατά τη διαδικασία μετονομασίας και διανομής των ρυθμίσεων, ορισμένες από αυτές είχαν λάθος επέκταση και δεν έμπαιναν στην οδηγία include *.conf. Ως αποτέλεσμα, ορισμένοι κεντρικοί υπολογιστές δεν ήταν διαθέσιμοι και επέστρεψαν το 301 στην κύρια σελίδα. Λόγω του γεγονότος ότι ο κωδικός απόκρισης δεν ήταν 5xx/4xx, αυτό δεν έγινε αντιληπτό αμέσως, αλλά μόνο το πρωί. Μετά από αυτό, ξεκινήσαμε να γράφουμε δοκιμές για να ελέγξουμε τα στοιχεία της υποδομής.
Συμπεράσματα:
- Δομήστε σωστά τις διαμορφώσεις σας (όχι μόνο το nginx) και σκεφτείτε τη δομή από νωρίς στο έργο. Αυτό θα τις κάνει πιο κατανοητές στην ομάδα, κάτι που με τη σειρά του θα μειώσει το TTM.
- Γράψτε δοκιμές για ορισμένα στοιχεία υποδομής. Για παράδειγμα: ελέγξτε ότι όλα τα κλειδιά server_name επιστρέφουν τη σωστή κατάσταση + σώμα απόκρισης. Θα είναι αρκετό να έχετε μερικά σενάρια διαθέσιμα που ελέγχουν τις κύριες λειτουργίες του στοιχείου, ώστε να μην θυμάστε μανιωδώς στις 3 π.μ. τι άλλο πρέπει να ελεγχθεί.
Τρίτη θέση - "Ξαφνικά έμεινε από χώρο στην Κασσάνδρα"
Τα δεδομένα αυξάνονταν σταθερά και όλα ήταν καλά μέχρι τη στιγμή που οι επισκευές μεγάλων keyspaces άρχισαν να αποτυγχάνουν στο cluster Cassandra επειδή η συμπύκνωση δεν μπορούσε να λειτουργήσει σε αυτά.
Μια βροχερή μέρα το σμήνος παραλίγο να μετατραπεί σε κολοκύθα, δηλαδή:
- υπήρχε περίπου το 20% του χώρου που είχε απομείνει στο σύμπλεγμα συνολικά.
- Είναι αδύνατο να προσθέσετε πλήρως κόμβους επειδή ο καθαρισμός δεν πραγματοποιείται μετά την προσθήκη ενός κόμβου λόγω έλλειψης χώρου στα διαμερίσματα.
- η παραγωγικότητα μειώνεται σταδιακά επειδή η συμπύκνωση δεν λειτουργεί.
- Το σύμπλεγμα λειτουργεί σε κατάσταση έκτακτης ανάγκης.

Η λύση ήταν να προστεθούν 5 ακόμη κόμβοι χωρίς καθαρισμό, μετά τους οποίους άρχισαν να τους αφαιρούν συστηματικά από το σύμπλεγμα και να τους εισάγουν ξανά ως κενούς κόμβους που είχαν εξαντλήσει τον χώρο. Ξοδεύτηκε πολύ περισσότερος χρόνος από ό,τι θα θέλαμε. Υπήρχε κίνδυνος μερικής ή πλήρους μη διαθεσιμότητας του συμπλέγματος.
Συμπεράσματα:
- Όλοι οι διακομιστές cassandra δεν θα πρέπει να καταλαμβάνουν περισσότερο από το 60% του χώρου σε κάθε partition.
- Δεν θα πρέπει να φορτώνονται περισσότερο από το 50% της CPU.
- Δεν πρέπει να ξεχνάτε τον προγραμματισμό χωρητικότητας. Πρέπει να τον σκεφτείτε διεξοδικά για κάθε στοιχείο, με βάση τις ιδιαιτερότητές του.
- Όσο περισσότεροι κόμβοι σε ένα σύμπλεγμα, τόσο το καλύτερο. Οι διακομιστές που περιέχουν μικρή ποσότητα δεδομένων ξαναγεμίζονται πιο γρήγορα και ένα τέτοιο σύμπλεγμα είναι πιο εύκολο να αναζωογονηθεί.
Δεύτερη θέση - "Τα δεδομένα εξαφανίστηκαν από την αποθήκευση κλειδιών-τιμών consul"
Για την ανακάλυψη υπηρεσιών, εμείς, όπως πολλοί άλλοι, χρησιμοποιούμε το consul. Αλλά χρησιμοποιούμε επίσης το κλειδί-τιμή του για την μπλε-πράσινη ανάπτυξη του μονόλιθου. Αποθηκεύει πληροφορίες σχετικά με ενεργά και ανενεργά upstreams, τα οποία αλλάζουν θέσεις κατά την ανάπτυξη. Για τον σκοπό αυτό, γράφτηκε μια υπηρεσία ανάπτυξης που αλληλεπιδρούσε με το KV. Κάποια στιγμή, τα δεδομένα από το KV εξαφανίστηκαν. Τα επαναφέραμε από τη μνήμη, αλλά με ορισμένα σφάλματα. Ως αποτέλεσμα, το φορτίο στα upstreams κατανεμήθηκε άνισα κατά την ανάπτυξη και λάβαμε πολλά σφάλματα 502 λόγω υπερφόρτωσης του backend από την CPU. Ως αποτέλεσμα, μετακινηθήκαμε από το consul KV στο postgres, από όπου δεν είναι πλέον τόσο εύκολο να τα διαγράψουμε.
Συμπεράσματα:
- Οι υπηρεσίες χωρίς καμία εξουσιοδότηση δεν θα πρέπει να περιέχουν δεδομένα κρίσιμα για τη λειτουργία του ιστότοπου. Για παράδειγμα, εάν δεν έχετε εξουσιοδότηση στο ES, θα ήταν καλύτερο να απαγορεύσετε την πρόσβαση σε επίπεδο δικτύου από οπουδήποτε δεν είναι απαραίτητη, να αφήσετε μόνο τα απαραίτητα και επίσης να ορίσετε την τιμή action.destructive_requires_name: true.
- Εξασκηθείτε εκ των προτέρων στον μηχανισμό δημιουργίας αντιγράφων ασφαλείας και επαναφοράς. Για παράδειγμα, δημιουργήστε ένα σενάριο εκ των προτέρων (για παράδειγμα, σε Python) που να μπορεί να δημιουργεί αντίγραφα ασφαλείας και να επαναφέρει δεδομένα.
Πρώτη θέση - "Captain Obvious"
Κάποια στιγμή, παρατηρήσαμε άνιση κατανομή του φόρτου στα upstreams του nginx σε περιπτώσεις όπου υπήρχαν 10+ διακομιστές στο backend. Λόγω του γεγονότος ότι το round-robin έστελνε αιτήματα από το 1ο έως το τελευταίο upstream κατά σειρά, και κάθε επαναφόρτωση του nginx ξεκινούσε από την αρχή, τα πρώτα upstreams είχαν πάντα περισσότερα αιτήματα από τα άλλα. Ως αποτέλεσμα, λειτουργούσαν πιο αργά και ολόκληρος ο ιστότοπος υπέφερε. Αυτό γινόταν όλο και πιο αισθητό καθώς αυξανόταν η επισκεψιμότητα. Η απλή ενημέρωση του nginx για να ενεργοποιηθεί η τυχαία επιλογή δεν λειτούργησε — έπρεπε να επαναλάβουμε ένα σωρό κώδικα lua που δεν λειτούργησε στην έκδοση 1.15 (εκείνη την εποχή). Έπρεπε να ενημερώσουμε το nginx 1.14.2, εφαρμόζοντας τυχαία υποστήριξη σε αυτό. Αυτό έλυσε το πρόβλημα. Αυτό το σφάλμα κερδίζει την υποψηφιότητα "Captain Non-Obviousness".
Συμπεράσματα:
Ήταν πολύ ενδιαφέρον και συναρπαστικό να εξερευνήσουμε αυτό το έντομο).
- Δημιουργήστε την παρακολούθησή σας έτσι ώστε να σας βοηθά να εντοπίζετε γρήγορα τέτοιες διακυμάνσεις. Για παράδειγμα, μπορείτε να χρησιμοποιήσετε το ELK για να παρακολουθείτε τα rps για κάθε backend κάθε upstream, παρακολουθώντας τον χρόνο απόκρισης από την οπτική γωνία του nginx. Σε αυτήν την περίπτωση, αυτό μας βοήθησε να εντοπίσουμε το πρόβλημα.
Ως αποτέλεσμα, οι περισσότερες αποτυχίες θα μπορούσαν να είχαν αποφευχθεί με μια πιο σχολαστική προσέγγιση σε αυτό που κάνετε. Θα πρέπει πάντα να θυμάστε τον νόμο του Μέρφι: Οτιδήποτε μπορεί να πάει στραβά, θα πάει στραβά, και να κατασκευάζουν στοιχεία με βάση αυτό.
Πηγή: www.habr.com
