Недостасувачката swap датотека што ги замрзна нашите сервери
На 24 септември, еден од нашите продукциски сервери престана да одговара. Load balancer-от го означи како нездрав, а нашата алатка за далечинско извршување на команди не можеше да стигне до него, но ништо не падна и ниту еден процес не беше убиен поради премногу меморија. Серверот трешеше (thrashing): без swap простор и со контејнери што смееја да користат повеќе меморија отколку што машината ја имаше, Linux го трошеше времето на избришување и повторно читање на датотеките што му требаа за да работи, и речиси не напредуваше. Додавањето swap датотека од 2 GB на секој сервер го реши проблемот. Поинтересната лекција беше за health checks.
Како изгледаше „замрзнувањето“
- Health check-овите на load balancer-от истекуваа на тој сервер.
- Командите испратени преку AWS Systems Manager останаа во статус „Pending“ и никогаш не се извршија.
- Во логовите немаше грешка од недостиг на меморија. Ниту еден процес не умре.
Пред една недела ја префрливме продукцијата на помали ARM сервери со по 2 GB RAM. Нашиот staging сервер, уште помал, се замрзна на ист начин додека распакуваше container image за време на деплој.
Зошто нема грешка за меморија?
Linux убива процес само кога навистина ќе ја снема меморијата што може да ја повика назад. Пред тој момент, тој ја враќа меморијата од page cache — копиите на датотеки што ги чува во RAM, вклучително и кодот на секоја програма што работи.
На нашите сервери, збирот на лимитите на меморија на контејнерите даваше околу 3,3 GB на машина со околу 1,8 GB употреблива меморија. Вообичаено не сите го користат својот лимит истовремено. Кога го користеа, кернелот постојано го исфрлаше кодот на програмите од кешот за да направи место, а потоа мораше повторно да го чита од диск во моментот кога тој код повторно ќе се изврши. Тој циклус — thrashing — може машината да ја направи речиси целосно неодѕивна, а сепак технички никогаш да не остане без меморија. Затоа ништо не се убива и ништо не се опоравува.
Без swap, кернелот нема каде да стави меморија што е доделена, но ретко се користи, па мора да ја земе од кешот.
Поправката
Скриптата за подигање на серверот сега создава swap датотека од 2 GB со ниска swappiness вредност (10) при секое подигање или поправка на сервер. Ниската swappiness значи дека кернелот и понатаму претпочита да ја држи работната меморија во RAM, но ретко допирани страници можат да се префрлат на диск, наместо да ја исфрлат кодот на програмите од кешот. Ја применивме во живо на двата продукциски сервера и на staging, а деплој пајплајнот ја објавува скриптата за подигање при секој продукциски деплој, за да ја добие и заменетиот сервер.
Делот што нè загрижи повеќе: нездравиот сервер остана
Нашата група сервери користеше EC2 health checks („дали машината работи?“), а не load balancer health checks („дали одговара на барања?“). Значи, сервер од кој load balancer-от се откажал беше здрав за групата. Никогаш не би бил заменет. Продукцијата тивко би работела на еден сервер додека некој не забележи и не го рестартира другиот.
Префрлањето на групата на load balancer health checks звучи како очигледно решение, но за нас не е едноставно: при деплој, серверите намерно одговараат со грешка на својата health endpoint точка, за balancer-от да ги извлече од сервис (видете како дојдовме до деплои без 502 грешки). Група што ги заменува серверите на тој сигнал би ги заменувала среде секој деплој.
Затоа за сега додадовме чекор во нашата листа за деплој: по секој деплој, проверуваме здравјето на секој target на load balancer-от, а не само дали деплој job-от поминал. Ако командите до некој сервер се во „Pending“, тој трешти — се рестартира; веќе е надвор од сервис.
Што извлековме
- „Не паѓа“ не значи „здрав“. Thrashing не дава грешка, само толку голема бавност што машината престанува да одговара.
- Малите сервери имаат потреба од swap. Дури и мал, со ниска swappiness, му дава на кернелот место каде да ја стави ладната меморија.
- Лимитите на меморија чиј збир ја надминува машината се облог. Понекогаш го губите. Знајте кога сте го направиле.
- Знајте на кој health check се потпира вашето автоматско скалирање. Health check што само прашува „дали VM-от е вклучен?“ нема да замени сервер што престанал да работи.