Grafana-ს პანელი, რომელიც გეუბნება, როდის დაეცემა პოდი OOM-ით
როგორ ვაქციოთ მეხსიერების ტრენდი ETA-დ OOM-მდე და დისკის შევსებამდე PromQL-ით, ორი ხარვეზი, რის გამოც პანელი ცრუობდა, და გაჟონვა, რომელიც პირველივე დღეს დაიჭირა.
Gambchamp-ში მე ვპასუხობ მრავალმომხმარებლიან პროდუქტის პლატფორმაზე AWS EKS-ზე: ყველა კლასტერი მეტრიკებს ერთ VictoriaMetrics-ში წერს, რომლის წინაც საერთო Grafana დგას. Golden signals-ის დაშბორდები აჩვენებს, რა არის გაფუჭებული ახლა. მე მჭირდებოდა დაშბორდი, რომელიც სხვა კითხვას პასუხობს: თუ ტრენდი შენარჩუნდება, როდის ამოეწურება ამ პოდს მეხსიერება და როდის შეივსება ეს ტომი?
ფორმულა
საფუძველი ერთი სტრიქონია:
(limit - current) / clamp_min(deriv(usage[6h]), 1)
deriv ფანჯრის წერტილებზე სწორ ხაზს აგებს და მის დახრას ბაიტებში წამში აბრუნებს. დარჩენილი მარაგი, გაყოფილი ამ დახრაზე, ლიმიტამდე დარჩენილი წამებია. ორი ფანჯარა მნიშვნელოვანია: 6 საათი მწვავე ზრდას იჭერს, 24 საათი კი ნელ გაჟონვებს, რომლებსაც მოკლე ფანჯრის ხმაური მალავს.
clamp_min(..., 1) ორ საქმეს აკეთებს. ნულზე გაყოფისგან იცავს და კლებად რიგს უზარმაზარ ETA-დ აქცევს უარყოფითის ნაცვლად. ამის გარეშე ცხრილი -Inf-ითა და უარყოფითი ნაგვით ივსება. ცხრილის ზღვრები მცირედან დიდისკენ მიდის: წითელი 24 საათზე ნაკლები, ნარინჯისფერი 72 საათამდე, მწვანე უფრო მეტი. ერთეული s თავად ფორმატირდება დღეებად და საათებად.
იგივე იდეა managed ბაზებზეც გავავრცელეთ: თავისუფალი ადგილი, გაყოფილი მისი კლების სიჩქარეზე, დისკის შევსებამდე ETA-ს იძლევა.
გაჟონვა პირველივე დღეს
პირველივე დახატვისას პანელმა ერთი admin ბექენდი ნული საათის ETA-თი მონიშნა. შევამოწმე: მას OOMKill კლავდა 137 კოდით, დაახლოებით 900 Mi 1 Gi ლიმიტის პირობებში, ექვსი გადატვირთვა. CronJob მას განრიგით გადატვირთავდა, რაც სიმპტომს მალავდა და არაფერს ასწორებდა.
პირველი ხარვეზი: უარყოფითი ETA ლიმიტის გარეშე პოდებზე
ზოგიერთი ბექენდი მეხსიერების ლიმიტს მხოლოდ sidecar-ს უწესებდა და არა მთავარ კონტეინერს. მაშინ პოდის დონის sum(limits) - sum(used) მთელი პოდის მოხმარებას sidecar-ის ლიმიტს აკლებს, უარყოფითში გადადის და პანელი სრულიად სტაბილურ პოდს წითლად ღებავს. გამოსავალი: გამოთვლა კონტეინერების დონეზე inner join-ით, რომ ლიმიტის გარეშე კონტეინერები გამოვარდნენ, და მხოლოდ რეალურად დადებითი დახრის მქონე რიგების დატოვება.
მეორე ხარვეზი: ორმაგი დათვლა cAdvisor-ში
ამ ინსტალაციაზე cAdvisor პოდის cgroup-ის ყველა დონეს ერთსა და იმავე container მნიშვნელობას ანიჭებს: თავად პოდის cgroup-ს, თითოეულ კონტეინერს და pause კონტეინერს. sum by(pod) პოდის cgroup-ს, რომელიც უკვე ჯამია, კონტეინერებს უმატებს, და მეხსიერებისა და CPU-ს ყველა პანელი გაორმაგდა. ერთი პოდი ლიმიტის 156 %-ს აჩვენებდა, რეალურად კი 79 % იყო. წესი, რომელსაც ახლა მივყვები: max by(pod) cAdvisor-ის მეტრიკებისთვის, sum by(pod) kube-state-ის ლიმიტებისა და რექვესტებისთვის.
რას ვერ ხედავს პროგნოზი
ტრენდი მხოლოდ მონოტონურ გაჟონვებს იჭერს. პოდს, რომელიც უკვე წრეზე დადის „ზრდა, OOMKill, გადატვირთვა“, რეგრესიის დახრა ნულთან ახლოს აქვს და პროგნოზი მას სამართლიანად არ აჩვენებს. ასეთი პოდები გადატვირთვებისა და მეხსიერების პროცენტის პანელებზე ჩანს. პროგნოზის ცარიელი ცხრილი ნიშნავს „ახლა მონოტონური გაჟონვა არ არის“ და არა „ყველაფერი ჯანმრთელია“.
იმავე კვირის კიდევ ორი ხაფანგი. გადატვირთვებისთვის ნედლი მთვლელი წაიკითხე და არა increase[1h]: ხელახლა შექმნილი პოდი ახალ რიგს ნულიდან იწყებს და increase გადატვირთვების შტორმს სწორედ იმ პოდზე მალავს, რომელიც გჭირდება. ხოლო Y ღერძის ავტომასშტაბი დიდ ნელ მნიშვნელობებზე ცრუობს: ბაზის ტომი ისე ჩანდა, თითქოს კლდიდან ვარდებოდა, ცვლილება კი 163 GiB-დან 15 MB იყო, ETA თითქმის შვიდი წელი.
რა უნდა დავიმახსოვროთ
- დარჩენილი მარაგი, გაყოფილი
deriv-ზე, გამოსადეგი ETA-ა, მაგრამ დახრა ქვემოდან უნდა შეიზღუდოს და მხოლოდ მზარდი რიგები დარჩეს. - cAdvisor-ისა და kube-state-ის მეტრიკები სხვადასხვაგვარად აგრეგირდება; ნებისმიერ
sum by(pod)-ს სანამ ენდობი, ერთი პოდი ხელით შეამოწმე. - ტრენდული პროგნოზი და გადატვირთვების მთვლელი გაუმართაობის სხვადასხვა კლასს იჭერს. ორივე გჭირდება.
- ნელა ცვალებად სიმძლავრეს აბსოლუტური რიცხვითა და ETA-თი აფასებ და არა ხაზის სიციცაბით.