Neuspelo ponovno objavljivanje nikad ne briše radni backend
Dana 25. septembra neuspelo ponovno objavljivanje obrisalo je backend jedne aplikacije koji je bio živ i radio oko dva i po sata. Kod koji poništava neuspelu objavu uklonio je svaku server aplikaciju koju je objava dotakla, uključujući i one koje su već služile pre nego što je počela. Sada se samo aplikacije koje je objava napravila ikada brišu; aplikacija koja je već postojala vraća se tačno na verziju koju je imala pre toga. Dve naredne ispravke istog dana osigurale su da se to poništavanje izvrši i kada alat za deploy hosting platforme sam otkaže, i da zdrava aplikacija ne bude označena kao neuspela samo zato što je spavala.
Kako se objavljuje backend
Kada vaša Mythex aplikacija ima backend, odnosno API ili server, a ne samo statičke fajlove, objava ga gradi iz Dockerfile-a (recepta za pakovanje aplikacije u image) i postavlja taj image na mašine naše hosting platforme. Zatim health check čeka da se nova verzija zaista podigne i ostane pokrenuta pre nego što objava bude označena kao živa.
Ako nešto pođe po zlu usput, pokreće se rollback koji to poništava. Taj rollback mora da bude ispravan, a to je važnije od gotovo svega drugog u objavljivanju, jer se pokreće baš kada već nešto ne ide kako treba.
Problem 1: rollback je obrisao ono što je trebalo da vrati
Rollback je vodio listu server aplikacija na koje je objava postavljena i pri neuspehu ih je sve uništavao. To je ispravno za prvu objavu: aplikacija je napravljena pre tren oka, ništa ne zavisi od nje, a brisanje vraća stanje kakvo je bilo.
Za ponovno objavljivanje to je potpuno pogrešno. Aplikacija je već postojala i služila je prave posetioce. Dana 25. septembra API jedne aplikacije, koji je bio živ oko dva i po sata, obrisan je neuspelom ponovnom objavom.
U istom kodu krila se i druga greška. Kada brisanje nije uspelo, pokvareni novi image ostao bi na mašinama, dok smo vlasniku javljali da je njegov trenutni sajt i dalje živ. Kada health check može da padne, novi image je već postavljen na svaku mašinu. Ne raditi ništa nije isto što i poništiti.
Ispravka: obriši samo ono što si napravio, vrati ostalo
Svaka objava sada beleži, za svaku server aplikaciju koju dotakne:
- da li ju je ova objava napravila, i
- ako nije, koji image je radila neposredno pre nje.
Pri neuspehu svaka aplikacija dobija jedan od tri tretmana:
- Napravljena ovom objavom: uništi je, kao i ranije.
- Već je postojala, prethodni image poznat: ponovo postavi prethodni image uz istu konfiguraciju. Nema builda, samo se image vraća.
- Već je postojala, prethodni image nepoznat: ne diraj je. Postojeća aplikacija se nikada ne briše, čak ni kada je ne možemo vratiti.
Ta pravila su mala čista funkcija sa sopstvenim testovima, pa odluka ne može ponovo da skrene ka „uništi sve” a da test ne pukne.
Problem 2: alat za deploy mogao je da padne pre nego što smo zapisali kako se poništava
Ubrzo posle te ispravke našli smo prazninu u njoj. Alat za deploy hosting platforme pokreće sopstvene smoke provere: ako nove mašine padnu, izlazi sa greškom. Ali to radi nakon što je novi image već na mašinama.
Naš kod je aplikaciju registrovao za rollback tek kada je alat vratio uspeh. Kada je sam alat označio deploy kao neuspeo, aplikacija još nije bila na listi za rollback, pa ništa nije vraćeno. Na našem staging okruženju pokvarena ponovna objava ostavila je pokvareni image da radi.
Ispravka: zapiši poništaj pre početka
Aplikacija se sada registruje za rollback, sa podatkom da li je napravljena i koji image je radila, čim je pripremljena i pre nego što se bilo šta postavi na nju. Šta god da pukne posle, rollback zna šta da vrati. Proverili smo lokalno: pokvarena ponovna objava pala je na sopstvenim smoke proverama alata za deploy, a prethodna verzija je i dalje odgovarala.
Problem 3: spavajuća aplikacija označena je kao „nikad nije pokrenuta”
Objavljene aplikacije na Mythexu spavaju kada su neaktivne da bi uštedele vaše kredite. Ponovna objava ostavlja zaustavljenu mašinu koja spava, pa health check mora da je probudi. Tražio je od platforme da pokrene mašinu jednom i ignorisao je odgovor.
Dana 25. septembra taj jedan zahtev za pokretanje nikuda nije stigao. Zdrav API je stajao zaustavljen čitavih pet minuta health checka i bio je označen kao neuspeo sa porukom „1 mašina nikad nije pokrenuta (poslednje stanje: stopped)”.
Platforma ponekad i odbije pokretanje. Dok smo ovo reprodukovali, vratila je konflikt „lease currently held” bez ikakvog našeg izazivanja, a odbija pokretanja i dok se mašina ažurira.
Ispravka: pitaj ponovo i pokucaj na vrata
Health check sada:
- traži ponovno pokretanje zaustavljene mašine svakih 20 sekundi dok čeka,
- beleži šta je platforma odgovorila kada odbije, i
- šalje jedan običan zahtev aplikaciji, onako kako bi to uradio posetilac. Proxy platforme budi spavajuću mašinu kada stigne zahtev.
Reprodukovali smo to na pravoj platformi, sa test aplikacijom čije je prvo pokretanje bilo odbijeno. Pre ispravke objava je pala posle 91 sekunde. Posle nje objavljena je za 20 sekundi. Aplikacija koja zaista pada pri pokretanju i dalje pada, kao što treba.
Šta smo izvukli iz ovoga
- Rollback mora da razlikuje novo od postojećeg. „Poništi” za nešto što si napravio znači obriši. „Poništi” za nešto što je već bilo znači vrati. Tretirati ih isto je način na koji mehanizam za bezbednost obriše radnu aplikaciju.
- Zapiši kako se poništava pre nego što delaš. Ako se poništaj beleži tek kada korak uspe, korak koji pukne na pola ne ostavlja ništa čime bi se poništio.
- Nikada ne ignoriši odgovor na zahtev od kojeg zavisiš. Jedno ignorisano „ne” pretvorilo je zdravu aplikaciju u neuspelu objavu.
Povezana ispravka istog dana: hvatanje aplikacija koje padnu odmah posle objave. Za ono što se dešava kada objava ne uspe na vašoj strani, pogledajte rešavanje problema sa objavom u dokumentaciji.