Ограничувања на процесорот и агресивно пригушување во Кубернетес

Забелешка. превод.: Оваа впечатлива историја на Omio — европски агрегатор за патувања — ги носи читателите од основната теорија до фасцинантните практични сложености на конфигурацијата на Kubernetes. Запознавањето со таквите случаи помага не само да ги проширите вашите хоризонти, туку и да спречите нетривијални проблеми.

Ограничувања на процесорот и агресивно пригушување во Кубернетес

Дали некогаш ви се случило некоја апликација да се заглави, да престанете да одговарате на здравствените проверки и да не можете да сфатите зошто? Едно можно објаснување е поврзано со ограничувањата на квотата за ресурси на процесорот. Ова е она за што ќе зборуваме во оваа статија.

TL; ДР:
Силно препорачуваме да ги оневозможите ограничувањата на процесорот во Kubernetes (или да ги оневозможите квотите на CFS во Kubelet) ако користите верзија на кернелот на Linux со грешка во квотата на CFS. Во јадрото постои сериозни и добро познати бубачка што доведува до прекумерно гаснење и доцнење
.

Во Омио целата инфраструктура е управувана од Kubernetes. Сите наши оптоварувања со статус и без државјанство работат исклучиво на Kubernetes (ние користиме Google Kubernetes Engine). Во последните шест месеци почнавме да забележуваме случајни забавувања. Апликациите се замрзнуваат или престануваат да реагираат на здравствени проверки, ја губат врската со мрежата итн. Ваквото однесување долго не збунуваше и конечно решивме сериозно да го сфатиме проблемот.

Резиме на статијата:

  • Неколку зборови за контејнерите и Кубернетите;
  • Како се имплементираат барањата и ограничувањата на процесорот;
  • Како функционира ограничувањето на процесорот во средини со повеќе јадра;
  • Како да го следите пригушувањето на процесорот;
  • Решавање на проблемот и нијанси.

Неколку зборови за контејнерите и Кубернетите

Kubernetes во суштина е модерен стандард во инфраструктурниот свет. Неговата главна задача е оркестрација на контејнери.

Контејнери

Во минатото, моравме да создаваме артефакти како Java JARs/WARs, Python Eggs или извршни датотеки за да се извршуваат на серверите. Меѓутоа, за да функционираат, требаше да се направи дополнителна работа: инсталирање на опкружувањето за извршување (Java/Python), поставување на потребните датотеки на вистинските места, обезбедување компатибилност со одредена верзија на оперативниот систем итн. Со други зборови, требаше да се посвети внимателно внимание на управувањето со конфигурацијата (што често беше извор на расправии помеѓу програмерите и системските администратори).

Контејнерите сменија сè. Сега артефактот е слика на контејнер. Може да се претстави како еден вид проширена извршна датотека што ја содржи не само програмата, туку и целосно опкружување за извршување (Java/Python/...), како и потребните датотеки/пакети, претходно инсталирани и подготвени за трчај. Контејнерите може да се распоредат и работат на различни сервери без никакви дополнителни чекори.

Дополнително, контејнерите работат во сопствена средина за песок. Тие имаат свој виртуелен мрежен адаптер, свој датотечен систем со ограничен пристап, своја хиерархија на процеси, свои ограничувања на процесорот и меморијата итн. Сето ова е имплементирано благодарение на специјалниот потсистем на кернелот Линукс - именски простори.

Кубернети

Како што беше кажано претходно, Кубернетес е оркестратор на контејнери. Работи вака: му давате голем број машини, а потоа велите: „Еј, Кубернетес, ајде да лансираме десет примероци од мојот контејнер со 2 процесори и по 3 GB меморија и да ги задржиме да работат!“ Кубернетес ќе се погрижи за останатото. Ќе најде слободен капацитет, ќе лансира контејнери и ќе ги рестартира доколку е потребно, ќе објави ажурирања при менување на верзии итн. Во суштина, Kubernetes ви овозможува да ја апстрахирате хардверската компонента и прави широк спектар на системи погодни за распоредување и извршување на апликации.

Ограничувања на процесорот и агресивно пригушување во Кубернетес
Кубернети од гледна точка на лаик

Што се барања и ограничувања во Кубернетес

Во ред, ги опфативме контејнерите и Кубернетите. Исто така, знаеме дека повеќе контејнери можат да живеат на иста машина.

Може да се направи аналогија со комунален стан. Се зема пространа просторија (машини/единици) и се издава на неколку станари (контејнери). Kubernetes делува како realtor. Се поставува прашањето, како да се чуваат станарите од конфликти едни со други? Што ако некој од нив, да речеме, реши да ја позајми бањата за половина ден?

Тука влегуваат во игра барањата и ограничувањата. Процесорот Побара потребни исклучиво за планирање. Ова е нешто како „листа на желби“ на контејнерот и се користи за избор на најсоодветен јазол. Во исто време процесорот Ограничете може да се спореди со договор за изнајмување - штом ќе избереме единица за контејнерот, на не може оди подалеку од утврдените граници. И тука настанува проблемот...

Како барањата и ограничувањата се имплементираат во Kubernetes

Kubernetes користи механизам за пригушување (прескокнување циклуси на часовникот) вграден во кернелот за да ги имплементира границите на процесорот. Ако апликацијата ја надмине границата, овозможено е пригушување (т.е. добива помалку циклуси на процесорот). Барањата и ограничувањата за меморија се организирани поинаку, така што полесно се откриваат. За да го направите ова, само проверете го статусот на последното рестартирање на подлогата: дали е „OOMKilled“. Пригушувањето на процесорот не е толку едноставно, бидејќи K8s ги прави достапни метриките само по употреба, а не по cгрупи.

Барање процесорот

Ограничувања на процесорот и агресивно пригушување во Кубернетес
Како се имплементира барањето на процесорот

За едноставност, ајде да го разгледаме процесот користејќи машина со 4-јадрен процесор како пример.

K8s користи механизам за контролна група (cgroups) за контрола на распределбата на ресурсите (меморија и процесор). За него е достапен хиерархиски модел: детето ги наследува границите на родителската група. Деталите за дистрибуцијата се зачувани во виртуелен датотечен систем (/sys/fs/cgroup). Во случај на процесор ова е /sys/fs/cgroup/cpu,cpuacct/*.

K8s користи датотека cpu.share да ги распредели ресурсите на процесорот. Во нашиот случај, root cгрупата добива 4096 акции од ресурсите на процесорот - 100% од достапната моќност на процесорот (1 јадро = 1024; ова е фиксна вредност). Коренската група ги дистрибуира ресурсите пропорционално во зависност од уделот на потомците регистрирани во cpu.share, а тие пак истото го прават со своите потомци итн. На типичен Kubernetes јазол, root cгрупата има три деца: system.slice, user.slice и kubepods. Првите две подгрупи се користат за дистрибуција на ресурси помеѓу критичните оптоварувања на системот и корисничките програми надвор од K8s. Последно - kubepods — создаден од Kubernetes за дистрибуција на ресурси помеѓу подлоги.

Дијаграмот погоре покажува дека првата и втората подгрупа ја добиле секоја 1024 акции, со распределена подгрупата kuberpod 4096 акции Како е ова можно: на крајот на краиштата, root групата има пристап само до 4096 акции, а збирот на акциите на нејзините потомци значително го надминува овој број (6144)? Поентата е дека вредноста има логична смисла, па распоредувачот на Линукс (CFS) ја користи за пропорционална распределба на ресурсите на процесорот. Во нашиот случај, првите две групи добиваат 680 реални акции (16,6% од 4096), а кубепод ги добива останатите 2736 акции Во случај на застој, првите две групи нема да ги користат доделените ресурси.

За среќа, распоредувачот има механизам да избегне трошење неискористени ресурси на процесорот. Тој го пренесува капацитетот „неактивен“ на глобален базен, од кој се дистрибуира до групи на кои им е потребна дополнителна процесорска моќност (преносот се случува во серии за да се избегнат загуби од заокружување). Сличен метод се применува за сите потомци на потомци.

Овој механизам обезбедува правична распределба на моќта на процесорот и гарантира дека никој процес не „краде“ ресурси од други.

Ограничување на процесорот

И покрај фактот дека конфигурациите на ограничувањата и барањата во K8 изгледаат слично, нивната имплементација е радикално различна: ова најмногу погрешно а најмалку документиран дел.

K8s се вклучува Механизам за квоти на CFS да се имплементираат ограничувања. Нивните поставки се наведени во датотеките cfs_period_us и cfs_quota_us во директориумот cgroup (датотеката исто така се наоѓа таму cpu.share).

За разлика од cpu.share, квотата се заснова на временски период, а не на достапната моќност на процесорот. cfs_period_us го одредува времетраењето на периодот (епоха) - секогаш е 100000 μs (100 ms). Постои опција за промена на оваа вредност во K8s, но засега е достапна само во алфа. Распоредувачот ја користи епохата за да ги рестартира искористените квоти. Втора датотека cfs_quota_us, го одредува расположливото време (квота) во секоја епоха. Имајте на ум дека тоа е исто така наведено во микросекунди. Квотата може да ја надмине должината на епохата; со други зборови, може да биде поголемо од 100 ms.

Ајде да погледнеме две сценарија за машини со 16 јадра (најчестиот тип на компјутер што го имаме во Omio):

Ограничувања на процесорот и агресивно пригушување во Кубернетес
Сценарио 1: 2 нишки и ограничување од 200 ms. Без пригушување

Ограничувања на процесорот и агресивно пригушување во Кубернетес
Сценарио 2: 10 нишки и ограничување од 200 ms. Задушувањето започнува по 20 ms, пристапот до ресурсите на процесорот се обновува по уште 80 ms

Да речеме дека сте го поставиле ограничувањето на процесорот на 2 кернели; Kubernetes ќе ја преведе оваа вредност на 200 ms. Ова значи дека контејнерот може да користи максимум 200ms време на процесорот без пригушување.

И тука започнува забавата. Како што споменавме погоре, достапната квота е 200 ms. Ако работите паралелно десет нишки на машина со 12 јадра (видете ја илустрацијата за сценарио 2), додека сите други мешунки се неактивен, квотата ќе биде исцрпена за само 20 ms (бидејќи 10 * 20 ms = 200 ms), а сите нишки од оваа мешунка ќе висат » (гас) за следните 80 ms. Веќе споменатите грешка на распоредувачот, поради што доаѓа до прекумерно гаснење и контејнерот не може да ја исполни ни постоечката квота.

Како да се оцени пригушувањето во мешунките?

Само најавете се на подлогата и извршете cat /sys/fs/cgroup/cpu/cpu.stat.

  • nr_periods — вкупниот број на периоди на распоред;
  • nr_throttled — број на пригушени периоди во составот nr_periods;
  • throttled_time — кумулативно пригушено време во наносекунди.

Ограничувања на процесорот и агресивно пригушување во Кубернетес

Што навистина се случува?

Како резултат на тоа, добиваме високо пригушување во сите апликации. Понекогаш тој е внатре еден и пол пати посилно од пресметаното!

Ова води до разни грешки - неуспеси во проверката на подготвеноста, замрзнување на контејнерот, прекини на мрежната врска, тајм-аут во повиците на услугата. Ова на крајот резултира со зголемена латентност и повисоки стапки на грешки.

Одлука и последици

Сè е едноставно овде. Ги напуштивме ограничувањата на процесорот и почнавме да го ажурираме кернелот на ОС во кластери до најновата верзија, во која грешката беше поправена. Бројот на грешки (HTTP 5xx) во нашите услуги веднаш значително се намали:

Грешки HTTP 5xx

Ограничувања на процесорот и агресивно пригушување во Кубернетес
Грешки HTTP 5xx за една критична услуга

Време на одговор p95

Ограничувања на процесорот и агресивно пригушување во Кубернетес
Латентност на критичните барања за услуга, 95-ти перцентил

Оперативни трошоци

Ограничувања на процесорот и агресивно пригушување во Кубернетес
Број на часови потрошени на пример

Што е улов?

Како што е наведено на почетокот на статијата:

Може да се направи аналогија со комунален стан... Кубернетес делува како агент. Но, како да ги задржите станарите од конфликти едни со други? Што ако некој од нив, да речеме, реши да ја позајми бањата за половина ден?

Еве ја финтата. Еден невнимателен контејнер може да ги изеде сите достапни ресурси на процесорот на машината. Ако имате паметен куп апликации (на пример, JVM, Go, Node VM се правилно конфигурирани), тогаш ова не е проблем: можете да работите во такви услови долго време. Но, ако апликациите се слабо оптимизирани или воопшто не се оптимизирани (FROM java:latest), ситуацијата може да излезе од контрола. Во Omio имаме автоматизирани основни Dockerfiles со соодветни стандардни поставки за главниот јазичен оџак, така што овој проблем не постоеше.

Препорачуваме следење на метриката УПОТРЕБА (користење, заситеност и грешки), доцнења на API и стапки на грешки. Осигурајте се дека резултатите ги исполнуваат очекувањата.

референци

Ова е нашата приказна. Следниве материјали во голема мера помогнаа да се разбере што се случува:

Извештаи за бубачки на Kubernetes:

Дали сте наишле на слични проблеми во вашата пракса или имате искуство поврзано со пригушување во производствени средини со контејнери? Споделете ја вашата приказна во коментарите!

PS од преведувач

Прочитајте и на нашиот блог:

Извор: www.habr.com

Додадете коментар