Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес
Оваа статија ќе ви помогне да разберете како функционира балансирањето на оптоварување во Kubernetes, што се случува при скалирање на долготрајните врски и зошто треба да размислите за балансирање од страна на клиентот ако користите HTTP/2, gRPC, RSockets, AMQP или други долготрајни протоколи . 

Малку за тоа како сообраќајот се редистрибуира во Кубернетес 

Kubernetes обезбедува две практични апстракции за распоредување апликации: Услуги и распоредувања.

Распоредувањата опишуваат како и колку копии од вашата апликација треба да се извршуваат во даден момент. Секоја апликација е распоредена како Pod и ѝ е доделена IP адреса.

Услугите се слични по функција на балансерот на оптоварување. Тие се дизајнирани да дистрибуираат сообраќај низ повеќе мешунки.

Ајде да видиме како изгледа.

  1. На дијаграмот подолу можете да видите три примери на иста апликација и балансер на оптоварување:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  2. Балансерот на оптоварување се нарекува Service и му е доделена IP адреса. Секое дојдовно барање се пренасочува на еден од подлогите:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

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

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  4. На секој pod му е доделена своја IP адреса:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

Корисно е да се размислува за услугите како збирка од IP адреси. Секој пат кога ќе пристапите до услугата, една од IP-адресите се избира од списокот и се користи како адреса на дестинација.

Изгледа вака.

  1. Барањето curl 10.96.45.152 е примено до услугата:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  2. Услугата избира една од трите адреси како дестинација:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  3. Сообраќајот е пренасочен кон одредена подлога:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

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

Кога предниот дел ќе поднесе барање до заднината, не треба да знае точно колку подови опслужува заднината: може да има еден, десет или сто.

Исто така, предниот дел не знае ништо за адресите на подлогите што го служат заднината.

Кога предниот дел ќе поднесе барање до задниот дел, тој ја користи IP адресата на услугата за заднина, која не се менува.

Вака изгледа.

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

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  2. Услугата избира еден од подлогите за заднина како дестинација:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  3. Сообраќајот оди од Под 1 до Под 5, избран од услугата:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  4. Под 1 не знае точно колку мешунки како под 5 се скриени зад услугата:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

Но, како точно услугата ги дистрибуира барањата? Се чини дека се користи кружно балансирање? Ајде да го сфатиме. 

Балансирање во услугите на Кубернетес

Услугите на Кубернетс не постојат. Нема процес за услугата на која и е доделена IP адреса и порта.

Можете да го потврдите ова со најавување во кој било јазол во кластерот и извршување на командата netstat -ntlp.

Нема да можете ни да ја пронајдете IP адресата доделена на услугата.

IP адресата на услугата се наоѓа во контролниот слој, во контролорот и е снимена во базата на податоци - etcd. Истата адреса се користи од друга компонента - kube-proxy.
Kube-proxy добива список со IP адреси за сите услуги и генерира збир на правила iptables на секој јазол во кластерот.

Овие правила велат: „Ако ја видиме IP-адресата на услугата, треба да ја измениме одредишната адреса на барањето и да ја испратиме на една од местата“.

IP адресата на услугата се користи само како влезна точка и не се опслужува со никаков процес што ја слуша таа IP адреса и порта.

Ајде да го погледнеме ова

  1. Размислете за кластер од три јазли. Секој јазол има мешунки:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  2. Врзаните мешунки обоени во беж се дел од услугата. Бидејќи услугата не постои како процес, таа е прикажана во сиво:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  3. Првиот подлог бара услуга и мора да оди на една од поврзаните подлоги:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  4. Но, услугата не постои, процесот не постои. Како работи?

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  5. Пред барањето да го напушти јазолот, тој поминува низ правилата iptables:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  6. Правилата iptables знаат дека услугата не постои и ја заменуваат нејзината IP адреса со една од IP адресите на подовите поврзани со таа услуга:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  7. Барањето добива валидна IP адреса како дестинација и се обработува нормално:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  8. Во зависност од мрежната топологија, барањето на крајот стигнува до подлогата:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

Дали iptables може да го вчита балансот?

Не, iptables се користат за филтрирање и не се дизајнирани за балансирање.

Сепак, можно е да се напише збир на правила кои функционираат како псевдобалансер.

И токму тоа е имплементирано во Кубернетес.

Ако имате три подлоги, kube-proxy ќе ги напише следниве правила:

  1. Изберете го првиот под со веројатност од 33%, во спротивно одете на следното правило.
  2. Изберете го вториот со веројатност од 50%, во спротивно одете на следното правило.
  3. Изберете го третиот под.

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

Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

И нема гаранција дека Pod 2 ќе биде избран следниот по Pod 1.

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

Сега кога разбирате како функционираат услугите, ајде да погледнеме поинтересни сценарија за услуги.

Долготрајните врски во Kubernetes стандардно не се зголемуваат

Секое HTTP барање од предниот дел до задниот дел се опслужува со посебна TCP конекција, која се отвора и затвора.

Ако предниот дел испрати 100 барања во секунда до задниот дел, тогаш 100 различни TCP конекции се отвораат и затвораат.

Можете да го намалите времето за обработка на барањата и оптоварувањето со отворање на една TCP конекција и користење за сите последователни HTTP барања.

Протоколот HTTP има карактеристика наречена HTTP keep-live, или повторна употреба на конекцијата. Во овој случај, една TCP конекција се користи за испраќање и примање на повеќе HTTP барања и одговори:

Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

Оваа функција не е стандардно овозможена: и серверот и клиентот мора да бидат соодветно конфигурирани.

Самото поставување е едноставно и достапно за повеќето програмски јазици и средини.

Еве неколку линкови до примери на различни јазици:

Што ќе се случи ако користиме keep-alive во услуга на Kubernetes?
Да претпоставиме дека и frontend и backend поддржуваат keep-alive.

Имаме една копија од предниот дел и три копии од заднината. Предниот дел го прави првото барање и отвора TCP конекција со задниот дел. Барањето стигнува до услугата, еден од подлогите за заднина е избран како дестинација. Заднината испраќа одговор, а предниот дел го прима.

За разлика од вообичаената ситуација кога TCP-врската е затворена по добивањето одговор, таа сега е отворена за дополнителни HTTP барања.

Што се случува ако предниот дел испрати повеќе барања до задниот дел?

За да се препратат овие барања, ќе се користи отворена TCP конекција, сите барања ќе одат во истиот заден дел каде што отишло првото барање.

Дали iptables не треба да го редистрибуира сообраќајот?

Не во овој случај.

Кога ќе се креира TCP конекција, таа поминува низ правилата на iptables, кои избираат специфичен бекенд каде ќе оди сообраќајот.

Бидејќи сите последователни барања се на веќе отворена TCP конекција, правилата iptables повеќе не се повикуваат.

Ајде да видиме како изгледа.

  1. Првиот pod испраќа барање до услугата:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  2. Веќе знаете што ќе се случи следно. Услугата не постои, но постојат правила на iptables кои ќе го обработат барањето:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  3. Еден од подлогата за заднина ќе биде избрана како адреса на дестинација:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  4. Барањето стигнува до подлогата. Во овој момент, ќе се воспостави постојана TCP врска помеѓу двата подлога:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  5. Секое следно барање од првиот под ќе помине преку веќе воспоставената врска:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

Резултатот е побрзо време на одговор и поголема пропусност, но ја губите способноста да го зголемите задниот дел.

Дури и ако имате две подлоги во задниот дел, со постојана врска, сообраќајот секогаш ќе оди на еден од нив.

Дали може да се поправи ова?

Бидејќи Kubernetes не знае како да ги балансира постојаните врски, оваа задача паѓа на вас.

Услугите се збирка од IP адреси и порти наречени крајни точки.

Вашата апликација може да добие листа на крајни точки од услугата и да одлучи како да ги дистрибуира барањата меѓу нив. Може да отворите постојана врска со секоја подлога и да барате рамнотежа помеѓу овие конекции со помош на круг-робин.

Или аплицирајте повеќе сложени алгоритми за балансирање.

Кодот од клиентот кој е одговорен за балансирање треба да ја следи оваа логика:

  1. Добијте листа на крајни точки од услугата.
  2. Отворете постојана врска за секоја крајна точка.
  3. Кога треба да се поднесе барање, користете една од отворените врски.
  4. Редовно ажурирајте ја листата на крајни точки, креирајте нови или затворете ги старите постојани врски доколку списокот се промени.

Вака ќе изгледа.

  1. Наместо првиот подлог да го испраќа барањето до услугата, можете да ги балансирате барањата на страната на клиентот:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  2. Треба да напишете код кој прашува кои подлоги се дел од услугата:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  3. Откако ќе го имате списокот, зачувајте го на страната на клиентот и користете го за да се поврзете со подлогите:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

  4. Вие сте одговорни за алгоритмот за балансирање на оптоварување:

    Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

Сега се поставува прашањето: дали овој проблем се однесува само на HTTP keep-live?

Балансирање на оптоварување од страна на клиентот

HTTP не е единствениот протокол што може да користи постојани TCP конекции.

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

Наместо тоа, се отвора и користи постојана TCP врска со базата на податоци.

Ако вашата база на податоци е распоредена на Kubernetes и пристапот е обезбеден како услуга, тогаш ќе наидете на истите проблеми опишани во претходниот дел.

Една реплика на базата на податоци ќе биде пооптоварена од другите. Kube-proxy и Kubernetes нема да помогнат во балансирање на врските. Мора да внимавате да ги балансирате прашањата во вашата база на податоци.

Во зависност од тоа која библиотека ја користите за поврзување со базата на податоци, може да имате различни опции за решавање на овој проблем.

Подолу е пример за пристап до кластер на база на податоци MySQL од Node.js:

var mysql = require('mysql');
var poolCluster = mysql.createPoolCluster();

var endpoints = /* retrieve endpoints from the Service */

for (var [index, endpoint] of endpoints) {
  poolCluster.add(`mysql-replica-${index}`, endpoint);
}

// Make queries to the clustered MySQL database

Постојат многу други протоколи кои користат постојани TCP конекции:

  • WebSockets и обезбедени WebSockets
  • HTTP / 2
  • gRPC
  • RSockets
  • AMQP

Веќе треба да сте запознаени со повеќето од овие протоколи.

Но, ако овие протоколи се толку популарни, зошто не постои стандардизирано решение за балансирање? Зошто треба да се промени логиката на клиентот? Дали има домашно решение на Кубернетес?

Kube-proxy и iptables се дизајнирани да ги покриваат најчестите случаи кога се распоредуваат во Kubernetes. Ова е за погодност.

Ако користите веб-услуга што изложува REST API, имате среќа - во овој случај, постојаните TCP конекции не се користат, можете да користите која било услуга на Kubernetes.

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

Сепак, сигурно има опции кои можат да помогнат.

Балансирање на долготрајните врски во Кубернетес

Постојат четири типа на услуги во Kubernetes:

  1. ClusterIP
  2. NodePort
  3. LoadBalancer
  4. Безглав

Првите три услуги работат врз основа на виртуелна IP адреса, која се користи од kube-proxy за да се изградат правила за iptables. Но, основната основа на сите услуги е услуга без глава.

Услугата без глава нема никаква IP адреса поврзана со неа и обезбедува само механизам за преземање листа на IP адреси и порти на подовите (крајните точки) поврзани со неа.

Сите услуги се засноваат на услугата без глава.

Услугата ClusterIP е услуга без глава со некои додатоци: 

  1. Слојот за управување му доделува IP адреса.
  2. Kube-proxy ги генерира потребните правила за iptables.

На овој начин можете да го игнорирате kube-proxy и директно да ја користите листата на крајни точки добиени од услугата без глава за да ја вчитате рамнотежата на вашата апликација.

Но, како можеме да додадеме слична логика на сите апликации распоредени во кластерот?

Ако вашата апликација е веќе распоредена, оваа задача може да изгледа невозможна. Сепак, постои алтернативна опција.

Service Mesh ќе ви помогне

Веројатно веќе сте забележале дека стратегијата за балансирање на оптоварување од страна на клиентот е сосема стандардна.

Кога апликацијата ќе започне, тоа:

  1. Добива листа на IP адреси од услугата.
  2. Отвора и одржува базен за поврзување.
  3. Периодично го ажурира базенот со додавање или отстранување крајни точки.

Откако апликацијата сака да поднесе барање, таа:

  1. Избира достапна врска користејќи некоја логика (на пр. круг-робин).
  2. Го извршува барањето.

Овие чекори работат и за WebSockets, gRPC и AMQP врски.

Можете да ја одделите оваа логика во посебна библиотека и да ја користите во вашите апликации.

Сепак, наместо тоа, можете да користите сервисни мрежи како Istio или Linkerd.

Service Mesh ја зголемува вашата апликација со процес кој:

  1. Автоматски бара сервисни IP адреси.
  2. Тестира врски како што се WebSockets и gRPC.
  3. Ги балансира барањата користејќи го правилниот протокол.

Service Mesh помага во управувањето со сообраќајот во кластерот, но тоа е доста интензивно за ресурси. Други опции се користење библиотеки од трети страни како Netflix Ribbon или програмабилни прокси како Envoy.

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

Можете да изберете да не користите балансирање на оптоварување и сепак да не забележувате никакви промени. Ајде да погледнеме неколку работни сценарија.

Ако имате повеќе клиенти отколку сервери, ова не е толку голем проблем.

Да речеме дека има пет клиенти кои се поврзуваат на два сервери. Дури и ако нема балансирање, ќе се користат двата сервери:

Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

Врските може да не се рамномерно распределени: можеби четири клиенти поврзани на истиот сервер, но има добри шанси да се користат и двата сервери.

Она што е попроблематично е спротивното сценарио.

Ако имате помалку клиенти и повеќе сервери, вашите ресурси може да бидат недоволно искористени и ќе се појави потенцијално тесно грло.

Да речеме дека има два клиенти и пет сервери. Во најдобар случај, ќе има две постојани врски со два од пет сервери.

Останатите сервери ќе бидат неактивен:

Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес

Ако овие два сервери не можат да се справат со барањата на клиентите, хоризонталното скалирање нема да помогне.

Заклучок

Услугите на Kubernetes се дизајнирани да работат во повеќето стандардни сценарија за веб-апликации.

Меѓутоа, штом ќе започнете да работите со апликативни протоколи кои користат постојани TCP конекции, како што се бази на податоци, gRPC или WebSockets, услугите веќе не се соодветни. Kubernetes не обезбедува внатрешни механизми за балансирање на постојаните TCP конекции.

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

Превод подготвен од тимот Kubernetes aaS од Mail.ru.

Што друго да се прочита на темата:

  1. Три нивоа на автоматско скалирање во Kubernetes и како ефикасно да ги користите
  2. Kubernetes во духот на пиратеријата со шаблон за имплементација.
  3. Нашиот Телеграмски канал за дигитална трансформација.

Извор: www.habr.com

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