Så fick vi deployerna ner till noll 502-fel
Varje gång vi deployade Mythex backend fick några förfrågningar 502 Bad Gateway. Inte många — men en 502 under en deploy betyder att någons klick inte gjorde något, eller att ett bygge någon tittade på stannade upp ett ögonblick. Vi spårade det till två separata problem: en självläkande watchdog som motarbetade våra deployer, och ett gap mellan när en server slutade lyssna och när lastbalanseraren slutade skicka trafik till den. Under tre på varandra följande produktionsdeployer efter åtgärderna gick antalet från 10 till 6 till 0.
Uppsättningen
Vårt API och vår orkestrerare körs som containrar på en liten grupp servrar bakom en AWS Application Load Balancer (ALB). En deploy uppdaterar en server i taget: den tömmer de gamla containrarna, startar nya och går vidare. En watchdog på varje server kontrollerar att containrarna är igång och startar om alla som inte är det. Den finns så att en krashad process klockan tre på natten kommer tillbaka utan att någon behöver väckas.
Båda är rimliga var för sig. Tillsammans fungerade de inte.
Problem 1: watchdogen tävlade med deployen
Watchdogen kördes en gång i minuten och »läkte« vid ett misslyckat hälsotest. En deploy tömmer medvetet tjänster i upp till 150 sekunder — under den tiden ska ett hälsotest förväntas misslyckas. Så watchdogen slog till under varje enskild deploy.
Oftast förlorade den loppet utan skada. En gång vann den: den skrev om compose-filen (beskrivningen av vilka containrar som ska köras) mitt i en deploy, och lämnade en server utan någon gateway-container alls — medan deployjobbet rapporterade att allt lyckats.
Åtgärderna:
- Ett lås. Deployen tar nu samma fillås som serverns bootstrap-skript, så watchdogen och en deploy aldrig kan köra samtidigt.
- Fyra varningar, inte en. Watchdogen behöver fyra misslyckade kontroller i rad innan den agerar. En tjänst under tömning ser inte längre död ut.
- En definition. Compose-filen har nu exakt en källa, publicerad till servrarna, i stället för kopior som kunde glida isär.
- Verifiera, lita inte blint. Deployjobbet lyckas inte längre bara för att ett kommando returnerade. Det kontrollerar varje servers API, orkestrerare, gateway och publiceringsworker innan det kan bli godkänt.
Problem 2: servern gick innan trafiken gjorde det
Det andra problemet var tystare. När en deploy tömmer en server börjar servern svara 503 på sin hälsoendpoint så att lastbalanseraren tar bort den från rotationen. Vår gjorde så i 5 sekunder och stängde sedan av sig.
Men ALB kontrollerade hälsan bara med vissa mellanrum, och krävde flera misslyckade kontroller i rad innan den slutade skicka trafik till ett mål — upp till cirka 60 sekunder från början till slut. Så i ungefär 55 sekunder fortsatte lastbalanseraren skicka riktiga förfrågningar till en server som redan slutat lyssna. Det var våra 502:or.
Lösningen var att få de två tidslinjerna att stämma överens:
- Målgruppen kontrollerar nu varje 5 sekunder och markerar ett mål som ohälsosamt efter 2 misslyckanden — cirka 10 sekunder att upptäcka det.
- Tömningen fortsätter att servera riktig trafik i 15 sekunder efter att den börjat misslyckas med hälsokontroller, så det alltid finns överlapp.
- Dessa inställningar ligger i skriptet som provisionerar infrastrukturen, inte i en konsol som någon klickade i en gång.
Att få talen att hålla ihop
Timeouts som dessa beror på varandra. Tömningen måste vara längre än tiden lastbalanseraren behöver för att märka något; tömning plus nedstängning måste bli klar innan containern tvångsstoppas. Det är lätt att någon ändrar ett tal om sex månader och oavsiktligt återinför felet.
Så vi skrev ner sambanden som tester: tömningen måste vara längre än hälsokontrollens intervall gånger tröskeln för misslyckanden; tiden tills något åter markeras som friskt måste förbli kort; hela tömningen måste få plats inom containerns stoppgrace-period. Ändrar du ett tal utan de andra faller CI. Vi mutationstestade själva testerna — ändrade varje tal till ett felaktigt värde och kontrollerade att ett test fångade det.
Att mäta en deploy är lätt att göra fel
Vår första mätning sa att en ren deploy var ungefär 3 % trasig. Det var den inte. Vi räknade varje svar som inte var 200, och en 503 från en hälsoendpoint under en tömning är inget fel. Det är tömningen som gör sitt jobb och säger till lastbalanseraren att inte välja den servern medan den fortsätter att servera riktiga förfrågningar.
Det användarna märker är en 502: förfrågan nådde ingenting. Att bara räkna dem gav den verkliga bilden — 10, sedan 6, sedan 0 under tre produktionsdeployer.
Det vi tog med oss
- Självläkning behöver veta om planerade driftstopp. En watchdog som inte kan skilja en deploy från en krasch kommer så småningom att slåss mot deployen.
- Tömning är en handskakning. Servern måste fortsätta servera tills lastbalanseraren bevisligen slutat skicka trafik, och den tidpunkten sätts i balanseraren, inte i servern.
- Koda invarianter som tester. Driftstal glider. Tester som relaterar dem till varandra gör inte det.
- Bestäm vad du mäter innan du mäter. »Fel« var fel mätvärde; »förfrågningar som nådde ingenting« var rätt.
Om du publicerar en app på Mythex syns inget av detta för dig, och det är poängen. Relaterat: hur din publicerade app förblir uppe medan du publicerar om den.