คืนนั้นเราเพิ่งต่อ pipeline อ่านบิลด้วย GPU เสร็จหมาดๆ งานใบแรกวิ่งเข้าไปที่ worker บนคลาวด์ แล้วก็เงียบหายไป เราเปิดคำสั่ง modal container list ดู ไม่เห็นมีอะไรค้าง ก็เลยสบายใจว่าไม่มีอะไรพัง
จริงๆ ตอนนั้นมันพังไปแล้วตัวหนึ่ง และกำลังวน retry ซ้ำอยู่ worker ตัวนั้นพังตั้งแต่จังหวะโหลดโมเดลขึ้น GPU พอพังระบบก็ retry ให้ พอ retry ก็จุดเครื่องใหม่ โหลดใหม่ แล้วพังอีก วนแบบนี้กินเวลา GPU ไปเรื่อยๆ ราวๆ ชั่วโมง โดยที่ container list ไม่โชว์มันสักครั้ง เพราะคำสั่งนั้นโชว์แค่ตัวที่กำลังทำงานอยู่ ตัวที่ตายแล้วเกิดใหม่ไม่นับ กว่าจะจับได้ก็ตอนยิ้มเปิดหน้า dashboard บนเว็บให้ดู
โพสต์นี้เลยอยากเล่าเรื่องการเอางานหนักอย่างงานที่ต้องใช้ GPU ไปวางบน serverless ว่ามันคุ้มตรงไหน และงบรั่วตรงไหนที่คนไม่ค่อยเตือนกัน
ช่วงที่ 1ทำไมต้องเอางาน GPU ไปไว้บน serverless
GPU เป็นของแพง ถ้าเช่ามาเปิดค้างไว้ 24 ชั่วโมง เราจ่ายเต็มทุกชั่วโมงถึงจะไม่มีงานเข้าเลยก็ตาม แต่งานอ่านบิลของเราไม่ได้มาตลอดเวลา มาเป็นครั้งคราว วันหนึ่งไม่กี่ใบ เอา GPU มานั่งเปิดค้างรอทั้งวันก็ไม่คุ้ม
serverless GPU แก้ตรงนี้ เราไม่ต้องดูแลเครื่องเอง เครื่องจะตื่นมาตอนมีงาน ทำเสร็จก็ดับ แล้วคิดเงินเราเป็นวินาทีเฉพาะช่วงที่มันทำงานจริง (เราใช้ Modal เป็นตัวจัดการส่วนนี้) ของแบบนี้เหมาะกับงานที่มาเป็นพักๆ มากกว่างานที่ต้องวิ่งตลอดเวลา
เลขจริงที่วัดได้ช่วยให้เห็นภาพ โมเดลอ่านบิล Typhoon OCR รุ่น 2B รันบน GPU รุ่น L4 (การ์ดรุ่นเล็กของ NVIDIA) ตกใบละราวๆ 0.14 บาทตอนเครื่องพร้อมอยู่แล้ว ใช้เวลาอ่านราว 18 วินาทีต่อใบ ราคาระดับนี้ถ้าไปจ่ายแบบเปิดเครื่องค้างทั้งวันเพื่ออ่านแค่ไม่กี่ใบ ต้นทุนต่อใบจะพุ่งขึ้นอีกหลายเท่า
ช่วงที่ 2cold start คือฟิสิกส์ ไม่ใช่รสนิยม
เลข 18 วินาทีต่อใบเป็นราคาตอนเครื่องพร้อมอยู่แล้ว แต่ตอนเครื่องเพิ่งตื่นจากศูนย์เรื่องเปลี่ยนไปเยอะ เครื่องต้องโหลดตัวโมเดลขึ้น GPU ให้เสร็จก่อนถึงจะเริ่มอ่านได้ ช่วงตื่นเครื่องนี้ (cold start) เราวัดได้ที่ 344 วินาที หรือเกือบ 6 นาที
ตัวเลขเดียวนี้ตัดสินหน้าตาของทั้งระบบเลย ถ้าให้คนกดปุ่มแล้วยืนรอ 6 นาทีกว่าเครื่องจะตื่น ไม่มีใครรอไหว และถ้าให้ตัวเรียกงานอย่าง cron หรือหน้าเว็บยืนรอเหมือนกัน ตัวเรียกส่วนใหญ่ก็จะ timeout ตัดงานทิ้งไปก่อนที่ GPU จะตื่นด้วยซ้ำ
นี่คือเหตุผลที่ระบบเราไม่ให้ใครยืนรอ GPU เลย ตัวเรียกงานจะยิงงานทิ้งไว้แล้วไปทำอย่างอื่นต่อ พองานเสร็จค่อยส่งผลกลับทางอีเมล แล้วก็รวบงานหลายใบมาทำเป็นรอบเดียว เพื่อจ่ายค่าตื่นเครื่องแค่ครั้งเดียวต่อรอบ ดีไซน์แบบนี้ไม่ได้มาจากรสนิยมว่าเราชอบ async มันมาจากฟิสิกส์ของ cold start ที่บังคับให้เป็นแบบนั้นต่างหาก
ช่วงที่ 3จุดที่เผาเงินเงียบๆ
พอระบบเป็นแบบยิงงานทิ้งไว้ ก็มีหลายจุดที่พลาดแล้วเงินรั่วโดยไม่มีเสียงเตือน
- จุดแรก ตัวเรียกงานห้ามยืนรอผล ถ้าสั่งให้มันเรียกแบบรอผลตรงๆ (
.remote()) cold start 6 นาทีจะนานกว่าเวลาที่ตัวเรียกยอมรอ งานเลยตายคาที่ ต้องสั่งแบบยิงทิ้ง (.spawn()) ให้มันไปทำเองเบื้องหลังเท่านั้น - จุดที่สอง คำสั่งยิงทิ้งต้องเกิดในแอปที่ deploy ค้างไว้จริง ถ้ายิงงานจากการรันแบบชั่วคราวบนเครื่องตัวเอง พอคำสั่งบนเครื่องเราจบ แอปฝั่งคลาวด์ก็ดับตามไปพร้อมงาน งานเลยค้างสถานะ 'processing' เงียบๆ ไม่มีใครมาเก็บต่อ ทางที่ถูกคือปล่อยให้ cron ของแอปที่ deploy ไว้แล้วเป็นคนหยิบงานเอง
- จุดที่สาม คือตัวที่เผาเงินจริง ถ้า worker พังตั้งแต่จังหวะเปิดเข้า GPU มันจะ retry ไม่เลิก แต่ละรอบจุดเครื่องใหม่ ก็คือจ่ายใหม่ทุกรอบ และอย่างที่เล่าไปตอนต้น
container listไม่โชว์ตัวที่วนพังแบบนี้ เห็นได้แค่บน dashboard บนเว็บ บทเรียนที่ได้คือ พังเมื่อไหร่ต้องสั่งปิดแอปตัวนั้นก่อน (modal app stop) แล้วค่อยแก้ ไม่ใช่ปล่อยให้มัน retry ต่อ การเก็บกวาดตอนพัง (teardown) เป็นส่วนหนึ่งของการรัน ไม่ใช่ของแถมที่ค่อยทำทีหลัง
ก่อนจะถึงเรื่องเงิน ยังมีจุดสะดุดตอนตั้งเครื่องอีกสองสามจุดที่ทำเราเสียเวลา เช่น vLLM รุ่นใหม่ที่ต้องมีตัว compile ของ NVIDIA (nvcc) ติดมาใน base image ด้วย ไม่งั้น build ไม่ผ่าน รายละเอียดพวกนี้เราเก็บไว้ในโค้ด แต่หลักการเปิดได้หมด
ช่วงที่ 4ตั้งงบ GPU ยังไง ไม่ให้เสียเงินเปล่า
ถ้าจะสรุปเป็นนิสัยที่ติดตัวไว้ได้ มีอยู่ไม่กี่ข้อ
- อย่าเชื่อ CLI อย่างเดียว เพราะมันบอกแค่ภาพบางส่วน อย่างที่เจอมาแล้วว่าโชว์แค่ตัวที่ active หน้า dashboard บนเว็บกับหน้าบิลค่าใช้จ่ายต่างหากที่บอกความจริงว่ามีเครื่องไหนกำลังกินเงินเราอยู่
- พังแล้ว teardown ก่อนเสมอ อย่าปล่อยให้ของที่พังวน retry ค้างไว้
- ออกแบบงานให้ยิงทิ้งแล้วรอผลทีหลังตั้งแต่แรก เพื่อไม่ให้ cold start มาขวางแล้วทำงานตายกลางทาง
มองย้อนกลับไป งบ GPU ของเราไม่ได้รั่วตอนงานเยอะ มันรั่วตอนงานพังแล้วไม่มีใครเห็นต่างหาก ค่าเช่าเครื่องตอนอ่านบิลจริงๆ ถูกจนน่าตกใจ ที่แพงคือชั่วโมงที่เครื่องวนพังอยู่ตอนที่เรานั่งดู CLI เขียวๆ แล้วนึกว่าทุกอย่างเรียบร้อย
ช่วงที่ 5แล้วมันจบยังไง
พอมองให้ถูกจุดแล้ว pipeline ก็เดินครบรอบจริงเป็นครั้งแรก เราสลับมาใช้โมเดล Qwen3-VL รุ่น 4B คู่กับการบังคับให้มันตอบเป็นรูปแบบตายตัว (structured outputs) คราวนี้มันอ่านยอดรวมจากบิลออกมาถูกครบ 3 จาก 3 ใบ อีเมลเด้งกลับเข้ากล่องจริงพร้อมเลขที่ถูกต้อง เครื่องที่ประกอบมาหลายคืนหมุนครบรอบแรกได้สำเร็จ
ถ้าคุณกำลังจะเอางาน GPU ไปวางบน serverless เรื่องที่อยากฝากไว้ไม่ใช่ว่าโมเดลไหนแม่นกว่ากัน แต่เป็นการจับตาให้เห็นของที่พังเงียบๆ ให้ได้ก่อน เพราะบิลค่า GPU ที่แพงที่สุด มักไม่ได้มาจากงานที่วิ่ง แต่มาจากงานที่พังแล้วไม่มีใครดู
วัดเองจากการรัน pipeline อ่านบิลด้วย GPU จริง (บันทึกการรัน (log) กับหน้า dashboard และบิลของ Modal) ตัวเลขทั้งหมดในโพสต์นี้มาจากการรันจริง ไม่ได้อ้างที่อื่น