«Kubernetes-ը 10 անգամ ավելացրել է ուշացումը». ո՞վ է դրա մեղավորը:

Նշում. թարգմ.Այս հոդվածը, որը գրվել է Գալո Նավարոյի կողմից, ով զբաղեցնում է եվրոպական Adevinta ընկերության գլխավոր ծրագրային ինժեների պաշտոնը, հետաքրքրաշարժ և ուսանելի «հետաքննություն» է ենթակառուցվածքների գործառնությունների ոլորտում: Նրա սկզբնական վերնագիրը թարգմանության մեջ փոքր-ինչ ընդարձակվել է այն պատճառով, որը հեղինակը բացատրում է հենց սկզբում։

«Kubernetes-ը 10 անգամ ավելացրել է ուշացումը». ո՞վ է դրա մեղավորը:

Նշում հեղինակից: Կարծես այս գրառմանը ներգրավված սպասվածից շատ ավելի մեծ ուշադրություն։ Ես դեռ զայրացած մեկնաբանություններ եմ ստանում, որ հոդվածի վերնագիրը ապակողմնորոշիչ է, և որոշ ընթերցողներ տխրում են: Ես հասկանում եմ տեղի ունեցողի պատճառները, հետևաբար, չնայած ամբողջ ինտրիգը փչացնելու ռիսկին, ուզում եմ անմիջապես պատմել, թե ինչի մասին է այս հոդվածը։ Հետաքրքիր բան, որ ես տեսել եմ, երբ թիմերը տեղափոխվում են Կուբերնետես, այն է, որ երբ խնդիր է առաջանում (օրինակ՝ միգրացիայից հետո հետաձգման ավելացումը), առաջինը, որին մեղադրում են Կուբերնետեսը, բայց հետո պարզվում է, որ նվագախումբն իրականում չի ցանկանում մեղադրել. Այս հոդվածը պատմում է նման մի դեպքի մասին։ Դրա անունը կրկնում է մեր ծրագրավորողներից մեկի բացականչությունը (հետագայում կտեսնեք, որ Kubernetes-ը դրա հետ կապ չունի)։ Այստեղ Kubernetes-ի մասին որևէ զարմանալի բացահայտում չեք գտնի, բայց բարդ համակարգերի մասին կարող եք մի քանի լավ դաս ակնկալել:

Մի քանի շաբաթ առաջ իմ թիմը տեղափոխում էր մեկ միկրոսերվիս դեպի հիմնական հարթակ, որը ներառում էր CI/CD, Kubernetes-ի վրա հիմնված գործարկման ժամանակ, չափումներ և այլ առավելություններ: Այս քայլը փորձնական բնույթ էր կրում. մենք նախատեսում էինք հիմք ընդունել այն և մոտ 150 ծառայություններ փոխանցել առաջիկա ամիսներին: Նրանք բոլորն էլ պատասխանատու են Իսպանիայի մի քանի խոշոր առցանց հարթակների գործունեության համար (Infojobs, Fotocasa և այլն):

Այն բանից հետո, երբ մենք տեղադրեցինք հավելվածը Kubernetes-ում և որոշ տրաֆիկ վերահասցեավորեցինք դեպի այն, մեզ տագնապալի անակնկալ էր սպասվում: Հետաձգում (ուշացում) Kubernetes-ում հարցումները 10 անգամ ավելի շատ էին, քան EC2-ում: Ընդհանրապես, պետք էր կամ լուծում գտնել այս խնդրին, կամ հրաժարվել միկրոսերվիսի միգրացիայից (և, հնարավոր է, ամբողջ նախագծից):

Ինչո՞ւ է ուշացումը Kubernetes-ում այդքան ավելի բարձր, քան EC2-ում:

Խցանումը գտնելու համար մենք չափումներ ենք հավաքել հարցումների ողջ ճանապարհով: Մեր ճարտարապետությունը պարզ է. API gateway-ը (Zuul) վստահված անձը խնդրում է միկրոսպասարկման օրինակներ EC2-ում կամ Kubernetes-ում: Kubernetes-ում մենք օգտագործում ենք NGINX Ingress Controller, իսկ հետնամասերը սովորական օբյեկտներ են, ինչպիսիք են տեղակայումը JVM հավելվածով Spring հարթակում:

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

Թվում էր, թե խնդիրը կապված է սկզբնական հետաձգման հետ (ես գծապատկերում խնդրի տարածքը նշել եմ որպես «xx»): EC2-ում դիմումի պատասխանը տևեց մոտ 20 ms: Kubernetes-ում հետաձգումը բարձրացել է մինչև 100-200 ms:

Մենք արագ հեռացրինք գործարկման ժամանակի փոփոխության հետ կապված հավանական կասկածյալներին: JVM տարբերակը մնում է նույնը: Կոնտեյներացման խնդիրները նույնպես կապ չունեին դրա հետ. հավելվածն արդեն հաջողությամբ աշխատում էր EC2-ի բեռնարկղերում: Բեռնվում է Բայց մենք նկատեցինք բարձր հետաձգումներ նույնիսկ վայրկյանում 1 խնդրանքով: Աղբահանության դադարները նույնպես կարող են անտեսվել:

Մեր Kubernetes-ի ադմիններից մեկը հետաքրքրվեց, թե արդյոք հավելվածն ունի արտաքին կախվածություն, քանի որ DNS հարցումները նախկինում նմանատիպ խնդիրներ են առաջացրել:

Վարկած 1. DNS անվան լուծում

Յուրաքանչյուր հարցման համար մեր հավելվածը մեկից երեք անգամ մուտք է գործում AWS Elasticsearch օրինակ՝ նման տիրույթում elastic.spain.adevinta.com. Մեր տարաների ներսում կա պատյան, այնպես որ մենք կարող ենք ստուգել, ​​թե արդյոք տիրույթի որոնումը իրականում երկար ժամանակ է պահանջում։

DNS հարցումներ կոնտեյներից.

[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

Նմանատիպ հարցումներ EC2 դեպքերից մեկից, որտեղ դիմումն աշխատում է.

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

Հաշվի առնելով, որ որոնումը տևել է մոտ 30 մս, պարզ դարձավ, որ Elasticsearch մուտք գործելու ժամանակ DNS լուծումը իսկապես նպաստում է հետաձգման ավելացմանը:

Այնուամենայնիվ, սա տարօրինակ էր երկու պատճառով.

  1. Մենք արդեն ունենք մի տոննա Kubernetes հավելվածներ, որոնք փոխազդում են AWS ռեսուրսների հետ՝ չտուժելով բարձր ուշացումից: Ինչ էլ որ լինի պատճառը, դա վերաբերում է կոնկրետ այս գործին:
  2. Մենք գիտենք, որ JVM-ն կատարում է հիշողության մեջ DNS քեշավորում: Մեր պատկերներում TTL արժեքը գրված է $JAVA_HOME/jre/lib/security/java.security և սահմանել 10 վայրկյան. networkaddress.cache.ttl = 10. Այլ կերպ ասած, JVM-ը պետք է պահի բոլոր DNS հարցումները 10 վայրկյան:

Առաջին վարկածը հաստատելու համար մենք որոշեցինք դադարեցնել DNS զանգերը որոշ ժամանակով և տեսնել, թե արդյոք խնդիրը վերացել է: Նախ, մենք որոշեցինք վերակազմավորել հավելվածն այնպես, որ այն ուղղակիորեն հաղորդակցվի Elasticsearch-ի հետ IP հասցեով, այլ ոչ թե տիրույթի անվան միջոցով: Սա կպահանջի կոդի փոփոխություններ և նոր տեղակայում, այնպես որ մենք պարզապես քարտեզագրեցինք տիրույթը իր IP հասցեով /etc/hosts:

34.55.5.111 elastic.spain.adevinta.com

Այժմ կոնտեյները գրեթե ակնթարթորեն ստացավ IP: Սա հանգեցրեց որոշակի բարելավման, բայց մենք միայն մի փոքր ավելի մոտ էինք սպասվող հետաձգման մակարդակներին: Չնայած DNS-ի լուծումը երկար ժամանակ տևեց, իրական պատճառը դեռևս մեզանից խուսափում էր:

Ախտորոշում ցանցի միջոցով

Մենք որոշեցինք վերլուծել երթևեկությունը բեռնարկղից՝ օգտագործելով tcpdumpտեսնել, թե կոնկրետ ինչ է կատարվում ցանցում.

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

Այնուհետև մենք ուղարկեցինք մի քանի հարցումներ և ներբեռնեցինք դրանց գրավումը (kubectl cp my-service:/capture.pcap capture.pcap) հետագա վերլուծության համար Wireshark.

DNS հարցումների մեջ կասկածելի ոչինչ չկար (բացառությամբ մի փոքր բանի, որի մասին ես կխոսեմ ավելի ուշ): Բայց կային որոշակի տարօրինակություններ մեր ծառայության կողմից յուրաքանչյուր խնդրանքով: Ստորև ներկայացված է նկարահանման սքրինշոթը, որը ցույց է տալիս, որ հարցումն ընդունվել է մինչև պատասխանի սկիզբը.

«Kubernetes-ը 10 անգամ ավելացրել է ուշացումը». ո՞վ է դրա մեղավորը:

Փաթեթի համարները ներկայացված են առաջին սյունակում: Պարզության համար ես գունավոր կոդավորել եմ TCP-ի տարբեր հոսքերը:

328 փաթեթով սկսվող կանաչ հոսքը ցույց է տալիս, թե ինչպես է հաճախորդը (172.17.22.150) TCP կապ հաստատել կոնտեյների հետ (172.17.36.147): Նախնական ձեռքսեղմումից հետո (328-330) բերվեց 331 փաթեթը HTTP GET /v1/.. — մուտքային հարցում մեր ծառայությանը: Ամբողջ գործընթացը տևեց 1 ms.

Մոխրագույն հոսքը (339 փաթեթից) ցույց է տալիս, որ մեր ծառայությունը HTTP հարցում է ուղարկել Elasticsearch օրինակին (չկա TCP ձեռքսեղմում, քանի որ այն օգտագործում է գոյություն ունեցող կապ): Սա տևեց 18 ms:

Առայժմ ամեն ինչ լավ է, և ժամանակները մոտավորապես համապատասխանում են սպասվող ուշացումներին (20-30 ms, երբ չափվում է հաճախորդից):

Այնուամենայնիվ, կապույտ հատվածը տեւում է 86 մս: Ի՞նչ է կատարվում դրա մեջ։ 333 փաթեթով մեր ծառայությունն ուղարկեց HTTP GET հարցում /latest/meta-data/iam/security-credentials, և դրանից անմիջապես հետո, նույն TCP կապի միջոցով, մեկ այլ GET հարցում /latest/meta-data/iam/security-credentials/arn:...

Մենք գտանք, որ դա կրկնվում է հետքի ընթացքում յուրաքանչյուր խնդրանքով: DNS լուծումը իսկապես մի փոքր ավելի դանդաղ է մեր կոնտեյներներում (այս երեւույթի բացատրությունը բավականին հետաքրքիր է, բայց ես այն կպահեմ առանձին հոդվածի համար): Պարզվեց, որ երկար ձգձգումների պատճառը յուրաքանչյուր հարցումով AWS Instance Metadata ծառայության զանգերն էին:

Վարկած 2. անհարկի զանգեր դեպի AWS

Երկու վերջնակետերը պատկանում են AWS Instance Metadata API. Մեր միկրոսերվիսը օգտագործում է այս ծառայությունը Elasticsearch-ը գործարկելիս: Երկու զանգերն էլ հիմնական թույլտվության գործընթացի մի մասն են: Վերջնական կետը, որը հասանելի է առաջին հարցումով, թողարկում է IAM դերը, որը կապված է օրինակի հետ:

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

Երկրորդ հարցումը խնդրում է երկրորդ վերջնակետին ժամանակավոր թույլտվություններ տրամադրել այս օրինակի համար.

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

Հաճախորդը կարող է դրանք օգտագործել կարճ ժամանակով և պետք է պարբերաբար ձեռք բերի նոր վկայականներ (նախքան դրանք Expiration) Մոդելը պարզ է. AWS-ը հաճախակի պտտում է ժամանակավոր ստեղները՝ անվտանգության նկատառումներից ելնելով, բայց հաճախորդները կարող են դրանք մի քանի րոպե պահել քեշում՝ փոխհատուցելու նոր վկայականներ ստանալու հետ կապված կատարողականի տույժը:

AWS Java SDK-ն պետք է ստանձնի այս գործընթացի կազմակերպման պատասխանատվությունը, բայց ինչ-ինչ պատճառներով դա տեղի չի ունենում:

GitHub-ում խնդիրներ փնտրելուց հետո մենք հանդիպեցինք խնդրի #1921. Նա օգնեց մեզ որոշել այն ուղղությունը, որով պետք է ավելի «փորել»:

AWS SDK-ն թարմացնում է վկայագրերը, երբ տեղի է ունենում հետևյալ պայմաններից մեկը.

  • Ժամկետի ժամկետը (Expiration) Ընկնել EXPIRATION_THRESHOLD, կոշտ կոդավորված մինչև 15 րոպե:
  • Ավելի շատ ժամանակ է անցել վկայականների թարմացման վերջին փորձից REFRESH_THRESHOLD, կոշտ կոդավորված 60 րոպե:

Մեր ստացած վկայագրերի վավերականության ժամկետը տեսնելու համար մենք գործարկեցինք վերը նշված cURL հրամանները ինչպես կոնտեյներից, այնպես էլ EC2 օրինակից: Բեռնարկղից ստացված վկայագրի վավերականության ժամկետը շատ ավելի կարճ է ստացվել՝ ուղիղ 15 րոպե։

Այժմ ամեն ինչ պարզ է դարձել՝ առաջին իսկ խնդրանքով մեր ծառայությունը ստացել է ժամանակավոր վկայականներ։ Քանի որ դրանք վավեր չէին ավելի քան 15 րոպե, AWS SDK-ն կորոշի թարմացնել դրանք հետագա խնդրանքով: Եվ դա տեղի էր ունենում յուրաքանչյուր խնդրանքով:

Ինչու՞ է կրճատվել վկայականների վավերականության ժամկետը։

AWS Instance Metadata-ն նախատեսված է ոչ թե Kubernetes, այլ EC2 օրինակների հետ աշխատելու համար: Մյուս կողմից, մենք չէինք ցանկանում փոխել հավելվածի ինտերֆեյսը: Դրա համար մենք օգտագործել ենք ՔԻԱՄ - գործիք, որը, օգտագործելով գործակալներ յուրաքանչյուր Kubernetes հանգույցի վրա, թույլ է տալիս օգտվողներին (ճարտարագետներին, որոնք հավելվածներ տեղադրում են կլաստերի վրա) IAM դերեր վերագրել կոնտեյներներին, կարծես դրանք EC2 օրինակներ լինեն: KIAM-ը գաղտնալսում է AWS Instance Metadata ծառայության զանգերը և մշակում դրանք իր քեշից՝ նախկինում դրանք ստանալով AWS-ից: Կիրառական տեսանկյունից ոչինչ չի փոխվում։

KIAM-ը կարճաժամկետ վկայականներ է տրամադրում պատիճներին: Սա խելամիտ է հաշվի առնելով, որ պատիճների կյանքի միջին տևողությունը ավելի կարճ է, քան EC2 օրինակը: Վկայականների լռելյայն վավերականության ժամկետը հավասար է նույն 15 րոպեին.

Արդյունքում, եթե դուք երկու լռելյայն արժեքները ծածկում եք միմյանց վրա, խնդիր է առաջանում: Դիմումին տրամադրված յուրաքանչյուր վկայագրի ժամկետը լրանում է 15 րոպեից հետո: Այնուամենայնիվ, AWS Java SDK-ն ստիպում է թարմացնել ցանկացած վկայագրի ժամկետը, որի ժամկետի ավարտին մնացել է 15 րոպեից պակաս:

Արդյունքում, ժամանակավոր վկայականը ստիպված է լինում թարմացնել յուրաքանչյուր հարցումով, ինչը ենթադրում է մի քանի զանգ դեպի AWS API և հանգեցնում է հետաձգման զգալի աճի: AWS Java SDK-ում մենք գտանք հատկությունների հարցում, որը նշում է նմանատիպ խնդիր։

Լուծումը պարզվեց. Մենք պարզապես վերակազմավորել ենք KIAM-ը՝ ավելի երկար գործողության ժամկետով վկայագրեր պահանջելու համար: Երբ դա տեղի ունեցավ, հարցումները սկսեցին հոսել առանց AWS Metadata ծառայության մասնակցության, և հետաձգումը իջավ նույնիսկ ավելի ցածր մակարդակների, քան EC2-ում:

Արդյունքները

Ելնելով միգրացիաների հետ կապված մեր փորձից՝ խնդիրների ամենատարածված աղբյուրներից մեկը Kubernetes-ի կամ հարթակի այլ տարրերի վրիպակներ չեն: Այն նաև չի անդրադառնում մեր կողմից տեղափոխվող միկրոծառայությունների որևէ հիմնարար թերության: Խնդիրները հաճախ առաջանում են պարզապես այն պատճառով, որ մենք միացնում ենք տարբեր տարրեր:

Մենք խառնում ենք բարդ համակարգեր, որոնք նախկինում երբեք չեն փոխազդել միմյանց հետ՝ ակնկալելով, որ նրանք միասին կկազմեն մեկ, ավելի մեծ համակարգ: Ավաղ, որքան շատ տարրեր, այնքան շատ սխալների տեղ, այնքան բարձր է էնտրոպիան:

Մեր դեպքում, բարձր հետաձգումը Kubernetes-ում, KIAM-ում, AWS Java SDK-ում կամ մեր միկրոսերվիսում սխալների կամ սխալ որոշումների արդյունք չէր: Դա երկու անկախ լռելյայն կարգավորումների համատեղման արդյունք էր՝ մեկը KIAM-ում, մյուսը՝ AWS Java SDK-ում: Առանձին վերցրած, երկու պարամետրերն էլ իմաստ ունեն. AWS Java SDK-ում վկայագրի ակտիվ թարմացման քաղաքականությունը և KAIM-ում վկայագրերի վավերականության կարճ ժամկետը: Բայց երբ դրանք միացնում ես, արդյունքները դառնում են անկանխատեսելի։ Պարտադիր չէ, որ երկու անկախ և տրամաբանական լուծումները համակցված իմաստ ունենան:

PS թարգմանչից

Դուք կարող եք ավելին իմանալ KIAM կոմունալ ծրագրի ճարտարապետության մասին՝ AWS IAM-ը Kubernetes-ի հետ ինտեգրելու համար այստեղ՝ այս հոդվածը իր ստեղծողներից։

Կարդացեք նաև մեր բլոգում.

Source: www.habr.com

Добавить комментарий