← ყველა სტატია

ექვსი საათი NotReady-ში, ნოდა კი მთელი ამ დროის განმავლობაში ცოცხალი იყო

ყველა სიგნალი ამბობდა, რომ ნოდა მკვდარია. სინამდვილეში მეხსიერება აკლდა, ხოლო აშკარა გამოსავალი — ნოდის წაშლა და ტომის იძულებითი მოხსნა — ერთი ბრძანებით იყო დაშორებული ბაზის დაზიანებისგან.

Gambchamp-ში, AWS EKS-ზე მომუშავე პროდუქტის პლატფორმაზე, მონაცემთა ბაზის ნოდა ექვსი საათი იყო NotReady მდგომარეობაში. kubectl describe node ყველა პირობაზე წერდა Kubelet stopped posting node status. ClickHouse-ის StatefulSet-ის წევრი ამ ნოდაზე Terminating-ში გაიჭედა, ტომი ისევ მიერთებული ჰქონდა, მისმა წყვილმა კი 96-ჯერ გადაიტვირთა.

ყველაფერი ამბობდა: „მანქანა აღარ არსებობს“. სახელმძღვანელოს გამოსავალიც ისეთივე ნათელია: kubectl delete node, რათა გაჭედილი პოდი გათავისუფლდეს, ტომი იძულებით მოიხსნას და StatefulSet ხელახლა დაიგეგმოს. ეს ბრძანება ერთი ღილაკით იყო დაშორებული მონაცემების განადგურებისგან.

ჯერ fencing

ნებისმიერ force-detach-მდე უნდა დაამტკიცო, რომ ძველი ჩამწერი ნამდვილად მკვდარია. ამიტომ მოქმედების ნაცვლად fencing გავაკეთე — მხოლოდ წაკითხვით:

  • EC2 ინსტანსი running მდგომარეობაში იყო;
  • ორივე სტატუსის შემოწმება, სისტემური და ინსტანსის, — ok;
  • SSM აგენტმა პასუხი kubelet-იდან დაახლოებით სამ წუთში შეწყვიტა.

მომუშავე ინსტანსი მწვანე შემოწმებებით მკვდარი მანქანა არ არის. ნამდვილი მინიშნება ის იყო, რომ ორი დამოუკიდებელი დემონი თითქმის ერთდროულად გაჩუმდა: დემონები სინქრონულად არ ვარდებიან, მაგრამ სინქრონულად შიმშილობენ.

შიმშილობს და არა მკვდარია

CloudWatch მთელ ფანჯარაში აჩვენებდა CPU-ს თანაბრად 55 %-ზე, 99,9 %-მდე ნახტომებით. 2 vCPU-იან ნოდაზე 55 % ნიშნავს ერთ ბირთვს, სრულად დატვირთულს. ასე გამოიყურება მეხსიერების უწყვეტი reclaim.

მიზეზი არითმეტიკის ერთი სტრიქონია:

container limits.memory (4Gi)  >  node allocatable (3.07Gi)

cgroup-ის ლიმიტი, რომელიც აღემატება იმას, რისი მიცემაც ნოდას რეალურად შეუძლია, cgroup-ის დონეზე OOM-kill-ს არასოდეს გამოიწვევს. კონტეინერი იზრდება, სანამ მთელი მანქანის მეხსიერება არ ამოიწურება, და რადგან swap არ არის, ბირთვი დროს პირდაპირ reclaim-ზე ხარჯავს. kubelet და SSM აგენტი CPU-ს ვერ იღებენ და heartbeat-ს ვერ აგზავნიან. NotReady kubelet-ზე იყო ნათქვამი და არა მანქანაზე — ბაზა კი, დიდი ალბათობით, ისევ წერდა თავის ტომში.

ცოცხალი ჩამწერისგან ტომის მოხსნა დაზიანებული დისკის მიღების საიმედო გზაა. reclaim=Retain დისკს ინახავს, მის მთლიანობას — არა.

რა მოხდა შემდეგ

არაფერი — განზრახ. Fencing-მა კითხვაზე „უსაფრთხოა თუ არა force-detach?“ არა უპასუხა და არც ერთი ცვლილება არ გამიკეთებია. გარკვეული დროის შემდეგ ბირთვის სისტემურმა OOM killer-მა საბოლოოდ ClickHouse-ის პროცესი აირჩია, მეხსიერება დაბრუნდა, kubelet-მა სტატუსის გაგზავნა განაახლა და ნოდა Ready-ში დაბრუნდა.

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

დასკვნები

  • NotReady kubelet-ს აღწერს და არა მანქანას. ნებისმიერ დესტრუქციულ ნაბიჯამდე — fencing: ინსტანსის მდგომარეობა, სტატუსის შემოწმებები, აგენტის პინგი, CPU ინციდენტის ფანჯარაში.
  • running და status checks ok მხოლოდ იმას ნიშნავს, რომ ჰიპერვიზორი ბირთვს წვდება. შეამოწმეთ CPU-სა და აგენტის heartbeat-თან ერთად.
  • ორი ერთად გაჩუმებული დემონი რესურსების შიმშილზე მიუთითებს და არა ქსელურ იზოლაციაზე.
  • მეხსიერების ლიმიტები ნოდის allocatable-ს შეადარეთ და არა capacity-ს. ლიმიტი, რომელიც არ ეტევა, ნოდას ვერ დაიცავს.
  • delete node-ს force-detach-თან ერთად უკან დაბრუნება არ აქვს. თუ ვერ ამტკიცებთ, რომ ჩამწერი მკვდარია — ნუ გააკეთებთ.