"Kubernetes ferhege latency mei 10 kear": wa is dêr skuldich foar?

Noat. transl.: Dit artikel, skreaun troch Galo Navarro, dy't de posysje hâldt fan Principal Software Engineer by it Europeeske bedriuw Adevinta, is in fassinearjende en ynstruktive "ûndersyk" op it mêd fan ynfrastruktuer operaasjes. De oarspronklike titel waard wat útwreide yn oersetting foar in reden dy't de auteur oan it begjin ferklearret.

"Kubernetes ferhege latency mei 10 kear": wa is dêr skuldich foar?

Notysje fan de skriuwer: Liket dizze post oanlutsen folle mear omtinken as ferwachte. Ik krij noch altyd lilke opmerkingen dat de titel fan it artikel misleidend is en dat guon lêzers fertrietlik binne. Ik begryp de redenen foar wat der bart, dêrom, nettsjinsteande it risiko fan it ferneatigjen fan 'e hiele yntrige, wol ik jo fuortendaliks fertelle wêr't dit artikel oer giet. In nijsgjirrich ding dat ik haw sjoen doe't teams migrearje nei Kubernetes is dat wannear't in probleem ûntstiet (lykas ferhege latency nei in migraasje), it earste ding dat de skuld kriget is Kubernetes, mar dan docht bliken dat de orkestrator net echt is om skuld. Dit artikel fertelt oer ien sa'n gefal. De namme werhellet de útrop fan ien fan ús ûntwikkelders (letter sille jo sjen dat Kubernetes der neat mei te krijen hat). Jo sille hjir gjin ferrassende iepenbieringen fine oer Kubernetes, mar jo kinne in pear goede lessen ferwachtsje oer komplekse systemen.

In pear wike lyn migrearre myn team in inkele mikrotsjinst nei in kearnplatfoarm dat CI / CD, in Kubernetes-basearre runtime, metriken en oare goodies omfette. De ferhuzing wie fan proefkarakter: wy wiene fan plan om it as basis te nimmen en de kommende moannen noch sa'n 150 tsjinsten oer te dragen. Allegear binne se ferantwurdlik foar de eksploitaasje fan guon fan 'e grutste online platfoarms yn Spanje (Infojobs, Fotocasa, ensfh).

Nei't wy de applikaasje nei Kubernetes ynsetten en wat ferkear dernei omlieden, wachte ús in alarmearjende ferrassing. Fertraging (latency) oanfragen yn Kubernetes wiene 10 kear heger as yn EC2. Yn 't algemien wie it nedich om in oplossing te finen foar dit probleem, of de migraasje fan' e mikrotsjinst (en, mooglik, it heule projekt) te ferlitten.

Wêrom is latency safolle heger yn Kubernetes dan yn EC2?

Om it knelpunt te finen, hawwe wy metriken sammele lâns it heule fersykpaad. Us arsjitektuer is ienfâldich: in API-gateway (Zuul) proxies fersiken nei mikroservice-eksimplaren yn EC2 of Kubernetes. Yn Kubernetes brûke wy NGINX Ingress Controller, en de backends binne gewoane objekten lykas Deployment mei in JVM-applikaasje op it Spring-platfoarm.

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

It probleem like te wêzen relatearre oan inisjele latency yn 'e efterkant (ik markearre it probleemgebiet op' e grafyk as "xx"). Op EC2 naam de oanfraachreaksje sawat 20ms. Yn Kubernetes is de latency ferhege nei 100-200 ms.

Wy hawwe de wierskynlike fertochten yn ferbân mei de runtimeferoaring fluch ôfwiisd. De JVM-ferzje bliuwt itselde. Kontenerisaasjeproblemen hiene der ek neat mei te krijen: de applikaasje draaide al mei sukses yn konteners op EC2. Loading? Mar wy observearre hege latencies sels by 1 fersyk per sekonde. Pauzes foar it opheljen fan jiskefet kinne ek negeare wurde.

Ien fan ús Kubernetes-admins frege har ôf oft de applikaasje eksterne ôfhinklikens hie, om't DNS-fragen yn it ferline ferlykbere problemen hawwe feroarsake.

Hypoteze 1: DNS-nammeresolúsje

Foar elk fersyk hat ús applikaasje ien oant trije kear tagong ta in AWS Elasticsearch-eksimplaar yn in domein lykas elastic.spain.adevinta.com. Binnen ús konteners der is in skulp, sadat wy kinne kontrolearje oft it sykjen nei in domein eins lang duorret.

DNS-fragen fan kontener:

[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

Fergelykbere oanfragen fan ien fan 'e EC2-eksimplaren wêr't de applikaasje rint:

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

Yn betinken nommen dat it sykjen sawat 30ms duorre, waard it dúdlik dat DNS-resolúsje by tagong ta Elasticsearch yndie bydroegen oan de ferheging fan latency.

Dit wie lykwols frjemd om twa redenen:

  1. Wy hawwe al in ton Kubernetes-applikaasjes dy't ynteraksje mei AWS-boarnen sûnder te lijen fan hege latency. Wat de reden ek is, it giet spesifyk oer dit gefal.
  2. Wy witte dat de JVM DNS-caching yn it ûnthâld docht. Yn ús ôfbyldings is de TTL-wearde yn skreaun $JAVA_HOME/jre/lib/security/java.security en set op 10 sekonden: networkaddress.cache.ttl = 10. Mei oare wurden, de JVM moat alle DNS-fragen foar 10 sekonden cache.

Om de earste hypoteze te befêstigjen, hawwe wy besletten om in skoft te stopjen mei DNS te roppen en te sjen oft it probleem fuortgie. Earst hawwe wy besletten de applikaasje opnij te konfigurearjen sadat it direkt mei Elasticsearch kommunisearre troch IP-adres, ynstee fan fia in domeinnamme. Dit soe koadeferoarings en in nije ynset fereaskje, dus wy hawwe it domein gewoan yn kaart brocht oan it IP-adres yn /etc/hosts:

34.55.5.111 elastic.spain.adevinta.com

No krige de kontener hast daliks in IP. Dit resultearre yn wat ferbettering, mar wy wiene mar wat tichter by de ferwachte latencynivo's. Hoewol DNS-resolúsje in lange tiid duorre, ûntgiek de echte reden ús noch.

Diagnostyk fia netwurk

Wy besletten om te analysearjen ferkear út de kontener mei help tcpdumpom te sjen wat der krekt bart op it netwurk:

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

Wy hawwe doe ferskate oanfragen stjoerd en har opname downloade (kubectl cp my-service:/capture.pcap capture.pcap) foar fierdere analyze yn Wireshark.

D'r wie neat fertocht oer de DNS-fragen (útsein ien lyts ding dêr't ik letter oer sil prate). Mar d'r wiene bepaalde nuverheden yn 'e manier wêrop ús tsjinst elk fersyk behannele. Hjirûnder is in skermôfbylding fan 'e opname dy't sjen lit dat it fersyk wurdt akseptearre foardat it antwurd begjint:

"Kubernetes ferhege latency mei 10 kear": wa is dêr skuldich foar?

Pakketnûmers wurde werjûn yn 'e earste kolom. Foar dúdlikens haw ik de ferskillende TCP-streamen kleurkodearre.

De griene stream dy't begjint mei pakket 328 lit sjen hoe't de kliïnt (172.17.22.150) in TCP-ferbining mei de kontener oprjochte (172.17.36.147). Nei de earste handshake (328-330), pakket 331 brocht HTTP GET /v1/.. - in ynkommend fersyk oan ús tsjinst. It hiele proses duorre 1 ms.

De grize stream (fan pakket 339) lit sjen dat ús tsjinst in HTTP-fersyk nei de Elasticsearch-eksimplaar stjoerde (d'r is gjin TCP-handshake omdat it in besteande ferbining brûkt). Dit duorre 18 ms.

Oant no ta is alles goed, en de tiden komme rûchwei oerien mei de ferwachte fertragingen (20-30 ms as mjitten fan 'e kliïnt).

De blauwe seksje nimt lykwols 86ms. Wat bart der yn? Mei pakket 333 stjoerde ús tsjinst in HTTP GET-fersyk nei /latest/meta-data/iam/security-credentials, en fuort dêrnei, oer deselde TCP-ferbining, in oare GET-fersyk oan /latest/meta-data/iam/security-credentials/arn:...

Wy fûnen dat dit werhelle mei elk fersyk yn it spoar. DNS-resolúsje is yndie wat stadiger yn ús konteners (de ferklearring foar dit ferskynsel is frij nijsgjirrich, mar ik bewarje it foar in apart artikel). It die bliken dat de oarsaak fan 'e lange fertragingen oproppen wiene nei de AWS Instance Metadata-tsjinst op elk fersyk.

Hypothese 2: ûnnedige oproppen nei AWS

Beide einpunten hearre ta AWS Instance Metadata API. Us mikrotsjinst brûkt dizze tsjinst by it útfieren fan Elasticsearch. Beide oproppen meitsje diel út fan it basisautorisaasjeproses. It einpunt dat tagong is op it earste fersyk jout de IAM-rol út dy't ferbûn is mei it eksimplaar.

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

It twadde fersyk freget it twadde einpunt foar tydlike tagongsrjochten foar dit eksimplaar:

/ # 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"
}

De kliïnt kin se foar in koarte perioade brûke en moat periodyk nije sertifikaten krije (foardat se binne Expiration). It model is ienfâldich: AWS draait tydlike kaaien faak om feiligensredenen, mar kliïnten kinne se in pear minuten cache om te kompensearjen foar de prestaasjeboete ferbûn mei it krijen fan nije sertifikaten.

De AWS Java SDK moat de ferantwurdlikens oernimme foar it organisearjen fan dit proses, mar om ien of oare reden bart dit net.

Nei it sykjen fan problemen op GitHub, kamen wy in probleem tsjin #1921. Se holp ús de rjochting te bepalen wêryn't wy fierder "grave".

De AWS SDK fernijt sertifikaten as ien fan 'e folgjende betingsten foarkomt:

  • Ferfaldatum (Expiration) Falle yn EXPIRATION_THRESHOLD, hardcoded oant 15 minuten.
  • Mear tiid is ferrûn sûnt de lêste poging om sertifikaten te fernijen REFRESH_THRESHOLD, hardcoded foar 60 minuten.

Om de werklike ferfaldatum te sjen fan 'e sertifikaten dy't wy ûntfange, hawwe wy de boppesteande cURL-kommando's útfierd fan sawol de kontener as de EC2-eksimplaar. De jildigensperioade fan it út de kontener ûntfongen sertifikaat blykte folle koarter te wêzen: krekt 15 minuten.

No is alles dúdlik wurden: foar it earste fersyk krige ús tsjinst tydlike sertifikaten. Om't se net mear dan 15 minuten jildich wiene, soe de AWS SDK beslute om se te aktualisearjen op in folgjende fersyk. En dit barde mei elk fersyk.

Wêrom is de jildigensperioade fan sertifikaten koarter wurden?

AWS Instance Metadata is ûntworpen om te wurkjen mei EC2-eksimplaren, net Kubernetes. Oan 'e oare kant woene wy ​​de applikaasje-ynterface net feroarje. Hjirfoar hawwe wy brûkt KIAM - in ark dat, mei help fan aginten op elke Kubernetes-knooppunt, brûkers (yngenieurs dy't applikaasjes ynsette op in kluster) kinne IAM-rollen tawize oan konteners yn pods as wiene se EC2-eksimplaren. KIAM ûnderskept oproppen nei de AWS Instance Metadata-tsjinst en ferwurket se út syn cache, nei't se se earder ûntfongen hawwe fan AWS. Ut it eachpunt fan tapassing feroaret neat.

KIAM leveret koarte termyn sertifikaten oan pods. Dit makket sin yn betinken dat de gemiddelde libbensduur fan in pod koarter is dan dy fan in EC2-eksimplaar. Standert jildigens perioade foar sertifikaten gelyk oan deselde 15 minuten.

As gefolch, as jo beide standertwearden boppe-op elkoar oerlizze, ûntstiet in probleem. Elk sertifikaat levere oan in applikaasje ferrint nei 15 minuten. De AWS Java SDK twingt lykwols in fernijing fan elk sertifikaat dat minder dan 15 minuten oer hat foar syn ferfaldatum.

As resultaat wurdt it tydlike sertifikaat twongen om te fernijen mei elk fersyk, wat in pear oproppen omfettet nei de AWS API en resultearret yn in signifikante ferheging fan latency. Yn AWS Java SDK fûnen wy funksje fersyk, dy't in ferlykber probleem neamt.

De oplossing blykte ienfâldich te wêzen. Wy hawwe KIAM gewoan opnij konfigureare om sertifikaten oan te freegjen mei in langere jildigensperioade. Sadree't dit barde, begûnen fersiken te streamen sûnder de dielname fan 'e AWS Metadata-tsjinst, en de latency sakke nei noch legere nivo's dan yn EC2.

befinings

Op grûn fan ús ûnderfining mei migraasjes is ien fan 'e meast foarkommende boarnen fan problemen gjin bugs yn Kubernetes of oare eleminten fan it platfoarm. It behannelet ek gjin fûnemintele gebreken yn 'e mikrotsjinsten dy't wy portearje. Problemen ûntsteane faak gewoan om't wy ferskate eleminten byinoar sette.

Wy mingje komplekse systemen dy't noch noait earder mei-inoar ynteraksje hawwe, en ferwachtsje dat se tegearre in ienich, grutter systeem sille foarmje. Och, hoe mear eleminten, hoe mear romte foar flaters, hoe heger de entropy.

Yn ús gefal wie de hege latency net it gefolch fan bugs of minne besluten yn Kubernetes, KIAM, AWS Java SDK, of ús mikroservice. It wie it resultaat fan it kombinearjen fan twa ûnôfhinklike standertynstellingen: ien yn KIAM, de oare yn 'e AWS Java SDK. Apart nommen binne beide parameters sinfol: it aktive belied foar sertifikaatfernijing yn 'e AWS Java SDK, en de koarte jildigensperioade fan sertifikaten yn KAIM. Mar as jo se byinoar sette, wurde de resultaten ûnfoarspelber. Twa ûnôfhinklike en logyske oplossingen hoege gjin sin te meitsjen as se kombineare.

PS fan oersetter

Jo kinne mear leare oer de arsjitektuer fan it KIAM-hulpprogramma foar yntegraasje fan AWS IAM mei Kubernetes op dit artikel fan syn skeppers.

Lês ek op ús blog:

Boarne: www.habr.com

Add a comment