Како стигнавме до нула 502 грешки при објавувања
Секој пат кога објавувавме backend на Mythex, неколку барања пропаѓаа со 502 Bad Gateway. Не многу — но 502 за време на објавување значи дека нечиј клик не направил ништо, или дека изградбата што некој ја гледаше застанала за момент. Го проследивме до два одделни проблеми: самолекуван watchdog што се бореше со нашите објавувања, и јаз помеѓу тоа кога еден сервер престанува да слуша и кога балансерот на товар престанува да му испраќа сообраќај. Во три последователни production објавувања по поправките, бројот падна од 10 на 6 и на 0.
Поставката
Нашиот API и оркестраторот работат како контејнери на мала група сервери зад AWS Application Load Balancer (ALB). Објавувањето ажурира по еден сервер: ги исклучува старите контејнери, ги подига новите и продолжува. Watchdog на секој сервер ги проверува контејнерите и рестартира сè што не работи — постои за да се врати процес што паднал во 3 часот наутро без никој да се буди.
Обете се разумни сами по себе. Заедно не беа.
Проблем 1: watchdog-от се трката со објавувањето
Watchdog-от работеше еднаш во минута и „лечеше“ на еден неуспешен health check. Објавувањето намерно ги дренира сервисите до 150 секунди — за тоа време health check треба да падне. Значи, watchdog-от се активираше за време на секое објавување.
Најчесто ја губеше трката безопасно. Еднаш ја доби: ја пренапиша compose датотеката (описот на тоа кои контејнери треба да работат) среде објавување и остави сервер без ниту еден gateway контејнер — додека задачата за објавување пријавуваше успех.
Поправките:
- Една брава. Објавувањето сега ја зема истата датотечна брава што ја користи bootstrap скриптата на серверот, така што watchdog-от и објавувањето никогаш не можат да работат истовремено.
- Четири удари, не еден. Watchdog-от бара четири последователни неуспешни проверки пред да дејствува. Сервис што се дренира повеќе не изгледа мртов.
- Една дефиниција. Compose датотеката сега има точно еден извор, објавен на серверите, наместо копии што можеа да се разликуваат.
- Провери, не верувај. Задачата за објавување повеќе не минува затоа што командата вратила резултат. Ги проверува API-то, оркестраторот, gateway-от и publish worker-от на секој сервер пред да може да успее.
Проблем 2: серверот заминуваше пред сообраќајот
Втората основа беше потивка. Кога објавувањето дренира сервер, серверот почнува да одговара на својата health крајна точка со 503, за балансерот да го извади од ротација. Нашиот го правеше тоа 5 секунди, па се исклучуваше.
Но ALB ја проверуваше исправноста само повремено и бараше неколку неуспешни проверки една по друга пред да престане да праќа на цел — до околу 60 секунди вкупно. Значи, околу 55 секунди балансерот продолжуваше да испраќа вистински барања на сервер што веќе не слушаше. Тоа беа нашите 502.
Поправката беше двете временски линии да се совпаѓаат:
- Target group-от сега проверува на секои 5 секунди и ја означува цел како нездрава по 2 неуспеси — околу 10 секунди да се забележи.
- Дренажата продолжува да опслужува вистински сообраќај 15 секунди откако почнува да пропаѓа health check-овите, така што секогаш има преклопување.
- Овие поставки живеат во скриптата што ја обезбедува инфраструктурата, не во конзола што некој ја кликнал еднаш.
Нека броевите се држат еден за друг
Временските ограничувања како овие зависат едно од друго. Дренажата мора да трае подолго од времето што му треба на балансерот да забележи; дренажата плус исклучувањето мора да заврши пред контејнерот да биде принудно убиен. Лесно е некој да промени еден број по шест месеци и тивко да го врати багот.
Затоа ги запишавме тие односи како тестови: дренажата мора да биде подолга од интервалот на health check-от помножен со прагот на неуспеси; времето за повторно означување како здрава мора да остане кратко; целата дренажа мора да се собере во грејс периодот на контејнерот за запирање. Промени еден број без останатите и CI паѓа. Ги мутиравме самите тестови — го менувавме секој број на погрешна вредност и проверувавме дали тест го фаќа.
Мерењето на објавување е лесно погрешно
Нашето прво мерење кажуваше дека чисто објавување е околу 3% расипано. Не беше. Броевме секој одговор различен од 200, а 503 од health крајна точка за време на дренажа не е неуспех — тоа е дренажата што си ја врши работата, велејќи му на балансерот да не го избира тој сервер додека сепак опслужува вистински барања.
Што корисниците го чувствуваат е 502: барањето не стигнало никаде. Броењето само на нив ја даде вистинската слика — 10, потоа 6, потоа 0 во три production објавувања.
Што научивме
- Самолекувањето треба да знае за планиран престој. Watchdog што не може да разликува објавување од пад на крајот ќе се бори со објавувањето.
- Дренажата е ракување. Серверот мора да продолжи да опслужува додека балансерот докажано не престане да праќа сообраќај, а тоа време се поставува на балансерот, не на серверот.
- Инвариантите кодирајте ги како тестови. Оперативните броеви се поместуваат. Тестовите што ги поврзуваат меѓусебно — не.
- Одлучете што мерите пред да мерите. „Грешки“ беше погрешна метрика; „барања што не стигнале никаде“ беше точната.
Ако објавувате апликација на Mythex, ништо од ова не ви е видливо — и во тоа е поентата. Поврзано: како вашата објавена апликација останува достапна додека ја објавувате повторно.