10 gabime tipike në përdorimin e Kubernetes

Shën. përk.: autorët e këtij artikulli janë inxhinierë nga një kompani e vogël çeke pipetail. Ata arritën të compilojnë një listë të shkëlqyer nga [disa probleme banale, por ende] të tilla të rëndësishme që lidhen me përdorimin e klastereve Kubernetes.

10 gabime tipike në përdorimin e Kubernetes

Gjatë viteve të përdorimit të Kubernetes, kemi punuar me një numër të madh klasteresh (si të menaxhuara, ashtu edhe të pa menaxhuara — në GCP, AWS dhe Azure). Me kalimin e kohës, filluam të vëmë re se disa gabime përsëriteshin vazhdimisht. Megjithatë, në këtë nuk ka asgjë për t'u turpëruar: ne vetë kemi bërë shumicën e tyre!

Në artikull janë mbledhur gabimet më të zakonshme, si dhe është përmendur se si mund t'i rregullojmë ato.

1. Burimet: kërkesat dhe kufijtë

Ky pikë padyshim meritojnë vëmendjen më të madhe dhe vendin e parë në listë.

Kërkesa e CPU zakonisht ose nuk është e vendosur fare, ose ka një vlerë shumë të ulët (për të vendosur sa më shumë pod’ë në çdo nyje). Si pasojë, nyjat ngarkohen tepër. Gjatë ngarkesës së lartë, fuqitë procesorëve të nyjes janë plotësisht të angazhuara dhe ngarkesa specifike merr vetëm atë që "kërkoi" përmes ntrottlinit të CPU. Kjo çon në vonesa të rritura në aplikacion, kohë të skadimit dhe pasoja të tjera të pakëndshme. (Më shumë për këtë lexoni në një përkthim të fundit nga ne: "Kufijtë e CPU dhe ngadalësimi agresiv në Kubernetes" – shënim i përkthyesit)

BestEffort (ekstremisht nuk e rekomanduar):

resources: {}

Kërkesa ekstremisht e ulët e CPU (ekstremisht nuk e rekomanduar):

   resources:
      Requests:
        cpu: "1m"

Nga ana tjetër, pranimi i një kufiri të CPU mund të çojë në kalimin e papërshtatshëm të cikleve nga pod’ët, edhe nëse procesori i nyjes nuk është plotësisht i ngarkuar. Sërish, kjo mund të shkaktojë rritje të vonesave. Debatet vazhdojnë rreth parametrave CPU CFS quota në bërthamën Linux dhe ntrottlinit të CPU në varësi të kufijve të vendosur, si dhe deaktivizimi i kuotave CFS... Fatkeqësisht, kufijtë e CPU mund të shkaktojnë më shumë probleme sesa mund të zgjidhin. Më shumë për këtë mund të lexoni në lidhjen më poshtë.

Teprica e alokimit (overcommiting) e memories mund të çojë në probleme më të mëdha. Arritja e kufirit të CPU sjell kalimin e cikleve, ndërsa arritja e kufirit të memories sjell "vrasjen" e pod’ët. A keni parë ndonjëherë OOMkill? Да, речь идет именно о нем.

Doni të minimizoni mundësinë e këtij evenimenti? Mos alokoni sasi tepër të madhe memories dhe përdorni Garantuar QoS (Cilësia e Shërbimit), duke vendosur kërkesën e memories të barabartë me kufirin (siç është në shembullin më poshtë). Më shumë për këtë lexoni në prezantimin e Henning Jacobs (inxhinier i lartë në Zalando).

Burstable (mundësi më të lartë për të marrë OOMkilled):

   resources:
      requests:
        memory: "128Mi"
        cpu: "500m"
      limits:
        memory: "256Mi"
        cpu: 2

Guaranteed:

   resources:
      requests:
        memory: "128Mi"
        cpu: 2
      limits:
        memory: "128Mi"
        cpu: 2

Çfarë potencialisht ndihmon në konfigurimin e burimeve?

Me metrics-server mund të shihni konsumimin aktual të burimeve të CPU dhe përdorimin e memories nga pod’ët (dhe kontejnerët brenda tyre). Më shumë gjasa ju tashmë po e përdorni atë. Thjesht ekzekutoni komandat e mëposhtme:

kubectl top pods
kubectl top pods --containers
kubectl top nodes

Megjithatë, ato tregojnë vetëm përdorimin aktual. Me të mund të merrni një ide të përafërt për rendin e madhësive, por në fund do t'ju nevojitet historiku i ndryshimit të metrikave në kohë (për të përgjigjur në pyetje të tilla si: "Cila ishte ngarkesa maksimale e CPU?", "Cila ishte ngarkesa dje në mëngjes?" — etj.). Për këtë mund të përdorni Prometheus, DataDog dhe mjete të tjera. Ato thjesht marrin metrikat nga metrics-server dhe i ruajnë, ndërsa përdoruesi mund t'i kërkojë dhe të ndërtojë grafike përkatëse.

VerticalPodAutoscaler lejon për ta automatizuar ky proces. Ai ndjek historinë e përdorimit të procesorit dhe memories dhe përshtat kërkesat dhe kufijtë e rinj mbi bazën e kësaj informacioni.

Përdorimi efikas i burimeve llogaritative është një detyrë e vështirë. Kjo është si të luash vazhdimisht tetris. Nëse po paguani shumë për fuqitë llogaritëse në një konsum të ulët mesatar (le të themi, ~10%), rekomandojmë të shikoni produktet të bazuara në AWS Fargate ose Virtual Kubelet. Ato janë të ndërtuara mbi modelin e faturimit serverless/pay-per-usage, që në këto kushte mund të rezultojë më e lirë.

2. Provat e liveness dhe readiness

Për default, provat e gjendjes liveness dhe readiness në Kubernetes nuk janë të aktivizuara. Dhe ndonjëherë i harrojnë ato…

Por si mund të niste një rinovim i shërbimit në rast të një gabimi të pazgjidhshëm? Dhe si e di balancuesi i ngarkesës që një pod është i gatshëm të pranojë trafikun? Ose se mund të trajtojë më shumë trafik?

Shpesh këto prova ngatërrohen mes tyre:

  • Vitaliteti — provimi "i gjallë", i cili rinovon pod-in në rast të dështimit;
  • Readiness — provimi i gatishmërisë, i cili në rast dështimi çon pod-in nga shërbimi Kubernetes (kjo mund të kontrollohet me kubectl get endpoints) dhe trafiku nuk i dërgohet atij deri sa një provë tjetër të përfundojë me sukses.

Të dy këto kontrolle KRYHEN GJATË TË GJITHË CIKLIT TË JETES POD’it. Kjo është shumë e rëndësishme.

Një keqkuptim i zakonshëm është se provat e readiness fillojnë vetëm në fillim, në mënyrë që balancuesi të mund ta kuptojë që pod'i është i gatshëm (Gati) dhe mund të fillojë të trajtojë trafikun. Sidoqoftë, ky është vetëm një nga mënyrat e përdorimit të tyre.

Një tjetër është mundësia për të kuptuar se trafiku i pod'it është tepër i madh dhe po e ngarkon atë (ose pod'i po kryen llogaritje me burime intensive). Në këtë rast, kontrolli i readiness ndihmon të reduktojë ngarkesën në pod dhe ta "ftohë" atë. Përfundimi i suksesshëm i kontrollit të readiness në të ardhmen lejon që ngarkesa në pod të rritet përsëri. Në këtë rast (në rast dështimi të provës së readiness) dështimi i kontrollit të liveness do të ishte shumë kontraproduktiv. Pse të rinisni një pod që është i shëndetshëm dhe punon me gjithë forcën?

Prandaj, në disa raste, mospranimi i kontrolleve është më mirë se sa aktivizimi i tyre me parametra të ndërtuar gabimisht. Siç u theksua më lart, nëse kontrolli i liveness kopjon kontrollin e readiness, atëherë jeni në një situatë të vështirë. Një mundësi është të konfiguroni vetëm testin e readiness, dhe duke lënë të anashkaluar liveness-in. Të dy llojet e kontrolleve nuk duhet të dështojnë në rastin e dështimeve të përgjithshme, përndryshe do të shtyjnë një dështim në kaskadë të të gjitha pod'ëve. Në fjalë të tjera,

mos e dëmtoni veten mos mos e dëmtoni veten tuaj.

3. LoadBalancer për çdo shërbim HTTP

Mund të keni shërbime HTTP në klastrin tuaj që dëshironi t’i çoni në botën e jashtme.

Nëse e hapni shërbimin si type: LoadBalancer, kontrolleu i tij (në varësi të ofruesit të shërbimeve) do të ofrojë dhe miratojë një LoadBalancer të jashtëm (nuk është patjetër të funksionojë në L7, ndoshta më shumë në L4), dhe kjo mund të ndikojë në kosto (në adresën e jashtme statike IPv4, fuqitë përpunuese, tarifimi për sekondë) për shkak të nevojës për krijimin e shumë burimeve të tilla.

Në këtë rast, është shumë më logjike të përdorni një LoadBalancer të jashtëm, duke hapur shërbimet si type: NodePort. Ose, çfarë është edhe më mirë, të implementoni diçka si nginx-ingress-controller (ose traefik), i cili do të veprojë si pika e vetme NodePort konektuar me LoadBalancer-in e jashtëm dhe do të drejtojë trafikun në klasër duke përdorur ingress-burimet e Kubernetes.

Shërbimet e tjera të (mikro)shërbimeve brenda klastrit, që bashkëpunojnë me njëra-tjetrën, mund të "flasin" përmes shërbimeve të tipit ClusterIP dhe mekanizmit të ndjekjes së shërbimeve përmes DNS. Një gjë tjetër, mos përdorni DNS/IP publike sepse mund të ndikojë në vonesë dhe të rrisë koston e shërbimeve në re.

4. Autoskalimi i klastri pa marrë parasysh veçoritë e tij

Gjatë shtimit dhe heqjes së nyjeve nga klastrit, nuk duhet të mbështeteni në disa metrika bazike si përdorimi i CPU në këto nyje. Planifikimi i pod'ëve duhet të bëhet duke marrë parasysh shumë kufizimeve, si afiniteti i pod’ëve/nyjeve, taints dhe tolerations, kërkesat për burime, QoS etj. Përdorimi i një autoskaleri të jashtme, që nuk merr parasysh këto nuanca, mund të shkaktojë probleme.

Imagjinoni se një pod duhet planifikuar, por të gjitha fuqitë e disponueshme të CPU janë kërkuar/dezinfektuar dhe pod'i ngjyrat në një gjendje Pending. Autoskaleri i jashtëm sheh ngarkesën aktuale mesatare të CPU (dhe jo të kërkuarën) dhe nuk iniciaton zgjerimin (scale-out) - nuk shton një nyje tjetër. Si rezultat, ky pod nuk do të planifikohet.

Në këtë rast, rikthimi i shkallës (scale-in) - heqja e një nyje nga klustri - gjithmonë është më e vështirë për t'u realizuar. Imagjinoni se keni një pod stateful (me një ruajtje të përhershme të lidhur). Tomasat e përhershme zakonisht i përkasin një zone të caktuar të disponueshmërisë dhe nuk replikohen në rajon. Kështu, nëse autoskaleri i jashtëm heq një nyje me këtë pod, planifikuesi nuk do të jetë në gjendje ta planifikojë këtë pod në një nyje tjetër, sepse mund të bëhet vetëm në zonën e disponueshmërisë ku ndodhet ruajtja e përhershme. Pod'i do të ngecë në një gjendje Pending.

Në komunitetin e Kubernetes është shumë popullor cluster-autoscaler. Ai funksionon në klasër, mbështet API nga ofruesit kryesorë të shërbimeve në re, merr parasysh të gjitha kufizimet dhe është në gjendje të shkallëzohet në rastet e mësipërme. Ai gjithashtu është në gjendje të bëjë shkallëzim (scale-in) duke ruajtur të gjitha kufizimet e vendosura, kështu duke kursyer para (të cilat përndryshe do të shpenzoheshin për fuqitë e papërdorura).

5. Neglizhimi i mundësive IAM/RBAC

Kujdesi për të përdorur përdorues IAM me sekrete të përhershme për makinat dhe aplikacionet. Organizoni aksesin e përkohshëm, duke përdorur role dhe llogaritë e shërbimeve (service accounts).

Shpesh përballemi me faktin se çelësat e aksesit (dhe sekretet) janë 'hardcode' në konfigurimin e aplikacionit, si dhe me neglizhencën në rotacionin e sekretet, pavarësisht nga access-i në Cloud IAM. Përdorni rolet IAM dhe llogaritë shërbimi në vend të përdoruesve, ku është e përshtatshme.

10 gabime tipike në përdorimin e Kubernetes

Harroje kube2iam dhe kaloni menjëherë te rolet IAM për llogaritë e shërbimit (siç përshkruhet në artikullin me të njëjtin emër Štěpán Vraný):

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/my-app-role
  name: my-serviceaccount
  namespace: default

Një annotim. Nuk është aq e vështirë, apo jo?

Për më tepër, mos i jepni llogarive të shërbimit dhe profileve të instancave privilegje admin dhe cluster-admin, nëse nuk kanë nevojë për to. Kjo është më e vështirë për t'u realizuar, veçanërisht në RBAC K8s, por padyshim që e justifikon përpjekjen.

6. Mos u mbështetni në anti-affinity automatike për podët

Imagjinoni se keni tre replika të një deploymenti në një nod. Nod-i bie, dhe së bashku me të bien të gjitha replikat. Një situatë e pakëndshme, apo? Por pse të gjitha replikat ishin në një nod? A nuk duhet Kubernetes të sigurojë qëndrueshmëri të lartë (HA)?!

Fatkeqësisht, planifikuesi i Kubernetes nuk respekton rregullat e ndarjes së podëve me iniciativën e tij (anti-affinity) për podët. Këto duhet të specifikohen qartë:

// опущено для краткости
      labels:
        app: zk
// опущено для краткости
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: "app"
                    operator: In
                    values:
                    - zk
              topologyKey: "kubernetes.io/hostname"

Ja ku është. Tani podët do të planifikohen në nodë të ndryshme (kjo kusht verifikohet vetëm gjatë planifikimit, por jo gjatë funksionimit të tyre — andaj requiredDuringSchedulingIgnoredDuringExecution).

Këtu flasim për podAntiAffinity në nodë të ndryshme: topologyKey: "kubernetes.io/hostname", — dhe jo në zona të ndryshme disponueshmërie. Për të realizuar një HA të plotë, do të duhet të thelloheni më tej në këtë temë.

7. Injorimi i PodDisruptionBudget-ëve

Imagjinoni se keni një ngarkesë production në klasterin Kubernetes. Periudha e përkrahjes së nodëve dhe gjithë klasterit duhet të përditësohet (ose të shpërbëhet). PodDisruptionBudget (PDB) është diçka si një marrëveshje garantimi midis administratorëve të klasterit dhe përdoruesve.

PDB ndihmon në shmangien e ndërprerjeve të shërbimeve, të shkaktuara nga mungesa e nodëve:

apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: zookeeper

Në këtë shembull, si përdorues i klasterit, po i thoni administratorëve: "Hej, kam një shërbim zookeeper, dhe pavarësisht nga ajo që bëni, do doja që të paktën 2 replika të këtij shërbimi të ishin gjithmonë të disponueshme".

Më shumë mund të lexoni për këtë këtu.

8. Disa përdorues ose mjedise në një klasër të përbashkët

Hapësirat emrave Kubernetes (hapësira emrash) nuk sigurojnë izolim të fortë.

Është një mit i zakonshëm se nëse e shpërndani një ngarkesë jo-prodhuese në një hapësirë emrash dhe një ngarkesë prodhuese në tjetrën, ato nuk do të ndikojnë njëra-tjetrën... Megjithatë, një nivel i caktuar izolimi mund të arrihet përmes kërkesave/llogaritjeve të burimeve, përcaktimit të kuotave, vendosjes së priorityClass'ëve. Ndonjë "izolim" "fizik" në planin e të dhënave sigurohet nga affinitetet, tolerancat, ndotjet (ose nodeselectors), por kjo ndarje është mjaft e vështirë për t'u realizuar.

Atyre që u nevojitet të kombinojnë të dyja llojet e ngarkesave të punës në një klasër do t'u duhet të pajtohen me vështirësitë. Nëse nuk ka një nevojë të tillë, dhe ju është e mundur të hapni një klasër tjetër (p.sh., në një cloud publik), atëherë është më mirë ta bëni kështu. Kjo do të lejojë arritjen e një niveli shumë më të lartë izolimi.

9. externalTrafficPolicy: Cluster

Shpesh herë ne vërejmë se gjithë trafiku në klasër hyn përmes një shërbimi të tipit NodePort, për të cilin standardi është externalTrafficPolicy: Cluster. Kjo do të thotë se NodePort është e hapur në çdo nyje në klasër dhe mund të përdoret çfarëdo prej tyre për të komunikuar me shërbimin e nevojshëm (grupin e pod'ëve).

10 gabime tipike në përdorimin e Kubernetes

Ndërkohë, pod'ët realë që lidhen me shërbimin NodePort të përmendur më sipër zakonisht janë vetëm në një nëngrup të këtyre nyjeve. Me fjalë të tjera, nëse lidhem me një nyje ku nuk ka pod'in e nevojshëm, ai do ta redirezhojë trafikun në një nyje tjetër, duke shtuar një segment kalimi (hop) dhe duke rritur vonesën (nëse nyjet ndodhen në zona të ndryshme për akses/centra të të dhënave, vonesa mund të jetë duke u rritur ndjeshëm; për më tepër, shpenzimet për trafikun e daljeve do të rriten).

Në anën tjetër, nëse për një shërbim të caktuar Kubernetes është caktuar politika externalTrafficPolicy: Local, atëherë NodePort hapet vetëm në ato nyje ku realisht janë të aktivizuara pod'ët e nevojshëm. Kur përdoret një balancues ngarkese të jashtme, që kontrollon gjendjen (healthchecking) pikave (siç bën AWS ELB), ai do të dërgojë trafik vetëm në nyjet e nevojshme, që do të ndikojë pozitivisht në vonesat, nevojat computacionale, faturat për trafikun e daljeve (dhe shëndeti i arsyeshëm gjithashtu sugjeron të njëjtën gjë).

Ishte e mundur që ju tashmë po përdorni diçka si traefik ose nginx-ingress-controller si pikë përfundimtare NodePort (ose LoadBalancer, i cili gjithashtu përdor NodePort) për të rregulluar trafikun HTTP ingress, dhe vendosja e kësaj opcioni mund të reduktojë ndjeshëm vonesën për kërkesa të tilla.

këtë publikim mund të mësoni më shumë rreth externalTrafficPolicy, përfitimeve dhe disavantazheve të saj.

10. Mos e lidhni veten me klasterët dhe mos abuzoni me kontrollin e planeve

Dikur serverët quheshin me emra të veçantë: Anton, HAL9000 dhe Colossus… Sot, në vend të tyre, janë identifikatorët e gjeneruar rastësisht. Megjithatë, zakoni ka mbetur, dhe tani emrat e veçantë i takojnë klasterëve.

Një histori tipike (e bazuar në ngjarje reale): gjithçka filloi me një provë koncepti, prandaj klasteri kishte një emër të ndritur testing… Kaluan vitet, dhe ai ende përdoret në prodhim, dhe të gjithë kanë frikë ta prekin atë.

Nuk ka asgjë qesharake në atë që klasterët bëhen si kafshë shtëpiake, prandaj rekomandojmë që t'i fshini ato periodikisht, duke praktikuar rikuperimin nga dështimet (në këtë do të ndihmojë chaos engineering — shënim i përkthyesit.). Për më tepër, do të ndihmojë të merret edhe me nivelin menaxhues (kontrolli i planeve). Frika për ta prekur atë nuk është një shenjë shumë e mirë. Etcd ka vdekur? Djem, ju jeni vërtet në telashe!

Nga ana tjetër, nuk duhet të teproni me manipulimet me të. Me kalimin e kohës niveli menaxhues mund të bëhet i ngadaltë.Shumë mundësisht, kjo është për shkak të numrit të madh të objekteve që krijohen pa rotacion (një situatë e zakonshme kur përdoret Helm me parametrat e paracaktuar, gjë që nuk e përditëson gjendjen e tij në configmap'ave/sekrete – si rezultat, mbledhni mijëra objekte në nivelin menaxhues) ose me redaktimin e vazhdueshëm të objekteve kube-api (për auto-skalimin, për CI/CD, për monitorimin, logjet e ngjarjeve, kontrolluesit etj.).

Për më tepër, rekomandojmë të kontrolloni marrëveshjet SLA/SLO me ofruesin e Kubernetes të menaxhuar dhe të kushtoni vëmendje garantive. Ekipi mund të garantojë disponueshmërinë e nivelit menaxhues (ose përbërësit e tij), por jo p99-vonesën e kërkesave që i dërgoni atij. Në fjalë të tjera, mund të futni kubectl get nodes, dhe të merrni përgjigje vetëm pas 10 minutash, dhe kjo nuk do të ishte një shkelje e kushteve të marrëveshjes për shërbim.

11. Bonusi: përdorimi i etiketës latest

Kjo tashmë është klasik. Kohët e fundit, nuk po hasim shpesh në këtë teknikë, pasi shumë, të mësuar nga përvoja e hidhur, kanë ndaluar përdorimin e etiketës :latest dhe kanë filluar të ankorojnë (pin) versionet. Hurra!

ECR mbështet qëndrueshmërinë e etiketave të imazheve; rekomandojmë të njiheni me këtë veçori të jashtëzakonshme.

Curriculum Vitae

Mos prisni që gjithçka të funksionojë me një përkëdhelje: Kubernetes nuk është një panacë. Një aplikacion i keq do të mbetet i tillë edhe në Kubernetes (dhe ndoshta do të bëhet edhe më keq). Moskujdesi do të çojë në kompleksitet të tepërt, punë të ngadaltë dhe stresuese në nivelin menaxhues. Për më tepër, rrezikoni të mbeteni pa një strategji rikuperimi nga fatkeqësitë. Mos mendoni se Kubernetes "nga fabrika" do të marrë përsipër sigurimin e izolimit dhe disponueshmërisë së lartë. Shpenzojini pak kohë për ta bërë aplikacionin tuaj vërtet cloud native.

Mund të njihni përvojën e dështuar të ekipeve të ndryshme në këtë koleksion historish nga Henning Jacobs.

Ata që dëshirojnë të shtojnë listën e gabimeve të përmendura në këtë artikull, mund të na kontaktojnë në Twitter (@MarekBartik, @MstrsObserver).

P.S. nga përkthyesi

Lexoni gjithashtu në blogun tonë:

Burimi: habr.com

Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS 🔥 Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS | ProHoster