Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

27 април на конференцията Стачка 2019, като част от раздела „DevOps“, беше изнесен докладът „Автомащабиране и управление на ресурсите в Kubernetes“. Той говори за това как можете да използвате K8s, за да осигурите висока наличност на вашите приложения и да осигурите върхова производителност.

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

По традиция имаме удоволствието да ви представим видео на репортажа (44 минути, много по-информативен от статията) и основното резюме в текстов вид. Отивам!

Нека анализираме темата на доклада дума по дума и да започнем от края.

Kubernetes

Да кажем, че имаме Docker контейнери на нашия хост. За какво? За да се осигури повторяемост и изолация, което от своя страна позволява лесно и добро внедряване, CI/CD. Имаме много такива автомобили с контейнери.

Какво предоставя Kubernetes в този случай?

  1. Спираме да мислим за тези машини и започваме да работим с „облака“ клъстер от контейнери или шушулки (групи от контейнери).
  2. Нещо повече, ние дори не мислим за отделни капсули, а управляваме повечеопо-големи групи. Такива примитиви от високо ниво ни позволяват да кажем, че има шаблон за изпълнение на определено работно натоварване и ето необходимия брой екземпляри за изпълнението му. Ако впоследствие променим шаблона, всички екземпляри ще се променят.
  3. С декларативен API Вместо да изпълняваме поредица от конкретни команди, ние описваме „структурата на света“ (в YAML), която е създадена от Kubernetes. И отново: когато описанието се промени, действителният му дисплей също ще се промени.

Управление на ресурси

процесор

Нека стартираме nginx, php-fpm и mysql на сървъра. Тези услуги всъщност ще имат още повече работещи процеси, всеки от които изисква изчислителни ресурси:

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)
(числата на слайда са „папагали“, абстрактната нужда на всеки процес от изчислителна мощност)

За да се улесни работата с това, е логично процесите да се комбинират в групи (например всички процеси на nginx в една група „nginx“). Прост и очевиден начин да направите това е да поставите всяка група в контейнер:

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

За да продължите, трябва да запомните какво е контейнер (в Linux). Появата им стана възможна благодарение на три ключови функции в ядрото, внедрени преди доста време: възможности, именни пространства и c групи. И по-нататъшното развитие беше улеснено от други технологии (включително удобни „черупки“ като Docker):

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

В контекста на доклада ни интересува само c групи, тъй като контролните групи са част от функционалността на контейнерите (Docker и т.н.), която реализира управление на ресурсите. Процесите, комбинирани в групи, както искахме, са контролни групи.

Нека се върнем към изискванията на процесора за тези процеси, а сега и за групи от процеси:

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)
(Повтарям, че всички числа са абстрактен израз на нуждата от ресурси)

В същото време самият процесор има определен ограничен ресурс (в примера това е 1000), които може да липсват на всеки (сумата от нуждите на всички групи е 150+850+460=1460). Какво ще стане в този случай?

Ядрото започва да разпределя ресурси и го прави „справедливо“, като предоставя еднакво количество ресурси на всяка група. Но в първия случай има повече от необходимото (333>150), така че излишъкът (333-150=183) остава в резерв, който също се разпределя равномерно между два други контейнера:

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

В резултат на това първият контейнер имаше достатъчно ресурси, вторият – нямаше достатъчно ресурси, третият – нямаше достатъчно ресурси. Това е резултат от действия "честен" планировчик в Linux - CFS. Работата му може да се регулира с помощта на заданието тежести всеки от контейнерите. Например така:

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

Нека да разгледаме случая с липса на ресурси във втория контейнер (php-fpm). Всички ресурси на контейнера се разпределят по равно между процесите. В резултат на това основният процес работи добре, но всички работници се забавят, получавайки по-малко от половината от необходимото:

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

Ето как работи планировчикът на CFS. Освен това ще наричаме теглата, които присвояваме на контейнерите заявки. Защо това е така - вижте по-нататък.

Нека погледнем цялата ситуация от другата страна. Както знаете, всички пътища водят до Рим, а в случай на компютър - до процесора. Един процесор, много задачи - трябва ви светофар. Най-простият начин за управление на ресурсите е „светофар“: те дават на един процес фиксирано време за достъп до процесора, след това на следващия и т.н.

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

Този подход се нарича твърди квоти (твърдо ограничаване). Нека го запомним просто като граници. Ако обаче разпределите ограничения за всички контейнери, възниква проблем: mysql се движеше по пътя и в някакъв момент нуждата му от процесор приключи, но всички други процеси са принудени да чакат, докато процесорът празен.

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

Да се ​​върнем към ядрото на Linux и неговото взаимодействие с процесора - общата картина е следната:

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

cgroup има две настройки - по същество това са две прости "обратки", които ви позволяват да определите:

  1. тегло за контейнер (заявки) е акции;
  2. процентът от общото процесорно време за работа върху контейнерни задачи (ограничения) е дял.

Как да измерим CPU?

Има различни начини:

  1. Какво е папагали, никой не знае - трябва да преговаряте всеки път.
  2. интерес по-ясно, но относително: 50% сървър с 4 ядра и с 20 ядра са напълно различни неща.
  3. Можете да използвате вече споменатите тежести, които Линукс познава, но и те са относителни.
  4. Най-адекватният вариант е измерването на изчислителните ресурси в секунди. Тези. в секунди процесорно време спрямо секунди реално време: 1 секунда процесорно време беше дадена за 1 реална секунда - това е едно цяло процесорно ядро.

За да се говори още по-лесно, започнаха да мерят директно ядки, означавайки под тях същото процесорно време спрямо реалното. Тъй като Linux разбира теглата, но не толкова много процесорно време/ядра, беше необходим механизъм за превод от едно към друго.

Нека разгледаме един прост пример със сървър с 3 процесорни ядра, където на три подове ще бъдат дадени тегла (500, 1000 и 1500), които лесно се преобразуват в съответните части от разпределените им ядра (0,5, 1 и 1,5).

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

Ако вземете втори сървър, където ще има два пъти повече ядра (6), и поставите същите подове там, разпределението на ядрата може лесно да се изчисли чрез просто умножаване по 2 (съответно 1, 2 и 3). Но важен момент настъпва, когато на този сървър се появи четвърти pod, чието тегло за удобство ще бъде 3000. Той отнема част от ресурсите на процесора (половината ядра), а за останалите pods те се преизчисляват (наполовина):

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

Kubernetes и ресурси на процесора

В Kubernetes ресурсите на процесора обикновено се измерват в милиадракс, т.е. 0,001 ядра са взети като базово тегло. (Същото нещо в терминологията на Linux/cgroups се нарича CPU дял, въпреки че по-точно 1000 милиядра = 1024 CPU дяла.) K8s гарантира, че не поставя повече подове на сървъра, отколкото има CPU ресурси за сумата от теглата на всички подове.

как става това Когато добавите сървър към Kubernetes клъстер, се отчита колко CPU ядра има на разположение. И когато създава нов пакет, програмата за планиране на Kubernetes знае колко ядра ще са необходими на този пакет. По този начин pod ще бъде присвоен на сървър, където има достатъчно ядра.

Какво ще се случи, ако не заявката е посочена (т.е. подът няма определен брой ядра, от които се нуждае)? Нека да разберем как Kubernetes обикновено брои ресурсите.

За под можете да посочите както заявки (планировчик на CFS), така и ограничения (помните ли светофара?):

  • Ако те са определени равни, тогава на групата се присвоява QoS клас гарантирани. Този брой ядра винаги на разположение е гарантиран.
  • Ако заявката е по-малка от ограничението - клас QoS разрушаващ се. Тези. Очакваме под, например, винаги да използва 1 ядро, но тази стойност не е ограничение за него: понякога pod може да използва повече (когато сървърът има свободни ресурси за това).
  • Има и QoS клас най-добри усилия — включва точно тези шушулки, за които не е посочено искане. Ресурсите им се дават последни.

Память

С паметта ситуацията е подобна, но малко по-различна - все пак естеството на тези ресурси е различно. Най-общо аналогията е следната:

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

Нека да видим как заявките се изпълняват в паметта. Оставете подовете да живеят на сървъра, променяйки потреблението на памет, докато един от тях стане толкова голям, че паметта му свърши. В този случай OOM убиецът се появява и убива най-големия процес:

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

Това не винаги ни устройва, така че е възможно да регулираме кои процеси са важни за нас и не трябва да бъдат убивани. За да направите това, използвайте параметъра oom_score_adj.

Нека се върнем към QoS класовете на CPU и да направим аналогия със стойностите oom_score_adj, които определят приоритетите за потребление на памет за pods:

  • Най-ниската стойност на oom_score_adj за капсула - -998 - означава, че такава капсула трябва да бъде убита последна, това гарантирани.
  • Най-високата - 1000 - е най-добри усилия, такива шушулки се умъртвяват първо.
  • За да изчислите останалите стойности (разрушаващ се) има формула, чиято същност се свежда до факта, че колкото повече ресурси е поискала една капсула, толкова по-малка е вероятността да бъде убита.

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

Вторият "обрат" - лимит_в_байтове - за лимити. С него всичко е по-просто: ние просто задаваме максималното количество издадена памет и тук (за разлика от процесора) няма въпрос как да го измерим (памет).

Общо

Всяка капсула в Kubernetes е дадена requests и limits - двата параметъра за CPU и памет:

  1. въз основа на заявки работи Kubernetes Scheduler, който разпределя pods между сървърите;
  2. въз основа на всички параметри се определя QoS класът на под;
  3. Относителните тегла се изчисляват въз основа на заявките на процесора;
  4. планировчикът на CFS е конфигуриран въз основа на заявки на процесора;
  5. OOM killer е конфигуриран въз основа на заявки за памет;
  6. „светофар“ е конфигуриран въз основа на ограниченията на процесора;
  7. Въз основа на ограниченията на паметта, ограничение е конфигурирано за cgroup.

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

Като цяло тази картина отговаря на всички въпроси за това как се извършва основната част от управлението на ресурсите в Kubernetes.

Автоматично мащабиране

K8s клъстер-autoscaler

Нека си представим, че целият клъстер вече е зает и трябва да се създаде нова група. Въпреки че подът не може да се появи, той виси в състояние в очакване на. За да се появи, можем да свържем нов сървър към клъстера или... да инсталираме cluster-autoscaler, който ще го направи вместо нас: поръчайте виртуална машина от облачния доставчик (чрез API заявка) и я свържете към клъстера , след което подът ще бъде добавен.

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

Това е автоматично мащабиране на клъстера Kubernetes, което работи чудесно (според нашия опит). Въпреки това, както навсякъде другаде, тук има някои нюанси...

Докато увеличавахме размера на клъстера, всичко беше наред, но какво се случва, когато клъстерът започна да се освобождава? Проблемът е, че мигрирането на подове (за освобождаване на хостове) е много трудно технически и скъпо от гледна точка на ресурси. Kubernetes използва напълно различен подход.

Помислете за клъстер от 3 сървъра, който има разполагане. Има 6 pods: сега има 2 за всеки сървър. По някаква причина искахме да изключим един от сървърите. За целта ще използваме командата kubectl drain, който:

  • ще забрани изпращането на нови подове към този сървър;
  • ще изтрие съществуващи подове на сървъра.

Тъй като Kubernetes отговаря за поддържането на броя на подовете (6), това просто ще пресъздаде ги на други възли, но не и на този, който е деактивиран, тъй като той вече е маркиран като недостъпен за хостване на нови подове. Това е основна механика за Kubernetes.

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

Тук обаче също има нюанс. В подобна ситуация, за StatefulSet (вместо Deployment), действията ще бъдат различни. Сега вече имаме приложение със състояние - например три пода с MongoDB, единият от които има някакъв проблем (данните са повредени или друга грешка, която пречи на пода да стартира правилно). И ние отново решаваме да деактивираме един сървър. Какво ще се случи?

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

MongoDB бих могъл умира, защото се нуждае от кворум: за клъстер от три инсталации трябва да функционират поне две. Това обаче няма да се случи - благодарение на PodDisruptionBudget. Този параметър определя минималния необходим брой работещи капсули. Знаейки, че един от модулите MongoDB вече не работи, и виждайки, че PodDisruptionBudget е зададен за MongoDB minAvailable: 2, Kubernetes няма да ви позволи да изтриете под.

В крайна сметка: за да може движението (и всъщност повторното създаване) на pods да работи правилно, когато клъстерът бъде освободен, е необходимо да конфигурирате PodDisruptionBudget.

Хоризонтално мащабиране

Нека разгледаме друга ситуация. Има приложение, работещо като разполагане в Kubernetes. Потребителският трафик идва към неговите подове (например има три от тях) и ние измерваме определен индикатор в тях (да речем натоварване на процесора). Когато натоварването се увеличи, ние записваме това по график и увеличаваме броя на подовете за разпределяне на заявките.

Днес в Kubernetes това не е необходимо да се прави ръчно: автоматично увеличаване/намаляване на броя на подовете се конфигурира в зависимост от стойностите на измерените индикатори за натоварване.

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

Основните въпроси тук са: какво точно да меря и как да тълкувам получени стойности (за вземане на решение за промяна на броя на шушулките). Можете да измерите много:

Автоматично мащабиране и управление на ресурсите в Kubernetes (общ преглед и видео отчет)

Как да стане това технически - събиране на метрики и т.н. — Говорих подробно в доклада за Мониторинг и Kubernetes. И основният съвет за избор на оптимални параметри е експеримент!

Има USE метод (Наситеност при използване и грешки), чийто смисъл е следният. На каква основа има смисъл да се мащабира, например, php-fpm? Въз основа на факта, че работниците свършват, това е оползотворяване. И ако работниците са свършили и не се приемат нови връзки, това е вече насищане. И двата параметъра трябва да бъдат измерени и в зависимост от стойностите трябва да се извърши мащабиране.

Вместо заключение

Докладът има продължение: за вертикалното мащабиране и как да изберете правилните ресурси. Ще говоря за това в следващите видеоклипове нашия YouTube - абонирайте се, за да не пропуснете!

Видеоклипове и слайдове

Видео от представлението (44 минути):

Представяне на доклада:

PS

Други доклади за Kubernetes в нашия блог:

Източник: www.habr.com

Добавяне на нов коментар