ShĂ«n. pĂ«rkth.: Ky artikull, i shkruar nga Galo Navarro, Principal Software Engineer nĂ« kompaninĂ« evropiane Adevinta, Ă«shtĂ« njĂ« âhetimâ tĂ«rheqĂ«s dhe mjaft udhĂ«zues nĂ« fushĂ«n e operimit tĂ« infrastrukturĂ«s. Titulli origjinal Ă«shtĂ« plotĂ«suar paksa nĂ« pĂ«rkthim pĂ«r njĂ« arsye qĂ« autori e shpjegon qĂ« nĂ« fillim.

ShĂ«nim nga autori: Me sa duket, ky publikim shumĂ« mĂ« tepĂ«r vĂ«mendje sesa pritej. Ende marr komente tĂ« zemĂ«ruara se titulli i artikullit Ă«shtĂ« çorientues dhe se disa lexues janĂ« zhgĂ«njyer. E kuptoj pse po ndodh kjo, ndaj, edhe me rrezikun pĂ«r tâia prishur gjithĂ« intrigĂ«n, dua ta sqaroj menjĂ«herĂ« se pĂ«r çfarĂ« flet ky artikull. GjatĂ« kalimit tĂ« ekipeve nĂ« Kubernetes, kam vĂ«rejtur njĂ« dukuri interesante: sa herĂ« qĂ« shfaqet njĂ« problem, pĂ«r shembull rritja e vonesĂ«s pas migrimit, faji i vihet menjĂ«herĂ« Kubernetes, por mĂ« pas del se orkestruesi, nĂ« thelb, nuk ka faj. Ky artikull tregon njĂ« rast tĂ« tillĂ«. Titulli i tij pĂ«rsĂ«rit thirrjen e njĂ«rit prej zhvilluesve tanĂ« (mĂ« vonĂ« do ta shihni se Kubernetes nuk ka aspak lidhje me kĂ«tĂ«). KĂ«tu nuk do tĂ« gjeni zbulime tĂ« papritura pĂ«r Kubernetes, por mund tĂ« prisni disa mĂ«sime tĂ« vlefshme mbi sistemet komplekse.
Para disa javësh, ekipi im po merrej me migrimin e një mikroshërbimi në platformën kryesore, e cila përfshin CI/CD, një mjedis pune të bazuar në Kubernetes, metrika dhe mjete të tjera të dobishme. Ky transferim kishte karakter pilot: planifikonim ta përdornim si bazë dhe gjatë muajve në vijim të transferonim edhe rreth 150 shërbime të tjera. Të gjitha ato mbështesin funksionimin e disa prej platformave më të mëdha online në Spanjë (Infojobs, Fotocasa etj.).
Pasi e vendosëm aplikacionin në Kubernetes dhe ridrejtuam drejt tij një pjesë të trafikut, na priste një surprizë shqetësuese. Vonesa e (latency) kërkesave në Kubernetes ishte 10 herë më e lartë se në EC2. Pra, duhej ose të gjenim një zgjidhje për këtë problem, ose të hiqnim dorë nga migrimi i mikroshërbimit (dhe ndoshta nga i gjithë projekti).
Pse është vonesa në Kubernetes kaq shumë më e lartë se në EC2?
Për të gjetur ngushticën, mblodhëm metrika përgjatë gjithë rrugës së kërkesës. Arkitektura jonë është e thjeshtë: API gateway (Zuul) i prokson kërkesat te instancat e mikroshërbimit në EC2 ose Kubernetes. Në Kubernetes përdorim NGINX Ingress Controller, ndërsa backend-et janë objekte të zakonshme të llojit me një aplikacion JVM në platformën Spring.
EC2
+---------------+
| +---------+ |
| | | |
+-------> BACKEND | |
| | | | |
| | +---------+ |
| +---------------+
+------+ |
Publik | | |
-------> ZUUL +--+
trafik | | | Kubernetes
+------+ | +-----------------------------+
| | +-------+ +---------+ |
| | | | xx | | |
+-------> NGINX +------> BACKEND | |
| | | xx | | |
| +-------+ +---------+ |
+-----------------------------+Dukej se problemi lidhej me vonesĂ«n nĂ« fazĂ«n fillestare tĂ« pĂ«rpunimit nĂ« backend (segmentin problematik nĂ« skemĂ« e kam shĂ«nuar si «xx»). NĂ« EC2, pĂ«rgjigjja e aplikacionit merrte rreth 20 ms. NĂ« Kubernetes, vonesa rritej nĂ« 100â200 ms.
Shpejt përjashtuam të dyshuarit më të mundshëm që lidhen me ndryshimin e mjedisit të ekzekutimit. Versioni i JVM mbeti i njëjtë. As kontejnerizimi nuk ishte shkaku: aplikacioni tashmë funksiononte me sukses në kontejnerë në EC2. Ngarkesa? Por vërenim vonesë të lartë edhe me 1 kërkesë në sekondë. Edhe pauzat e mbledhjes së mbeturinave mund të shpërfilleshin.
Njëri nga administratorët tanë të Kubernetes pyeti nëse aplikacioni kishte varësi të jashtme, pasi më parë kërkesat DNS kishin shkaktuar probleme të ngjashme.
Hipoteza 1: Zgjidhja e emrave DNS
Në çdo kërkesë, aplikacioni ynë kontakton nga një deri në tre herë një instancë AWS Elasticsearch në një domen si p.sh. elastic.spain.adevinta.com. Brenda kontejnerëve ne , prandaj mund të verifikojmë nëse kërkimi i domenit vërtet kërkon shumë kohë.
Kërkesa DNS nga kontejneri:
[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 msecKërkesa të ngjashme nga një prej instancave EC2 ku po ekzekutohet aplikacioni:
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 msecDuke qenë se kërkimi zgjat rreth 30 ms, u bë e qartë se zgjidhja DNS gjatë lidhjes me Elasticsearch vërtet kontribuonte në rritjen e vonesës.
Megjithatë, kjo ishte e çuditshme për dy arsye:
- Ne kemi tashmë shumë aplikacione në Kubernetes që ndërveprojnë me burimet e AWS, por nuk vuajnë nga vonesa të mëdha. Cilado qoftë arsyeja, ajo lidhet pikërisht me këtë rast.
- Ne e dimë që JVM kryen cache DNS në memorie. Në imazhet tona, vlera TTL është përcaktuar në
$JAVA_HOME/jre/lib/security/java.securitydhe Ă«shtĂ« vendosur nĂ« 10 sekonda:networkaddress.cache.ttl = 10. Me fjalĂ« tĂ« tjera, JVM duhet tâi ruajĂ« nĂ« cache tĂ« gjitha kĂ«rkesat DNS pĂ«r 10 sekonda.
Për të konfirmuar hipotezën e parë, vendosëm të heqim përkohësisht dorë nga thirrjet DNS dhe të shohim nëse problemi do të zhdukej. Fillimisht vendosëm ta rikonfiguronim aplikacionin që të lidhej me Elasticsearch drejtpërdrejt përmes adresës IP, dhe jo përmes emrit të domenit. Kjo do të kërkonte ndryshim të kodit dhe një deployment të ri, ndaj thjesht e mapuam domenin me adresën e tij IP në /etc/hosts:
34.55.5.111 elastic.spain.adevinta.comTani kontejneri e merrte IP-në pothuajse menjëherë. Kjo solli një përmirësim të caktuar, por ne vetëm sa iu afruam pak nivelit të pritshëm të vonesës. Edhe pse zgjidhja DNS merrte shumë kohë, shkaku i vërtetë ende na shpëtonte.
Diagnostikimi përmes rrjetit
Vendosëm të analizonim trafikun nga kontejneri me ndihmën e tcpdump, për të ndjekur saktësisht se çfarë po ndodh në rrjet:
[root@be-851c76f696-alf8z /]# tcpdump -leni any -w capture.pcap Më pas dërguam disa kërkesa dhe shkarkuam capture-in e tyre (kubectl cp my-service:/capture.pcap capture.pcap) për analizë të mëtejshme në .
Në kërkesat DNS nuk kishte asgjë të dyshimtë (përveç një detaji të vogël, për të cilin do të flas më vonë). Por kishte disa çudi në mënyrën se si shërbimi ynë përpunonte çdo kërkesë. Më poshtë është një pamje nga capture-i, që tregon pranimin e kërkesës deri në fillimin e përgjigjes:

Numrat e paketave janë dhënë në kolonën e parë. Për qartësi, kam theksuar me ngjyra rrjedhat e ndryshme TCP.
Rrjedha e gjelbĂ«r, qĂ« fillon nga paketa 328, tregon se si klienti (172.17.22.150) krijoi njĂ« lidhje TCP me kontejnerin (172.17.36.147). Pas handshake-it fillestar (328-330), paketa 331 solli HTTP GET /v1/.. â kĂ«rkesĂ«n hyrĂ«se drejt shĂ«rbimit tonĂ«. I gjithĂ« procesi zgjati 1 ms.
Rrjedha gri (nga paketa 339) tregon se shërbimi ynë dërgoi një kërkesë HTTP te instanca e Elasticsearch (nuk ka TCP handshake, sepse po përdoret një lidhje ekzistuese). Kjo mori 18 ms.
Deri këtu gjithçka është në rregull dhe kohët përafërsisht përputhen me vonesat e pritshme (20-30 ms në matjet nga klienti).
MegjithatĂ«, seksioni blu zgjat 86 ms. ĂfarĂ« ndodh aty? Me paketĂ«n 333, shĂ«rbimi ynĂ« dĂ«rgoi njĂ« kĂ«rkesĂ« HTTP GET te /latest/meta-data/iam/security-credentials, dhe menjĂ«herĂ« pas saj, mbi tĂ« njĂ«jtin lidhje TCP, edhe njĂ« kĂ«rkesĂ« tjetĂ«r GET te /latest/meta-data/iam/security-credentials/arn:...
Zbuluam se kjo përsëritej me çdo kërkesë përgjatë gjithë gjurmimit. Zgjidhja e DNS është vërtet pak më e ngadaltë në kontejnerët tanë (shpjegimi i këtij fenomeni është mjaft interesant, por do ta ruaj për një artikull më vete). Doli se shkaku i vonesave të mëdha ishin thirrjet ndaj shërbimit AWS Instance Metadata në çdo kërkesë.
Hipoteza 2: thirrje të panevojshme drejt AWS
Të dy endpoint-et i përkasin . Mikrosherbimi ynë e përdor këtë shërbim gjatë punës me Elasticsearch. Të dyja thirrjet janë pjesë e procesit bazë të autorizimit. Endpoint-i që thirret nga kërkesa e parë kthen rolin IAM të lidhur me instancën.
/ # curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
arn:aws:iam::<account_id>:role/some_roleKërkesa e dytë i drejtohet endpoint-it të dytë për të marrë kredenciale të përkohshme për këtë instancë:
/ # 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"
} Klienti mund tâi pĂ«rdorĂ« ato pĂ«r njĂ« periudhĂ« tĂ« shkurtĂ«r kohe dhe periodikisht duhet tĂ« marrĂ« kredenciale tĂ« reja (deri nĂ« Expiration). Modeli Ă«shtĂ« i thjeshtĂ«: AWS bĂ«n rotacion tĂ« shpeshtĂ« tĂ« çelĂ«save tĂ« pĂ«rkohshĂ«m pĂ«r arsye sigurie, por klientĂ«t mund tâi ruajnĂ« nĂ« cache pĂ«r disa minuta, duke kompensuar uljen e performancĂ«s qĂ« lidhet me marrjen e kredencialeve tĂ« reja.
AWS Java SDK duhet të merret me organizimin e këtij procesi, por për ndonjë arsye kjo nuk po ndodh.
Duke kërkuar te issues në GitHub, hasëm problemin . Ai na ndihmoi të përcaktonim drejtimin ku duhej të "gërmonim" më tej.
AWS SDK i rifreskon kredencialet kur plotësohet një nga kushtet e mëposhtme:
- Afati i skadimit të tyre (
Expiration) hyn nëEXPIRATION_THRESHOLD, i vendosur në mënyrë të pandryshueshme në kod në 15 minuta. - Që nga përpjekja e fundit për të rifreskuar kredencialet ka kaluar më shumë kohë se
REFRESH_THRESHOLD, i hardcode-uar në 60 minuta.
Për të parë afatin real të skadimit të kredencialeve që merrnim, ekzekutuam komandat cURL të mësipërme nga kontejneri dhe nga instanca EC2. Kohëzgjatja e kredencialit të marrë nga kontejneri doli të ishte shumë më e shkurtër: saktësisht 15 minuta.
Tani gjithçka u bĂ« e qartĂ«: pĂ«r kĂ«rkesĂ«n e parĂ«, shĂ«rbimi ynĂ« merrte certifikata tĂ« pĂ«rkohshme. MeqenĂ«se afati i tyre i vlefshmĂ«risĂ« nuk i kalonte 15 minuta, gjatĂ« kĂ«rkesĂ«s pasuese AWS SDK vendoste tâi rinovonte. Dhe kjo ndodhte me çdo kĂ«rkesĂ«.
Pse afati i vlefshmërisë së certifikatave u shkurtua?
ShĂ«rbimi AWS Instance Metadata Ă«shtĂ« krijuar pĂ«r tĂ« punuar me instancat EC2, jo me Kubernetes. Nga ana tjetĂ«r, ne nuk donim tĂ« ndryshonim ndĂ«rfaqen e aplikacioneve. PĂ«r kĂ«tĂ« pĂ«rdorĂ«m â njĂ« mjet qĂ«, me ndihmĂ«n e agjentĂ«ve nĂ« çdo nyje Kubernetes, u lejon pĂ«rdoruesve (inxhinierĂ«ve qĂ« vendosin aplikacionet nĂ« cluster) tâu caktojnĂ« role IAM konteinerĂ«ve nĂ« pod-e, sikur tĂ« ishin instanca EC2. KIAM kap thirrjet drejt shĂ«rbimit AWS Instance Metadata dhe i pĂ«rpunon ato nga cache-i i vet, pasi i merr paraprakisht nga AWS. Nga kĂ«ndvĂ«shtrimi i aplikacionit, asgjĂ« nuk ndryshon.
KIAM u furnizon pod-eve certifikata afatshkurtra. Kjo është e arsyeshme, duke pasur parasysh se kohëzgjatja mesatare e jetës së një pod-i është më e shkurtër se ajo e një instance EC2. Si parazgjedhje, afati i vlefshmërisë së certifikatave .
Si pĂ«rfundim, nĂ«se i vendosim kĂ«to dy vlera tĂ« parazgjedhura njĂ«ra mbi tjetrĂ«n, lind problemi. Ădo certifikatĂ« e dhĂ«nĂ« aplikacionit skadon pas 15 minutash. NdĂ«rkohĂ«, AWS Java SDK rinovon me detyrim çdo certifikatĂ« sĂ« cilĂ«s i kanĂ« mbetur mĂ« pak se 15 minuta deri nĂ« skadim.
Si rezultat, certifikata e përkohshme rinovohet me detyrim në çdo kërkesë, gjë që sjell disa thirrje drejt API të AWS dhe çon në një rritje të ndjeshme të vonesës. Në AWS Java SDK gjetëm një , ku përmendet një problem i ngjashëm.
Zgjidhja doli të ishte e thjeshtë. Thjesht e rikonfiguruam KIAM që të kërkonte certifikata me afat vlefshmërie më të gjatë. Sapo ndodhi kjo, kërkesat filluan të kalonin pa përfshirjen e shërbimit AWS Metadata, dhe vonesa ra madje në një nivel edhe më të ulët se në EC2.
Përfundimet
Bazuar në përvojën tonë me migrimet, mund të themi se një nga burimet më të shpeshta të problemeve nuk janë gabimet në Kubernetes apo në elementë të tjerë të platformës. Po ashtu, ai nuk lidhet me ndonjë mangësi themelore të mikrosherbimeve që po migrojmë. Problemet shpesh lindin thjesht sepse po bashkojmë së bashku elementë të ndryshëm.
Ne po përziejmë sisteme komplekse që më parë nuk kanë ndërvepruar kurrë me njëri-tjetrin, duke pritur që së bashku të formojnë një sistem të vetëm, më të madh. Fatkeqësisht, sa më shumë elemente të ketë, aq më shumë hapësirë ka për gabime dhe aq më e lartë bëhet entropia.
Në rastin tonë, vonesa e lartë nuk ishte rezultat i gabimeve ose vendimeve të këqija në Kubernetes, KIAM, AWS Java SDK apo në mikroshërbimin tonë. Ajo ishte pasojë e bashkimit të dy parametrave të pavarur të vendosur si parazgjedhje: njëri në KIAM, tjetri në AWS Java SDK. Veçmas, të dy parametrat kanë kuptim: si politika aktive e rinovimit të certifikatave në AWS Java SDK, ashtu edhe afati i shkurtër i vlefshmërisë së certifikatave në KIAM. Por kur i kombinon, rezultatet bëhen të paparashikueshme. Dy zgjidhje të pavarura dhe logjike nuk do të thotë domosdoshmërisht se kanë kuptim kur bashkohen.
P.S. nga përkthyesi
Mësoni më shumë rreth arkitekturës së mjetit KIAM për integrimin e AWS IAM me Kubernetes në nga krijuesit e tij.
Ndërsa në blogun tonë lexoni edhe:
- «»;
- «»;
- «»;
- «».
Burimi: habr.com
