
Shumë besojnë se mjafton të kaloni aplikacionin në Kubernetes (nëpërmjet Helm ose manualisht) — dhe gjithçka do të jetë mirë. Por nuk është kaq e thjeshtë.
Ekipa përktheu artikelin e inxhinierit DevOps, Julian Gindi. Ai tregon për pengesat që hasi kompania e tij gjatë migrimit, në mënyrë që ju të mos bini në të njëjtat grepa.
Hapi i parë: konfigurimi i kërkesave të pods dhe kufijve
Të fillojmë me konfigurimin e një ambienti të pastër, në të cilin do të funksionojnë pods tona. Kubernetes bën një punë të shkëlqyer në planifikimin e pods dhe menaxhimin e gjendjeve të dështimit. Por, ka rezultuar se planifikuesi ndonjëherë nuk mund të vendosë një pod nëse ka vështirësi në vlerësimin se sa burime i nevojiten për të funksionuar me sukses. Këtu dalin në pah kërkesat për burime dhe kufijtë. Ka shumë debat në lidhje me qasjen më të mirë për konfigurimin e kërkesave dhe kufijve. Nd sometimes duket se në të vërtetë është më shumë një art sesa një shkencë. Ky është qasja jonë.
Kërkesat për pod (pod requests) — janë vlera kryesore që përdoret nga planifikuesi për vendosjen optimale të podit.
Nga : në fazën e filtrimit përcaktohet një grup nyjash, ku mund të planifikohet pod-i. Për shembull, filtrori PodFitsResources kontrollon nëse ka mjaft burime në nyjë për të përmbushur kërkesat specifike të burimeve të pod-it.
Kërkesat e aplikacioneve i përdorim që të mund të vlerësojmë se sa burime në të vërtetë i nevojiten aplikacionit për të funksionuar normalisht. Kështu, planifikuesi mund të vendosë nyjat në mënyrë realiste. Fillimisht dëshironim të vendosnim kërkesat me një rezervë, për të garantuar një sasi të mjaftushme burimesh për secilin pod, por vërejtëm se koha e planifikimit u rrit ndjeshëm, dhe disa pod-e as që u planifikuan plotësisht, si të mos kishin marrë asnjë kërkesë për burime.
Në këtë rast, planifikuesi shpesh "nxitonte" pod-et dhe nuk mund t'i planifikonte përsëri, për shkak se plani i menaxhimit nuk kishte asnjë ide se sa burime do të nevojiteshin për aplikacionin, përveçse ishte një komponent kyç i algoritmit të planifikimit.
Kufijtë e pod-it (pod limits) janë një kufizim më i qartë për pod-in. Ai paraqet sasinë maksimale të burimeve që klasteri do të asignojë kontejnerit.
Sërish, nga : nëse për konteinerin është vendosur një kufizim memorie prej 4 GiB, kubelet (dhe mjedisi i ekzekutimit të konteinerit) do ta ndalë atë për forcë. Mjedisi i ekzekutimit nuk lejon që konteineri të përdorë më shumë se kufiri i caktuar i burimeve. Për shembull, kur një proces në konteiner përpiqet të përdorë më shumë se sasia e lejuar e memories, bërthama e sistemit e përfundon këtë proces me gabimin "jashtë memories" (OOM).
Konteineri gjithmonë mund të përdorë më shumë burime se sa janë të specifikuara në kërkesën për burime, por kurrë nuk mund të përdorë më shumë se sa është e caktuar në kufizim. Ky vlerë është e vështirë të vendoset saktësisht, por është shumë e rëndësishme.
Idealisht, ne duam që kërkesat për burimet e pod-it të ndryshojnë gjatë ciklit të jetës së procesit, pa u ndërhyrë në proceset e tjera në sistem — kjo është qëllimi i vendosjes së kufijve.
Për fat të keq, nuk mund të jap udhëzime specifike mbi vlerat që duhet të vendosen, por ne vetë ndjekim këto rregulla:
- Duke përdorur një mjet testimi të ngarkesës, simulojmë nivelin bazë të trafikut dhe monitorojmë përdorimin e burimeve të pod-it (memorisë dhe procesorit).
- Ne vendosim kërkesat e podit në një nivel të ulët të rastësishëm (me kufizim burimesh rreth 5 herë më të larta se vlera e kërkesave) dhe vëzhgojmë. Kur kërkesat janë shumë të ulëta, procesi nuk mund të fillojë, gjë që shpesh shkakton gabime misteri në kohën e ekzekutimit të Go.
Dua të theksoj se kufizimet më të larta të burimeve e bëjnë planifikimin më të komplikuar, pasi podi ka nevojë për një nyje qëllimore me mjaftueshëm burime të disponueshme.
Imagjinoni një situatë ku keni një server të lehtë web me një kufizim shumë të lartë burimesh, për shembull, 4 GB memorie. Me siguri do të duhet ta shkallëzoni horizontalisht këtë proces dhe çdo modul të ri do të duhet të planifikohet në një nyje me një kapacitet memorie të paktën 4 GB. Nëse një nyje e tillë nuk ekziston, klustëri duhet të shtojë një nyje të re për të trajtuar këtë pod, gjë që mund të marrë pak kohë. Është e rëndësishme të arrihet një diferencë minimale midis kërkesave të burimeve dhe kufijve për të siguruar një shkallëzim të shpejtë dhe të qetë.
Hapi i dytë: konfigurimi i testeve Liveness dhe Readiness
Kjo është një temë delikate që shpesh diskutohet në komunitetin Kubernetes. Është e rëndësishme të kuptohet mirë testet e vitalitetit (Liveness) dhe gatishmërisë (Readiness), pasi ato ofrojnë një mekanizëm për funksionimin e qëndrueshëm të softuerit dhe minimizojnë kohën e pushimit. Megjithatë, ato mund t'i japin goditje të rëndësishme performancës së aplikacionit tuaj nëse nuk janë konfiguruar siç duhet. Më poshtë është një përmbledhje e asaj se çfarë përfaqësojnë të dy provat.
Vitaliteti tregon nëse kontejneri po funksionon. Nëse dështon, kubelet-i vret kontejnerin dhe aktivizon politikën e ri-ngritjes për të. Nëse kontejneri nuk është i pajisur me provën e Vitalitetit, gjendja e paracaktuar do të jetë suksesi — kështu thuhet në .
Provën e Vitalitetit duhet të jenë të lira, dmth, të mos konsumojnë shumë burime, sepse ato ekzekutohen shpesh dhe duhet të informojnë Kubernetes se aplikacioni është në funksionim.
Nëse vendosni parametrin për ta ekzekutuar çdo sekondë, do të shtoni 1 kërkesë në sekondë, prandaj merrni parasysh se do të nevojiten burime shtesë për të përballuar këtë trafik.
Në kompaninë tonë, testet e Liveness verifikojnë komponentët kryesorë të aplikacionit, edhe nëse të dhënat (për shembull, nga një bazë të dhënash të largët ose nga cache) nuk janë plotësisht të pranishme.
Kemi konfiguruar në aplikacione një pikë fundi 'këtu punon', e cila thjesht kthen një kod përgjigjeje 200. Ky është një tregues se procesi është i aktivizuar dhe është në gjendje të përpunojë kërkesat (por ende jo trafikun).
Proba Readiness tregon nëse kontejneri është i gatshëm për shërbimin e kërkesave. Nëse proba e gatishmërisë dështon, kontrolluesi i pikave të fundit largon adresën IP të pod-it nga të gjitha pikët përkatëse të shërbimeve. Kjo gjithashtu përmendet në dokumentacionin e Kubernetes.
Probat e gatishmërisë konsumojnë më shumë burime, pasi ato duhet të arrijnë në backend në mënyrë që të tregojnë gatishmërinë e aplikacionit për të pranuar kërkesa.
Në komunitet ka shumë polemika për atë nëse duhet të drejtohemi drejtpërdrejt në bazën e të dhënave. Duke marrë parasysh kostot (kontrollimet bëhen shpesh, por mund të rregullohen), ne vendosëm se për disa aplikacione gatishmëria për të shërbyer trafikun merret parasysh vetëm pas verifikimit që regjistrimet kthehen nga baza e të dhënave. Probat e menduara mirë të gatishmërisë ofruan një nivel më të lartë disponueshmërie dhe eliminojnë ndërprerjet gjatë shpërndarjes.
Nëse vendosni të bëni një kërkesë në bazën e të dhënave për të verifikuar gatishmërinë e aplikacionit, sigurohuni që ajo të jetë sa më e lirë. Të marrim si shembull këtë kërkesë:
SELECT small_item FROM table LIMIT 1Ja një shembull se si ne konfiguroni këto dy vlera në Kubernetes:
livenessProbe:
httpGet:
path: /api/liveness
port: http
readinessProbe:
httpGet:
path: /api/readiness
port: http periodSeconds: 2
Mund të shtoni disa parametra të tjerë të konfigurimit:
initialDelaySeconds— sa sekonda do të kalojnë midis nisjes së kontejnerit dhe fillimit të ekzekutimit të probeve.periodSeconds— intervali i pritjes midis ekzekutimeve të probeve.timeoutSeconds— numri i sekondave pas të cilave podi konsiderohet si në dështim. Një kohë standarde e skadimit.failureThreshold— numri i dështimeve të testeve përpara se të dërgohet një sinjal rifillimi në pod.successThreshold— numri i provave të suksesshme përpara se pod-i të kalojë në gjendjen e gatishmërisë (pas dështimit, kur pod-i nis ose rikuperohet).
Hapi tre: konfigurimi i politikave të defoltit të rrjetit të pod-it
Në Kubernetes, topologjia e rrjetit është "e sheshtë"; me default, të gjithë pod-et komunikojnë direkt me njëri-tjetrin. Në disa raste, kjo nuk është e dëshirueshme.
Një potenciale problem sigurie është se një sulmues mund të përdorë një aplikacion të vetëm të brishtë për të dërguar trafik në të gjithë pod-et në rrjet. Siç ndodh në shumë fushë të sigurisë, parimi i privilegjeve të pakta është i aplikueshëm këtu. Idealisht, politikat e rrjetit duhet të tregojnë qartë cilat lidhje midis pod-eve lejohet dhe cilat jo.
Për shembull, më poshtë është një politikë e thjeshtë që ndalon çdo trafik hyrës për një hapësirë emri të caktuar:
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
Vizualizimi i kësaj konfigurimi:

(https://miro.medium.com/max/875/1*-eiVw43azgzYzyN1th7cZg.gif)
Më në detaje .
Hapi i katërt: sjellje e personalizuar me ndihmën e hooks dhe kontejnerëve init
Një nga detyrat tona kryesore ishte të siguronim deploy në Kubernetes pa ndalim për zhvilluesit. Kjo është e vështirë për shkak se ka shumë mënyra për të përfunduar aplikacionet dhe liruar burimet e përdorura prej tyre.
Vështirësi të veçanta kanë dalë me . Vërejtëm se gjatë implementimit të këtyre pod-eve, lidhjet aktive prishej përpara se të përfundonin me sukses.
Pas kërkimeve të gjera në internet, doli se Kubernetes nuk pret që lidhjet Nginx të shpenzohen para se të përfundojë pod-in. Me ndihmën e hook-ut pre-stop, ne implementuam një funksionalitet të tillë dhe shpëtuam plotësisht nga ndalimet:
lifecycle:
preStop:
exec:
command: ["/usr/local/bin/nginx-killer.sh"]
Ndërsa nginx-killer.sh:
#!/bin/bash
sleep 3
PID=$(cat /run/nginx.pid)
nginx -s quit
while [ -d /proc/$PID ]; do
echo "Waiting while shutting down nginx..."
sleep 10
done
Një tjetër paradigmë jashtëzakonisht e dobishme është përdorimi i kontejnerëve init për trajtimin e nisjes së aplikacioneve specifike. Kjo është veçanërisht e dobishme në rastin kur keni një proces migruese të dhënash që kërkon burime të konsiderueshme, duke u nisur para aplikacionit. Për këtë proces, gjithashtu mund të specifikoni një kufi më të lartë resursesh, pa vendosur një kufi të tillë për aplikacionin kryesor.
Një skemë tjetër e zakonshme është aksesimi i sekreteve në kontejnerin init, i cili ofron këto të dhëna kredencialesh për modulin kryesor, duke parandaluar aksesin e paautorizuar në sekretet nga vetë moduli kryesor i aplikacionit.
Si zakonisht, citimi nga dokumentacioni: kontejnerët init nisin në mënyrë të sigurt kodin ose utilitarët e përdoruesit, të cilat ndryshe do të zvoglonin sigurinë e imazhit të kontejnerit të aplikacionit. Duke mbajtur veçmas mjetet e panevojshme, ju kufizoni sipërfaqen e sulmit të imazhit të kontejnerit të aplikacionit.
Hapi i pestë: konfigurimi i kernelit
Në fund, do të flasim për një teknikë më të avancuar.
Kubernetes është një platformë jashtëzakonisht fleksibile që ju lejon të ekzekutoni ngarkesat e punës siç e shihni të arsyeshme. Ne kemi një sërë aplikacionesh me performancë të lartë që kërkojnë shumë burime. Pas testimeve të gjera të ngarkesës, e kuptuam se një nga aplikacionet po përballonte me vështirësi ngarkesën e pritshme të trafikut nëse vepronin konfigurimet e paracaktuara të Kubernetes.
Megjithatë, Kubernetes lejon ekzekutimin e një kontejneri privilegji që ndryshon parametërat e kernelit vetëm për podin specifik. Kjo është ajo që ne përdorëm për të ndryshuar numrin maksimal të lidhjeve të hapura:
initContainers:
- name: sysctl
image: alpine:3.10
securityContext:
privileged: true
command: ['sh', '-c', "sysctl -w net.core.somaxconn=32768"]
Kjo është një teknikë më e avancuar, e cila shpesh nuk është e nevojshme. Por nëse aplikacioni juaj me vështirësi përballon një ngarkesë të madhe, mund të provoni të konfiguroni disa nga këto parametra. Informacione më të detajuara mbi këtë proces dhe konfigurimin e vlerave të ndryshme — si gjithmonë .
Në përfundim
Edhe pse Kubernetes mund të duket si një zgjidhje e gatshme "nga kutia", për të siguruar funksionimin e pandërprerë të aplikacioneve është e nevojshme të ndërmerren disa hapa kyç.
Gjatë migrimit në Kubernetes, është e rëndësishme të ndiqni "ciklin e testimit të ngarkesës": nisni aplikacionin, testi nën ngarkesë, përcillni metrikat dhe sjelljen gjatë shkallëzimit, konfiguroni hapësirën në bazë të këtyre të dhënave, pastaj përsëritni këtë cikël.
Vlerësoni realistisht trafikun e pritur dhe provoni të shkoni përtej tij për të parë se cilat komponentë do të dështojnë të parët. Me një qasje iteruese, disa nga këto rekomandime mund të mjaftojnë për të arritur suksesin. Ose mund të nevojitet konfigurim më i thellë.
Gjithmonë bëni vetes këto pyetje:
- Sa burime konsumojnë aplikacionet dhe si do të ndryshojë ky volum?
- Cilat janë kërkesat reale për shkallëzim? Sa trafik do të përpunojë mesatarisht aplikacioni? Dhe si është për trafikun maksimal?
- Sa shpesh i duhet shërbimit skalimi horizontal? Sa shpejt duhet të vendosen pod-et e rinj për të pranuar trafikun?
- Sa korrekt përfundon puna e pod-eve? A është kjo e nevojshme? A është e mundur të arrihet shpërndarja pa ndonjë pezullim?
- Si mund të minimizoni rreziqet për sigurinë dhe të kufizoni dëmet nga çdo pod të komprometuar? A kanë disa shërbime leje ose akses që nuk u nevojiten?
Kubernetes ofron një platformë të jashtëzakonshme që lejon përdorimin e praktikave më të mira për shpërndarjen e mijëra shërbimeve në një grup. Megjithatë, aplikacionet janë të ndryshme. Ndonjëherë implementimi kërkon pak më shumë punë.
Fatmirësisht, Kubernetes ofron konfigurimet e nevojshme për të arritur të gjitha qëllimet teknike. Duke përdorur një kombinim të kërkesave të burimeve dhe kufijve, provave të Liveness dhe Readiness, konteinerëve të init, politikave rrjetore dhe konfigurimit të personalizuar të bërthamës, mund të arrini performancë të lartë së bashku me qëndrueshmëri dhe shkallëzim të shpejtë.
Çfarë tjetër mund të lexoni:
- .
- .
- .
Burimi: habr.com
