SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

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

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

Το Go είναι ιδιαίτερα καλό εδώ επειδή διαθέτει εργαλεία δημιουργίας προφίλ pprof στην τυπική βιβλιοθήκη.

SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

στρατηγική

Ας δημιουργήσουμε μια συνοπτική λίστα για τη δομική μας ανάλυση. Θα προσπαθήσουμε να χρησιμοποιήσουμε κάποια δεδομένα για να λάβουμε αποφάσεις αντί να κάνουμε αλλαγές με βάση τη διαίσθηση ή τις εικασίες. Για να το κάνουμε αυτό θα κάνουμε το εξής:

  • Καθορίζουμε τα όρια βελτιστοποίησης (απαιτήσεις).
  • Υπολογίζουμε το φορτίο συναλλαγής για το σύστημα.
  • Εκτελούμε τη δοκιμή (δημιουργούμε δεδομένα).
  • Παρατηρούμε.
  • Αναλύουμε - πληρούνται όλες οι απαιτήσεις;
  • Το στήσαμε επιστημονικά, κάνουμε μια υπόθεση.
  • Πραγματοποιούμε ένα πείραμα για να ελέγξουμε αυτή την υπόθεση.

SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

Απλή αρχιτεκτονική διακομιστή HTTP

Για αυτό το άρθρο θα χρησιμοποιήσουμε έναν μικρό διακομιστή HTTP στο Golang. Μπορείτε να βρείτε όλο τον κώδικα από αυτό το άρθρο εδώ.

Η εφαρμογή που αναλύεται είναι ένας διακομιστής HTTP που ψηφίζει το Postgresql για κάθε αίτημα. Επιπλέον, υπάρχουν οι Prometheus, node_exporter και Grafana για τη συλλογή και εμφάνιση μετρήσεων εφαρμογών και συστημάτων.

SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

Για απλοποίηση, θεωρούμε ότι για την οριζόντια κλιμάκωση (και την απλοποίηση των υπολογισμών) κάθε υπηρεσία και βάση δεδομένων αναπτύσσονται μαζί:

SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

Καθορισμός στόχων

Σε αυτό το βήμα, αποφασίζουμε για τον στόχο. Τι προσπαθούμε να αναλύσουμε; Πώς ξέρουμε πότε είναι ώρα να τελειώσουμε; Σε αυτό το άρθρο, θα φανταστούμε ότι έχουμε πελάτες και ότι η υπηρεσία μας θα επεξεργάζεται 10 αιτήματα ανά δευτερόλεπτο.

В Βιβλίο Google SRE Οι μέθοδοι επιλογής και μοντελοποίησης συζητούνται λεπτομερώς. Ας κάνουμε το ίδιο και ας φτιάξουμε μοντέλα:

  • Καθυστέρηση: Το 99% των αιτημάτων θα πρέπει να ολοκληρωθεί σε λιγότερο από 60 ms.
  • Κόστος: Η υπηρεσία θα πρέπει να καταναλώνει το ελάχιστο ποσό χρημάτων που πιστεύουμε ότι είναι εύλογα δυνατό. Για να γίνει αυτό, μεγιστοποιούμε την απόδοση.
  • Σχεδιασμός χωρητικότητας: Απαιτεί την κατανόηση και την τεκμηρίωση πόσων παρουσιών της εφαρμογής θα πρέπει να εκτελεστούν, συμπεριλαμβανομένης της συνολικής λειτουργικότητας κλιμάκωσης και πόσες παρουσίες θα χρειαστούν για την κάλυψη των αρχικών απαιτήσεων φόρτωσης και παροχής πλεονασμός n+1.

Η καθυστέρηση μπορεί να απαιτεί βελτιστοποίηση εκτός από την ανάλυση, αλλά η απόδοση πρέπει σαφώς να αναλυθεί. Όταν χρησιμοποιείτε τη διαδικασία SRE SLO, το αίτημα καθυστέρησης προέρχεται από τον πελάτη ή την επιχείρηση, που εκπροσωπείται από τον ιδιοκτήτη του προϊόντος. Και η υπηρεσία μας θα εκπληρώσει αυτή την υποχρέωση από την αρχή χωρίς καμία ρύθμιση!

Ρύθμιση περιβάλλοντος δοκιμής

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

Φορτίο συναλλαγής

Αυτό το περιβάλλον χρησιμοποιεί Φυτόζωω για να δημιουργήσετε ένα προσαρμοσμένο ποσοστό αιτήματος HTTP μέχρι να σταματήσει:

$ make load-test LOAD_TEST_RATE=50
echo "POST http://localhost:8080" | vegeta attack -body tests/fixtures/age_no_match.json -rate=50 -duration=0 | tee results.bin | vegeta report

Παρατήρηση

Το συναλλακτικό φορτίο θα εφαρμοστεί κατά το χρόνο εκτέλεσης. Εκτός από τις μετρήσεις εφαρμογών (αριθμός αιτημάτων, καθυστέρηση απόκρισης) και λειτουργικού συστήματος (μνήμη, CPU, IOPS), το προφίλ εφαρμογής θα εκτελεστεί για να κατανοήσει πού έχει προβλήματα και πώς καταναλώνεται ο χρόνος της CPU.

Προφίλ

Το προφίλ είναι ένας τύπος μέτρησης που σας επιτρέπει να βλέπετε πού πηγαίνει ο χρόνος της CPU όταν εκτελείται μια εφαρμογή. Σας επιτρέπει να προσδιορίσετε ακριβώς πού και πόσο χρόνο δαπανάται ο επεξεργαστής:

SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

Αυτά τα δεδομένα μπορούν να χρησιμοποιηθούν κατά την ανάλυση για την απόκτηση εικόνας για τον χαμένο χρόνο της CPU και την περιττή εργασία που εκτελείται. Το Go (pprof) μπορεί να δημιουργήσει προφίλ και να τα απεικονίσει ως γραφήματα φλόγας χρησιμοποιώντας ένα τυπικό σύνολο εργαλείων. Θα μιλήσω για τον οδηγό χρήσης και ρύθμισης αργότερα στο άρθρο.

Εκτέλεση, παρατήρηση, ανάλυση.

Ας κάνουμε ένα πείραμα. Θα εκτελέσουμε, θα παρατηρήσουμε και θα αναλύσουμε μέχρι να είμαστε ικανοποιημένοι με την απόδοση. Ας επιλέξουμε μια αυθαίρετα χαμηλή τιμή φορτίου για να την εφαρμόσουμε για να λάβουμε τα αποτελέσματα των πρώτων παρατηρήσεων. Σε κάθε επόμενο βήμα θα αυξήσουμε το φορτίο με έναν συγκεκριμένο συντελεστή κλιμάκωσης, που επιλέγεται με κάποια παραλλαγή. Κάθε δοκιμή φόρτωσης εκτελείται με προσαρμοσμένο τον αριθμό των αιτημάτων: make load-test LOAD_TEST_RATE=X.

50 αιτήματα ανά δευτερόλεπτο

SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

Δώστε προσοχή στα δύο πρώτα γραφήματα. Επάνω αριστερά δείχνει ότι η εφαρμογή μας επεξεργάζεται 50 αιτήματα ανά δευτερόλεπτο (νομίζει) και επάνω δεξιά δείχνει τη διάρκεια κάθε αιτήματος. Και οι δύο παράμετροι μας βοηθούν να δούμε και να αναλύσουμε εάν βρισκόμαστε εντός των ορίων απόδοσής μας ή όχι. Κόκκινη γραμμή στο γράφημα Καθυστέρηση αιτήματος HTTP δείχνει SLO στα 60ms. Η γραμμή δείχνει ότι είμαστε πολύ κάτω από τον μέγιστο χρόνο απόκρισης.

Ας δούμε την πλευρά του κόστους:

10000 αιτήματα ανά δευτερόλεπτο / 50 αιτήματα ανά διακομιστή = 200 διακομιστές + 1

Μπορούμε ακόμα να βελτιώσουμε αυτό το ποσοστό.

500 αιτήματα ανά δευτερόλεπτο

Πιο ενδιαφέροντα πράγματα αρχίζουν να συμβαίνουν όταν το φορτίο φτάσει στα 500 αιτήματα ανά δευτερόλεπτο:

SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

Και πάλι, στο επάνω αριστερό γράφημα μπορείτε να δείτε ότι η εφαρμογή καταγράφει κανονικό φορτίο. Εάν δεν συμβαίνει αυτό, υπάρχει πρόβλημα στον διακομιστή στον οποίο εκτελείται η εφαρμογή. Το γράφημα λανθάνοντος χρόνου απόκρισης βρίσκεται επάνω δεξιά, δείχνοντας ότι 500 αιτήματα ανά δευτερόλεπτο είχαν ως αποτέλεσμα καθυστέρηση απόκρισης 25-40 ms. Το 99ο εκατοστημόριο εξακολουθεί να ταιριάζει καλά στο SLO των 60ms που επιλέχθηκε παραπάνω.

Ως προς το κόστος:

10000 αιτήματα ανά δευτερόλεπτο / 500 αιτήματα ανά διακομιστή = 20 διακομιστές + 1

Όλα μπορούν ακόμα να βελτιωθούν.

1000 αιτήματα ανά δευτερόλεπτο

SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

Μεγάλη εκτόξευση! Η εφαρμογή δείχνει ότι επεξεργάστηκε 1000 αιτήματα ανά δευτερόλεπτο, αλλά το όριο καθυστέρησης παραβιάστηκε από το SLO. Αυτό φαίνεται στη γραμμή p99 στο πάνω δεξιά γράφημα. Παρά το γεγονός ότι η γραμμή p100 είναι πολύ υψηλότερη, οι πραγματικές καθυστερήσεις είναι μεγαλύτερες από το μέγιστο των 60ms. Ας βουτήξουμε στη δημιουργία προφίλ για να μάθουμε τι κάνει στην πραγματικότητα η εφαρμογή.

Προφίλ

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

$ curl http://localhost:8080/debug/pprof/profile?seconds=29 > cpu.1000_reqs_sec_no_optimizations.prof

Τα αποτελέσματα μπορούν να εμφανιστούν ως εξής:

$ go tool pprof -http=:12345 cpu.1000_reqs_sec_no_optimizations.prof

SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

Το γράφημα δείχνει πού και πόσο η εφαρμογή ξοδεύει χρόνο CPU. Από την περιγραφή από Μπρένταν Γκρεγκ:

Ο άξονας X είναι ο πληθυσμός προφίλ στοίβας, ταξινομημένος αλφαβητικά (δεν είναι ώρα), ο άξονας Y δείχνει το βάθος της στοίβας, μετρώντας από το μηδέν στο [πάνω]. Κάθε ορθογώνιο είναι ένα πλαίσιο στοίβας. Όσο πιο φαρδύ είναι το πλαίσιο, τόσο πιο συχνά υπάρχει στις στοίβες. Αυτό που βρίσκεται στην κορυφή εκτελείται στη CPU και αυτό που ακολουθεί είναι τα θυγατρικά στοιχεία. Τα χρώματα συνήθως δεν σημαίνουν τίποτα, αλλά απλώς επιλέγονται τυχαία για να διαφοροποιήσουν τα καρέ.

Ανάλυση – υπόθεση

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

Ακολουθώντας τις συστάσεις του Brendan Gregg, θα διαβάσουμε το γράφημα από πάνω προς τα κάτω. Κάθε γραμμή εμφανίζει ένα πλαίσιο στοίβας (κλήση συνάρτησης). Η πρώτη γραμμή είναι το σημείο εισόδου στο πρόγραμμα, ο γονέας όλων των άλλων κλήσεων (με άλλα λόγια, όλες οι άλλες κλήσεις θα το έχουν στη στοίβα τους). Η επόμενη γραμμή είναι ήδη διαφορετική:

SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

Εάν τοποθετήσετε τον κέρσορα πάνω από το όνομα μιας συνάρτησης στο γράφημα, θα εμφανιστεί ο συνολικός χρόνος που ήταν στη στοίβα κατά τον εντοπισμό σφαλμάτων. Η συνάρτηση HTTPServe ήταν εκεί το 65% του χρόνου, άλλες λειτουργίες χρόνου εκτέλεσης runtime.mcall, mstart и gc, πήρε τον υπόλοιπο χρόνο. Διασκεδαστικό γεγονός: 5% του συνολικού χρόνου δαπανάται σε ερωτήματα DNS:

SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

Οι διευθύνσεις που αναζητά το πρόγραμμα ανήκουν στην Postgresql. Κάντε κλικ στο FindByAge:

SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

Είναι ενδιαφέρον ότι το πρόγραμμα δείχνει ότι, καταρχήν, υπάρχουν τρεις κύριες πηγές που προσθέτουν καθυστερήσεις: άνοιγμα και κλείσιμο συνδέσεων, αίτημα δεδομένων και σύνδεση στη βάση δεδομένων. Το γράφημα δείχνει ότι τα αιτήματα DNS, το άνοιγμα και το κλείσιμο των συνδέσεων καταλαμβάνουν περίπου το 13% του συνολικού χρόνου εκτέλεσης.

Υπόθεση: Η επαναχρησιμοποίηση συνδέσεων με χρήση ομαδοποίησης θα πρέπει να μειώσει τον χρόνο ενός μεμονωμένου αιτήματος HTTP, επιτρέποντας υψηλότερη απόδοση και χαμηλότερο λανθάνοντα χρόνο.

Ρύθμιση της εφαρμογής - πείραμα

Ενημερώνουμε τον πηγαίο κώδικα, προσπαθούμε να καταργήσουμε τη σύνδεση με την Postgresql για κάθε αίτημα. Η πρώτη επιλογή είναι να χρησιμοποιήσετε πισίνα σύνδεσης σε επίπεδο εφαρμογής. Σε αυτό το πείραμα εμείς ας το στήσουμε συγκέντρωση σύνδεσης με χρήση προγράμματος οδήγησης sql for go:

db, err := sql.Open("postgres", dbConnectionString)
db.SetMaxOpenConns(8)

if err != nil {
   return nil, err
}

Εκτέλεση, παρατήρηση, ανάλυση

Μετά την επανεκκίνηση του τεστ με 1000 αιτήματα ανά δευτερόλεπτο, είναι σαφές ότι τα επίπεδα καθυστέρησης του p99 έχουν επιστρέψει στο φυσιολογικό με SLO 60ms!

Ποιο είναι το κόστος;

10000 αιτήματα ανά δευτερόλεπτο / 1000 αιτήματα ανά διακομιστή = 10 διακομιστές + 1

Ας το κάνουμε ακόμα καλύτερα!

2000 αιτήματα ανά δευτερόλεπτο

SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

Ο διπλασιασμός του φορτίου δείχνει το ίδιο πράγμα, το επάνω αριστερό γράφημα δείχνει ότι η εφαρμογή καταφέρνει να επεξεργαστεί 2000 αιτήματα ανά δευτερόλεπτο, το p100 είναι χαμηλότερο από 60ms, το p99 ικανοποιεί το SLO.

Ως προς το κόστος:

10000 αιτήματα ανά δευτερόλεπτο / 2000 αιτήματα ανά διακομιστή = 5 διακομιστές + 1

3000 αιτήματα ανά δευτερόλεπτο

SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

Εδώ η εφαρμογή μπορεί να επεξεργαστεί 3000 αιτήματα με καθυστέρηση p99 μικρότερη από 60ms. Το SLO δεν παραβιάζεται και το κόστος γίνεται δεκτό ως εξής:

10000 αιτήματα ανά δευτερόλεπτο / ανά 3000 αιτήματα ανά διακομιστή = 4 διακομιστές + 1 (ο συγγραφέας έχει στρογγυλοποιήσει, περίπου. μεταφράστης)

Ας δοκιμάσουμε έναν άλλο γύρο ανάλυσης.

Ανάλυση – υπόθεση

Συλλέγουμε και εμφανίζουμε τα αποτελέσματα του εντοπισμού σφαλμάτων της εφαρμογής με 3000 αιτήματα ανά δευτερόλεπτο:

SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

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

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

Ρύθμιση της εφαρμογής - πείραμα

Προσπάθεια εγκατάστασης MaxIdleConns ίσο με το μέγεθος της πισίνας (περιγράφεται επίσης εδώ):

db, err := sql.Open("postgres", dbConnectionString)
db.SetMaxOpenConns(8)
db.SetMaxIdleConns(8)
if err != nil {
   return nil, err
}

Εκτέλεση, παρατήρηση, ανάλυση

3000 αιτήματα ανά δευτερόλεπτο

SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

Το p99 είναι μικρότερο από 60ms με σημαντικά λιγότερα p100!

SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

Ο έλεγχος του γραφήματος φλόγας δείχνει ότι η σύνδεση δεν είναι πλέον αισθητή! Ας ελέγξουμε λεπτομερέστερα pg(*conn).query — Επίσης, δεν παρατηρούμε τη σύνδεση εδώ.

SRE: Ανάλυση απόδοσης. Μέθοδος διαμόρφωσης χρησιμοποιώντας έναν απλό διακομιστή ιστού στο Go

Συμπέρασμα

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

Πηγή: www.habr.com

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