Στις 27 Μαΐου, στην κεντρική αίθουσα του συνεδρίου DevOpsConf 2019, το οποίο πραγματοποιείται στο πλαίσιο του φεστιβάλ , ως μέρος της ενότητας Συνεχούς Παράδοσης, δόθηκε μια αναφορά με τίτλο «werf — το εργαλείο μας για CI/CD στο Kubernetes». Αναφέρεται σε αυτά τα προβλήματα και τις προκλήσεις που αντιμετωπίζει ο καθένας κατά την ανάπτυξη στο Kubernetes, καθώς και λεπτές αποχρώσεις που μπορεί να μην είναι άμεσα αντιληπτές. Εξετάζοντας πιθανές λύσεις, δείχνουμε πώς αυτό υλοποιείται σε ένα εργαλείο ανοιχτού κώδικα .
Από την παρουσίαση, το βοηθητικό μας πρόγραμμα (παλαιότερα γνωστό ως dapp) έχει διανύσει ένα ιστορικό ορόσημο 1000 αστέρια στο GitHub — Ελπίζουμε ότι η αυξανόμενη κοινότητα χρηστών του θα κάνει τη ζωή ευκολότερη για πολλούς μηχανικούς DevOps.

Ας παρουσιάσουμε λοιπόν (~47 λεπτά, πολύ πιο κατατοπιστικό από το άρθρο) και η κύρια περίληψή του σε μορφή κειμένου. Πάμε!
Παράδοση κώδικα στο Kubernetes
Η συζήτηση στην έκθεση θα αφορά περισσότερο το CI/CD στο Kubernetes παρά στο werf, υπονοώντας ότι το λογισμικό μας είναι συσκευασμένο σε κοντέινερ Docker. (Μίλησα γι' αυτό στο ), και τα K8s θα χρησιμοποιηθούν για την παραγωγή του. (περισσότερα για αυτό στο ).
Πώς μοιάζει η παράδοση στο Kubernetes;
- Υπάρχει ένα αποθετήριο Git με τον κώδικα και τις οδηγίες για την κατασκευή του. Η εφαρμογή είναι ενσωματωμένη σε μια εικόνα Docker και δημοσιεύεται στο Docker Registry.
- Το ίδιο αποθετήριο περιέχει επίσης οδηγίες σχετικά με τον τρόπο ανάπτυξης και εκτέλεσης της εφαρμογής. Στο στάδιο της ανάπτυξης, αυτές οι οδηγίες αποστέλλονται στο Kubernetes, το οποίο λαμβάνει την απαιτούμενη εικόνα από το μητρώο και την εκτελεί.
- Επιπλέον, συνήθως υπάρχουν δοκιμές. Μερικές από αυτές μπορούν να εκτελεστούν όταν δημοσιεύετε την εικόνα. Μπορείτε επίσης (χρησιμοποιώντας τις ίδιες οδηγίες) να αναπτύξετε ένα αντίγραφο της εφαρμογής (σε ξεχωριστό χώρο ονομάτων K8s ή σε ξεχωριστό σύμπλεγμα) και να εκτελέσετε δοκιμές εκεί.
- Τέλος, χρειάζεστε ένα σύστημα CI που λαμβάνει συμβάντα από το Git (ή πατήματα κουμπιών) και καλεί όλα τα καθορισμένα στάδια: δημιουργία, δημοσίευση, ανάπτυξη, δοκιμή.

Υπάρχουν μερικές σημαντικές σημειώσεις εδώ:
- Επειδή έχουμε αμετάβλητες υποδομές (αμετάβλητη υποδομή), μια εικόνα εφαρμογής που χρησιμοποιείται σε όλα τα στάδια (σκηνοθεσία, παραγωγή κ.λπ.), πρέπει να υπάρχει ένας. Μίλησα για αυτό με περισσότερες λεπτομέρειες και με παραδείγματα. .
- Δεδομένου ότι ακολουθούμε την προσέγγιση της υποδομής ως κώδικα (IaC), ο κώδικας της εφαρμογής, οι οδηγίες για τη συναρμολόγηση και την εκκίνησή της θα πρέπει να βρίσκονται ακριβώς σε ένα αποθετήριο. Για περισσότερες λεπτομέρειες, βλ. .
- Αλυσίδα παράδοσης (διανομή) συνήθως το βλέπουμε ως εξής: η εφαρμογή συναρμολογείται, δοκιμάζεται, κυκλοφορεί (στάδιο απελευθέρωσης) και αυτό είναι όλο - η παράδοση έλαβε χώρα. Αλλά στην πραγματικότητα, ο χρήστης λαμβάνει αυτό που παρουσιάσατε, όχι όταν το παραδώσατε στην παραγωγή, και όταν μπόρεσε να πάει εκεί και αυτή η παραγωγή λειτουργούσε. Νομίζω λοιπόν ότι η αλυσίδα παράδοσης τελειώνει μόνο στο στάδιο της λειτουργίας (τρέξιμο), ή για να είμαστε πιο ακριβείς, ακόμη και τη στιγμή που ο κώδικας αφαιρέθηκε από την παραγωγή (αντικαταστάθηκε με έναν νέο).
Ας επιστρέψουμε στο προαναφερθέν σχήμα παράδοσης του Kubernetes: δεν το εφηύραμε μόνο εμείς, αλλά κυριολεκτικά όλοι όσοι αντιμετώπισαν αυτό το πρόβλημα. Στην πραγματικότητα, αυτό το μοτίβο ονομάζεται πλέον GitOps. (μπορείτε να διαβάσετε περισσότερα για τον όρο και τις ιδέες που τον κρύβουν πίσω από αυτόν) )Ας δούμε τα στάδια του σχεδίου.
Στάδιο κατασκευής
Φαίνεται ότι δεν υπάρχει τίποτα να πούμε το 2019 για τη δημιουργία εικόνων Docker, όταν όλοι ξέρουν πώς να γράφουν αρχεία Docker και να τα εκτελούν. docker build;.. Ακολουθούν οι αποχρώσεις στις οποίες θα ήθελα να επιστήσω την προσοχή:
- Βάρος της εικόνας έχει σημασία, οπότε χρησιμοποίησέ το , να αφήσετε στην εικόνα μόνο ό,τι πραγματικά χρειάζεται για να λειτουργήσει η εφαρμογή.
- Αριθμός στρώσεων πρέπει να ελαχιστοποιηθεί συνδυάζοντας αλυσίδες
RUN-εντολές με νόημα. - Ωστόσο, αυτό προσθέτει προβλήματα. εντοπισμός σφαλμάτων, επειδή όταν η συναρμολόγηση καταρρέει, πρέπει να βρείτε την απαραίτητη εντολή από την αλυσίδα που προκάλεσε το πρόβλημα.
- Ταχύτητα συναρμολόγησης είναι σημαντικό επειδή θέλουμε να εφαρμόσουμε γρήγορα τις αλλαγές και να δούμε το αποτέλεσμα. Για παράδειγμα, δεν θέλουμε να αναδημιουργούμε εξαρτήσεις σε βιβλιοθήκες γλωσσών κάθε φορά που δημιουργούμε την εφαρμογή.
- Συχνά, από ένα αποθετήριο Git χρειάζεστε πολλές εικόνες, το οποίο μπορεί να λυθεί από ένα σύνολο Dockerfiles (ή ονομασμένων σταδίων σε ένα αρχείο) και ένα σενάριο Bash με τη διαδοχική τους συναρμολόγηση.
Αυτή ήταν μόνο η κορυφή του παγόβουνου που αντιμετωπίζουν όλοι. Υπάρχουν όμως και άλλα προβλήματα, όπως:
- Συχνά στο στάδιο της συναρμολόγησης χρειαζόμαστε κάτι βουνό (για παράδειγμα, αποθηκεύστε προσωρινά το αποτέλεσμα μιας εντολής όπως η εντολή apt σε έναν κατάλογο τρίτου μέρους).
- Θέλουμε Πιθανό αντί να γράφω σε κέλυφος.
- Θέλουμε κατασκευή χωρίς Docker (Γιατί χρειαζόμαστε μια επιπλέον εικονική μηχανή στην οποία πρέπει να ρυθμίσουμε τα πάντα για αυτό, όταν έχουμε ήδη ένα σύμπλεγμα Kubernetes στο οποίο μπορούμε να εκτελέσουμε κοντέινερ;).
- Παράλληλη συναρμολόγηση, το οποίο μπορεί να γίνει κατανοητό με διαφορετικούς τρόπους: διαφορετικές εντολές από ένα Dockerfile (εάν χρησιμοποιείται πολυβάθμιο αρχείο), αρκετές υποβολές ενός αποθετηρίου, πολλά Dockerfiles.
- Κατανεμημένη συναρμολόγηση: θέλουμε να συλλέξουμε κάτι σε pods που είναι "εφήμερο" επειδή η προσωρινή μνήμη τους εξαφανίζεται, πράγμα που σημαίνει ότι πρέπει να αποθηκευτεί κάπου ξεχωριστά.
- Τελικά, ονόμασα την κορυφή των επιθυμιών αυτόματη μαγείαΘα ήταν ιδανικό να πάτε στο αποθετήριο, να πληκτρολογήσετε κάποια εντολή και να λάβετε μια έτοιμη εικόνα, συναρμολογημένη με κατανόηση του πώς και τι να κάνετε σωστά. Ωστόσο, προσωπικά δεν είμαι σίγουρος ότι όλες οι λεπτομέρειες μπορούν να προβλεφθούν με αυτόν τον τρόπο.
Και ιδού τα έργα:
- — ένας συλλέκτης από την Docker Inc (ήδη ενσωματωμένος στις τρέχουσες εκδόσεις του Docker), ο οποίος προσπαθεί να λύσει όλα αυτά τα προβλήματα·
- — ένα εργαλείο δημιουργίας από την Google που σας επιτρέπει να δημιουργείτε χωρίς το Docker·
- — Η προσπάθεια του CNCF να κάνει αυτόματη μαγεία και, ειδικότερα, μια ενδιαφέρουσα λύση με επαναφορά βάσης για επίπεδα.
- και μια σειρά από άλλα βοηθητικά προγράμματα, όπως , ...
... και δείτε πόσα αστέρια έχουν στο GitHub. Έτσι, από τη μία πλευρά, docker build υπάρχει και μπορεί να κάνει κάτι, αλλά στην πραγματικότητα το ζήτημα δεν έχει επιλυθεί πλήρως — απόδειξη αυτού είναι η παράλληλη ανάπτυξη εναλλακτικών συναρμολογητών, καθένας από τους οποίους λύνει κάποιο μέρος των προβλημάτων.
Συναρμολόγηση σε werf
Έτσι λοιπόν πρέπει να (προηγουμένως όπως το dapp) — Βοηθητικό πρόγραμμα ανοιχτού κώδικα της εταιρείας "Flant", το οποίο κατασκευάζουμε εδώ και πολλά χρόνια. Όλα ξεκίνησαν πριν από περίπου 5 χρόνια με σενάρια Bash που βελτιστοποιούσαν τη συναρμολόγηση των αρχείων Docker και τα τελευταία 3 χρόνια η πλήρης ανάπτυξή του πραγματοποιήθηκε στο πλαίσιο ενός έργου με δικό του αποθετήριο Git. (πρώτα σε Ruby, και μετά on Go, και ταυτόχρονα μετονομάστηκε)Ποια προβλήματα συναρμολόγησης λύνονται στο werf;

Τα ζητήματα που επισημαίνονται με μπλε χρώμα έχουν ήδη υλοποιηθεί, έχει γίνει παράλληλη συναρμολόγηση εντός ενός κεντρικού υπολογιστή και τα ζητήματα που επισημαίνονται με κίτρινο χρώμα έχουν προγραμματιστεί να ολοκληρωθούν μέχρι το τέλος του καλοκαιριού.
Στάδιο δημοσίευσης στο μητρώο (δημοσίευση)
Έχουμε προσλάβει docker push… — τι θα μπορούσε να είναι δύσκολο σχετικά με τη φόρτωση μιας εικόνας στο μητρώο; Και εδώ προκύπτει το ερώτημα: «Ποια ετικέτα πρέπει να βάλω στην εικόνα;» Προκύπτει επειδή έχουμε Gitflow (ή κάποια άλλη στρατηγική Git) και Kubernetes, και η βιομηχανία θέλει ό,τι συμβαίνει στα Kubernetes να ακολουθεί ό,τι συμβαίνει στο Git. Επειδή το Git είναι η μοναδική μας πηγή αλήθειας.
Τι είναι τόσο δύσκολο σε αυτό; Εξασφαλίστε την αναπαραγωγιμότητα: από μια υποβολή Git, η οποία είναι αμετάβλητη εκ φύσεως (αμετάβλητος), στην εικόνα Docker, η οποία θα πρέπει να παραμείνει η ίδια.
Είναι επίσης σημαντικό για εμάς προσδιορίστε την προέλευση, επειδή θέλουμε να καταλάβουμε από ποια υποβολή δημιουργήθηκε η εφαρμογή που εκτελείται στο Kubernetes (τότε μπορούμε να κάνουμε diffs και τέτοια).
Στρατηγικές ετικετών
Το πρώτο είναι απλό ετικέτα gitΈχουμε ένα μητρώο με μια εικόνα με ετικέτα ως 1.0Στο Kubernetes, υπάρχει το στάδιο και η παραγωγή, όπου αναπτύσσεται αυτή η εικόνα. Στο Git, κάνουμε commits και σε κάποιο σημείο τοποθετούμε μια ετικέτα. 2.0Το μεταγλωττίζουμε σύμφωνα με τις οδηγίες από το αποθετήριο και το τοποθετούμε στο μητρώο με την ετικέτα 2.0Το κυκλοφορούμε στη σκηνή και, αν όλα πάνε καλά, τότε στην παραγωγή.

Το πρόβλημα με αυτήν την προσέγγιση είναι ότι πρώτα ορίζουμε την ετικέτα και μόνο στη συνέχεια την δοκιμάζουμε και την λανσάρουμε. Γιατί; Πρώτον, είναι απλώς παράλογο: κυκλοφορούμε μια έκδοση λογισμικού που δεν έχουμε καν δοκιμάσει ακόμα (δεν μπορούμε να κάνουμε διαφορετικά, γιατί για να το δοκιμάσουμε, πρέπει να ορίσουμε την ετικέτα). Δεύτερον, αυτή η προσέγγιση δεν λειτουργεί με το Gitflow.
Η δεύτερη επιλογή - git commit + ετικέταΥπάρχει μια ετικέτα στον κύριο κλάδο 1.0; για αυτό στο μητρώο — μια εικόνα που έχει αναπτυχθεί στην παραγωγή. Επιπλέον, το σύμπλεγμα Kubernetes διαθέτει περιγράμματα προεπισκόπησης και σταδιοποίησης. Στη συνέχεια, ακολουθούμε το Gitflow: στον κύριο κλάδο για ανάπτυξη (develop) δημιουργούμε νέα χαρακτηριστικά, τα οποία έχουν ως αποτέλεσμα μια υποβολή με το αναγνωριστικό #c1Το συλλέγουμε και το δημοσιεύουμε στο μητρώο χρησιμοποιώντας αυτό το αναγνωριστικό (#c1). Με το ίδιο αναγνωριστικό κυκλοφορούμε για προεπισκόπηση. Κάνουμε το ίδιο και με τις υποβολές #c2 и #c3.
Όταν συνειδητοποιήσουμε ότι υπάρχουν αρκετές δυνατότητες, αρχίζουμε να σταθεροποιούμε τα πάντα. Στο Git, δημιουργούμε έναν κλάδο. release_1.1 (στη βάση #c3 του develop). Δεν χρειάζεται να δημιουργήσουμε αυτήν την έκδοση, όπως έγινε στο προηγούμενο στάδιο. Επομένως, μπορούμε απλώς να την κυκλοφορήσουμε σε στάδιο προετοιμασίας. Διορθώνουμε σφάλματα στο #c4 και ομοίως, προχωράμε σε σταδιακή ανάπτυξη. Παράλληλα, ταυτόχρονα, η ανάπτυξη βρίσκεται σε εξέλιξη develop, όπου οι αλλαγές λαμβάνονται περιοδικά από release_1.1Κάποια στιγμή λαμβάνουμε μια υποβολή με την οποία είμαστε ικανοποιημένοι, την οποία κατασκευάζουμε και την προωθούμε στη φάση προετοιμασίας (#c25).
Στη συνέχεια, κάνουμε μια συγχώνευση (με γρήγορη προώθηση) του κλάδου έκδοσης (release_1.1) στο master. Τοποθετούμε μια ετικέτα με τη νέα έκδοση σε αυτήν την υποβολή (1.1). Αλλά αυτή η εικόνα έχει ήδη δημιουργηθεί στο μητρώο, οπότε για να μην την δημιουργήσουμε ξανά, απλώς προσθέτουμε μια δεύτερη ετικέτα στην υπάρχουσα εικόνα (τώρα έχει ετικέτες στο μητρώο #c25 и 1.1). Μετά από αυτό, το θέτουμε σε παραγωγή.
Υπάρχει το μειονέκτημα ότι μόνο μία εικόνα αναπτύσσεται για την καταγραφή (#c25), και στην παραγωγή - σαν να ήταν διαφορετικά (1.1), αλλά γνωρίζουμε ότι «φυσικά» αυτή είναι η ίδια εικόνα από το μητρώο.

Το πραγματικό μειονέκτημα είναι ότι δεν υπάρχει υποστήριξη για υποβολές συγχώνευσης. Πρέπει να κάνετε γρήγορη προώθηση.
Μπορούμε να προχωρήσουμε παραπέρα και να κάνουμε ένα κόλπο... Ας δούμε ένα παράδειγμα ενός απλού Dockerfile:
FROM ruby:2.3 as assets
RUN mkdir -p /app
WORKDIR /app
COPY . ./
RUN gem install bundler && bundle install
RUN bundle exec rake assets:precompile
CMD bundle exec puma -C config/puma.rb
FROM nginx:alpine
COPY --from=assets /app/public /usr/share/nginx/www/publicΑς δημιουργήσουμε ένα αρχείο από αυτό σύμφωνα με την ακόλουθη αρχή: παίρνουμε:
- SHA256 από τα αναγνωριστικά των εικόνων που χρησιμοποιήθηκαν (
ruby:2.3иnginx:alpine), τα οποία είναι αθροίσματα ελέγχου του περιεχομένου τους· - όλες οι ομάδες (
RUN,CMDκαι ούτω καθεξής.); - SHA256 από αρχεία που προστέθηκαν.
... και πάρτε το άθροισμα ελέγχου (ξανά SHA256) από ένα τέτοιο αρχείο. Αυτό είναι υπογραφή όλα όσα ορίζουν τα περιεχόμενα μιας εικόνας Docker.

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

Τώρα, όταν χρειάζεται, για παράδειγμα, να συγχωνεύσουμε αλλαγές από μια έκδοση στην κύρια έκδοση, μπορούμε να κάνουμε μια πραγματική συγχώνευση: θα έχει διαφορετικό αναγνωριστικό, αλλά την ίδια υπογραφή. Με το ίδιο αναγνωριστικό, θα διαθέσουμε την εικόνα στην παραγωγή.
Το μειονέκτημα είναι ότι πλέον είναι αδύνατο να προσδιοριστεί ποια υποβολή προωθήθηκε στην παραγωγή — τα checksums λειτουργούν μόνο προς μία κατεύθυνση. Αυτό το πρόβλημα λύνεται με ένα επιπλέον επίπεδο μεταδεδομένων — θα σας πω περισσότερα γι' αυτό αργότερα.
Ετικέτες στο werf
Στο werf έχουμε προχωρήσει ακόμη περισσότερο και ετοιμαζόμαστε να κάνουμε μια κατανεμημένη κατασκευή με μια προσωρινή μνήμη που δεν είναι αποθηκευμένη σε ένα μόνο μηχάνημα... Έτσι, έχουμε δύο τύπους εικόνων Docker που κατασκευάζονται, τους ονομάζουμε στάδιο и εικόνα.
Το αποθετήριο werf Git περιέχει συγκεκριμένες οδηγίες κατασκευής που περιγράφουν τα διαφορετικά στάδια της κατασκευής (πριν από την εγκατάσταση, εγκαθιστώ, πριν από την εγκατάσταση, setup). Συγκεντρώνουμε την πρώτη εικόνα σταδίου με μια υπογραφή που ορίζεται ως άθροισμα ελέγχου των πρώτων βημάτων. Στη συνέχεια, προσθέτουμε τον πηγαίο κώδικα, για τη νέα εικόνα σταδίου υπολογίζουμε το άθροισμα ελέγχου του... Αυτές οι λειτουργίες επαναλαμβάνονται για όλα τα στάδια, με αποτέλεσμα να λαμβάνουμε ένα σύνολο εικόνων σταδίου. Στη συνέχεια, δημιουργούμε την τελική εικόνα, η οποία περιέχει επίσης μεταδεδομένα σχετικά με την προέλευσή της. Και επισημαίνουμε αυτήν την εικόνα με διαφορετικούς τρόπους (λεπτομέρειες αργότερα).

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

Καθαρισμός του μητρώου
Δεν μιλάμε για διαγραφή επιπέδων που παραμένουν κρεμασμένα μετά τη διαγραφή ετικετών - αυτό είναι ένα τυπικό χαρακτηριστικό του ίδιου του Docker Registry. Μιλάμε για μια κατάσταση όπου πολλές ετικέτες Docker συσσωρεύονται και καταλαβαίνουμε ότι δεν χρειαζόμαστε πλέον μερικές από αυτές, αλλά καταλαμβάνουν χώρο (ή/και πληρώνουμε για αυτόν).
Ποιες είναι οι στρατηγικές καθαρισμού;
- Δεν μπορείς να κάνεις τίποτα μην καθαρίζετεΜερικές φορές είναι πραγματικά πιο εύκολο να πληρώσετε λίγα χρήματα για επιπλέον χώρο παρά να ξεμπερδέψετε ένα τεράστιο κουβάρι από ετικέτες. Αλλά αυτό λειτουργεί μόνο μέχρι ένα συγκεκριμένο σημείο.
- Πλήρης επαναφοράΕάν διαγράψετε όλες τις εικόνες και δημιουργήσετε ξανά μόνο τις τρέχουσες στο σύστημα CI, τότε ενδέχεται να προκύψει πρόβλημα. Εάν το κοντέινερ επανεκκινηθεί κατά την παραγωγή, θα φορτωθεί μια νέα εικόνα για αυτό - μια εικόνα που δεν έχει δοκιμαστεί ακόμη από κανέναν. Αυτό καταργεί την ιδέα της αμετάβλητης υποδομής.
- Μπλε πράσινοΈνα μητρώο έχει αρχίσει να υπερχειλίζει - φορτώνουμε εικόνες σε ένα άλλο. Το ίδιο πρόβλημα με την προηγούμενη μέθοδο: σε ποιο σημείο μπορούμε να καθαρίσουμε το μητρώο που έχει αρχίσει να υπερχειλίζει;
- Μέχρι την ώρα. Να διαγραφούν όλες οι εικόνες που είναι παλαιότερες από 1 μήνα; Αλλά σίγουρα θα υπάρχει κάποια υπηρεσία που δεν έχει ενημερωθεί για ένα μήνα...
- Μη αυτόματα προσδιορίστε τι μπορεί ήδη να διαγραφεί.
Υπάρχουν δύο πραγματικά βιώσιμες επιλογές: να μην καθαρίσετε ή ένας συνδυασμός μπλε-πράσινου + χειροκίνητου. Στην τελευταία περίπτωση, μιλάμε για τα εξής: όταν καταλάβετε ότι ήρθε η ώρα να καθαρίσετε το μητρώο, δημιουργήστε ένα νέο και προσθέστε όλες τις νέες εικόνες σε αυτό για, ας πούμε, ένα μήνα. Και μετά από ένα μήνα, δείτε ποια pod στο Kubernetes εξακολουθούν να χρησιμοποιούν το παλιό μητρώο και μετακινήστε τα και αυτά στο νέο μητρώο.
Σε τι έχουμε καταλήξει; werfΣυλλέγουμε:
- Git head: όλες οι ετικέτες, όλα τα κλαδιά, υποθέτοντας ότι όλα όσα έχουν επισημανθεί στο Git, τα χρειαζόμαστε στις εικόνες (και αν όχι, τότε πρέπει να τα διαγράψουμε στο ίδιο το Git).
- όλα τα pods που έχουν αναπτυχθεί αυτήν τη στιγμή στο Kubernetes.
- παλιά ReplicaSets (αυτά που κυκλοφόρησαν πρόσφατα) και σχεδιάζουμε επίσης να σαρώσουμε τις κυκλοφορίες του Helm και να επιλέξουμε τις πιο πρόσφατες εικόνες από εκεί.
... και δημιουργούμε μια λίστα επιτρεπόμενων εικόνων από αυτό το σύνολο - μια λίστα εικόνων που δεν θα διαγράψουμε. Καθαρίζουμε όλα τα άλλα, στη συνέχεια βρίσκουμε ορφανές εικόνες σκηνής και τις διαγράφουμε κι αυτές.
Στάδιο ανάπτυξης
Αξιόπιστη Δηλωτική Ικανότητα
Το πρώτο σημείο στο οποίο θα ήθελα να επιστήσω την προσοχή κατά την ανάπτυξη είναι η κυκλοφορία της ενημερωμένης διαμόρφωσης πόρων που δηλώνεται δηλωτικά. Το αρχικό έγγραφο YAML που περιγράφει τους πόρους Kubernetes διαφέρει πάντα σημαντικά από το αποτέλεσμα που λειτουργεί πραγματικά στο σύμπλεγμα. Επειδή το Kubernetes προσθέτει στη διαμόρφωση:
- αναγνωριστικά στοιχεία·
- πληροφορίες υπηρεσίας·
- σύνολο προεπιλεγμένων τιμών·
- ενότητα με την τρέχουσα κατάσταση·
- αλλαγές που έγιναν ως μέρος του webhook εισαγωγής·
- το αποτέλεσμα της εργασίας διαφόρων ελεγκτών (και του χρονοπρογραμματιστή).
Επομένως, όταν εμφανίζεται μια νέα διαμόρφωση πόρων (νέος), δεν μπορούμε απλώς να το πάρουμε και να αντικαταστήσουμε την τρέχουσα, "ενεργή" διαμόρφωση (ζω). Για να το κάνουμε αυτό θα πρέπει να συγκρίνουμε νέος με την προηγουμένως εφαρμοσμένη διαμόρφωση (τελευταία εφαρμογή) και κυλήστε ζω έλαβε ενημέρωση κώδικα.
Αυτή η προσέγγιση ονομάζεται Αμφίδρομη συγχώνευσηΧρησιμοποιείται, για παράδειγμα, στο Helm.
Υπάρχει επίσης Αμφίδρομη συγχώνευση, η οποία διακρίνεται από το γεγονός ότι:
- σύγκριση τελευταία εφαρμογή и νέος, εξετάζουμε τι αφαιρέθηκε.
- σύγκριση νέος и ζω, εξετάζουμε τι έχει προστεθεί ή αλλάξει.
- εφαρμόζουμε το αθροισμένο έμπλαστρο σε ζω.
Αναπτύσσουμε πάνω από 1000 εφαρμογές με το Helm, επομένως ουσιαστικά ζούμε με αμφίδρομη συγχώνευση. Ωστόσο, έχει μια σειρά από προβλήματα που λύσαμε με τις ενημερώσεις κώδικα που βοηθούν το Helm να λειτουργεί σωστά.
Πραγματική κατάσταση κυκλοφορίας
Αφού το σύστημα CI μας δημιουργήσει μια νέα διαμόρφωση για το Kubernetes για το επόμενο συμβάν, την μεταβιβάζει για εφαρμογή. (εφαρμόζω) σε ένα σύμπλεγμα - χρησιμοποιώντας Helm ή kubectl applyΣτη συνέχεια, λαμβάνει χώρα η ήδη περιγραφείσα συγχώνευση N-οδών, στην οποία το API του Kubernetes ανταποκρίνεται εγκριτικά στο σύστημα CI και το σύστημα CI ανταποκρίνεται στον χρήστη του.

Ωστόσο, υπάρχει ένα τεράστιο πρόβλημα: Η επιτυχής εφαρμογή δεν σημαίνει επιτυχή κυκλοφορίαΕάν το Kubernetes κατανοήσει ποιες αλλαγές πρέπει να εφαρμοστούν και τις εφαρμόσει, δεν γνωρίζουμε ακόμη ποιο θα είναι το αποτέλεσμα. Για παράδειγμα, η ενημέρωση και η επανεκκίνηση των pods στο frontend μπορεί να είναι επιτυχής, αλλά όχι στο backend, και θα λάβουμε διαφορετικές εκδόσεις των εικόνων εφαρμογών που εκτελούνται.
Για να γίνουν όλα σωστά, αυτό το σχήμα απαιτεί έναν επιπλέον σύνδεσμο - έναν ειδικό ιχνηλάτη που θα λαμβάνει πληροφορίες κατάστασης από το API Kubernetes και θα τις μεταδίδει για περαιτέρω ανάλυση της πραγματικής κατάστασης των πραγμάτων. Δημιουργήσαμε μια βιβλιοθήκη ανοιχτού κώδικα στο Go - (δείτε την ανακοίνωσή της) ), - το οποίο λύνει αυτό το πρόβλημα και είναι ενσωματωμένο στο werf.
Η συμπεριφορά αυτού του tracker σε επίπεδο werf διαμορφώνεται χρησιμοποιώντας σχολιασμούς που τοποθετούνται σε Deployments ή StatefulSets. Ο κύριος σχολιασμός είναι fail-mode — κατανοεί τις ακόλουθες έννοιες:
-
IgnoreAndContinueDeployProcess— αγνοούμε τα προβλήματα με την ανάπτυξη αυτού του στοιχείου και συνεχίζουμε την ανάπτυξη· -
FailWholeDeployProcessImmediately- ένα σφάλμα σε αυτό το στοιχείο διακόπτει τη διαδικασία ανάπτυξης. -
HopeUntilEndOfDeployProcess— ελπίζουμε ότι αυτό το στοιχείο θα λειτουργήσει μέχρι το τέλος της ανάπτυξης.
Για παράδειγμα, αυτός ο συνδυασμός πόρων και τιμών σχολιασμού fail-mode:

Κατά την πρώτη ανάπτυξη, η βάση δεδομένων (MongoDB) ενδέχεται να μην είναι ακόμη έτοιμη - Οι αναπτύξεις θα παρουσιάσουν σφάλμα. Μπορείτε όμως να περιμένετε μέχρι να ξεκινήσει και η ανάπτυξη θα ολοκληρωθεί.
Υπάρχουν δύο ακόμη σχολιασμοί για το kubedog στο werf:
-
failures-allowed-per-replica— ο αριθμός των πτώσεων που επιτρέπονται για κάθε αντίγραφο· -
show-logs-until— ρυθμίζει τη στιγμή μέχρι την οποία το werf εμφανίζει (σε stdout) αρχεία καταγραφής από όλα τα ανεπτυγμένα pod. Από προεπιλογή, αυτόPodIsReady(για να αγνοήσουμε μηνύματα που πιθανώς δεν θέλουμε όταν η ομάδα αρχίσει να λαμβάνει επισκεψιμότητα), αλλά οι τιμέςControllerIsReadyиEndOfDeploy.
Τι άλλο θέλουμε από την ανάπτυξη;
Εκτός από τα δύο σημεία που έχουν ήδη περιγραφεί, θα θέλαμε:
- για να δει κορμοί - και μόνο τα απαραίτητα, όχι όλα·
- τροχιά πρόοδο, επειδή αν μια εργασία κολλήσει «σιωπηλά» για αρκετά λεπτά, είναι σημαντικό να κατανοήσουμε τι συμβαίνει εκεί·
- έχουν αυτόματη επαναφορά σε περίπτωση που κάτι πάει στραβά (και γι' αυτό είναι κρίσιμο να γνωρίζουμε την πραγματική κατάσταση της ανάπτυξης). Η ανάπτυξη πρέπει να είναι ατομική: είτε φτάνει στο τέλος, είτε όλα επιστρέφουν στην προηγούμενη κατάσταση.
Αποτελέσματα της
Για εμάς ως εταιρεία, για την εφαρμογή όλων των περιγραφόμενων λεπτομερειών σε διαφορετικά στάδια παράδοσης (κατασκευή, δημοσίευση, ανάπτυξη), ένα σύστημα CI και μια βοηθητική εφαρμογή είναι επαρκή. .
Αντί για συμπέρασμα:

Με το werf, έχουμε σημειώσει σημαντική πρόοδο στην επίλυση ενός μεγάλου αριθμού προβλημάτων για τους μηχανικούς DevOps και θα ήμασταν χαρούμενοι αν η ευρύτερη κοινότητα δοκίμαζε τουλάχιστον αυτό το βοηθητικό πρόγραμμα στην πράξη. Θα είναι ευκολότερο να επιτύχουμε καλά αποτελέσματα μαζί.
Βίντεο και διαφάνειες
Βίντεο από την παράσταση (~47 λεπτά):

Παρουσίαση της έκθεσης:
PS
Άλλες αναφορές για το Kubernetes στο ιστολόγιό μας:
- «» (Dmitry Stolyarov; 27 Απριλίου 2019 στο "Strachka");
- «» (Andrey Polovov; 8 Απριλίου 2019 στο Saint HighLoad++);
- «» (Dmitry Stolyarov, 8 Νοεμβρίου 2018 στο HighLoad++);
- «» (Dmitry Stolyarov; 28 Μαΐου 2018 στο RootConf);
- «» (Dmitry Stolyarov, 7 Νοεμβρίου 2017 στο HighLoad++);
- «» (Dmitry Stolyarov, 6 Ιουνίου 2017 στο RootConf).
Πηγή: www.habr.com
