บล็อก

ไฟล์ swap ที่หายไปซึ่งทำให้เซิร์ฟเวอร์ของเราค้าง

เมื่อวันที่ 24 กันยายน เซิร์ฟเวอร์ production เครื่องหนึ่งหยุดตอบสนอง load balancer ทำเครื่องหมายว่าไม่พร้อมใช้งาน และเครื่องมือคำสั่งระยะไกลของเราเข้าถึงไม่ได้ แต่ไม่มีอะไรล่มและไม่มีโปรเซสใดถูกฆ่าเพราะใช้หน่วยความจำมากเกินไป เซิร์ฟเวอร์กำลัง thrash: เมื่อไม่มี swap space และ container ได้ใช้หน่วยความจำมากกว่าที่เครื่องมี Linux จึงใช้เวลาทั้งหมดไปกับการเอาไฟล์ที่จำเป็นต่อการรันออกจากแคชและอ่านกลับจากดิสก์อีกครั้ง จนแทบไม่มีความคืบหน้า การเพิ่มไฟล์ swap ขนาด 2 GB บนทุกเซิร์ฟเวอร์แก้ปัญหานี้ได้ บทเรียนที่น่าสนใจกว่าคือเรื่อง health check

อาการ "ค้าง" เป็นอย่างไร

  • health check ของ load balancer หมดเวลาบนเซิร์ฟเวอร์นั้น
  • คำสั่งที่ส่งผ่าน AWS Systems Manager ค้างอยู่ในสถานะ "Pending" และไม่เคยรัน
  • ไม่มี out-of-memory kill ในล็อก ไม่มีโปรเซสตาย

หนึ่งสัปดาห์ก่อนหน้านั้น เราได้ย้ายเซิร์ฟเวอร์ production ไปยังเครื่อง ARM ที่เล็กลง ซึ่งแต่ละเครื่องมี RAM 2 GB เซิร์ฟเวอร์ staging ของเราซึ่งเล็กกว่าอีก ก็เคยค้างแบบเดียวกันระหว่างการแตก container image ตอน deploy

ทำไมจึงไม่มีข้อผิดพลาดหน่วยความจำ?

Linux จะฆ่าโปรเซสก็ต่อเมื่อหน่วยความจำที่กู้คืนได้หมดจริงๆ ก่อนถึงจุดนั้น มันจะกู้หน่วยความจำจาก page cache ซึ่งคือสำเนาของไฟล์ที่เก็บในแรม รวมถึงโค้ดของทุกโปรแกรมที่กำลังรัน

บนเซิร์ฟเวอร์ของเรา ขีดจำกัดหน่วยความจำรวมของ container รวมกันได้ประมาณ 3.3 GB ในเครื่องที่ใช้งานได้ประมาณ 1.8 GB ปกติพวกมันไม่ได้ใช้ถึงขีดจำกัดพร้อมกันทั้งหมด แต่เมื่อใช้ kernel จะนำโค้ดโปรแกรมออกจากแคชเพื่อหาที่ว่าง และต้องอ่านกลับจากดิสก์ทันทีที่โค้ดนั้นกลับมาทำงาน วงจรนั้น หรือ thrashing สามารถทำให้เครื่องแทบไม่ตอบสนองทั้งที่ไม่ได้หมดหน่วยความจำทางเทคนิค ดังนั้นจึงไม่มีอะไรถูกฆ่า และไม่มีอะไรฟื้นตัว

เมื่อไม่มี swap kernel ไม่มีที่ให้หน่วยความจำที่ถูกจองไว้แต่ไม่ค่อยได้ใช้ ดังนั้นมันต้องเอาจากแคชแทน

วิธีแก้

สคริปต์ bootstrap ของเซิร์ฟเวอร์ตอนนี้สร้างไฟล์ swap ขนาด 2 GB พร้อม swappiness ต่ำ (10) ทุกครั้งที่เซิร์ฟเวอร์บูตหรือถูกซ่อมแซม swappiness ต่ำหมายความว่า kernel ยังคงชอบเก็บหน่วยความจำที่ใช้งานไว้ในแรม แต่หน้าที่ไม่ค่อยถูกแตะต้องสามารถย้ายไปดิสก์ได้ แทนที่จะบังคับให้โค้ดโปรแกรมออกจากแคช เราใช้มันสดกับเซิร์ฟเวอร์ production ทั้งสองเครื่องและ staging และ pipeline ของ deploy จะเผยแพร่สคริปต์ bootstrap ใหม่ทุกครั้งที่ deploy production เพื่อให้เซิร์ฟเวอร์ที่ถูกแทนที่ก็ได้มันไปด้วย

สิ่งที่น่ากังวลกว่า: เซิร์ฟเวอร์ที่ไม่ปกติยังคงอยู่

กลุ่มเซิร์ฟเวอร์ของเราใช้ EC2 health check คือ "เครื่องกำลังรันอยู่ไหม" ไม่ใช่ load balancer health check คือ "กำลังตอบคำขอไหม" ดังนั้นเซิร์ฟเวอร์ที่ load balancer ยอมแพ้ไปแล้วยังถือว่าปกติสำหรับกลุ่ม และจะไม่ถูกแทนที่เลย production จะเงียบๆ ทำงานบนเซิร์ฟเวอร์เครื่องเดียวจนมีใครสังเกตเห็นและรีบูตอีกเครื่อง

การสลับกลุ่มไปใช้ load balancer health check ฟังดูเป็นวิธีแก้ที่ชัดเจน แต่สำหรับเรามันไม่ตรงไปตรงมา ระหว่าง deploy เซิร์ฟเวอร์จะตอบ health endpoint ด้วยข้อผิดพลาดโดยตั้งใจ เพื่อให้ balancer ระบายการเชื่อมต่อออก (ดูวิธีที่เราทำให้ deploy เหลือ 502 เป็นศูนย์) กลุ่มที่แทนที่เซิร์ฟเวอร์ตามสัญญาณนั้นจะแทนที่มันกลางการ deploy ทุกครั้ง

ดังนั้นตอนนี้เราเพิ่มขั้นตอนหนึ่งในเช็คลิสต์การ deploy: หลังทุกการ deploy ให้ตรวจ health ของทุก target บน load balancer ไม่ใช่แค่ดูว่างาน deploy ผ่านหรือไม่ หากคำสั่งไปยังเซิร์ฟเวอร์ค้างอยู่ใน "Pending" แสดงว่ามันกำลัง thrash ให้รีบูต เพราะมันอยู่นอกการให้บริการแล้ว

สิ่งที่เราเรียนรู้

  • "ไม่ล่ม" ไม่ได้แปลว่า "ปกติ" Thrashing ไม่ผลิตข้อผิดพลาด มีแค่ความช้าที่รุนแรงจนเครื่องหยุดตอบ
  • เซิร์ฟเวอร์ขนาดเล็กต้องมี swap แม้เพียงเล็กน้อย พร้อม swappiness ต่ำ ก็ให้ kernel มีที่เก็บหน่วยความจำที่ไม่ได้ใช้
  • ขีดจำกัดหน่วยความจำที่รวมกันแล้วเกินเครื่องคือการเดิมพัน บางครั้งคุณแพ้ รู้ว่าเมื่อไรที่คุณเดิมพันไปแล้ว
  • รู้ว่า health check ไหนที่ autoscaling เชื่อถือ health check ที่ถามแค่ว่า "VM เปิดอยู่ไหม" จะไม่แทนที่เซิร์ฟเวอร์ที่หยุดทำงานแล้ว

Mythex · Pricing · สร้างอะไรก็ได้ด้วย AI · เทมเพลตเว็บไซต์และแอป · เปรียบเทียบ Mythex · อภิธานศัพท์การสร้างแอปด้วย AI · เครื่องมือฟรีสำหรับสร้างแอปด้วย AI · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti فارسی Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)