English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

un

სტუმარი
1 / ?
უკან გაკვეთილებზე

ტვირთის გადატანის ორი გზა

კეთილი იყოს თქვენი მობრძანება

როდესაც სერვისი იწყებს დატვირთვის ქვეშ მოღუნვას, ოპერატორი დგას არჩევანის წინაშე. არსებული მანქანა გახადოს უფრო დიდი (მეტი CPU, მეტი RAM, უფრო სწრაფი დისკები). თუ დაამატოს მეტი მანქანა, რომლებიც თითოეული ერთსა და იმავე სამუშაოს ასრულებს.

პირველ გზას ვერტიკალური სკალირება (scale up) ეწოდება. მეორეს — ჰორიზონტული სკალირება (scale out).

ეს გაკვეთილი განმარტავს, რატომ ირჩევს თითქმის ყველა თანამედროვე ვებ-არქიტექტურა ჰორიზონტალურ სკალირებას და რომელი თვისება განაპირობებს ამ არჩევანის შესაძლებლობას. პასუხი ერთ სიტყვაშია დამალული: მდგომარეობა (state).

საბოლოოდ თქვენ გაიგებთ:

- ვერტიკალური და ჰორიზონტალური სკალირების ღირებულების მრუდებს და თითოეული სად არის მიზანშეწონილი

- რას ნიშნავს „stateful“ და „stateless“ პრაქტიკაში, და რატომ მრავლდება ერთ-ერთი მათგანი იაფად

- მათემატიკას, რომელიც ითვლის რეპლიკების ფლოტის ზომას მოსალოდნელი და პიკური დატვირთვის დროს

- „headroom“-ის წესს, რომელიც ხელს უშლის ტიერს ჩამოიშალოს რიგის მუხლის შემდეგ

- სად უნდა იცხოვროს მდგომარეობამ (ის არასდროს ქრება) და როგორ გადავიტანოთ იგი იმ ფენებიდან, რომლებსაც სკალირება სჭირდებათ

რატომ იგებს ჰორიზონტალური სკალირება ზღვარზე მიღწევის შემდეგ

ვერტიკალური სკალირება: ერთი უფრო დიდი ყუთი

დადებითი: მარტივია. კოდის ცვლილებები არ სჭირდება. კოორდინაცია არ არის საჭირო. იგივე პროცესს ახლა მეტი CPU აქვს.

უარყოფითი: ჭერი. კომერციულად ხელმისაწვდომი უდიდეს VM-საც კი აქვს შეზღუდული RAM და ბირთვები. ამ ზღვარზე მეტისთვის ფული აღარ ყიდულობს დამატებით სივრცეს. ხარჯები სუპერხაზურად იზრდება გამყიდველის შეთავაზების sweet spot-ის მიღმა. ამ ერთი მანქანის გაუმართაობამ შეიძლება მთლიანი სერვისი ჩამოაგდოს.

ჰორიზონტალური მასშტაბირება: ბევრი პატარა ყუთი

დადებითი: ჭერი არ არსებობს (თქვენი მზადყოფნის ფარგლებში გადახდისა და მანქანების კოორდინაციის). ტევადობა წრფივად იზრდება რეპლიკების რაოდენობასთან ერთად, წინასწარ განჭვრეტადად. ერთი რეპლიკის გაუმართაობა აკლებს ტევადობის 1/N-ს, და არა 100%-ს.

უარყოფითი: საჭიროებს, რომ დატვირთვამ მხარი დაუჭიროს მას. ზოგიერთი დატვირთვა (ერთი დიდი ბაზა, სტეიტფულ თამაშის სერვერი live სესიებით) წინააღმდეგობას უწევს ჰორიზონტალურ მასშტაბირებას. კოორდინაცია და დატვირთვის განაწილება ხდება საოპერაციო საზრუნავი.

გარდამტეხი წერტილი: ნებისმიერი პროდაქშენ სერვისი, რომელსაც სურს გადარჩეს ერთი მანქანის გაუმართაობას, უნდა გაუშვას მინიმუმ ორ მანქანაზე. როგორც კი ორ მანქანას მიიღებთ, თქვენ უკვე აირჩიეთ ჰორიზონტალური მასშტაბირება. ამის შემდეგ საკითხი აღარ არის „უნდა გავაკეთოთ თუ არა?“, არამედ — „რამდენად იაფად შეგვიძლია დავამატოთ შემდეგი რეპლიკა?“

მთავარი ჩართვის ფაქტორი: დატვირთვა, რომელიც თავად მანქანაზე არ ინახავს მოთხოვნაზე დამოკიდებულ მდგომარეობას (per-request state). ამ შემთხვევაში ნებისმიერ რეპლიკას შეუძლია ნებისმიერი მოთხოვნის დამუშავება, და რეპლიკის დამატება ზრდის ტევადობას კოორდინაციის გარეშე.

ვერტიკალური vs ჰორიზონტალური მასშტაბირება: ხარჯების მრუდი და ჭერი

გუნდი აწარმოებს სერვისს ერთ VM-ზე, რომელსაც აქვს 8 CPU და ამუშავებს 800 მოთხოვნას წამში 60%-იანი CPU დატვირთვით. ისინი მოელოდებიან, რომ ტრაფიკი გაიზრდება 4-ჯერ მომავალი წლის განმავლობაში. ისინი კამათობენ: იქირაონ 32-CPU-იანი VM (ვერტიკალური მასშტაბირება), თუ გაუშვან ოთხი იდენტური 8-CPU-იანი VM ლოდბალანსერის უკან (ჰორიზონტალური მასშტაბირება). რომელ ვარიანტს გირჩევთ და დაასახელეთ ორი მიზეზი, რომელიც სცდება უბრალო გამტარუნარიანობას.

სახელმწიფოიანი და უსახელმწიფოიანი პრაქტიკაში

მდგომარეობა არასდროს ქრება, უბრალოდ გადადის

მდგომარეობიანი კომპონენტი: ინახავს ინფორმაციას, რომლის დაკარგვაც შეცვლიდა მის ქცევას. მომხმარებლის ანგარიშების მონაცემთა ბაზა. ქეში, რომელიც ინახავს სესიის ტოკენებს. ვორკერი, რომელიც მომხმარებელთან გრძელვადიან სტრიმინგ კავშირს აკავშირებს კონკრეტულ პროცესთან.

მდგომარეობის არმქონე კომპონენტი: არ ინახავს ინფორმაციას, რომლის დაკარგვასაც მნიშვნელობა ექნებოდა. ვებ-ფენა, რომელიც კითხულობს მოთხოვნას, კითხავს მონაცემთა ბაზას და წერს პასუხს. ყველა მოთხოვნა დამოუკიდებელია; ფენა არაფერს ახსოვს მოთხოვნებს შორის.

მთავარი ინსაიტი: მდგომარეობა სისტემიდან არასდროს ქრება. ის გადადის იმ ფენაში, რომელიც სპეციალურადაა შექმნილი მის შესანახად (მონაცემთა ბაზა, Redis კლასტერი, ობიექტების საცავი). ტრაფიკის წინა ფენები შემდეგ შეიძლება გახდეს მდგომარეობის არმქონე, ხოლო მდგომარეობის არმქონე ფენები ჰორიზონტალურად მასშტაბირდება, რადგან ნებისმიერი რეპლიკა შეუძლია უპასუხოს ნებისმიერ მოთხოვნას.

პრაქტიკული ტესტი: თუ ამ ფენაში შემთხვევით მოკლავთ ერთ პროცესს და გადატვირთავთ, მიიღებს თუ არა რომელიმე მომხმარებელი არასწორ პასუხს ან დაკარგულ სესიას? თუ დიახ — ის ინახავს მდგომარეობას. თუ არა — არ ინახავს.

მაგალითები

- პითონის ვებ-პროცესი, რომელიც კითხულობს მოთხოვნებს, კითხავს Postgres-ს და აბრუნებს JSON-ს: მდგომარეობის არმქონე. მდგომარეობა ინახება Postgres-ში.

- პითონის ვებ-პროცესი, რომელიც მომხმარებლის საყიდლების კალათებს ახსოვს ლოკალურ მეხსიერებაში: მდგომარეობიანი. პროცესის მოკვლისას კალათები იკარგება.

- WebSocket სერვერი, რომელიც ღია კავშირებს ინახავს ჩატის მომხმარებლებთან: stateful კავშირის გაგებით. პროცესის მოკვლა აუქმებს კავშირებს; კლიენტებს თავიდან უნდა დაუკავშირდნენ. ასეთი სისტემები ხშირად მაინც ჰორიზონტალურად მასშტაბირდება სიფრთხილით (sticky sessions, consistent hashing).

- Redis კეში Postgres-ის წინ: stateful კეშის შიგთავსის გაგებით, მაგრამ მისაღებია, თუ კეშის მისსვლები (cache miss) დასაშვებია. რეპლიკის გაუმართაობა ნიშნავს კეშის მისსვლას, არა მონაცემების დაკარგვას.

ჰორიზონტალური მასშტაბირებისთვის დიზაინი = სახელმწიფოს (state) გატანა იმ ფენიდან, რომელიც უნდა მასშტაბირდეს.

საეჭვო ტირის აუდიტი

გუნდი აწარმოებს რეკომენდაციის API-ს 6 ბეკენდ VM-ზე, რომლებიც reverse proxy-ის უკან დგას. აპლიკაცია: კითხულობს მომხმარებლის ID-ს მოთხოვნიდან, იღებს მომხმარებლის ბოლო აქტივობას Postgres-იდან, ასრულებს სკორირების ალგორითმს და აბრუნებს რეკომენდებული ელემენტების სიას. ორი არასტანდარტული ქცევა:

- აპლიკაცია პროცესის მეხსიერებაში ინახავს „ბოლო მომხმარებლის აქტივობის“ კეშს, რომელიც ივსება მომხმარებლის პირველი მოთხოვნისას და ხელახლა გამოიყენება შემდგომ მოთხოვნებზე.

- აპლიკაცია იყენებს sticky sessions-ს: როგორც კი მომხმარებელი მოხვდება VM #3-ზე, მისი ყველა შემდგომი მოთხოვნა მიდის VM #3-ზე (პროქსი კონფიგურირებულია cookie-ზე დაფუძნებული sticky routing-ით).

განსაზღვრეთ რომელი ორი ქცევიდან რომელი ხდის ტირს stateful-ად და ახსენით, რა გაფუჭდება, თუ გუნდი 6 VM-დან 12-მდე სცდება მასშტაბირებას. შემდეგ შემოგთავაზეთ რედიზაინი, რომელიც საშუალებას მისცემს ტირს ჰორიზონტალურად მასშტაბირდეს მომხმარებლის კეშის სარგებლის დაკარგვის გარეშე.

რეპლიკის ფორმულა

უმარტივესი ტევადობის ფორმულა

როგორც კი ტიერი უწყობილობას (stateless) გახდება, მისი ზომის განსაზღვრა არითმეტიკად იქცევა. საჭიროა საკმარისი რეპლიკები, რომ სტაბილური დატვირთვა შემოდიოდეს და გადიოდეს ერთი და იმავე სიჩქარით, ზრდისთვის (surge) საკმარისი თავისუფალი ადგილით.

ფორმულა:

replicas = ⌈ (peak_load × surge_factor) / per_replica_capacity ⌉ + headroom

სადაც:

- peak_load: მაქსიმალური მდგრადი მოთხოვნები/წამში, რომელსაც ნორმალური მუშაობისას მოელოდით

- surge_factor: მულტიპლიკატორი, რომელიც ფარავს მოკლევადიან მწვერვალებს პიკურ დატვირთვაზე მაღლა (ჩვეულებრივ 1.5x-დან 2x-მდე პროგნოზირებადი ტრაფიკისთვის, 3x ან მეტი ვირუსული/არაპროგნოზირებადი ტრაფიკისთვის)

- per_replica_capacity: მოთხოვნები/წამში, რომელსაც ერთი რეპლიკა მართავს მისაღებ ლატენციასა და გამოყენებაზე (ჩვეულებრივ იზომება 70% CPU-ზე, არა გაჯერების დროს)

- headroom: დამატებითი რეპლიკები, რათა რამდენიმე რეპლიკის გაუმართავობამ ტიერი არ ჩამოაგდოს (ჩვეულებრივ 1-2 რეპლიკა მცირე ფლოტებისთვის, 10-20% უფრო დიდებისთვის)

გამოთვლის მაგალითი: ბეკენდი ამუშავებს 100 მოთხოვნას/წმ-ში 70% CPU-ზე თითოეული რეპლიკისთვის. პიკური დატვირთვაა 600 მოთხოვნა/წმ. თქვენ მოელით დროდადრო 2-ჯერ გადატვირთვას. გსურთ გადარჩეთ 2 რეპლიკის გაუმართავობას.

რეპლიკები = ⌈ (600 × 2) / 100 ⌉ + 2 = 12 + 2 = 14 რეპლიკა

80%-ის წესი

თითოეული რეპლიკის სიმძლავრე არ არის გაჯერების წერტილი. გაზომეთ სიმძლავრე 70-80% CPU-ზე, არასდროს 100%-ზე.

80%-ის გარდამავალობის შემდეგ რიგის (queueing) მრუდები მკვეთრად იზრდება: რიგი, რომელიც 60%-ზე 10 მწმ-ში მუშაობდა, 90%-ზე უკვე 80 მწმ-ში გარბენს. ლატენცია, და არა გამტარუნარიანობა, პირველი იმსხვრევა. (თანმხვედრი გაკვეთილი geometry_of_stateless_horizontal_scaling ამ მრუდს მათემატიკურად განსაზღვრავს.)

ავტომატური მასშტაბირება vs სტატიკური პროვიზიონინგი

სტატიკური: მოამზადეთ პიკური დატვირთვა × გადატვირთვის თავსატევი და მიიღეთ დაბალი გამოყენების ხარჯი არაპიკურ დროს.

ავტომატური მასშტაბირება: კონტროლერი ამატებს და აშორებს რეპლიკებს დაკვირვებული CPU-ს გამოყენების, სამიზნე ლატენციის ან რიგის სიღრმის მიხედვით.

ავტომატური მასშტაბირების გაფრთხილება: cold-start-ის დრო მნიშვნელოვანია. თუ ახალი რეპლიკის გაშვნას 2 წუთი სჭირდება, ავტომატური მასშტაბირება ვერ უპასუხებს 30-წამიან მკვეთრ მატებას. მოწიფული ავტომასშტაბირება ინახავს „თბილ“ (warm) რეპლიკების 풀ს, რომელიც წინასწარ არის მომზადებული და რჩება მასშტაბირების ზღვარზე ოდნავ ქვემოთ.

რეპლიკის ზომის ფორმულა გამოთვლილი მაგალითით

ახალი სერვისისთვის ფლოტის ზომის გამოთვლა

თქვენი გუნდი გეგმავს ვიდეო მეტადატის API-ის გაშვებას. ბენჩმარკებმა აჩვენა, რომ ერთი რეპლიკა უმკლავდება 250 მოთხოვნას წამში 70%-იან CPU-ზე და 50 მწმ p99 ლატენტობით. მარკეტინგი პროგნოზირებს პიკურ დატვირთვას 4000 მოთხოვნამდე წამში საღამოს პიკის საათებში. დაგეგმილი პრომო-აქცია შეიძლება გამოიწვიოს მოკლევადიანი მატება პიკურ დატვირთვაზე 3-ჯერ. გსურთ, რომ სერვისმა გაუძლოს 3 ერთდროულ რეპლიკის ავარიას ისე, რომ გადარჩენილ რეპლიკებზე გამოყენება არ აღემატებოდეს 80%-ს.

გამოიყენეთ რეპლიკის ფორმულა გაშვების ფლოტის ზომის გამოსათვლელად. აჩვენეთ გამოთვლები ეტაპობრივად, შემდეგ კი ახსენით ერთი მიზეზი, თუ რატომ შეიძლება თქვენი რიცხვი მაინც არასწორი იყოს წარმოებაში (cold start, ქეშის გახურება, დამოკიდებულებების ლატენტობა, ან ნებისმიერი სხვა დაცვადი მიზეზი).

Cold Start, Slow Drain და სხვა რეალური ზღვრები

რეალურ ფლოტებს აქვთ რეალური ზღვრები

ფორმულა ვარაუდობს, რომ რეპლიკები მყისიერად ჩნდებიან, მყისიერად იღებენ ტრაფიკს და მყისიერად ათავისუფლებენ ტრაფიკს. წარმოებაში არც ერთი ეს პირობა არ სრულდება.

Cold start: ახალ რეპლიკას სჭირდება ოპერაციული სისტემის ჩატვირთვა, პროცესის გაშვება, კონფიგურაციის ჩატვირთვა, ქეშების გათბობა და ჯანმრთელობის შემოწმების გავლა. დრო მერყეობს 5 წამიდან (კონტეინერის რესტარტი) 5 წუთამდე (სრული VM-ის ჩატვირთვა + იმიჯის გადმოწერა). ავტოსკალირება ვერ რეაგირებს იმ ბურსტებზე, რომლებიც ამ დაყოვნებაზე მოკლეა.

Slow drain: 풀იდან ამოღებულ რეპლიკას სჭირდება დრო, რომ დაასრულოს მიმდინარე მოთხოვნები, სანამ ტერმინირდება. წინააღმდეგ შემთხვევაში მომხმარებლები ხედავენ შეწყვეტილ პასუხებს. Reverse proxy-ები უჭერენ მხარს drain-ს (ახალი მოთხოვნების მიღების შეწყვეტა და აქტიურების დასრულება), მაგრამ ამასაც სჭირდება წამებიდან წუთებამდე დრო.

თბილი 풀ა: პროდაქშენის ფლოტები ინახავს წინასწარ მომზადებული, მაგრამ უქმე რეპლიკების პულს, რომლებიც მზად არიან სიგნალისთანავე ტრაფიკის მისაღებად. მცირე მუდმივ ხარჯს სანაცვლოდ იღებს სწრაფ რეაგირებას მკვეთრ ზრდაზე.

კავშირის დრენაჟი წინააღმდეგ მყისიერი მოკვლის: გლუვი გამორთვა მნიშვნელოვანია. SIGTERM, რომელიც დრენაჟს იწვევს, უფრო დიდხანს გრძელდება, ვიდრე SIGKILL, მაგრამ არ არღვევს მომხმარებლის მოთხოვნებს.

ჯანმრთელობის შემოწმების ფანჯარა: რეპლიკა, რომელიც ახლახან გაეშვა, შეიძლება გაიაროს პირველი ჯანმრთელობის შემოწმება მანამ, სანამ მისი მონაცემთა ბაზის კავშირების პული გათბება; პროქსი მაშინ უგზავნის რეალურ ტრაფიკს და პირველი ათეული მოთხოვნა ნელია. დაარეგულირეთ ჯანმრთელობის შემოწმებები ისე, რომ ისინი რეალურ გზას ამოწმებდნენ, და არა მხოლოდ პროცესის სიცოცხლეს.

„Stickiness creep“ (წებოვნების მცოცავი ზრდა): თუნდაც ფორმალურად სტეიტლეს ტიერები დროთა განმავლობაში იძენენ წებოვნებას (CDN-ის ქეშები, DNS რეზოლვერის ქეშები, კავშირების პულები). ეჭვის თვალით უყურეთ „იდენტურ რეპლიკებს“, რომლებიც მიუხედავად ამისა, განსხვავებულად იქცევიან.

თბილი პული თუ რეაქტიული ავტომასშტაბირება?

თქვენი ვიდეოს მეტა-მონაცემების API (იგივე, რაც წინა კითხვაში იყო, 51 რეპლიკით ნორმალური პიკის + სურჯისთვის) განიცდის 30-წამიან სურჯს 5-ჯერ ნორმალურ დატვირთვამდე, როცა ახალი ვირუსული ვიდეო აიტვირთება. ავტომასშტაბირება ამჟამად 90 წამს იღებს ახალი რეპლიკის დასამატებლად ცივი მდგომარეობიდან (image pull + warmup). ამ 90-წამიან შუალედში ლატენცია მკვეთრად იზრდება და ზოგიერთი მოთხოვნა აჭარბებს დროის ლიმიტს.

შემოგთავაზეთ გამოსავალი. აირჩიეთ: (a) შევინარჩუნოთ რეაქტიული ავტომასშტაბირება, მაგრამ გავხადოთ მისი ტუნინგი სხვანაირი, (b) მოვამზადოთ უქმე რეპლიკების თბილი პული, თუ (c) სტატიკურად მოვამზადოთ 5-ჯერადი პიკისთვის სამუდამოდ. დაასაბუთეთ თქვენი არჩევანი და დაასახელეთ ერთი ხარჯი, რომელსაც დანარჩენი ორი ვარიანტი დააკისრებდა.

შეიმუშავეთ უწყვეტი (Stateless) დონე შეზღუდვების პირობებში

სინთეზი

თქვენ ისწავლეთ, რატომ იგებს ჰორიზონტალური სკალირება მცირე ზღვრის შემდეგ, რას ნიშნავს სახელმწიფო (state) პრაქტიკაში, როგორ უნდა განსაზღვროთ ფლოტის ზომა მოსალოდნელი და პიკური დატვირთვისას, და სად წყდება ჰორიზონტალური სკალირება კიდეებზე.

გამოიყენეთ ოთხივე.

შეიმუშავეთ backend tier feed.example.com-სთვის — სოციალური ფიდის API-სთვის. შეზღუდვები: თითოეული რეპლიკის სიმძლავრე 200 req/s 70%-იან CPU-ზე; მოსალოდნელი პიკური დატვირთვა 1500 req/s; მკვეთრი ზრდის კოეფიციენტი 2.5x (დროდადრო trending ამბები); გაუძლოს 2 ერთდროულ რეპლიკის ავარიას; cold-start დრო 60 წამი; ბასტები შეიძლება გაგრძელდეს 45 წამი; ბიუჯეტი იძლევა გარკვეული უსაქმო სიმძლავრის საშუალებას, მაგრამ არა 2.5x მუდმივ პროვიზიონირებას.

გამოთვალეთ სტაბილური ფლოტის ზომა (აჩვენეთ გამოთვლები), აირჩიეთ მკვეთრი ზრდის გამკლავების სტრატეგია (warm pool / reactive autoscaling / ორივე), და დაასახელეთ ერთი per-user მდგომარეობა, რომელსაც აპლიკაცია სავარაუდოდ ინახავს და სად უნდა გადავიტანოთ იგი, რომ tier დარჩეს stateless.

სად მიდის ეს კურსი შემდეგ

სად მიდის ეს კურსი შემდეგ

თქვენ უკვე გაქვთ მოქმედი მენტალური მოდელი სტეიტლეს ტიერის შესახებ: რატომ მასშტაბირდება იგი, როგორ უნდა დავადგინოთ მისი ზომა, რა იშლება მის საზღვრებზე და სად უნდა გადავიტანოთ სტეიტი, როდესაც გვსურს რომ ფენა, რომელიც უნდა გაიზარდოს, გაიზარდოს.

ამ კურსის შემდეგი გაკვეთილი (cs_distsys_ingress_egress_separation) ეხება უფრო დახვეწილ პრობლემას: თუნდაც იდეალურად დაბალანსებული სტეიტლეს ტიერი შეიძლება მოულოდნელად ჩავარდეს, როდესაც შემომავალი და გამავალი ტრაფიკი იზიარებს ერთსა და იმავე ქსელურ გზას. კლასიკური მაგალითია პროქსი, რომელიც ცდილობს დაუკავშირდეს საკუთარ თავს; გადაწყვეტა მოიცავს ერთი ტიერის ორად გაყოფას სხვადასხვა პასუხისმგებლობებით.

თანმხვედრი გაკვეთილი: geometry_of_stateless_horizontal_scaling გამოყვანს რიგის მრუდს, Little-ის კანონს გამოყენებულ რეპლიკების ფლოტზე და 80%-იანი გამოყენების „მუხლის“ გეომეტრიულ მნიშვნელობას.

კარგად შეასრულეთ. წინსვლა.