ProHoster > блог > Администрација > Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес
Балансирање на оптоварување и скалирање на долготрајните врски во Кубернетес
Оваа статија ќе ви помогне да разберете како функционира балансирањето на оптоварување во Kubernetes, што се случува при скалирање на долготрајните врски и зошто треба да размислите за балансирање од страна на клиентот ако користите HTTP/2, gRPC, RSockets, AMQP или други долготрајни протоколи .
Малку за тоа како сообраќајот се редистрибуира во Кубернетес
Kubernetes обезбедува две практични апстракции за распоредување апликации: Услуги и распоредувања.
Распоредувањата опишуваат како и колку копии од вашата апликација треба да се извршуваат во даден момент. Секоја апликација е распоредена како Pod и ѝ е доделена IP адреса.
Услугите се слични по функција на балансерот на оптоварување. Тие се дизајнирани да дистрибуираат сообраќај низ повеќе мешунки.
Ајде да видиме како изгледа.
На дијаграмот подолу можете да видите три примери на иста апликација и балансер на оптоварување:
Балансерот на оптоварување се нарекува Service и му е доделена IP адреса. Секое дојдовно барање се пренасочува на еден од подлогите:
Сценариото за распоредување го одредува бројот на примероци на апликацијата. Речиси никогаш нема да мора да се проширите директно под:
На секој pod му е доделена своја IP адреса:
Корисно е да се размислува за услугите како збирка од IP адреси. Секој пат кога ќе пристапите до услугата, една од IP-адресите се избира од списокот и се користи како адреса на дестинација.
Изгледа вака.
Барањето curl 10.96.45.152 е примено до услугата:
Услугата избира една од трите адреси како дестинација:
Сообраќајот е пренасочен кон одредена подлога:
Ако вашата апликација се состои од преден и заден дел, тогаш ќе имате и услуга и распоредување за секој.
Кога предниот дел ќе поднесе барање до заднината, не треба да знае точно колку подови опслужува заднината: може да има еден, десет или сто.
Исто така, предниот дел не знае ништо за адресите на подлогите што го служат заднината.
Кога предниот дел ќе поднесе барање до задниот дел, тој ја користи IP адресата на услугата за заднина, која не се менува.
Вака изгледа.
Под 1 бара внатрешна компонента за заднина. Наместо да избере специфичен за задниот дел, тој испраќа барање до услугата:
Услугата избира еден од подлогите за заднина како дестинација:
Сообраќајот оди од Под 1 до Под 5, избран од услугата:
Под 1 не знае точно колку мешунки како под 5 се скриени зад услугата:
Но, како точно услугата ги дистрибуира барањата? Се чини дека се користи кружно балансирање? Ајде да го сфатиме.
Балансирање во услугите на Кубернетес
Услугите на Кубернетс не постојат. Нема процес за услугата на која и е доделена IP адреса и порта.
Можете да го потврдите ова со најавување во кој било јазол во кластерот и извршување на командата netstat -ntlp.
Нема да можете ни да ја пронајдете IP адресата доделена на услугата.
IP адресата на услугата се наоѓа во контролниот слој, во контролорот и е снимена во базата на податоци - etcd. Истата адреса се користи од друга компонента - kube-proxy.
Kube-proxy добива список со IP адреси за сите услуги и генерира збир на правила iptables на секој јазол во кластерот.
Овие правила велат: „Ако ја видиме IP-адресата на услугата, треба да ја измениме одредишната адреса на барањето и да ја испратиме на една од местата“.
IP адресата на услугата се користи само како влезна точка и не се опслужува со никаков процес што ја слуша таа IP адреса и порта.
Ајде да го погледнеме ова.
Размислете за кластер од три јазли. Секој јазол има мешунки:
Врзаните мешунки обоени во беж се дел од услугата. Бидејќи услугата не постои како процес, таа е прикажана во сиво:
Првиот подлог бара услуга и мора да оди на една од поврзаните подлоги:
Но, услугата не постои, процесот не постои. Како работи?
Пред барањето да го напушти јазолот, тој поминува низ правилата iptables:
Правилата iptables знаат дека услугата не постои и ја заменуваат нејзината IP адреса со една од IP адресите на подовите поврзани со таа услуга:
Барањето добива валидна IP адреса како дестинација и се обработува нормално:
Во зависност од мрежната топологија, барањето на крајот стигнува до подлогата:
Дали iptables може да го вчита балансот?
Не, iptables се користат за филтрирање и не се дизајнирани за балансирање.
Сепак, можно е да се напише збир на правила кои функционираат како псевдобалансер.
И токму тоа е имплементирано во Кубернетес.
Ако имате три подлоги, kube-proxy ќе ги напише следниве правила:
Изберете го првиот под со веројатност од 33%, во спротивно одете на следното правило.
Изберете го вториот со веројатност од 50%, во спротивно одете на следното правило.
Изберете го третиот под.
Овој систем резултира со тоа што секоја подлога е избрана со веројатност од 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 повеќе не се повикуваат.
Ајде да видиме како изгледа.
Првиот pod испраќа барање до услугата:
Веќе знаете што ќе се случи следно. Услугата не постои, но постојат правила на iptables кои ќе го обработат барањето:
Еден од подлогата за заднина ќе биде избрана како адреса на дестинација:
Барањето стигнува до подлогата. Во овој момент, ќе се воспостави постојана TCP врска помеѓу двата подлога:
Секое следно барање од првиот под ќе помине преку веќе воспоставената врска:
Резултатот е побрзо време на одговор и поголема пропусност, но ја губите способноста да го зголемите задниот дел.
Дури и ако имате две подлоги во задниот дел, со постојана врска, сообраќајот секогаш ќе оди на еден од нив.
Дали може да се поправи ова?
Бидејќи Kubernetes не знае како да ги балансира постојаните врски, оваа задача паѓа на вас.
Услугите се збирка од IP адреси и порти наречени крајни точки.
Вашата апликација може да добие листа на крајни точки од услугата и да одлучи како да ги дистрибуира барањата меѓу нив. Може да отворите постојана врска со секоја подлога и да барате рамнотежа помеѓу овие конекции со помош на круг-робин.
Кодот од клиентот кој е одговорен за балансирање треба да ја следи оваа логика:
Добијте листа на крајни точки од услугата.
Отворете постојана врска за секоја крајна точка.
Кога треба да се поднесе барање, користете една од отворените врски.
Редовно ажурирајте ја листата на крајни точки, креирајте нови или затворете ги старите постојани врски доколку списокот се промени.
Вака ќе изгледа.
Наместо првиот подлог да го испраќа барањето до услугата, можете да ги балансирате барањата на страната на клиентот:
Треба да напишете код кој прашува кои подлоги се дел од услугата:
Откако ќе го имате списокот, зачувајте го на страната на клиентот и користете го за да се поврзете со подлогите:
Вие сте одговорни за алгоритмот за балансирање на оптоварување:
Сега се поставува прашањето: дали овој проблем се однесува само на 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:
ClusterIP
NodePort
LoadBalancer
Безглав
Првите три услуги работат врз основа на виртуелна IP адреса, која се користи од kube-proxy за да се изградат правила за iptables. Но, основната основа на сите услуги е услуга без глава.
Услугата без глава нема никаква IP адреса поврзана со неа и обезбедува само механизам за преземање листа на IP адреси и порти на подовите (крајните точки) поврзани со неа.
Сите услуги се засноваат на услугата без глава.
Услугата ClusterIP е услуга без глава со некои додатоци:
Слојот за управување му доделува IP адреса.
Kube-proxy ги генерира потребните правила за iptables.
На овој начин можете да го игнорирате kube-proxy и директно да ја користите листата на крајни точки добиени од услугата без глава за да ја вчитате рамнотежата на вашата апликација.
Но, како можеме да додадеме слична логика на сите апликации распоредени во кластерот?
Ако вашата апликација е веќе распоредена, оваа задача може да изгледа невозможна. Сепак, постои алтернативна опција.
Service Mesh ќе ви помогне
Веројатно веќе сте забележале дека стратегијата за балансирање на оптоварување од страна на клиентот е сосема стандардна.
Кога апликацијата ќе започне, тоа:
Добива листа на IP адреси од услугата.
Отвора и одржува базен за поврзување.
Периодично го ажурира базенот со додавање или отстранување крајни точки.
Откако апликацијата сака да поднесе барање, таа:
Избира достапна врска користејќи некоја логика (на пр. круг-робин).
Го извршува барањето.
Овие чекори работат и за WebSockets, gRPC и AMQP врски.
Можете да ја одделите оваа логика во посебна библиотека и да ја користите во вашите апликации.
Сепак, наместо тоа, можете да користите сервисни мрежи како Istio или Linkerd.
Service Mesh ја зголемува вашата апликација со процес кој:
Автоматски бара сервисни IP адреси.
Тестира врски како што се WebSockets и gRPC.
Ги балансира барањата користејќи го правилниот протокол.
Service Mesh помага во управувањето со сообраќајот во кластерот, но тоа е доста интензивно за ресурси. Други опции се користење библиотеки од трети страни како Netflix Ribbon или програмабилни прокси како Envoy.
Што се случува ако ги игнорирате проблемите со балансирањето?
Можете да изберете да не користите балансирање на оптоварување и сепак да не забележувате никакви промени. Ајде да погледнеме неколку работни сценарија.
Ако имате повеќе клиенти отколку сервери, ова не е толку голем проблем.
Да речеме дека има пет клиенти кои се поврзуваат на два сервери. Дури и ако нема балансирање, ќе се користат двата сервери:
Врските може да не се рамномерно распределени: можеби четири клиенти поврзани на истиот сервер, но има добри шанси да се користат и двата сервери.
Она што е попроблематично е спротивното сценарио.
Ако имате помалку клиенти и повеќе сервери, вашите ресурси може да бидат недоволно искористени и ќе се појави потенцијално тесно грло.
Да речеме дека има два клиенти и пет сервери. Во најдобар случај, ќе има две постојани врски со два од пет сервери.
Останатите сервери ќе бидат неактивен:
Ако овие два сервери не можат да се справат со барањата на клиентите, хоризонталното скалирање нема да помогне.
Заклучок
Услугите на Kubernetes се дизајнирани да работат во повеќето стандардни сценарија за веб-апликации.
Меѓутоа, штом ќе започнете да работите со апликативни протоколи кои користат постојани TCP конекции, како што се бази на податоци, gRPC или WebSockets, услугите веќе не се соодветни. Kubernetes не обезбедува внатрешни механизми за балансирање на постојаните TCP конекции.
Ова значи дека мора да пишувате апликации со балансирање од страна на клиентот на ум.