Una republicació fallida mai no esborra un backend que funciona
El 25 de setembre, una republicació fallida va esborrar el backend d'una app que havia estat en marxa i funcionant durant unes dues hores i mitja. El codi que desfà una publicació fallida va eliminar totes les apps de servidor que la publicació havia tocat, incloses les que ja servien abans que comencés. Ara només s'eliminen les apps que una publicació ha creat; una app que ja existia torna exactament a la versió que executava abans. Dues correccions posteriors el mateix dia van garantir que la desfeta també s'executi quan falla l'eina de desplegament de la plataforma d'allotjament, i que una app sana no es doni per fallida només perquè estava adormida.
Com es publica un backend
Quan la teva app de Mythex té un backend —una API o un servidor, i no només fitxers estàtics—, publicar-la construeix el backend a partir d'un Dockerfile (una recepta per empaquetar l'app en una imatge) i desplega aquesta imatge en màquines de la nostra plataforma d'allotjament. Després, una comprovació de salut espera que la versió nova arrenqui de debò i es mantingui activa abans que la publicació es marqui com a en línia.
Si alguna cosa falla pel camí, s'executa una reversió per desfer-ho. Fer-la bé importa més que gairebé qualsevol altra cosa de la publicació, perquè s'executa precisament quan ja hi ha un problema.
Problema 1: la reversió esborrava el que havia de restaurar
La reversió guardava una llista dels servidors de l'app als quals una publicació havia desplegat alguna cosa i, en cas de fallada, els destruïa tots. Això és correcte en una primera publicació: l'app s'acaba de crear, res no en depèn, i esborrar-la deixa les coses com estaven.
És exactament incorrecte en una republicació. L'app ja existia i servia visitants reals. El 25 de setembre, una republicació fallida va esborrar l'API d'una app que havia estat en línia unes dues hores i mitja.
Hi havia un segon error amagat en el mateix codi. Quan l'esborrat no es completava, la imatge nova i trencada es quedava a les màquines, mentre nosaltres dèiem al propietari que el seu lloc actual seguia en línia. Quan la comprovació de salut pot fallar, la imatge nova ja s'ha desplegat a totes les màquines. No fer res no és el mateix que desfer.
La correcció: destruir només el que has creat, i restaurar la resta
Cada publicació ara registra, per a cada app de servidor que toca:
- si aquesta publicació l'ha creada, i
- si no, quina imatge executava just abans.
En cas de fallada, cada app rep un de tres tractaments:
- Creada per aquesta publicació: es destrueix, com abans.
- Ja existia i se sap quina era la imatge anterior: es torna a desplegar aquella imatge amb la mateixa configuració. Sense compilació: només es mou la imatge enrere.
- Ja existia i la imatge anterior és desconeguda: no es toca. Una app existent mai no s'elimina, encara que no la puguem restaurar.
Aquestes regles són una petita funció pura amb les seves pròpies proves, de manera que la decisió no pot tornar a ser «destruir-ho tot» sense que falli una prova.
Problema 2: l'eina de desplegament podia fallar abans que haguéssim anotat com desfer
Poc després d'aquesta correcció, vam trobar un forat. L'eina de desplegament de la plataforma executa les seves pròpies comprovacions de fum: si les màquines noves es bloquegen, surt amb error. Però ho fa després que la imatge nova ja sigui a les màquines.
El nostre codi només registrava una app per a la reversió quan l'eina tornava amb èxit. Quan l'eina mateixa feia fallar el desplegament, l'app encara no era a la llista de reversió i, per tant, no es restaurava res. Al nostre entorn de staging, una republicació trencada va deixar la imatge trencada servint.
La correcció: registrar la desfeta abans de començar
Ara l'app es registra per a la reversió —amb si s'ha creat i quina imatge executava— en el moment que es prepara i abans que s'hi desplegui res. Falli el que falli després, la reversió sap què cal tornar a posar. Ho vam verificar en local: una republicació trencada va fallar a les comprovacions de fum de la mateixa eina de desplegament, i la versió anterior va continuar responent.
Problema 3: una app adormida es va donar per fallida com a «mai no ha arrencat»
Les apps publicades a Mythex s'adormen quan estan inactives per estalviar crèdits. Una republicació deixa una màquina adormida aturada, de manera que la comprovació de salut l'ha de despertar. Demanava a la plataforma que engegués la màquina una sola vegada, i ignorava la resposta.
El 25 de setembre, aquella única sol·licitud d'arrencada no va servir de res. Una API sana es va quedar aturada durant tota la comprovació de salut de cinc minuts i es va donar per fallida amb «1 machine never started (last state: stopped)».
La plataforma de vegades sí que rebutja una arrencada. Mentre reproduíem el problema, va retornar un conflicte «lease currently held» sense que nosaltres l'haguéssim provocat, i rebutja les arrencades mentre una màquina s'està actualitzant.
La correcció: insistir, i trucar a la porta
La comprovació de salut ara:
- torna a demanar d'arrencar una màquina aturada cada 20 segons mentre espera,
- registra què ha respost la plataforma quan rebutja la petició, i
- envia una sol·licitud normal a l'app, com ho faria un visitant: el proxy de la plataforma desperta una màquina adormida quan arriba una petició.
Ho vam reproduir a la plataforma real amb una app de prova la primera arrencada de la qual era rebutjada. Abans de la correcció, la publicació fallava als 91 segons. Després, es publicava en 20 segons. Una app que de debò es bloqueja en arrencar segueix fallant, com ha de ser.
Què n'hem après
- La reversió ha de saber la diferència entre nou i existent. «Desfer» una cosa que has creat és esborrar-la. «Desfer» una cosa que ja hi era és restaurar-la. Tractar-les igual és com un mecanisme de seguretat acaba esborrant una app que funciona.
- Anota com desfer abans d'actuar. Si la desfeta només es registra després que un pas tingui èxit, un pas que falla a mig camí no deixa res amb què desfer.
- Mai no ignoris la resposta a una sol·licitud de la qual depens. Un «no» ignorat va convertir una app sana en una publicació fallida.
Una correcció relacionada del mateix dia: les apps que es bloquegen just després de publicar. Per saber què passa quan una publicació falla per la teva part, consulta la resolució de problemes de publicació a la documentació.