Serverless Deployment
บาง service ของคุณทำงานจริงแต่เป็นแบบ bursty (พุ่งเป็นช่วง) service ที่ทำ thumbnail รูปจะรันเฉพาะเมื่อมีคนอัปโหลดรูป webhook handler ทำงานไม่กี่ครั้งต่อชั่วโมง report generator รายคืนรันวันละครั้ง ด้วยโมเดล container แต่ละตัวเหล่านี้ยังต้องมีอย่างน้อยหนึ่ง replica รันตลอดเวลา กิน memory และยึดช่องใน cluster รองานที่มาเป็นช่วงพุ่ง
คุณกำลังจ่ายทั้งเงินและแรงดูแล เพื่อเลี้ยง process ที่ว่างอยู่ให้อุ่นไว้ และเพื่อ patch เครื่องที่อยู่ข้างใต้ สำหรับ workload ที่ขับเคลื่อนด้วย event และมาเป็นช่วง ๆ นี่ดูเป็นค่าเริ่มต้นที่ผิดที่ผิดทาง สิ่งที่คุณอยากได้คือ service ที่ไม่ต้องมีตัวตนอยู่เลยจนกว่าจะมีงานเข้ามา
คุณอยากรัน business logic เป็นชิ้น ๆ โดยไม่ต้อง provision ไม่ต้อง patch และไม่ต้อง scale server เอง อยากให้ scale ลงไปถึง ศูนย์ ตอนว่างจะได้ไม่เสียค่าใช้จ่าย และ scale ขึ้นเองอัตโนมัติ หนึ่ง instance ต่อหนึ่ง request ที่เข้ามาพร้อมกันเวลา traffic พุ่ง โดยไม่ต้องไปตั้งค่า autoscaler แถมยังอยากจ่ายเฉพาะช่วงที่ code รันจริง ไม่ใช่จ่ายค่า capacity ที่จองทิ้งไว้
ดังนั้นแรงที่ขัดแย้งกันคือ: กำจัด operation ของ server และค่าใช้จ่ายขณะว่างให้หมดไปเลย โดยยัง scale แบบยืดหยุ่นตาม demand ได้ — โดยยอมรับว่าคุณยกการควบคุม runtime ให้ไปเป็นการแลกเปลี่ยน
วิธีแก้
หัวข้อที่มีชื่อว่า “วิธีแก้”รับเอา serverless deployment มาใช้ หรือที่เรียกว่า Functions as a Service (FaaS) คุณ package logic ของคุณเป็น function แล้วมอบให้ platform เช่น AWS Lambda, Google Cloud Functions หรือ Azure Functions คุณไม่จัดการ server ใด ๆ platform เป็นผู้ provision, patch และ scale สภาพแวดล้อมการรันให้คุณ
function ผูกอยู่กับ trigger ซึ่งอาจเป็น HTTP request, message บน queue, การอัปโหลดไฟล์ หรือ timer พอ trigger ทำงาน platform จะปั้น instance ของ function ขึ้นมา รัน แล้วรื้อทิ้ง หรือเก็บอุ่นไว้สั้น ๆ เพื่อใช้ซ้ำ ถ้ามีหลาย trigger ยิงพร้อมกัน platform ก็รันหลาย instance ขนานกัน หนึ่งตัวต่อหนึ่ง invocation และเมื่อไม่มี trigger ก็ไม่มีอะไรรัน คุณจึงไม่ต้องจ่ายเลย
invocation แรกหลังจากว่างต้องจ่าย cold start: platform ต้องสร้างสภาพแวดล้อมการรันและ initialize runtime ของคุณก่อนที่ code จะรัน invocation ถัด ๆ ไปบน instance ที่อุ่นแล้วจะข้ามต้นทุนนั้นไป
flowchart LR Up[Image uploaded] -->|event| Plat[FaaS platform] Plat -->|invoke| F1[function instance 1] Plat -->|invoke| F2[function instance 2] Plat -->|invoke| F3[function instance 3] F1 --> Store[(Thumbnail store)] F2 --> Store F3 --> Store Idle[No events] -.->|scales to zero| Plat
ตัวอย่าง
หัวข้อที่มีชื่อว่า “ตัวอย่าง”คุณแค่ประกาศคุณสมบัติของ function ไว้ ทั้ง runtime, handler entry point, trigger และ resource limit จากนั้น platform รับช่วงต่อทั้งหมด ไม่มี server ไม่มีจำนวน replica และไม่มี operating system ให้คุณต้องดูแล
functions: generate-thumbnail: runtime: nodejs20 handler: src/handler.onUpload memory: 512MB timeout: 30s environment: THUMBNAIL_BUCKET: thumbnails-prod trigger: type: object-storage bucket: uploads-prod event: object.created concurrency: maxInstances: 100 reservedInstances: 0 # scale all the way to zero when idleplatform อ่านสิ่งนี้และในแต่ละ event object.created ใน uploads bucket จะ invoke onUpload โดย scale จากศูนย์ขึ้นไปถึง 100 instance ขนานกันตามอัตราการอัปโหลดที่ต้องการ แล้วกลับลงไปถึงศูนย์เมื่อ bucket เงียบลง
ผลลัพธ์ที่ตามมา
หัวข้อที่มีชื่อว่า “ผลลัพธ์ที่ตามมา”สิ่งที่คุณได้:
- ไม่มี operation ของ server ไม่มี host ให้ provision, patch หรือ scale platform เป็นเจ้าของ operating system, การ upgrade runtime และการวางแผน capacity
- scale ลงถึงศูนย์ จ่ายตามการใช้งาน workload ที่ว่างไม่มีค่าใช้จ่าย และคุณจ่ายตามเวลาการรันแทนที่จะจ่ายตาม capacity ที่จองไว้ — เข้ากันได้ดีกับงานที่ bursty, ขับเคลื่อนด้วย event หรือเกิดไม่บ่อย
- ยืดหยุ่นโดยค่าเริ่มต้น การพุ่งของ traffic ถูกรองรับด้วย instance ที่มากขึ้นโดยอัตโนมัติ โดยไม่มี autoscaler ให้ปรับจูน
สิ่งที่คุณต้องเสีย:
- cold start request แรกหลังจากว่างต้องรอขณะที่ platform initialize สภาพแวดล้อม ซึ่งกระทบ endpoint ที่ไวต่อ latency และถูก invoke ไม่บ่อย
- ข้อจำกัด runtime function เผชิญเพดานด้านเวลาการรัน, memory และขนาด package และมักเป็นแบบ stateless และอายุสั้น — workload ที่รันยาวหรือมี state เข้ากันได้ไม่ดี
- lock-in และการทดสอบ local ที่ยากขึ้น trigger, การ package และ API เป็นแบบเฉพาะ platform ดังนั้น portability และการจำลอง local ที่ซื่อตรงจึงอ่อนกว่า container ธรรมดา
กฎที่ใช้ได้จริง: เอื้อมไปหา serverless เมื่องานขับเคลื่อนด้วย event, พุ่งเป็นช่วง หรือเกิดไม่บ่อย และความทนทานต่อ latency อยู่ในเกณฑ์ที่รับได้ ส่วน service ที่คงที่ วิกฤตต่อ latency หรือรันยาว ให้เก็บไว้บน container
เนื้อหาที่เกี่ยวข้อง
หัวข้อที่มีชื่อว่า “เนื้อหาที่เกี่ยวข้อง”- Service per Container — โมเดล runtime ทางเลือกสำหรับ service ที่คงที่ อยู่ยาว ที่คุณอยากควบคุมเอง
- Externalized Configuration — function ก็ยังรับการตั้งค่าจากค่า environment ที่ inject เข้ามา ไม่ใช่จากค่าคงที่ที่ฝังไว้
- Microservice Chassis — แม้แต่ function ก็ได้ประโยชน์จาก baseline ร่วมด้าน logging, metric และ config
| ข้อดี | ข้อแลกเปลี่ยน |
|---|---|
| ไม่ต้องจัดการ server — cloud provider ดูแล infra | cold start latency — function ที่ไม่ได้ถูกเรียกนาน ๆ ช้าครั้งแรก |
| scale ถึง zero — ไม่มี request ไม่เสีย cost | execution time limit — ไม่เหมาะกับ long-running process |
| pay per invocation — ประหยัดสำหรับ intermittent workload | vendor lock-in สูงกว่า container deployment |
| deploy เร็ว — แค่ upload code | debugging และ local development ยากกว่า |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”Serverless Monolith — ใส่ทุก business logic ลงใน function เดียว อาการ:
- function มีขนาดใหญ่มาก (>50MB)
- function ทำหลายอย่าง — ไม่ต่างจาก monolith ที่ deploy บน Lambda
- scale ไม่ได้แยก — ต้อง scale ทั้ง function
State ใน Function — เก็บ state ใน memory ระหว่าง invocation อาการ:
- ข้อมูลใน global variable หาย เมื่อ function instance ใหม่เริ่มทำงาน
- behavior ต่างกันระหว่าง warm และ cold invocation
💡 ตัวอย่างจากของจริง
Netflix:
- ใช้ AWS Lambda สำหรับ event-driven pipeline: encode video, resize image
- scale ได้ไม่จำกัดระหว่าง peak traffic
Coca-Cola:
- ย้าย vending machine backend มาใช้ AWS Lambda
- ประหยัด cost ได้ 65% เนื่องจาก intermittent traffic pattern