"Kubernetes iliongeza latency kwa mara 10": ni nani wa kulaumiwa kwa hili?

Kumbuka. tafsiri.: Makala haya, yaliyoandikwa na Galo Navarro, ambaye anashikilia wadhifa wa Mhandisi Mkuu wa Programu katika kampuni ya Uropa ya Adevinta, ni "uchunguzi" wa kuvutia na wa kufundisha katika uwanja wa uendeshaji wa miundombinu. Kichwa chake cha asili kilipanuliwa kidogo katika tafsiri kwa sababu ambayo mwandishi anaelezea mwanzoni kabisa.

"Kubernetes iliongeza latency kwa mara 10": ni nani wa kulaumiwa kwa hili?

Kumbuka kutoka kwa mwandishi: Inaonekana kama chapisho hili kuvutia umakini zaidi kuliko ilivyotarajiwa. Bado ninapata maoni yenye hasira kwamba kichwa cha makala hiyo kinapotosha na kwamba baadhi ya wasomaji wamehuzunishwa. Ninaelewa sababu za kile kinachotokea, kwa hiyo, licha ya hatari ya kuharibu fitina nzima, nataka kukuambia mara moja nini makala hii inahusu. Jambo la kustaajabisha ambalo nimeona wakati timu zinahamia Kubernetes ni kwamba wakati wowote shida inapotokea (kama vile muda wa kusubiri baada ya uhamiaji), jambo la kwanza ambalo hulaumiwa ni Kubernetes, lakini baadaye ikawa kwamba orchestrator sio kweli. lawama. Nakala hii inazungumza juu ya kesi kama hiyo. Jina lake linarudia mshangao wa mmoja wa watengenezaji wetu (baadaye utaona kuwa Kubernetes haina uhusiano wowote nayo). Hutapata ufunuo wowote wa kushangaza kuhusu Kubernetes hapa, lakini unaweza kutarajia masomo kadhaa mazuri kuhusu mifumo changamano.

Wiki chache zilizopita, timu yangu ilikuwa ikihamisha huduma ndogo ndogo hadi kwenye jukwaa la msingi lililojumuisha CI/CD, muda wa kukimbia unaotegemea Kubernetes, vipimo na vitu vingine vyema. Hatua hiyo ilikuwa ya majaribio: tulipanga kuichukulia kama msingi na kuhamisha takriban huduma 150 zaidi katika miezi ijayo. Wote wanawajibika kwa utendakazi wa baadhi ya majukwaa makubwa zaidi ya mtandaoni nchini Uhispania (Infojobs, Fotocasa, n.k.).

Baada ya sisi kupeleka ombi kwa Kubernetes na kuelekeza tena watu wengine huko, tukio la kushangaza lilitungoja. Kuchelewa (kuchelewa) maombi katika Kubernetes yalikuwa juu mara 10 kuliko katika EC2. Kwa ujumla, ilikuwa ni lazima ama kupata suluhisho la tatizo hili, au kuachana na uhamiaji wa microservice (na, ikiwezekana, mradi mzima).

Kwa nini muda wa kusubiri ni wa juu sana katika Kubernetes kuliko katika EC2?

Ili kupata kizuizi, tulikusanya vipimo kwenye njia nzima ya ombi. Usanifu wetu ni rahisi: lango la API (Zuul) hutuma maombi kwa hali ya huduma ndogo katika EC2 au Kubernetes. Katika Kubernetes tunatumia NGINX Ingress Controller, na backends ni vitu vya kawaida kama Kuhamishwa na programu ya JVM kwenye jukwaa la Spring.

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

Shida ilionekana kuhusishwa na latency ya awali kwenye sehemu ya nyuma (niliweka alama eneo la shida kwenye grafu kama "xx"). Kwenye EC2, jibu la maombi lilichukua takriban 20ms. Katika Kubernetes, latency iliongezeka hadi 100-200 ms.

Tuliwaondoa haraka washukiwa wanaohusishwa na mabadiliko ya wakati wa utekelezaji. Toleo la JVM linabaki kuwa sawa. Matatizo ya uwekaji vyombo pia hayakuwa na uhusiano wowote nayo: programu tayari ilikuwa ikifanya kazi kwa mafanikio katika vyombo kwenye EC2. Inapakia? Lakini tuliona ucheleweshaji wa hali ya juu hata kwa ombi 1 kwa sekunde. Vipindi vya kusitisha ukusanyaji wa taka vinaweza pia kupuuzwa.

Mmoja wa wasimamizi wetu wa Kubernetes alishangaa ikiwa programu ilikuwa na utegemezi wa nje kwa sababu hoja za DNS zilisababisha matatizo kama hayo hapo awali.

Dhana ya 1: azimio la jina la DNS

Kwa kila ombi, programu yetu inafikia mfano wa AWS Elasticsearch mara moja hadi tatu katika kikoa kama elastic.spain.adevinta.com. Ndani ya vyombo vyetu kuna ganda, ili tuweze kuangalia ikiwa kutafuta kikoa huchukua muda mrefu.

Hoja za DNS kutoka kwa kontena:

[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

Maombi sawa kutoka kwa mojawapo ya matukio ya EC2 ambapo programu inaendeshwa:

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

Kwa kuzingatia kwamba utafutaji ulichukua takriban 30ms, ikawa wazi kuwa azimio la DNS wakati wa kufikia Elasticsearch lilikuwa linachangia kuongezeka kwa muda wa kusubiri.

Walakini, hii ilikuwa ya kushangaza kwa sababu mbili:

  1. Tayari tunayo tani ya programu za Kubernetes zinazoingiliana na rasilimali za AWS bila kuteseka kutokana na muda wa kusubiri wa hali ya juu. Kwa sababu yoyote, inahusiana haswa na kesi hii.
  2. Tunajua kuwa JVM hufanya akiba ya DNS ya kumbukumbu. Katika picha zetu, thamani ya TTL imeandikwa $JAVA_HOME/jre/lib/security/java.security na kuweka kwa sekunde 10: networkaddress.cache.ttl = 10. Kwa maneno mengine, JVM inapaswa kuhifadhi maswali yote ya DNS kwa sekunde 10.

Ili kuthibitisha dhana ya kwanza, tuliamua kuacha kupiga simu kwa DNS kwa muda na kuona ikiwa tatizo limeondoka. Kwanza, tuliamua kusanidi upya programu ili iwasiliane moja kwa moja na Elasticsearch kwa anwani ya IP, badala ya kupitia jina la kikoa. Hili lingehitaji mabadiliko ya msimbo na utumaji mpya, kwa hivyo tulipanga kikoa kwa anwani yake ya IP /etc/hosts:

34.55.5.111 elastic.spain.adevinta.com

Sasa chombo kilipokea IP karibu mara moja. Hii ilisababisha uboreshaji fulani, lakini tulikuwa karibu kidogo na viwango vya kusubiri vilivyotarajiwa. Ingawa azimio la DNS lilichukua muda mrefu, sababu halisi bado ilitukwepa.

Uchunguzi kupitia mtandao

Tuliamua kuchambua trafiki kutoka kwa kontena kwa kutumia tcpdumpkuona nini hasa kinatokea kwenye mtandao:

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

Kisha tulituma maombi kadhaa na kupakua ukamataji wao (kubectl cp my-service:/capture.pcap capture.pcap) kwa uchambuzi zaidi katika Wireshark.

Hakukuwa na chochote cha kutiliwa shaka kuhusu maswali ya DNS (isipokuwa jambo moja dogo ambalo nitazungumzia baadaye). Lakini kulikuwa na mambo fulani yasiyo ya kawaida katika jinsi huduma yetu ilivyoshughulikia kila ombi. Ifuatayo ni picha ya skrini ya kunasa inayoonyesha ombi likikubaliwa kabla ya jibu kuanza:

"Kubernetes iliongeza latency kwa mara 10": ni nani wa kulaumiwa kwa hili?

Nambari za kifurushi zinaonyeshwa kwenye safu wima ya kwanza. Kwa uwazi, nimeweka rangi mitiririko tofauti ya TCP.

Mkondo wa kijani unaoanza na pakiti 328 unaonyesha jinsi mteja (172.17.22.150) alivyoanzisha muunganisho wa TCP kwenye kontena (172.17.36.147). Baada ya kupeana mkono kwa mara ya kwanza (328-330), kifurushi cha 331 kililetwa HTTP GET /v1/.. - ombi linaloingia kwa huduma yetu. Mchakato wote ulichukua 1 ms.

Mtiririko wa kijivu (kutoka kwa pakiti 339) unaonyesha kuwa huduma yetu ilituma ombi la HTTP kwa mfano wa Elasticsearch (hakuna TCP kupeana mkono kwa sababu inatumia muunganisho uliopo). Hii ilichukua 18ms.

Hadi sasa kila kitu kiko sawa, na nyakati takriban zinahusiana na ucheleweshaji unaotarajiwa (20-30 ms wakati unapimwa kutoka kwa mteja).

Walakini, sehemu ya bluu inachukua 86ms. Nini kinaendelea ndani yake? Kwa pakiti 333, huduma yetu ilituma ombi la HTTP GET kwa /latest/meta-data/iam/security-credentials, na mara baada yake, juu ya muunganisho sawa wa TCP, ombi lingine la GET kwa /latest/meta-data/iam/security-credentials/arn:...

Tuligundua kuwa hii ilirudiwa kwa kila ombi katika ufuatiliaji. Azimio la DNS kwa kweli ni polepole kidogo kwenye vyombo vyetu (maelezo ya jambo hili yanafurahisha sana, lakini nitaihifadhi kwa nakala tofauti). Ilibadilika kuwa sababu ya ucheleweshaji wa muda mrefu ilikuwa simu kwa huduma ya Metadata ya Instance ya AWS kwa kila ombi.

Hypothesis 2: simu zisizo za lazima kwa AWS

Miisho yote miwili ni ya AWS Instance Metadata API. Huduma yetu ndogo hutumia huduma hii inapoendesha Elasticsearch. Simu zote mbili ni sehemu ya mchakato wa uidhinishaji wa kimsingi. Mwisho unaofikiwa kwenye ombi la kwanza hutoa jukumu la IAM linalohusishwa na mfano.

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

Ombi la pili linauliza mwisho wa pili kwa ruhusa za muda kwa mfano huu:

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

Mteja anaweza kuzitumia kwa muda mfupi na lazima apate cheti kipya mara kwa mara (kabla hazijatolewa Expiration) Muundo ni rahisi: AWS huzungusha funguo za muda mara kwa mara kwa sababu za usalama, lakini wateja wanaweza kuzihifadhi kwa dakika chache ili kufidia adhabu ya utendakazi inayohusishwa na kupata vyeti vipya.

AWS Java SDK inapaswa kuchukua jukumu la kupanga mchakato huu, lakini kwa sababu fulani hii haifanyiki.

Baada ya kutafuta maswala kwenye GitHub, tulipata shida #1921. Alitusaidia kuamua mwelekeo wa "kuchimba" zaidi.

AWS SDK husasisha vyeti wakati mojawapo ya masharti yafuatayo yanapotokea:

  • Tarehe ya mwisho wa matumizi (Expiration) Kuanguka ndani EXPIRATION_THRESHOLD, zimewekwa misimbo ngumu hadi dakika 15.
  • Muda zaidi umepita tangu jaribio la mwisho la kufanya upya vyeti kuliko REFRESH_THRESHOLD, iliyohifadhiwa kwa muda wa dakika 60.

Ili kuona tarehe halisi ya mwisho wa matumizi ya vyeti tunavyopokea, tuliendesha amri za cURL zilizo hapo juu kutoka kwa kontena na mfano wa EC2. Kipindi cha uhalali wa cheti kilichopokelewa kutoka kwa chombo kiligeuka kuwa kifupi zaidi: dakika 15 haswa.

Sasa kila kitu kimekuwa wazi: kwa ombi la kwanza, huduma yetu ilipokea vyeti vya muda. Kwa kuwa hazikuwa halali kwa zaidi ya dakika 15, SDK ya AWS ingeamua kuzisasisha kuhusu ombi linalofuata. Na hii ilitokea kwa kila ombi.

Kwa nini muda wa uhalali wa vyeti umekuwa mfupi?

Metadata ya Instance ya AWS imeundwa kufanya kazi na matukio ya EC2, si Kubernetes. Kwa upande mwingine, hatukutaka kubadilisha kiolesura cha programu. Kwa hili tulitumia KIAM - zana ambayo, kwa kutumia mawakala kwenye kila nodi ya Kubernetes, inaruhusu watumiaji (wahandisi wanaopeleka programu kwenye kundi) kugawa majukumu ya IAM kwa vyombo kwenye maganda kana kwamba ni matukio ya EC2. KIAM hukata simu kwa huduma ya AWS Instance Metadata na kuzichakata kutoka kwa akiba yake, baada ya kuzipokea kutoka kwa AWS. Kutoka kwa mtazamo wa maombi, hakuna kinachobadilika.

KIAM inatoa vyeti vya muda mfupi kwa maganda. Hii inaleta maana kwa kuzingatia kwamba muda wa wastani wa maisha ya ganda ni mfupi kuliko ule wa mfano wa EC2. Kipindi chaguo-msingi cha uhalali wa vyeti sawa na dakika 15 sawa.

Kama matokeo, ikiwa unafunika maadili yote mawili juu ya kila mmoja, shida itatokea. Kila cheti kinachotolewa kwa ombi huisha baada ya dakika 15. Hata hivyo, SDK ya AWS Java inalazimisha usasishaji wa cheti chochote ambacho kimesalia chini ya dakika 15 kabla ya tarehe ya mwisho wa matumizi.

Kwa hivyo, cheti cha muda kinalazimishwa kufanywa upya kwa kila ombi, ambalo linajumuisha simu kadhaa kwa API ya AWS na kusababisha ongezeko kubwa la muda wa kusubiri. Katika AWS Java SDK tulipata ombi la huduma, ambayo inataja shida kama hiyo.

Suluhisho liligeuka kuwa rahisi. Tulisanidi upya KIAM ili kuomba vyeti vilivyo na muda mrefu zaidi wa uhalali. Mara hii ilipotokea, maombi yalianza kutiririka bila ushiriki wa huduma ya Metadata ya AWS, na muda wa kusubiri ulishuka hadi viwango vya chini zaidi kuliko EC2.

Matokeo

Kulingana na uzoefu wetu wa uhamaji, mojawapo ya vyanzo vya kawaida vya matatizo si hitilafu katika Kubernetes au vipengele vingine vya mfumo. Pia haishughulikii dosari zozote za kimsingi katika huduma ndogo ndogo tunazosambaza. Matatizo mara nyingi hutokea kwa sababu tu tunaweka vipengele tofauti pamoja.

Tunachanganya pamoja mifumo changamano ambayo haijawahi kuingiliana hapo awali, tukitarajia kwamba kwa pamoja itaunda mfumo mmoja, mkubwa zaidi. Ole, vipengele zaidi, nafasi zaidi ya makosa, juu ya entropy.

Kwa upande wetu, kusubiri kwa muda mrefu hakukuwa matokeo ya hitilafu au maamuzi mabaya katika Kubernetes, KIAM, AWS Java SDK, au huduma zetu ndogo. Ilikuwa ni matokeo ya kuchanganya mipangilio miwili ya chaguo-msingi huru: moja katika KIAM, nyingine katika AWS Java SDK. Zikichukuliwa kando, vigezo vyote viwili vina maana: sera amilifu ya kufanya upya cheti katika AWS Java SDK, na muda mfupi wa uhalali wa vyeti katika KAIM. Lakini unapoziweka pamoja, matokeo huwa hayatabiriki. Suluhu mbili za kujitegemea na za kimantiki sio lazima ziwe na maana zikiunganishwa.

PS kutoka kwa mtafsiri

Unaweza kujifunza zaidi kuhusu usanifu wa shirika la KIAM la kuunganisha AWS IAM na Kubernetes kwenye Makala hii kutoka kwa waundaji wake.

Soma pia kwenye blogi yetu:

Chanzo: mapenzi.com

Kuongeza maoni