☕ 5-Min Tech Story #29

Graceful Shutdown: ทำไมแค่ Deploy โค้ดใหม่ ถึงตัดแขนตัดขา Request ของลูกค้ากลางอากาศ?

⏱️ ใช้เวลาอ่านประมาณ: 4 นาที 20 วินาที

บ่ายวันศุกร์ ทีมงานกดปุ่ม Deploy เพื่ออัปเดตฟีเจอร์ใหม่ขึ้น Kubernetes ด้วยความภาคภูมิใจในระบบ Zero-Downtime Rolling Update...

แต่ไม่กี่นาทีต่อมา ฝ่ายบริการลูกค้าเดินมาสะกิด: "พี่คะ ลูกค้า 10 กว่าคนแจ้งว่าเงินในบัตรถูกตัดไปแล้ว แต่หน้าจอบอกว่าสั่งซื้อไม่สำเร็จ ออเดอร์ไม่โผล่ในระบบ!"

ระบบเรามี Pod สำรองรันรออยู่ตลอดเวลา แล้วทำไม Transaction ของลูกค้าถึงระเหยหายไปกลางคันได้? คำตอบคือแอปของคุณเพิ่งโดน "สับคัตเอาต์ตัดไฟกลางบ้าน" โดยไม่รู้ตัว

🗡️ คมดาบของ SIGTERM vs ความไร้เดียงสาของเซิร์ฟเวอร์

เวลา Kubernetes หรือระบบคลาวด์จะสลับเวอร์ชัน มันจะสั่งปลด Pod เก่าทิ้งโดยการส่งสัญญาณระดับระบบปฏิบัติการที่ชื่อว่า SIGTERM (Signal 15) เข้าไปที่ Container

สิ่งที่ Spring Boot ทำโดยค่าเริ่มต้นในอดีต (หรือถ้าไม่ได้เปิดคอนฟิกพิเศษ) คือ:

"พอได้รับสัญญาณปิดเครื่อง มันสั่งชัตดาวน์ Embedded Tomcat ทันที! ท่อเชื่อมต่อ HTTP Socket ถูกกระชากหลุด Database Connection Pool (HikariCP) โดนสั่งปิดทิ้ง ณ เสี้ยววินาทีนั้นเลย!"

แล้วคำขอของลูกค้าที่กำลังรันอยู่ล่ะ?

🚪 ปิดร้านอย่างมีมารยาท: กลไก Graceful Shutdown

การปิดระบบที่ดีไม่ใช่การทุบตึก แต่เหมือน "ร้านอาหารที่ถึงเวลาปิดรับลูกค้า":

  1. ติดป้ายหน้าร้าน: แจ้ง Load Balancer หรือ Kubernetes ทันทีว่า "ร้านนี้ไม่รับแขกใหม่แล้วนะ (Unhealthy/Terminating)" ห้ามส่ง Request ใหม่เข้ามาอีก
  2. เคลียร์แขกในร้านให้เสร็จ: แขกโต๊ะไหนที่กำลังกินข้าวอยู่ ให้พวกเขากินจนอิ่ม เช็กบิล และเดินออกจากร้านให้เรียบร้อย (ปล่อยให้ Request ที่ค้างอยู่ทำงานจนจบ)
  3. ปิดไฟล็อกประตู: เมื่อไม่มีใครค้างในร้านแล้ว ค่อยปิดเตาแก๊ส (DB Pool / MQ Producer) แล้วปิดร้านอย่างสงบ

🛡️ แค่ 2 บรรทัดใน application.yml ที่กอบกู้ทุกอย่าง

ใน Spring Boot 2.3 เป็นต้นมา คุณสามารถเปิดฟังก์ชันนี้ได้ง่ายๆ แค่เพิ่มคอนฟิก:

server:
  shutdown: graceful # สั่งให้รอ Request เดิมให้เสร็จก่อน

spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s # ให้เวลารอสูงสุด 30 วินาที

ข้อควรระวังบน Kubernetes: อย่าลืมเช็กค่า terminationGracePeriodSeconds (ค่าเริ่มต้น 30 วินาที) ของ Pod ด้วย ถ้าฝั่งแอปขอเวลารอ 30 วินาที แต่ฝั่ง Kube ดันตั้งใจร้อนส่ง SIGKILL (Kill -9) มาทุบทิ้งตั้งแต่ 10 วินาทีแรก ความพยายามทั้งหมดก็ไร้ความหมาย

💡 5-Min Takeaway

"การออกแบบระบบให้ตายอย่างสงบ สำคัญไม่แพ้การออกแบบให้เริ่มต้นอย่างรวดเร็ว" ในโลกของ Cloud-Native ที่ Pod เกิด ดับ และย้ายเครื่องแทบทุกวัน การเปิดใช้งาน Graceful Shutdown คือปราการด่านสุดท้ายที่จะคุ้มครองความถูกต้องของข้อมูล และทำให้การ Deploy งานเกิดขึ้นได้ตลอดเวลาโดยไม่มีผู้ใช้งานคนไหนต้องตกเป็นเหยื่อ