"Kubernetes-ek 10 aldiz handitu zuen latentzia": noren erruduna?

Ohar. itzul.: Adevinta Europako enpresan Software Ingeniari Nagusi kargua duen Galo Navarrok idatzitako artikulu hau “ikerketa” liluragarri eta hezigarria da azpiegituren operazioen arloan. Bere jatorrizko izenburua apur bat zabaldu zen itzulpenean egileak hasieran azaltzen duen arrazoi batengatik.

"Kubernetes-ek 10 aldiz handitu zuen latentzia": noren erruduna?

Egilearen oharra: mezu honen itxura du erakarri espero baino askoz arreta handiagoa. Oraindik ere iruzkin haserreak jasotzen ditut, artikuluaren izenburua engainagarria dela eta irakurle batzuk penatuta daudela. Gertatzen ari denaren arrazoiak ulertzen ditut, beraz, intriga osoa hondatzeko arriskua egon arren, berehala esan nahi dizut artikulu hau zertan den. Taldeak Kubernetesera migratzen diren heinean ikusi dudan gauza bitxia da arazoren bat sortzen den bakoitzean (adibidez, migrazio baten ondoren latentzia areagotzea), errua Kubernetes dela leporatzen zaion lehen gauza, baina gero ikusten da orkestratzailea ez dela benetan. errua. Artikulu honek horrelako kasu bati buruz hitz egiten du. Bere izenak gure garatzaileetako baten harridura errepikatzen du (gero ikusiko duzu Kubernetesek ez duela zerikusirik). Hemen ez duzu Kubernetes-i buruzko errebelazio harrigarririk aurkituko, baina sistema konplexuei buruzko ikasgai on pare bat espero ditzakezu.

Duela aste pare bat, nire taldeak mikrozerbitzu bakar bat migratzen ari zen CI/CD, Kubernetesen oinarritutako exekuzio-denbora, neurketak eta beste ontasun batzuk biltzen zituen oinarrizko plataforma batera. Mugimenduak proba izaera zuen: oinarritzat hartu eta datozen hilabeteetan gutxi gorabehera 150 zerbitzu gehiago transferitzea aurreikusi genuen. Horiek guztiak dira Espainiako sareko plataforma handienetako batzuen ustiapenaren arduradunak (Infojobs, Fotocasa, etab.).

Aplikazioa Kubernetes-en zabaldu eta hara trafiko pixka bat birbideratu ondoren, sorpresa kezkagarri bat itxaroten genuen. Atzerapena (latentzia) Kubernetesen eskaerak EC10n baino 2 aldiz handiagoak izan ziren. Orokorrean, beharrezkoa zen arazo honi irtenbidea aurkitzea, edo mikrozerbitzuaren (eta, agian, proiektu osoa) migrazioari uztea.

Zergatik da latentzia askoz handiagoa Kubernetes-en EC2-n baino?

Botil-lepoa aurkitzeko, eskaeren bide osoan neurketak bildu ditugu. Gure arkitektura sinplea da: API atebide batek (Zuul) eskaerak EC2 edo Kubernetes-eko mikrozerbitzuen instantzietara bidaltzen ditu. Kubernetes-en NGINX Ingress Controller erabiltzen dugu, eta backendak bezalako objektu arruntak dira Inplementazio Spring plataformako JVM aplikazio batekin.

                                  EC2
                            +---------------+
                            |  +---------+  |
                            |  |         |  |
                       +-------> BACKEND |  |
                       |    |  |         |  |
                       |    |  +---------+  |                   
                       |    +---------------+
             +------+  |
Public       |      |  |
      -------> ZUUL +--+
traffic      |      |  |              Kubernetes
             +------+  |    +-----------------------------+
                       |    |  +-------+      +---------+ |
                       |    |  |       |  xx  |         | |
                       +-------> NGINX +------> BACKEND | |
                            |  |       |  xx  |         | |
                            |  +-------+      +---------+ |
                            +-----------------------------+

Arazoa atzeko amaierako hasierako latentziarekin lotuta zegoela zirudien (arazoaren eremua grafikoan "xx" gisa markatu nuen). EC2-n, aplikazioaren erantzunak 20 ms inguru behar izan zituen. Kubernetes-en, latentzia 100-200 ms-ra igo zen.

Azkar baztertu genituen exekuzio-aldaketarekin zerikusia duten susmagarriak. JVM bertsioak berdin jarraitzen du. Edukiontzien arazoek ere ez zuten horrekin zerikusirik: aplikazioa arrakastaz exekutatzen ari zen jada EC2ko edukiontzietan. Kargatzen? Baina latentzia handiak ikusi genituen segundoko eskaera batean ere. Zabor bilketarako etenaldiak ere alde batera utzi daitezke.

Gure Kubernetes-eko administratzaile batek galdetu zuen aplikazioak kanpoko menpekotasunak ote zituen DNS kontsultek iraganean antzeko arazoak eragin baitzituzten.

1. hipotesia: DNS izenen ebazpena

Eskaera bakoitzerako, gure aplikazioak AWS Elasticsearch instantzia batera sartzen da bat edo hiru aldiz bezalako domeinu batean elastic.spain.adevinta.com. Gure edukiontzien barruan maskor bat dago, domeinu bat bilatzeko benetan denbora luzea hartzen duen egiaztatu ahal izango dugu.

Edukiontzitik DNS kontsultak:

[root@be-851c76f696-alf8z /]# while true; do dig "elastic.spain.adevinta.com" | grep time; sleep 2; done
;; Query time: 22 msec
;; Query time: 22 msec
;; Query time: 29 msec
;; Query time: 21 msec
;; Query time: 28 msec
;; Query time: 43 msec
;; Query time: 39 msec

Aplikazioa exekutatzen ari den EC2 instantzia bateko antzeko eskaerak:

bash-4.4# while true; do dig "elastic.spain.adevinta.com" | grep time; sleep 2; done
;; Query time: 77 msec
;; Query time: 0 msec
;; Query time: 0 msec
;; Query time: 0 msec
;; Query time: 0 msec

Bilaketak 30 ms inguru behar zituela kontuan hartuta, argi geratu zen Elasticsearch sartzean DNS ebazpenak latentzia handitzen laguntzen zuela.

Hala ere, hau arraroa izan zen bi arrazoirengatik:

  1. Dagoeneko baditugu Kubernetes aplikazio ugari, latentzia handirik jasan gabe AWS baliabideekin komunikatzen direnak. Arrazoia edozein dela ere, kasu honi dagokio bereziki.
  2. Badakigu JVM-k memorian DNS cachea egiten duela. Gure irudietan, TTL balioa idatzita dago $JAVA_HOME/jre/lib/security/java.security eta ezarri 10 segundotan: networkaddress.cache.ttl = 10. Beste era batera esanda, JVM-k DNS kontsulta guztiak 10 segundoz gorde behar ditu.

Lehenengo hipotesia berresteko, DNSra deitzeari uztea erabaki genuen denbora batez eta ea arazoa desagertu zen. Lehenik eta behin, aplikazioa birkonfiguratzea erabaki genuen, Elasticsearch-ekin zuzenean komunika zedin IP helbidearen bidez, ez domeinu-izen baten bidez. Honek kode aldaketak eta inplementazio berri bat eskatuko lituzke, beraz, domeinua bere IP helbidean mapatu besterik ez dugu egin /etc/hosts:

34.55.5.111 elastic.spain.adevinta.com

Orain edukiontziak IP bat jaso zuen ia berehala. Honek nolabaiteko hobekuntza ekarri zuen, baina espero ziren latentzia-mailetatik apur bat hurbilago egon ginen. DNS ebazpenak denbora luzea izan bazuen ere, benetako arrazoiak oraindik ere ihes egin zigun.

Diagnostikoak sare bidez

Erabiliz edukiontzitik trafikoa aztertzea erabaki genuen tcpdumpsarean zehazki zer gertatzen den ikusteko:

[root@be-851c76f696-alf8z /]# tcpdump -leni any -w capture.pcap

Ondoren, hainbat eskaera bidali genituen eta haien harrapaketa deskargatu genuen (kubectl cp my-service:/capture.pcap capture.pcap) azterketa gehiago egiteko Wireshark.

DNS kontsultei buruz ez zegoen ezer susmagarririk (geroago hitz egingo dudan gauza txiki bat izan ezik). Baina bitxikeria batzuk zeuden gure zerbitzuak eskaera bakoitza kudeatzeko moduan. Jarraian, erantzuna hasi aurretik eskaera onartzen ari den argazkiaren pantaila-argazkia dago:

"Kubernetes-ek 10 aldiz handitu zuen latentzia": noren erruduna?

Pakete-zenbakiak lehen zutabean agertzen dira. Argitasuna lortzeko, kolorez kodetu ditut TCP fluxu desberdinak.

328 paketearekin hasten den korronte berdeak bezeroak (172.17.22.150) TCP konexioa edukiontziarekin (172.17.36.147) nola ezarri zuen erakusten du. Hasierako bostekoaren ondoren (328-330), 331 paketea ekarri zuen HTTP GET /v1/.. — gure zerbitzurako sarrerako eskaera bat. Prozesu osoak 1 ms behar izan zuen.

Korronte grisak (339. paketetik) erakusten du gure zerbitzuak HTTP eskaera bidali zuela Elasticsearch instantziara (ez dago TCP esku-ematerik lehendik dagoen konexio bat erabiltzen ari delako). Honek 18 ms behar izan zituen.

Orain arte dena ondo dago, eta denborak gutxi gorabehera espero diren atzerapenei dagozkie (20-30 ms bezeroarengandik neurtuta).

Hala ere, sekzio urdinak 86 ms behar ditu. Zer gertatzen da bertan? 333 paketearekin, gure zerbitzuak HTTP GET eskaera bidali zion /latest/meta-data/iam/security-credentials, eta berehala, TCP konexio beraren bidez, beste GET eskaera bat /latest/meta-data/iam/security-credentials/arn:...

Traza osoan zehar eskaera guztietan errepikatzen zela ikusi genuen. DNS bereizmena apur bat motelagoa da gure edukiontzietan (fenomeno honen azalpena nahiko interesgarria da, baina aparteko artikulu baterako gordeko dut). Atzerapen luzeen kausa eskaera bakoitzean AWS Instantzia Metadatuen zerbitzura egindako deiak zirela ondorioztatu zen.

2. hipotesia: AWSra alferrikako deiak

Bi muturrak dira AWS Instance Metadata APIa. Gure mikrozerbitzuak zerbitzu hau erabiltzen du Elasticsearch exekutatzen ari den bitartean. Bi deialdiak oinarrizko baimen-prozesuaren parte dira. Lehenengo eskaeran atzitzen den amaiera-puntuak instantziari lotutako IAM rola ematen du.

/ # curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
arn:aws:iam::<account_id>:role/some_role

Bigarren eskaerak bigarren amaierako puntuari instantzia honetarako aldi baterako baimenak eskatzen dizkio:

/ # curl http://169.254.169.254/latest/meta-data/iam/security-credentials/arn:aws:iam::<account_id>:role/some_role`
{
    "Code" : "Success",
    "LastUpdated" : "2012-04-26T16:39:16Z",
    "Type" : "AWS-HMAC",
    "AccessKeyId" : "ASIAIOSFODNN7EXAMPLE",
    "SecretAccessKey" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
    "Token" : "token",
    "Expiration" : "2017-05-17T15:09:54Z"
}

Bezeroak denbora laburrean erabil ditzake eta aldiro ziurtagiri berriak eskuratu behar ditu (hau baino lehen Expiration). Eredua sinplea da: AWS-k aldi baterako gakoak maiz biratzen ditu segurtasun arrazoiengatik, baina bezeroek minutu batzuetan gorde ditzakete ziurtagiri berriak lortzearekin lotutako errendimendu-zigorra konpentsatzeko.

AWS Java SDK-k bere gain hartu beharko luke prozesu hau antolatzeko ardura, baina arrazoiren batengatik ez da hori gertatzen.

GitHub-en arazoak bilatu ondoren, arazo bat topatu dugu # 1921. Gehiago "zulatu" zein norabidetan zehazten lagundu zigun.

AWS SDK-k ziurtagiriak eguneratzen ditu baldintza hauetako bat gertatzen denean:

  • Iraungitze data (Expiration) erori EXPIRATION_THRESHOLD, 15 minuturako gogor kodetuta.
  • Ziurtagiriak berritzeko azken saiakeratik baino denbora gehiago igaro da REFRESH_THRESHOLD, 60 minutuz gogor kodetuta.

Jasotzen ditugun ziurtagirien benetako iraungitze-data ikusteko, goiko cURL komandoak exekutatu ditugu edukiontzitik zein EC2 instantziatik. Edukiontzitik jasotako ziurtagiriaren balio-epea askoz laburragoa izan zen: zehazki 15 minutu.

Orain dena argi geratu da: lehen eskaerarako, gure zerbitzuak behin-behineko ziurtagiriak jaso zituen. 15 minutu baino gehiago balio ez zutenez, AWS SDK-k hurrengo eskaera batean eguneratzea erabakiko luke. Eta hori gertatzen zen eskaera guztietan.

Zergatik laburtu da ziurtagirien balio-epea?

AWS Instance Metadata EC2 instantziekin lan egiteko diseinatuta dago, ez Kubernetesekin. Bestalde, ez dugu aplikazioaren interfazea aldatu nahi izan. Horretarako erabili dugu KIAM - Kubernetes nodo bakoitzeko agenteak erabiliz, erabiltzaileek (kluster batean aplikazioak zabaltzen dituzten ingeniariek) edukiontziei IAM rolak esleitzea ahalbidetzen dien tresna, EC2 instantziak balira bezala. KIAMek AWS Instance Metadata zerbitzurako deiak atzematen ditu eta bere cachetik prozesatzen ditu, aurretik AWStik jasota. Aplikazioaren ikuspuntutik, ez da ezer aldatzen.

KIAMek epe laburreko ziurtagiriak hornitzen ditu lekak. Honek zentzuzkoa du kontuan hartuta pod baten batez besteko iraupena EC2 instantzia batena baino laburragoa dela. Ziurtagirien balio-epe lehenetsia 15 minutu berdinen berdina.

Ondorioz, bi balio lehenetsiak bata bestearen gainean gainjartzen badituzu, arazo bat sortzen da. Aplikazio bati emandako ziurtagiri bakoitza 15 minuturen buruan iraungiko da. Hala ere, AWS Java SDK-k iraungitze-data baino 15 minutu baino gutxiago falta diren ziurtagiriak berritzera behartzen du.

Ondorioz, eskaera bakoitzarekin aldi baterako ziurtagiria berritzera behartuta dago, eta horrek AWS APIra dei pare bat dakar eta latentzia nabarmen handitzea eragiten du. AWS Java SDK-n aurkitu dugu eginbide eskaera, antzeko arazo bat aipatzen duena.

Irtenbidea sinplea izan zen. KIAM berriro konfiguratu dugu, balio-epe luzeagoa duten ziurtagiriak eskatzeko. Hori gertatu ondoren, eskaerak AWS Metadata zerbitzuaren parte-hartzerik gabe hasi ziren eta latentzia EC2-n baino are maila baxuagoetara jaitsi zen.

Findings

Migrazioekin dugun esperientzian oinarrituta, arazo-iturri ohikoenetako bat ez dira Kubernetesen edo plataformako beste elementu batzuen akatsak. Portatzen ari garen mikrozerbitzuen oinarrizko akatsik ere ez du zuzentzen. Arazoak askotan sortzen dira elementu desberdinak elkartzen ditugulako.

Bata bestearen artean inoiz elkarreragin ez duten sistema konplexuak nahasten ditugu, elkarrekin sistema bakarra eta handiagoa osatuko dutelakoan. Ai, zenbat eta elementu gehiago, orduan eta toki gehiago akatsetarako, orduan eta entropia handiagoa.

Gure kasuan, latentzia handia ez zen Kubernetes, KIAM, AWS Java SDK edo gure mikrozerbitzuetan akatsen edo erabaki txarren ondorio izan. Bi ezarpen lehenetsi independente konbinatzearen emaitza izan zen: bata KIAM-en, bestea AWS Java SDK-n. Bereiz hartuta, bi parametroek zentzua dute: ziurtagiriak berritzeko politika aktiboa AWS Java SDK-n eta ziurtagirien balio-epe laburra KAIM-en. Baina horiek bateratzen dituzunean, emaitzak ezustekoak bihurtzen dira. Bi soluzio independente eta logikoek ez dute zentzurik izan behar konbinatuta.

PS itzultzailetik

AWS IAM Kubernetes-ekin integratzeko KIAM utilitatearen arkitekturari buruz gehiago jakin dezakezu hemen: Artikulu honetan bere sortzaileengandik.

Irakurri ere gure blogean:

Iturria: www.habr.com

Gehitu iruzkin berria