The deploy that quietly undoes last month's fix
Γράφει ο Panagiotis Athanasakopoulos · Παναγιώτης Αθανασακόπουλος
Engineering14 Σεπτεμβρίου 20263 min
Ρωτήστε μια ομάδα πώς χάλασε το live σύστημα. Η ειλικρινής απάντηση σπάνια είναι ένα νέο bug. Πιο συχνά είναι μια παλιά διόρθωση που έπαψε να υπάρχει. Κάποιος διόρθωσε ένα αρχείο απευθείας στον server, ή σε ένα branch που δεν συγχωνεύτηκε ποτέ. Εβδομάδες αργότερα, ένα απολύτως συνηθισμένο deploy από κάπου αλλού αντέγραψε την παλιά έκδοση από πάνω. Το build πέρασε. Τα tests πέρασαν. Τίποτα δεν ήταν λάθος, εκτός από το μόνο πράγμα που μετρούσε.
Τα tests δεν το πιάνουν αυτό, και αξίζει να πούμε με ακρίβεια γιατί. Ένα test ελέγχει τι κάνει ο κώδικας. Δεν ξέρει τίποτα για όσα έκανε παλιότερα ο κώδικας και θέλατε να τα κρατήσετε. Και σίγουρα δεν ξέρει ότι υπάρχει μια διόρθωση στον server αλλά όχι στο repository. Ένα script deploy που απλώς αντιγράφει αρχεία είναι ακόμη χειρότερο: δεν έχει καθόλου μνήμη. Κάθε deploy είναι σαν να γίνεται για πρώτη φορά.
Γι' αυτό το πρώτο που ζητώ από ένα deploy είναι μνήμη. Καταγράψτε στον server ποιο commit έφτιαξε κάθε αρχείο που είναι live. Πριν ανεβάσετε κάτι, ελέγξτε ότι το commit πίσω από τον live κώδικα περιέχεται σε αυτό που πάτε να ανεβάσετε. Αν δεν περιέχεται, σταματήστε. Πάτε να σβήσετε δουλειά που το branch σας δεν έχει. Αυτός ο ένας έλεγχος μετατρέπει την πιο συνηθισμένη σιωπηλή οπισθοδρόμηση σε ένα καθαρό, ηχηρό «όχι», τη μόνη στιγμή που κάποιος κοιτάζει.
Το δεύτερο είναι μια σύντομη λίστα από invariants: απλούς κανόνες για το αποτέλεσμα του build, που ελέγχονται πριν αντιγραφεί οτιδήποτε. Αυτό το αρχείο πρέπει να περιέχει εκείνη την κλήση. Αυτό το κείμενο δεν πρέπει να εμφανιστεί ποτέ σε εκείνο το module. Είναι χοντροκομμένοι κανόνες, και αυτό ακριβώς είναι το νόημα. Τρέχουν σε ένα δευτερόλεπτο, δεν χρειάζονται περιβάλλον δοκιμών, και προστατεύουν τις διορθώσεις που ήταν τόσο λεπτές ώστε να μπορούν να αναιρεθούν χωρίς να το προσέξει κανείς. Ένας καλός πρακτικός κανόνας: αν ένα παλιό build θα μπορούσε να αναιρέσει σιωπηλά μια διόρθωση, η διόρθωση αξίζει μια γραμμή στη λίστα.
Το τρίτο είναι η ειλικρίνεια για το τι σημαίνει «χωρίς διακοπή». Τα εργαλεία διαχείρισης διεργασιών δηλώνουν χωρίς δισταγμό έναν worker ως online τη στιγμή που ξεκίνησε, όχι όταν δέχεται πράγματι αιτήματα, και κάνουν αμέσως reload στον επόμενο. Κάντε reload σε έναν worker τη φορά, περιμένετε να απαντήσει σε έναν έλεγχο υγείας (health check), και μόνο τότε πιάστε τον επόμενο. Η διαφορά μετριέται στα αιτήματα που βλέπουν οι πελάτες σας να αποτυγχάνουν. Και είναι αόρατη μέχρι να παρακολουθήσει κάποιος το site κατά τη διάρκεια ενός deploy.
Το τέταρτο είναι να τελειώνετε με πραγματικά αιτήματα. Λίγα smoke tests στο live site μετά την αλλαγή: η αρχική σελίδα, μια αναζήτηση, οι σελίδες που φέρνουν χρήματα. Αν κάποιο αποτύχει, επαναφέρετε αυτόματα την προηγούμενη έκδοση. Μια προειδοποίηση, που πολλές ομάδες έμαθαν με τον δύσκολο τρόπο: ένα smoke test συχνά μένει στη θέση του ακόμη κι όταν το endpoint που ελέγχει έχει φύγει. Αν αφαιρέσετε ένα endpoint χωρίς να αφαιρέσετε το test του, κάθε μελλοντικό deploy αποτυγχάνει στον δικό του έλεγχο και γυρίζει μόνο του πίσω.
Και προσέξτε τι δεν κάνει το deploy. Αν η διαδικασία σας ανεβάζει compiled κώδικα αλλά δεν εγκαθιστά ποτέ dependencies, η πρώτη αλλαγή που προσθέτει ένα πακέτο βγάζει ένα build που περνά στο laptop και μια εφαρμογή που δεν μπορεί να ξεκινήσει στον server. Ή κάντε την εγκατάσταση στον server ως μέρος του deploy, ή κάντε τη διαδικασία να αρνείται μια νέα runtime dependency μέχρι να την εγκαταστήσει κάποιος με το χέρι.
Τίποτα από αυτά δεν χρειάζεται πλατφόρμα ή προμηθευτή. Είναι ένα script, ένα manifest και μια λίστα κανόνων, και αποσβένεται την πρώτη φορά που σας σταματά πριν από ένα λάθος. Το deploy είναι το μόνο βήμα που αγγίζει κάθε γραμμή που έχετε γράψει ποτέ. Του αξίζει να ξέρει ποιες από αυτές μετρούν.
Common questions
- Τι είναι ένα deploy invariant;
- Ένας απλός κανόνας για το αποτέλεσμα του build, που πρέπει να ισχύει πριν επιτραπεί μια έκδοση. Για παράδειγμα, ότι μια συγκεκριμένη συνάρτηση καλείται ακόμη, ή ότι ένα απαγορευμένο κείμενο δεν εμφανίζεται ποτέ. Τα invariants προστατεύουν διορθώσεις που ένα παλιό ή λάθος build θα μπορούσε να αναιρέσει σιωπηλά, και σταματούν το deploy πριν φτάσει οτιδήποτε στον server.
- Πώς εμποδίζετε ένα deploy να αναιρέσει ένα hotfix;
- Καταγράφετε ποιο commit έφτιαξε τον κώδικα που είναι live. Πριν από κάθε deploy ελέγχετε ότι αυτό το commit είναι πρόγονος εκείνου που ανεβαίνει. Αν δεν είναι, ο live κώδικας περιέχει αλλαγές που λείπουν από το νέο build, και το deploy πρέπει να σταματήσει μέχρι να συγχωνευτούν.
- Αρκεί ένα rolling reload για deploy χωρίς διακοπή;
- Μόνο αν κάθε worker επιβεβαιώνεται έτοιμος πριν γίνει επανεκκίνηση στον επόμενο. Πολλά εργαλεία διαχείρισης διεργασιών θεωρούν έναν worker online μόλις ξεκινήσει, όχι όταν ακούει αιτήματα. Έτσι, αν κάνετε reload τον έναν μετά τον άλλον, μπορεί να μείνετε για αρκετά δευτερόλεπτα χωρίς κανέναν worker να εξυπηρετεί. Βάλτε έναν έλεγχο υγείας πριν από κάθε βήμα.