
ระวัง! การประหยัดค่าใช้จ่ายคลาวด์อาจแลกมาด้วยความล้มเหลวของบริการอย่างไม่คาดคิด
การลดต้นทุนบนระบบคลาวด์กลายเป็นเป้าหมายสำคัญของหลายองค์กรในยุคปัจจุบัน ใคร ๆ ก็อยากลดค่าใช้จ่าย AWS หรือผู้ให้บริการคลาวด์อื่น ๆ ลงให้มากที่สุด แต่บางครั้งการมุ่งมั่นประหยัดงบอย่างเดียว โดยไม่เข้าใจกลไกเชิงลึกของระบบ อาจนำไปสู่หายนะที่คาดไม่ถึงได้ เหมือนกับเหตุการณ์ที่บริการหลายตัวบน Kubernetes เริ่มล่มไม่เป็นท่า หลังจากการปรับลดค่าใช้จ่ายครั้งใหญ่
เมื่อความประหยัดนำมาซึ่งปัญหาใหญ่
การเริ่มต้นดูเหมือนจะเป็นเรื่องดี เมื่อทีมงานตั้งเป้าหมายลดค่าใช้จ่ายคลาวด์ลงถึง 37% วิธีการที่เลือกใช้คือการหันไปใช้ Graviton instances ของ AWS ซึ่งมีประสิทธิภาพดีในราคาที่ถูกกว่า และผสมผสานกับการใช้ Spot instances เพื่อลดต้นทุนเพิ่มเติมอีกขั้น ทุกอย่างดูเหมือนจะดำเนินไปตามแผน และตัวเลขค่าใช้จ่ายก็ลดลงอย่างน่าพึงพอใจ
แต่ไม่นานหลังจากนั้น ปัญหาที่ไม่พึงประสงค์ก็เริ่มปรากฏขึ้น เซอร์วิสหลายตัวบน Kubernetes เริ่มทยอยตายอย่างต่อเนื่อง โดยมีข้อความแจ้งเตือนว่าถูก “OOMKilled” หรือถูกระบบสั่งปิดเนื่องจากหน่วยความจำไม่พอ ซึ่งเป็นเรื่องที่น่าประหลาดใจอย่างยิ่ง
เจาะลึกหาสาเหตุ: ไม่ใช่แค่เรื่องแรมเต็ม
การตรวจสอบเบื้องต้นชี้ว่าปัญหาไม่ได้เกิดจากหน่วยความจำบนโหนด (Node) เต็มจริง ๆ เพราะ การมอนิเตอร์แสดงให้เห็นว่ายังมีหน่วยความจำว่างเหลือเฟือ และ CPU ก็ไม่ได้ทำงานหนักเกินไป ทีมงานตรวจสอบทั้งปัญหาการกำหนดเวลาของ Kubernetes (Scheduler) และข้อผิดพลาดในตัวแอปพลิเคชันเอง แต่ก็ยังไม่พบสาเหตุที่ชัดเจน
สิ่งที่ค้นพบในภายหลัง คือการเปลี่ยนแปลงที่สำคัญซึ่งแอบแฝงอยู่เบื้องหลังการใช้ Graviton instances และการจัดการหน่วยความจำของ Linux สิ่งที่เกิดขึ้นคือในระบบ Linux จะมีกลไกที่เรียกว่า cgroup memory limits ซึ่งใช้ควบคุมการใช้หน่วยความจำของแต่ละ Pod ใน Kubernetes
Graviton instances มีลักษณะพิเศษที่แตกต่างจาก x86 instances ทั่วไป คือใช้ ขนาดหน้าหน่วยความจำ (memory page size) ที่ใหญ่กว่า (64KB เทียบกับ 4KB ใน x86) ทำให้การจัดการหน่วยความจำแตกต่างออกไป
เมื่อแอปพลิเคชันต้องการเขียนข้อมูลจำนวนมากลงหน่วยความจำ และปล่อยให้เป็น “dirty pages” (หน้าที่ถูกแก้ไขแต่ยังไม่ได้เขียนกลับลงดิสก์) เคอร์เนลของ Linux บน Graviton จะใช้เวลานานกว่าในการจัดการและเรียกคืนพื้นที่หน่วยความจำเหล่านี้ ด้วยขนาดหน้าหน่วยความจำที่ใหญ่ ทำให้แม้จะยังเหลือ RAM ทางกายภาพอยู่ แต่พื้นที่ภายใต้ cgroup limits กลับถูกมองว่าหมดไปแล้ว ส่งผลให้ Kubernetes ตัดสินใจ “OOMKilled” Pod นั้นไปโดยไม่จำเป็น
บทเรียนสำคัญและทางออกที่ต้องพิจารณา
หลังจากค้นพบต้นตอของปัญหา ทางทีมงานได้พิจารณาแนวทางแก้ไขหลายอย่าง เช่น การเพิ่มขีดจำกัดหน่วยความจำให้กับ Pods (ซึ่งทำให้เสียค่าใช้จ่ายเพิ่มขึ้นอีกครั้ง) หรือการปรับแต่งพารามิเตอร์ของเคอร์เนล Linux ที่เกี่ยวข้องกับการจัดการหน่วยความจำชั่วคราว (เช่น vm.dirty_ratio, vm.dirty_background_ratio)
อีกหนึ่งทางเลือกคือการพิจารณากลับไปใช้ x86 instances สำหรับบริการที่สำคัญและมีความอ่อนไหวต่อประสิทธิภาพสูง ซึ่งแม้จะมีราคาสูงกว่า Graviton แต่ก็ให้เสถียรภาพที่คาดหวังได้มากกว่า
เหตุการณ์นี้เป็นบทเรียนสำคัญที่ย้ำเตือนว่า การประหยัดค่าใช้จ่ายบนคลาวด์นั้นจำเป็นต้องมาพร้อมกับ ความเข้าใจเชิงลึกเกี่ยวกับโครงสร้างพื้นฐาน และ พฤติกรรมของเวิร์คโหลดที่รันอยู่ ไม่ใช่เพียงแค่การดูตัวเลขค่าใช้จ่ายที่ลดลงเท่านั้น การมอนิเตอร์ที่ละเอียดอ่อนในระดับระบบปฏิบัติการ และการทำความเข้าใจความแตกต่างของสถาปัตยกรรมฮาร์ดแวร์ จะช่วยให้เราสามารถสร้างสมดุลระหว่างประสิทธิภาพ เสถียรภาพ และค่าใช้จ่ายได้อย่างยั่งยืนที่สุด