Կուբերնետեսում երկարատև կապերի հավասարակշռում և ընդլայնում
Այս հոդվածը կօգնի ձեզ հասկանալ, թե ինչպես է աշխատում բեռի հավասարակշռումը Kubernetes-ում, ինչ է տեղի ունենում երկարաժամկետ կապերի չափման ժամանակ և ինչու պետք է հաշվի առնել հաճախորդի կողմից հավասարակշռումը, եթե օգտագործում եք HTTP/2, gRPC, RSockets, AMQP կամ այլ երկարակյաց արձանագրություններ: .
Մի փոքր այն մասին, թե ինչպես է երթեւեկությունը վերաբաշխվում Kubernetes-ում
Kubernetes-ը ապահովում է երկու հարմար աբստրակցիա հավելվածների տեղակայման համար՝ Ծառայություններ և տեղակայումներ:
Տեղակայումները նկարագրում են, թե ինչպես և քանի օրինակ պետք է գործարկվի ձեր դիմումը ցանկացած պահի: Յուրաքանչյուր հավելված տեղադրվում է որպես «Pod» և նրան տրվում է IP հասցե:
Ծառայություններն իրենց գործառույթներով նման են բեռի հավասարակշռողին: Նրանք նախագծված են երթևեկությունը բաշխելու բազմաթիվ պատիճներով:
Տեսնենք, թե ինչ տեսք ունի.
Ստորև բերված գծապատկերում կարող եք տեսնել նույն հավելվածի երեք օրինակ և բեռի հավասարակշռիչ.
Բեռի հավասարակշռիչը կոչվում է Ծառայություն և նրան տրվում է IP հասցե: Ցանկացած մուտքային հարցում վերահղվում է պատյաններից մեկին՝
Տեղակայման սցենարը որոշում է հավելվածի օրինակների քանակը: Դուք գրեթե երբեք ստիպված չեք լինի ուղղակիորեն ընդլայնել հետևյալը.
Յուրաքանչյուր pod-ին հատկացվում է իր սեփական IP հասցեն.
Օգտակար է ծառայությունների մասին պատկերացնել որպես IP հասցեների հավաքածու: Ամեն անգամ, երբ մուտք եք գործում ծառայություն, IP հասցեներից մեկն ընտրվում է ցանկից և օգտագործվում որպես նպատակակետ:
Կարծես սա է.
Ծառայությանը ստացվում է curl 10.96.45.152 հարցում.
Ծառայությունը որպես նպատակակետ ընտրում է երեք pod հասցեներից մեկը.
Երթևեկությունը վերահղված է դեպի որոշակի պատիճ.
Եթե ձեր հավելվածը բաղկացած է ճակատից և հետին մասից, ապա յուրաքանչյուրի համար կունենաք և՛ ծառայություն, և՛ տեղակայում:
Երբ frontend-ը հարցում է անում backend-ին, նա պետք չէ հստակ իմանալ, թե քանի պատիճ է սպասարկում backend-ը. կարող է լինել մեկը, տասը կամ հարյուրը:
Բացի այդ, frontend-ը ոչինչ չգիտի backend-ը սպասարկող pod-ների հասցեների մասին:
Երբ frontend-ը հարցում է կատարում backend-ին, այն օգտագործում է backend ծառայության IP հասցեն, որը չի փոխվում:
Ահա թե ինչպես է այն տեսքը.
Under 1-ը պահանջում է ներքին հետին մասի բաղադրիչ: Հետին պլանի համար կոնկրետ մեկը ընտրելու փոխարեն, այն հարցում է կատարում ծառայությանը.
Ծառայությունը որպես նպատակակետի հասցե ընտրում է հետին պլաններից մեկը.
Երթևեկությունը գնում է Pod 1-ից մինչև Pod 5՝ ընտրված ծառայության կողմից.
Under 1-ը հստակ չգիտի, թե 5-ի նման քանի պատիճ է թաքնված ծառայության հետևում.
Բայց ինչպե՞ս է ծառայությունը բաշխում հարցումները: Թվում է, թե օգտագործվում է շրջանաձև հավասարակշռում: Եկեք պարզենք այն:
Հավասարակշռում Kubernetes ծառայություններում
Kubernetes ծառայություններ գոյություն չունեն: Ծառայության համար գործընթաց չկա, որին տրված է IP հասցե և նավահանգիստ:
Դուք կարող եք դա հաստատել՝ մուտք գործելով կլաստերի ցանկացած հանգույց և գործարկելով netstat -ntlp հրամանը։
Դուք նույնիսկ չեք կարողանա գտնել ծառայությանը հատկացված IP հասցեն:
Ծառայության IP հասցեն գտնվում է կառավարման շերտում, վերահսկիչում և գրանցվում է տվյալների բազայում - etcd: Նույն հասցեն օգտագործվում է մեկ այլ բաղադրիչի կողմից՝ kube-proxy:
Kube-proxy-ը ստանում է IP հասցեների ցանկ բոլոր ծառայությունների համար և ստեղծում է iptables կանոնների մի շարք կլաստերի յուրաքանչյուր հանգույցի վրա:
Այս կանոններն ասում են. «Եթե մենք տեսնում ենք ծառայության IP հասցեն, մենք պետք է փոփոխենք հարցման նպատակային հասցեն և ուղարկենք այն փոդներից մեկին»:
Ծառայության IP հասցեն օգտագործվում է միայն որպես մուտքի կետ և չի սպասարկվում այդ IP հասցեն և նավահանգիստը լսելու որևէ գործընթացի միջոցով:
Եկեք նայենք սա.
Դիտարկենք երեք հանգույցներից բաղկացած կլաստեր: Յուրաքանչյուր հանգույց ունի պատյաններ.
Կապված պատիճները, որոնք ներկված են բեժ գույնով, ծառայության մի մասն են: Քանի որ ծառայությունը որպես գործընթաց գոյություն չունի, այն ցուցադրվում է մոխրագույնով.
Առաջին պատիճը պահանջում է ծառայություն և պետք է գնա համապատասխան պատյաններից մեկին՝
Բայց ծառայությունը չկա, պրոցեսը չկա։ Ինչպես է դա աշխատում?
Նախքան հարցումը լքում է հանգույցը, այն անցնում է iptables կանոններով.
iptables-ի կանոնները գիտեն, որ ծառայությունը գոյություն չունի, և դրա IP հասցեն փոխարինում են այդ ծառայության հետ կապված pods-ի IP հասցեներից մեկով.
Հարցումը ստանում է վավեր IP հասցե որպես նպատակակետ և սովորաբար մշակվում է.
Կախված ցանցի տոպոլոգիայից, հարցումն ի վերջո հասնում է պատիճ.
Կարո՞ղ է iptables-ը բեռնել հավասարակշռությունը:
Ոչ, iptable-ներն օգտագործվում են զտման համար և նախատեսված չեն հավասարակշռելու համար:
Այնուամենայնիվ, հնարավոր է գրել մի շարք կանոններ, որոնք աշխատում են նման կեղծ հավասարակշռող.
Եվ սա հենց այն է, ինչ իրականացվում է Kubernetes-ում։
Եթե ունեք երեք պատիճ, kube-proxy-ը կգրի հետևյալ կանոնները.
Ընտրեք առաջին ենթակետը 33% հավանականությամբ, հակառակ դեպքում անցեք հաջորդ կանոնին:
Ընտրեք երկրորդը 50% հավանականությամբ, հակառակ դեպքում անցեք հաջորդ կանոնին։
Ընտրեք երրորդը տակ:
Այս համակարգի արդյունքում յուրաքանչյուր պատիճ ընտրվում է 33% հավանականությամբ:
Եվ երաշխիք չկա, որ Pod 2-ը կընտրվի Pod 1-ից հետո:
Նշումiptables-ն օգտագործում է վիճակագրական մոդուլ՝ պատահական բաշխմամբ: Այսպիսով, հավասարակշռման ալգորիթմը հիմնված է պատահական ընտրության վրա:
Այժմ, երբ հասկանում եք, թե ինչպես են աշխատում ծառայությունները, եկեք նայենք սպասարկման ավելի հետաքրքիր սցենարներին:
Kubernetes-ում երկարատև կապերը լռելյայնորեն չեն չափվում
Յուրաքանչյուր HTTP հարցում ճակատից մինչև հետնամաս սպասարկվում է առանձին TCP կապով, որը բացվում և փակվում է:
Եթե frontend-ը վայրկյանում 100 հարցում է ուղարկում backend-ին, ապա 100 տարբեր TCP կապեր բացվում և փակվում են:
Դուք կարող եք կրճատել հարցումների մշակման ժամանակը և բեռնվածությունը՝ բացելով մեկ TCP կապ և օգտագործելով այն բոլոր հետագա HTTP հարցումների համար:
HTTP արձանագրությունն ունի մի հատկություն, որը կոչվում է HTTP պահպանել կենդանի կամ կապի վերօգտագործում: Այս դեպքում մեկ TCP կապն օգտագործվում է բազմաթիվ HTTP հարցումներ և պատասխաններ ուղարկելու և ստանալու համար.
Այս հատկությունը լռելյայն միացված չէ. և՛ սերվերը, և՛ հաճախորդը պետք է համապատասխանաբար կազմաձևվեն:
Կարգավորումն ինքնին պարզ է և հասանելի ծրագրավորման լեզուների և միջավայրերի մեծ մասի համար:
Ահա տարբեր լեզուներով օրինակների մի քանի հղումներ.
Ի՞նչ է պատահում, եթե մենք օգտագործում ենք keep-alive-ը Kubernetes ծառայությունում:
Ենթադրենք, որ և՛ frontend-ը, և՛ backend-ը աջակցում են keep-alive-ին:
Մենք ունենք ճակատային մասի մեկ օրինակ և հետնամասի երեք օրինակ: Frontend-ը կատարում է առաջին հարցումը և բացում TCP կապը backend-ի հետ: Հարցումը հասնում է ծառայությանը, որպես նպատակակետ ընտրվում է հետնախորշերից մեկը: Backend-ը պատասխան է ուղարկում, իսկ ճակատը ստանում է այն:
Ի տարբերություն սովորական իրավիճակի, երբ TCP կապը փակվում է պատասխան ստանալուց հետո, այն այժմ բաց է պահվում հետագա HTTP հարցումների համար:
Ի՞նչ է պատահում, եթե ճակատային մասը ավելի շատ հարցումներ ուղարկի հետին պլան:
Այս հարցումները փոխանցելու համար կօգտագործվի բաց TCP կապ, բոլոր հարցումները կգնան նույն հետնամասում, որտեղ առաջին հարցումն է եղել:
Արդյո՞ք iptables-ը չպետք է վերաբաշխի տրաֆիկը:
Ոչ այս դեպքում:
Երբ TCP կապը ստեղծվում է, այն անցնում է iptables-ի կանոններով, որոնք ընտրում են կոնկրետ backend, որտեղ կանցնի տրաֆիկը:
Քանի որ բոլոր հետագա հարցումները արդեն բաց TCP կապի վրա են, iptables կանոններն այլևս չեն կանչվում:
Տեսնենք, թե ինչ տեսք ունի.
Առաջին պատիճը հարցում է ուղարկում ծառայությանը.
Դուք արդեն գիտեք, թե ինչ է լինելու հետո։ Ծառայությունը գոյություն չունի, բայց կան iptables կանոններ, որոնք կմշակեն հարցումը.
Որպես նպատակակետի հասցե կընտրվի հետնամասի փոդներից մեկը՝
Հարցումը հասնում է պատիճ: Այս պահին կստեղծվի մշտական TCP կապ երկու բլոկների միջև.
Առաջին պատյանից ցանկացած հետագա հարցում կանցնի արդեն հաստատված կապի միջոցով.
Արդյունքն ավելի արագ արձագանքման ժամանակն է և ավելի բարձր թողունակությունը, բայց դուք կորցնում եք հետևի մասշտաբը մեծացնելու ունակությունը:
Նույնիսկ եթե դուք ունեք երկու պատիճ հետնամասում, մշտական կապով, երթևեկությունը միշտ կգնա դրանցից մեկին:
Կարո՞ղ է սա շտկվել:
Քանի որ Kubernetes-ը չգիտի, թե ինչպես հավասարակշռել մշտական կապերը, այս խնդիրն ընկնում է ձեզ վրա:
Ծառայությունները IP հասցեների և նավահանգիստների հավաքածու են, որոնք կոչվում են վերջնակետեր:
Ձեր դիմումը կարող է ստանալ ծառայության վերջնակետերի ցանկը և որոշել, թե ինչպես բաշխել հարցումները նրանց միջև: Դուք կարող եք մշտական կապ բացել յուրաքանչյուր պատի հետ և հավասարակշռել հարցումները այս միացումների միջև՝ օգտագործելով շրջանաձև ռեժիմ:
Հաճախորդի կողմի կոդը, որը պատասխանատու է հավասարակշռման համար, պետք է հետևի հետևյալ տրամաբանությանը.
Ստացեք ծառայության վերջնակետերի ցանկը:
Բացեք մշտական կապ յուրաքանչյուր վերջնակետի համար:
Երբ անհրաժեշտ է հարցում կատարել, օգտագործեք բաց միացումներից մեկը:
Պարբերաբար թարմացրեք վերջնակետերի ցանկը, ստեղծեք նորերը կամ փակեք հին մշտական կապերը, եթե ցանկը փոխվի:
Ահա թե ինչպիսի տեսք կունենա այն.
Փոխարենը, որ առաջին պատիճը հարցումն ուղարկի ծառայությանը, դուք կարող եք հավասարակշռել հարցումները հաճախորդի կողմից.
Դուք պետք է գրեք կոդ, որը կհարցնի, թե որ pods են ծառայության մաս.
Ցանկը ունենալուց հետո պահեք այն հաճախորդի կողմից և օգտագործեք այն պատիճներին միանալու համար.
Դուք պատասխանատու եք բեռի հավասարակշռման ալգորիթմի համար.
Հիմա հարց է առաջանում՝ արդյոք այս խնդիրը վերաբերում է միայն HTTP-ին:
Հաճախորդի կողմից բեռի հավասարակշռում
HTTP-ը միակ արձանագրությունը չէ, որը կարող է օգտագործել մշտական TCP կապեր:
Եթե ձեր հավելվածն օգտագործում է տվյալների բազա, ապա TCP կապը չի բացվում ամեն անգամ, երբ անհրաժեշտ է հարցում կատարել կամ տվյալների բազայից փաստաթուղթ ստանալ:
Փոխարենը բացվում և օգտագործվում է տվյալների բազայի հետ մշտական TCP կապը:
Եթե ձեր տվյալների բազան տեղակայված է Kubernetes-ում, և մուտքն ապահովված է որպես ծառայություն, ապա դուք կհանդիպեք նախորդ բաժնում նկարագրված նույն խնդիրների:
Տվյալների բազայի մեկ կրկնօրինակը ավելի բեռնված կլինի, քան մյուսները: Kube-proxy-ը և Kubernetes-ը չեն օգնի հավասարակշռել կապերը: Դուք պետք է հոգ տանեք ձեր տվյալների բազայի հարցումները հավասարակշռելու համար:
Կախված նրանից, թե որ գրադարանն եք օգտագործում տվյալների շտեմարանին միանալու համար, դուք կարող եք ունենալ այս խնդիրը լուծելու տարբեր տարբերակներ:
Ստորև բերված է Node.js-ից MySQL տվյալների բազայի կլաստեր մուտք գործելու օրինակ.
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
Դուք արդեն պետք է ծանոթ լինեք այս արձանագրությունների մեծ մասին:
Բայց եթե այս արձանագրությունները այդքան տարածված են, ինչո՞ւ չկա ստանդարտացված հավասարակշռող լուծում: Ինչու՞ պետք է փոխվի հաճախորդի տրամաբանությունը: Կա՞ հայրենի Kubernetes լուծում:
Kube-proxy-ը և iptable-ները նախագծված են 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 կապերը հավասարակշռելու ներքին մեխանիզմներ:
Սա նշանակում է, որ դուք պետք է գրեք հավելվածներ՝ հաշվի առնելով հաճախորդի կողմից հավասարակշռությունը: