Pet napak pri uvajanju prve aplikacije na Kubernetes

Pet napak pri uvajanju prve aplikacije na KubernetesFail avtor Aris Dreamer

Mnogi ljudje mislijo, da je dovolj, da aplikacijo prenesete v Kubernetes (bodisi s pomočjo Helma ali ročno) - in sreča bo. Vendar ni vse tako preprosto.

Ekipa Mail.ru rešitve v oblaku prevedel članek inženirja DevOps Juliana Gindyja. Pove, s kakšnimi pastmi se je njegovo podjetje srečevalo pri procesu migracije, da ne boste stopili na iste grablje.

Prvi korak: Nastavite zahteve in omejitve podov

Začnimo z vzpostavitvijo čistega okolja, v katerem bodo delovali naši stroki. Kubernetes je odličen pri načrtovanju sklopov in preklopu. Izkazalo pa se je, da planer včasih ne more postaviti poda, če je težko oceniti, koliko virov potrebuje za uspešno delovanje. Tukaj se pojavijo zahteve za sredstva in omejitve. Obstaja veliko razprav o najboljšem pristopu k določanju zahtev in omejitev. Včasih se zdi, da je to res bolj umetnost kot znanost. Tukaj je naš pristop.

Pod zahteve je glavna vrednost, ki jo razporejevalnik uporablja za optimalno postavitev bloka.

Od Dokumentacija Kubernetes: Korak filtra definira nabor vozlišč, kjer je mogoče razporediti Pod. Filter PodFitsResources na primer preveri, ali ima vozlišče dovolj sredstev za izpolnitev določenih zahtev po virih iz sklopa.

Zahtevke aplikacij uporabljamo tako, da lahko ocenimo, koliko virov v resnici Aplikacija ga potrebuje za pravilno delovanje. Na ta način lahko razporejevalnik realno postavi vozlišča. Sprva smo želeli previsoko razporediti zahteve, da bi zagotovili dovolj virov za vsak Pod, vendar smo opazili, da se je čas razporejanja znatno podaljšal in nekateri Podi niso bili v celoti razporejeni, kot da zanje ni bilo zahtev za vire.

V tem primeru bi razporejevalnik pogosto "iztisnil" pode in jih ne bi mogel prerazporediti, ker nadzorna ravnina ni imela pojma, koliko sredstev bi potrebovala aplikacija, kar je ključna komponenta algoritma za razporejanje.

Omejitve pod je jasnejša meja za pod. Predstavlja največjo količino virov, ki jih bo gruča dodelila vsebniku.

Spet od uradna dokumentacija: Če ima vsebnik omejitev pomnilnika 4 GiB, jo bo uveljavil kubelet (in izvajalno okolje vsebnika). Izvajalno okolje preprečuje, da bi vsebnik uporabil več kot podano omejitev virov. Na primer, ko proces v vsebniku poskuša uporabiti več kot dovoljeno količino pomnilnika, sistemsko jedro prekine proces z napako "zmanjka pomnilnika" (OOM).

Vsebnik lahko vedno uporabi več virov, kot je določeno v zahtevi za vir, vendar nikoli ne more uporabiti več od omejitve. To vrednost je težko pravilno nastaviti, vendar je zelo pomembna.

V idealnem primeru želimo, da se zahteve po virih poda med življenjskim ciklom procesa spreminjajo brez poseganja v druge procese v sistemu – to je namen postavljanja omejitev.

Na žalost ne morem dati konkretnih navodil, katere vrednosti nastaviti, vendar se sami držimo naslednjih pravil:

  1. Z orodjem za testiranje obremenitve simuliramo osnovno raven prometa in opazujemo uporabo pod virov (pomnilnik in procesor).
  2. Zahteve za pod nastavite na poljubno nizko vrednost (z omejitvijo virov približno 5-kratne vrednosti zahteve) in opazujte. Ko so zahteve na prenizki ravni, se proces ne more zagnati, kar pogosto povzroči skrivnostne napake med izvajanjem Go.

Opažam, da višje omejitve virov otežujejo načrtovanje, ker pod potrebuje ciljno vozlišče z dovolj razpoložljivimi viri.

Predstavljajte si situacijo, ko imate lahek spletni strežnik z zelo visoko omejitvijo virov, na primer 4 GB pomnilnika. Ta postopek bo verjetno treba povečati vodoravno in vsak nov pod bo moral biti načrtovan v vozlišču z vsaj 4 GB razpoložljivega pomnilnika. Če takšno vozlišče ne obstaja, mora gruča uvesti novo vozlišče za obdelavo tega sklopa, kar lahko traja nekaj časa. Pomembno je doseči minimalno razliko med zahtevami in omejitvami virov, da se zagotovi hitro in nemoteno skaliranje.

Drugi korak: Nastavite teste živahnosti in pripravljenosti

To je še ena subtilna tema, o kateri se pogosto razpravlja v skupnosti Kubernetes. Pomembno je, da dobro razumete teste Liveness in Readiness, saj zagotavljajo mehanizem za stabilno delovanje programske opreme in zmanjšujejo izpade. Vendar pa lahko resno vplivajo na delovanje vaše aplikacije, če niso pravilno konfigurirani. Spodaj je povzetek, kaj sta oba vzorca.

Življenje pokaže, ali vsebnik deluje. Če ne uspe, kubelet uniči vsebnik in zanj je omogočen pravilnik o ponovnem zagonu. Če vsebnik ni opremljen s sondo Liveness, bo privzeto stanje uspešno - kot je navedeno v Dokumentacija Kubernetes.

Sonde Liveness bi morale biti poceni, tj. ne bi porabile veliko virov, ker se pogosto izvajajo in bi morale obvestiti Kubernetes, da se aplikacija izvaja.

Če nastavite možnost izvajanja vsako sekundo, bo to dodalo 1 zahtevo na sekundo, zato se zavedajte, da bodo za obdelavo tega prometa potrebni dodatni viri.

V našem podjetju Liveness testi testirajo ključne komponente aplikacije, tudi če podatki (na primer iz oddaljene baze podatkov ali predpomnilnika) niso v celoti na voljo.

V aplikacijah smo nastavili "zdravstveno" končno točko, ki preprosto vrne odzivno kodo 200. To je pokazatelj, da proces teče in je sposoben obravnavati zahteve (vendar še ne prometa).

Vzorec Pripravljenost označuje, ali je vsebnik pripravljen za streženje zahtev. Če sonda pripravljenosti ne uspe, krmilnik končne točke odstrani naslov IP sklopa iz končnih točk vseh storitev, ki se ujemajo s sklopom. To je navedeno tudi v dokumentaciji Kubernetes.

Sonde pripravljenosti porabijo več virov, saj morajo doseči zaledje na tak način, da pokažejo, da je aplikacija pripravljena na sprejemanje zahtev.

V skupnosti poteka veliko razprav o tem, ali dostopati do baze podatkov neposredno. Glede na režijske stroške (preverjanja so pogosta, a jih je mogoče nadzorovati) smo se odločili, da se pri nekaterih aplikacijah pripravljenost za oskrbo prometa šteje šele po preverjanju vrnjenih zapisov iz baze. Dobro zasnovani preizkusi pripravljenosti so zagotovili višje ravni razpoložljivosti in odpravili izpade med uvajanjem.

Če se odločite za poizvedovanje v zbirki podatkov, da preizkusite pripravljenost vaše aplikacije, poskrbite, da bo čim cenejša. Vzemimo to poizvedbo:

SELECT small_item FROM table LIMIT 1

Tukaj je primer, kako konfiguriramo ti dve vrednosti v Kubernetesu:

livenessProbe: 
 httpGet:   
   path: /api/liveness    
   port: http 
readinessProbe:  
 httpGet:    
   path: /api/readiness    
   port: http  periodSeconds: 2

Dodate lahko nekaj dodatnih konfiguracijskih možnosti:

  • initialDelaySeconds - koliko sekund bo minilo med izstrelitvijo vsebnika in začetkom izstrelitve sond.
  • periodSeconds — čakalni interval med serijami vzorcev.
  • timeoutSeconds — število sekund, po katerem se kapsula šteje za nujno. Običajna časovna omejitev.
  • failureThreshold je število neuspešnih testov, preden je bloku poslan signal za ponovni zagon.
  • successThreshold je število uspešnih poskusov, preden sklop preide v stanje pripravljenosti (po napaki, ko se sklop zažene ali obnovi).

Tretji korak: Nastavitev privzetih omrežnih pravilnikov Poda

Kubernetes ima "ravno" topografijo omrežja, privzeto vsi podi komunicirajo neposredno med seboj. V nekaterih primerih to ni zaželeno.

Morebitna varnostna težava je, da bi lahko napadalec uporabil eno samo ranljivo aplikacijo za pošiljanje prometa vsem podom v omrežju. Kot na mnogih področjih varnosti tudi tukaj velja načelo najmanjših privilegijev. V idealnem primeru bi morale omrežne politike izrecno navajati, katere povezave med sklopi so dovoljene in katere ne.

Na primer, naslednje je preprosto pravilo, ki zavrača ves dohodni promet za določen imenski prostor:

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:  
 name: default-deny-ingress
spec:  
 podSelector: {}  
 policyTypes:  
   - Ingress

Vizualizacija te konfiguracije:

Pet napak pri uvajanju prve aplikacije na Kubernetes
(https://miro.medium.com/max/875/1*-eiVw43azgzYzyN1th7cZg.gif)
Več podrobnosti tukaj.

Četrti korak: Obnašanje po meri s kavlji in zagonskimi vsebniki

Eden od naših glavnih ciljev je bil zagotoviti uvedbe v Kubernetes brez izpadov za razvijalce. To je težko, ker obstaja veliko možnosti za zaustavitev aplikacij in sprostitev njihovih uporabljenih virov.

Posebne težave so se pojavile pri Nginx. Opazili smo, da so bile pri zaporednem uvajanju teh podov aktivne povezave prekinjene, preden so bile uspešno dokončane.

Po obsežni raziskavi na internetu se je izkazalo, da Kubernetes ne čaka, da se povezave Nginx izčrpajo, preden izklopi pod. S pomočjo pre-stop hooka smo implementirali naslednje funkcionalnosti in se popolnoma znebili zastojev:

lifecycle: 
 preStop:
   exec:
     command: ["/usr/local/bin/nginx-killer.sh"]

Vendar 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

Druga izjemno uporabna paradigma je uporaba init vsebnikov za upravljanje zagona določenih aplikacij. To je še posebej uporabno, če imate proces selitve baze podatkov, ki zahteva veliko virov in ga je treba zagnati pred zagonom aplikacije. Določite lahko tudi višjo omejitev sredstev za ta proces, ne da bi nastavili takšno omejitev za glavno aplikacijo.

Druga pogosta shema je dostop do skrivnosti v zagonskem vsebniku, ki zagotavlja te poverilnice glavnemu modulu, kar preprečuje nepooblaščen dostop do skrivnosti iz samega modula glavne aplikacije.

Kot običajno, citat iz dokumentacije: zagonski vsebniki varno izvajajo uporabniško kodo ali pripomočke, ki bi sicer ogrozili varnost slike vsebnika aplikacije. Če nepotrebna orodja hranite ločeno, omejite napadalno površino slike vsebnika aplikacije.

Peti korak: Konfiguracija jedra

Na koncu se pogovorimo o naprednejši tehniki.

Kubernetes je izjemno prilagodljiva platforma, ki vam omogoča izvajanje delovnih obremenitev, kakor koli se vam zdi primerno. Imamo številne visoko učinkovite aplikacije, ki zahtevajo izjemno veliko virov. Po obsežnem testiranju obremenitve smo ugotovili, da je ena od aplikacij težko dohajala pričakovano prometno obremenitev, ko so veljale privzete nastavitve Kubernetes.

Vendar vam Kubernetes omogoča zagon privilegiranega vsebnika, ki spreminja samo parametre jedra za določen pod. Tukaj je tisto, kar smo uporabili za spremembo največjega števila odprtih povezav:

initContainers:
  - name: sysctl
     image: alpine:3.10
     securityContext:
         privileged: true
      command: ['sh', '-c', "sysctl -w net.core.somaxconn=32768"]

To je naprednejša tehnika, ki pogosto ni potrebna. Če pa se vaša aplikacija težko spopada z veliko obremenitvijo, lahko poskusite prilagoditi nekatere od teh nastavitev. Več informacij o tem procesu in nastavitvah različnih vrednosti - kot vedno v uradni dokumentaciji.

Na koncu

Čeprav se morda zdi, da je Kubernetes že pripravljena rešitev, je treba izvesti nekaj ključnih korakov, da bodo aplikacije delovale nemoteno.

Med celotno selitvijo na Kubernetes je pomembno slediti »ciklu testiranja obremenitve«: zaženite aplikacijo, jo preizkusite pod obremenitvijo, opazujte metrike in vedenje pri spreminjanju velikosti, prilagodite konfiguracijo na podlagi teh podatkov, nato znova ponovite ta cikel.

Bodite realni glede pričakovanega prometa in ga poskusite preseči, da vidite, katere komponente se najprej pokvarijo. S tem ponavljajočim se pristopom je lahko le nekaj od naštetih priporočil dovolj za uspeh. Morda bo potrebna bolj poglobljena prilagoditev.

Vedno si zastavite ta vprašanja:

  1. Koliko sredstev porabijo aplikacije in kako se bo ta količina spremenila?
  2. Kakšne so dejanske zahteve glede skaliranja? Koliko prometa bo v povprečju prenesla aplikacija? Kaj pa konice prometa?
  3. Kako pogosto se bo storitev morala razširiti? Kako hitro morajo novi podi začeti delovati, da prejmejo promet?
  4. Kako elegantno se stroki izklopijo? Je to sploh potrebno? Ali je mogoče doseči uvedbo brez izpadov?
  5. Kako zmanjšati varnostna tveganja in omejiti škodo zaradi morebitnih ogroženih podov? Ali imajo katere koli storitve dovoljenja ali dostope, ki jih ne potrebujejo?

Kubernetes ponuja neverjetno platformo, ki vam omogoča uporabo najboljših praks za uvajanje na tisoče storitev v gruči. Vendar so vse aplikacije različne. Včasih je za izvedbo potrebno malo več dela.

Na srečo Kubernetes ponuja potrebne nastavitve za doseganje vseh tehničnih ciljev. Z uporabo kombinacije zahtev in omejitev virov, sond Liveness in Readiness, inicialnih vsebnikov, omrežnih pravilnikov in prilagajanja jedra po meri lahko dosežete visoko zmogljivost skupaj s toleranco napak in hitro razširljivostjo.

Kaj še prebrati:

  1. Najboljše prakse in najboljše prakse za izvajanje vsebnikov in Kubernetes v produkcijskih okoljih.
  2. 90+ uporabnih orodij za Kubernetes: uvajanje, upravljanje, spremljanje, varnost in drugo.
  3. Naš kanal Around Kubernetes v Telegramu.

Vir: www.habr.com

Dodaj komentar