გამოტოვებული swap ფაილი, რომელმაც სერვერები გაყინა
24 სექტემბერს ერთ-ერთმა production სერვერმა პასუხის გაცემა შეწყვიტა. დატვირთვის ბალანსერმა იგი არაჯანმრთელად მონიშნა, ჩვენმა დისტანციური ბრძანებების ინსტრუმენტმა კი ვერ მიაღწია — მაგრამ არაფერი ჩამოვარდნილა და არაფერი მოკლულა მეხსიერების გადაჭარბებული გამოყენებისთვის. სერვერი თრეშინგში იყო: swap სივრცის გარეშე და კონტეინერებით, რომლებსაც მანქანაზე მეტი მეხსიერების გამოყენება შეეძლოთ, Linux დროს ატარებდა იმ ფაილების გამოდევნასა და ხელახლა წაკითხვაში, რომლებიც მას მუშაობისთვის სჭირდებოდა, და თითქმის არაფერს აკეთებდა. ყველა სერვერზე 2 GB swap ფაილის დამატებამ ეს მოაგვარა. უფრო საინტერესო გაკვეთილი ჯანმრთელობის შემოწმებებს ეხებოდა.
როგორ გამოიყურებოდა „გაყინვა“
- დატვირთვის ბალანსერის ჯანმრთელობის შემოწმებები ამ სერვერზე დრო ამოეწურა.
- AWS Systems Manager-ით გაგზავნილი ბრძანებები „Pending“-ში იყო და არასდროს გაეშვა.
- ლოგებში out-of-memory მკვლელობა არ ჩანდა. პროცესი არ დაღუპულა.
ერთი კვირით ადრე production გადავიყვანეთ პატარა ARM სერვერებზე, თითო 2 GB RAM-ით. ჩვენი სტეიჯინგ სერვერი — კიდევ უფრო პატარა — იგივე სახით გაიყინა, კონტეინერის ხატის გახსნისას დეპლოიმენტის დროს.
რატომ არ იყო მეხსიერების შეცდომა?
Linux პროცესს მხოლოდ მაშინ კლავს, როცა მართლა ამოიწურება მეხსიერება, რომლის დაბრუნებაც შეუძლია. მანამდე ის მეხსიერებას page cache-იდან იბრუნებს — ფაილების ასლებიდან, რომლებსაც RAM-ში ინახავს, მათ შორის ყოველი გაშვებული პროგრამის კოდიდან.
ჩვენს სერვერებზე კონტეინერების კომბინირებული მეხსიერების ლიმიტები დაახლოებით 3.3 GB-ს შეადგენდა, მანქანაზე დაახლოებით 1.8 GB ხელმისაწვდომი მეხსიერებით. ჩვეულებრივ ისინი ყველა ერთად თავის ლიმიტს არ იყენებს. როცა იყენებდა, კერნელი პროგრამის კოდს ქეშიდან ავსებდა სივრცის გასათავისუფლებლად, შემდეგ კი ის დისკიდან უკან უნდა წაეკითხა, როგორც კი კოდი ხელახლა გაეშვებოდა. ეს ციკლი — თრეშინგი — მანქანას თითქმის მთლიანად უპასუხოდ ხდის, მაშინ როცა ტექნიკურად მეხსიერება არ ამოწურულა. ამიტომ არაფერი კვდება და არაფერი აღდგება.
swap-ის გარეშე კერნელს არსად აქვს შესანახი მეხსიერება, რომელიც გამოყოფილია, მაგრამ იშვიათად გამოიყენება, ამიტომ მას ქეშიდან იღებს.
გამოსწორება
სერვერის bootstrap სკრიპტი ახლა ქმნის 2 GB swap ფაილს დაბალი swappiness პარამეტრით (10) ყოველ ჯერზე, როცა სერვერი ირთვება ან შეკეთდება. დაბალი swappiness ნიშნავს, რომ კერნელი მაინც ანიჭებს უპირატესობას სამუშაო მეხსიერების RAM-ში შენახვას, მაგრამ იშვიათად შეხებული გვერდები დისკზე შეიძლება გადავიდეს და არა პროგრამის კოდს ქეშიდან გამოდევნონ. ჩვენ ეს ცოცხლად გავუკეთეთ ორივე production სერვერს და სტეიჯინგს, და დეპლოიმენტის მილსადენი ყოველ production დეპლოიმენტზე თავიდან აქვეყნებს bootstrap სკრიპტს, რომ შეცვლილმა სერვერმაც მიიღოს.
ის, რაც უფრო გვაწუხებდა: არაჯანმრთელი სერვერი დარჩა
ჩვენს სერვერულ ჯგუფს EC2 ჯანმრთელობის შემოწმებები ჰქონდა — „მუშაობს თუ არა მანქანა?“ — და არა დატვირთვის ბალანსერის შემოწმებები — „პასუხობს თუ არა მოთხოვნებს?“. ამიტომ სერვერი, რომელსაც ბალანსერმა უკვე თავი დაანება, ჯგუფისთვის ჯანმრთელი რჩებოდა. მას არასდროს შეცვლიდნენ. production უხმოდ ერთ სერვერზე მუშაობდა, სანამ ვინმე არ შეამჩნევდა და მეორეს არ გადატვირთავდა.
ჯგუფის ბალანსერის შემოწმებებზე გადაყვანა თითქოს აშკარა გამოსწორებაა, მაგრამ ჩვენთვის ეს მარტივი არ არის: დეპლოიმენტის დროს სერვერები განზრახ აბრუნებენ შეცდომას თავიანთ ჯანმრთელობის ენდფოინთზე, რათა ბალანსერმა ისინი გამოიყვანოს (იხილეთ როგორ მივიღეთ დეპლოიმენტები ნულოვანი 502-ებით). ჯგუფი, რომელიც ამ სიგნალზე სერვერებს ცვლის, დეპლოიმენტის შუაში შეცვლიდა მათ.
ამიტომ ახლა დეპლოიმენტის ჩეკლისტში დავამატეთ ნაბიჯი: ყოველი დეპლოიმენტის შემდეგ შევამოწმოთ თითოეული სამიზნის ჯანმრთელობა დატვირთვის ბალანსერზე და არა მხოლოდ ის, წარმატებით დასრულდა თუ არა დეპლოიმენტის სამუშაო. თუ სერვერისადმი ბრძანებები „Pending“-ში რჩება, ის თრეშინგშია — გადატვირთეთ; ის უკვე ბრუნვიდან გამოსულია.
რა ვისწავლეთ
- „არ ჩამოვარდნა“ არ ნიშნავს „ჯანმრთელს“. თრეშინგი შეცდომას არ აწარმოებს, მხოლოდ იმდენად ნელ მდგომარეობას, რომ მანქანა პასუხს წყვეტს.
- პატარა სერვერებს swap სჭირდებათ. თუნდაც ცოტა, დაბალი swappiness-ით, რაც კერნელს აძლევს ადგილს ცივი მეხსიერებისთვის.
- მეხსიერების ლიმიტები, რომლებიც მანქანაზე მეტს იკრებს, ფსონია. ზოგჯერ აგებთ. იცოდეთ, როდის გაიმარჯვეთ ან წააგეთ.
- იცოდეთ, რომელ ჯანმრთელობის შემოწმებას ენდობა თქვენი ავტომასშტაბირება. შემოწმება, რომელიც მხოლოდ კითხულობს „ჩართულია თუ არა VM?“, სერვერს, რომელიც მუშაობას შეწყვეტდა, არ შეცვლის.