Miten saimme käyttöönotot nollaan 502-virhettä
Joka kerta kun käyttöönotimme Mythexin taustapalvelun, muutama pyyntö epäonnistui virheellä 502 Bad Gateway. Ei paljon, mutta 502 käyttöönoton aikana tarkoittaa, että joku napsautus ei tehnyt mitään tai build, jota joku seurasi, pysähtyi hetkeksi. Jäljitimme sen kahteen erilliseen ongelmaan: itsekorjaavaan valvontakoiraan, joka kilpaili käyttöönottojemme kanssa, ja aukkoon siinä, milloin palvelin lopetti kuuntelun ja milloin kuormantasaaja lopetti liikenteen lähettämisen sille. Kolmen peräkkäisen tuotantokäyttöönoton jälkeen korjausten jälkeen määrä meni 10:stä 6:een ja lopulta 0:aan.
Asetelma
API ja orkestroija toimivat konttina pienessä palvelinryhmässä AWS:n Application Load Balancerin (ALB) takana. Käyttöönotto päivittää yhden palvelimen kerrallaan: se tyhjentää vanhat kontit, käynnistää uudet ja siirtyy eteenpäin. Jokaisella palvelimella on valvontakoira, joka tarkistaa, että kontit ovat pystyssä, ja käynnistää uudelleen kaiken, mikä ei ole. Se on olemassa, jotta kaatunut prosessi kello 3 aamulla herää ilman, että kukaan joutuu hereille.
Molemmat ovat järkeviä yksinään. Yhdessä ne eivät olleet.
Ongelma 1: valvontakoira kilpaili käyttöönoton kanssa
Valvontakoira ajoi kerran minuutissa ja "paransi" yhden epäonnistuneen terveystarkistuksen jälkeen. Käyttöönotto tyhjentää palveluita tarkoituksella jopa 150 sekuntia — jolloin terveystarkistuksen pitääkin epäonnistua. Joten valvontakoira laukesi jokaisen käyttöönoton aikana.
Suurimman osan ajasta se hävisi kilpailun vahingoitta. Kerran se voitti: se kirjoitti uudelleen compose-tiedoston (kuvauksen siitä, mitkä kontit pitäisi ajaa) käyttöönoton keskellä, ja jätti palvelimen ilman yhdyskäytävä-konttia lainkaan — samalla kun käyttöönottotyö ilmoitti onnistumisesta.
Korjaukset:
- Yksi lukko. Käyttöönotto ottaa nyt saman tiedostolukon kuin palvelimen käynnistysskripti, joten valvontakoira ja käyttöönotto eivät voi koskaan ajaa samaan aikaan.
- Neljä varoitusta, ei yksi. Valvontakoira tarvitsee neljä peräkkäistä epäonnistunutta tarkistusta ennen toimintaa. Tyhjennettävä palvelu ei enää näytä kuolleelta.
- Yksi määritelmä. Compose-tiedostolla on nyt täsmälleen yksi lähde, joka julkaistaan palvelimille, sen sijaan että kopiot voisivat ajautua erilleen.
- Varmista, älä luota. Käyttöönottotyö ei enää mene läpi siksi, että komento palautti jotain. Se tarkistaa jokaisen palvelimen API:n, orkestroijan, yhdyskäytävän ja julkaisutyöntekijän ennen onnistumista.
Ongelma 2: palvelin lähti ennen liikennettä
Toinen ongelma oli hiljaisempi. Kun käyttöönotto tyhjentää palvelimen, palvelin alkaa vastata terveysendpointissaan 503-virheellä, jotta kuormantasaaja poistaa sen kierrosta. Meidän palvelimemme teki niin 5 sekuntia, ja sen jälkeen se sammui.
ALB tarkisti kuitenkin terveyden vain silloin tällöin ja tarvitsi useita peräkkäisiä epäonnistumisia, ennen kuin se lakkasi reitittämästä kohteeseen — pahimmillaan noin 60 sekuntia kokonaisuudessaan. Joten noin 55 sekuntia kuormantasaaja lähetti oikeita pyyntöjä palvelimelle, joka oli jo lakannut kuuntelemasta. Ne olivat meidän 502-virheitämme.
Korjaus oli saada kaksi aikajanaa täsmäämään:
- Kohderyhmä tarkistaa nyt terveyden 5 sekunnin välein ja merkitsee kohteen epäkunnossa olevaksi 2 epäonnistumisen jälkeen — noin 10 sekuntia havaitsemiseen.
- Tyhjennys palvelee oikeaa liikennettä 15 sekuntia sen jälkeen, kun se alkaa epäonnistua terveystarkistuksissa, joten päällekkäisyys on aina olemassa.
- Nämä asetukset elävät infrastruktuurin pystyttävässä skriptissä, eivät konsolissa, jonka joku klikkasi kerran.
Lukujen pitäminen toisiinsa sidottuina
Tällaiset aikarajat riippuvat toisistaan. Tyhjennyksen täytyy kestää kauemmin kuin tasapainottajan tarvitsema aika huomata; tyhjennyksen ja sammutuksen täytyy päättyä ennen kuin kontti tapetaan pakolla. On helppoa, että joku muuttaa yhtä lukua kuuden kuukauden päästä ja tuo bugin takaisin.
Kirjoitimme siksi nämä suhteet ylös testeinä: tyhjennyksen pitää olla pidempi kuin terveystarkistuksen väli kerrottuna epäonnistumisrajalla; aika, jolloin kohde merkitään jälleen terveeksi, pitää pysyä lyhyenä; koko tyhjennyksen pitää mahtua kontin pysäytysajan sisään. Muuta yhtä lukua ilman muita, ja CI epäonnistuu. Mutaatiotestasimme itse testit: muutimme jokaisen luvun väärään arvoon ja tarkistimme, että testi huomaa sen.
Käyttöönoton mittaaminen on helppo tehdä väärin
Ensimmäinen mittauksemme kertoi, että puhdas käyttöönotto oli noin 3 % rikki. Se ei ollut. Laskimme jokaisen muun kuin 200-vastauksen, ja 503 terveysendpointista tyhjennyksen aikana ei ole virhe — se on tyhjennys, joka tekee työnsä ja kertoo kuormantasaajalle, ettei sitä palvelinta pidä valita, kun se palvelee yhä oikeita pyyntöjä.
Käyttäjä kokee 502-virheen: pyyntö ei saavuttanut mitään. Kun laskimme vain ne, saimme oikean kuvan — 10, sitten 6, sitten 0 kolmen tuotantokäyttöönoton aikana.
Mitä tästä opimme
- Itsekorjaava järjestelmä tarvitsee tietää suunnitellusta seisokista. Valvontakoira, joka ei osaa erottaa käyttöönottoa kaatumisesta, taistelee lopulta käyttöönottoa vastaan.
- Tyhjennys on kättely. Palvelimen täytyy jatkaa palvelua, kunnes kuormantasaaja on todistettavasti lopettanut liikenteen lähettämisen, ja tuo ajoitus asetetaan tasapainottajalle, ei palvelimelle.
- Koodaa invariantit testeiksi. Operatiiviset luvut ajautuvat. Testit, jotka suhteuttavat ne toisiinsa, eivät.
- Päätä, mitä mittaat, ennen kuin mittaat. "Virheet" oli väärä mittari; "pyynnöt, jotka eivät saavuttaneet mitään" oli oikea.
Jos julkaiset sovelluksen Mythexissä, mikään tästä ei näy sinulle — ja juuri se on tarkoitus. Aiheeseen liittyen: miten julkaistu sovelluksesi pysyy pystyssä, kun julkaiset sen uudelleen.