- Unkey ผู้ให้บริการยืนยันตัวตนสำหรับ API ได้ ย้ายจากสถาปัตยกรรมเซิร์ฟเวอร์เลสบน Cloudflare Workers ไปเป็นเซิร์ฟเวอร์แบบมีสถานะที่พัฒนาด้วย Go เพื่อแก้ปัญหาด้านประสิทธิภาพและความซับซ้อนของสถาปัตยกรรม
- โครงสร้างใหม่ให้ ความเร็วในการตอบสนองดีขึ้น 6 เท่า พร้อมทั้ง ตัดวิธีอ้อมสำหรับแคชและโอเวอร์เฮดของดาต้าไปป์ไลน์ที่ซับซ้อน ออกไป
- ในสภาพแวดล้อมแบบเซิร์ฟเวอร์เลส ไม่มี การรับประกันหน่วยความจำแบบคงอยู่ ระหว่างการเรียกฟังก์ชัน ทำให้การอ่านแคชทุกครั้งต้องพึ่งพาการร้องขอผ่านเครือข่าย และเกิดค่า latency ของแคชที่ p99 มากกว่า 30ms
- เมื่อเปลี่ยนจากระบบกระจายไปเป็น สถาปัตยกรรมแอปพลิเคชันที่เรียบง่ายกว่า ก็ทำให้สามารถโฮสต์เองได้ มีความเป็นอิสระจากแพลตฟอร์ม และปรับปรุงประสบการณ์นักพัฒนาได้อย่างมาก
- เซิร์ฟเวอร์เลสยังคง เหมาะกับเวิร์กโหลดที่เกิดเป็นครั้งคราวหรือแพตเทิร์นคำขอ/คำตอบแบบเรียบง่าย แต่ถ้าต้องการ latency ต่ำอย่างสม่ำเสมอหรือจำเป็นต้องจัดการสถานะแบบคงอยู่ เซิร์ฟเวอร์แบบมีสถานะจะมีประสิทธิภาพมากกว่า
ข้อจำกัดของเซิร์ฟเวอร์เลสและคอขวดด้านประสิทธิภาพ
- สำหรับ Unkey การยืนยันตัวตนของ API อยู่ในเส้นทางหลักของทุกคำขอ ทำให้ ความล่าช้าระดับไม่กี่มิลลิวินาที ส่งผลโดยตรงต่อประสบการณ์ผู้ใช้
- แม้การกระจายไปยัง edge ทั่วโลกและการสเกลอัตโนมัติของ Cloudflare Workers จะน่าสนใจ แต่ การไม่มีความคงอยู่ของแคชและความหน่วงจากคำขอเครือข่าย กลับกลายเป็นปัญหา
-
ปัญหาเรื่องแคช
- ฟังก์ชันเซิร์ฟเวอร์เลสไม่มี หน่วยความจำที่คงอยู่ข้ามการเรียกใช้ ทำให้ทุกการค้นหาแคชต้องอาศัยคำขอเครือข่ายภายนอก
- วัดค่า p99 latency ของการค้นหาแคชบน Cloudflare ได้ มากกว่า 30ms
- ต่อให้ซ้อนแคชหลายชั้น (SWR, Redis ฯลฯ) ก็โดยพื้นฐานแล้ว ไม่มีทางเร็วไปกว่า ‘การไม่ต้องร้องขอผ่านเครือข่ายเลย 0 ครั้ง’
- ผลลัพธ์คือไม่สามารถทำเวลาในการตอบสนองให้ต่ำกว่า 10ms ตามเป้าหมายได้
-
ปัญหาการผูกติดกับ SaaS
- แม้เซิร์ฟเวอร์เลสจะช่วยลดภาระในการดูแลโครงสร้างพื้นฐาน แต่ในทางปฏิบัติกลับต้องพึ่งพา เครื่องมือ SaaS เพิ่มเติม อย่างหลีกเลี่ยงไม่ได้
- แคช → Redis, งานแบบแบตช์ → Queue, ล็อก → Durable Objects เป็นต้น
- แต่ละบริการเพิ่มทั้ง latency, ต้นทุน และจุดล้มเหลว
- แม้จะผสมใช้ Cloudflare Durable Objects, Queues, Workflows ฯลฯ แล้ว ความซับซ้อนจริงกลับเพิ่มขึ้น
-
ปัญหาด้านดาต้าไปป์ไลน์
- เพราะเซิร์ฟเวอร์เลสเป็นสภาพแวดล้อมชั่วคราว จึงต้อง flush ข้อมูลทันทีทุกครั้งที่มีการเรียกใช้
- เพื่อจัดการ event, log และ metric จึงต้องสร้างบริการ บัฟเฟอร์และตัวกลาง ที่ซับซ้อนขึ้นมาเอง
- ตัวอย่าง:
- ส่งล็อกแบบแบตช์ไปยัง ClickHouse ผ่าน
chproxy
- เมื่อต้องส่งล็อกไป Axiom ก็ต้องสร้างเซิร์ฟเวอร์บัฟเฟอร์แยกต่างหากเพื่อเลี่ยง rate limit
- ท้ายที่สุด แม้แต่ ฟีเจอร์วิเคราะห์ที่เรียบง่าย ก็ยังทำให้เกิดความซับซ้อนระดับระบบประมวลผล event แบบกระจาย
การย้ายไปสู่เซิร์ฟเวอร์แบบมีสถานะ
- เมื่อเริ่มใช้เซิร์ฟเวอร์แบบมีสถานะ v2 ที่พัฒนาด้วย Go ก็สามารถ ทำแบตช์ในหน่วยความจำและ flush เป็นรอบ ๆ ได้
- ไม่ต้องมีบริการเสริมหรือดาต้าไปป์ไลน์ซับซ้อนอีกต่อไป
- การบำรุงรักษา การดีบัก และการทดสอบในเครื่องง่ายขึ้นมาก
-
ผลลัพธ์ด้านประสิทธิภาพ
- เมื่อเปรียบเทียบ
/v1/keys.verifyKey กับ /v2/keys.verifyKey พบว่า latency ลดลง 6 เท่า
- แม้จะใช้โครงสร้างพื้นฐานน้อยกว่าการมี POP ทั่วโลก 300 แห่งของ Cloudflare แต่ประสิทธิภาพที่ผู้ใช้รับรู้กลับดีขึ้นอย่างชัดเจน
ประโยชน์นอกเหนือจากประสิทธิภาพ
-
การโฮสต์เอง (Self-Hosting)
- เพราะผูกติดกับรันไทม์ของ Cloudflare ลูกค้าจึง ไม่สามารถโฮสต์ Unkey เองได้
- แม้ Workers runtime จะเป็นโอเพนซอร์สในทางเทคนิค แต่การรันบนเครื่องโลคัล (แม้แต่โหมดพัฒนา) ก็ยากมาก
- เมื่อเปลี่ยนมาใช้เซิร์ฟเวอร์ Go มาตรฐาน การโฮสต์เองก็ง่ายขึ้น
- รันได้ด้วยคำสั่ง
docker run -p 8080:8080 unkey/api เพียงคำสั่งเดียว
- ไม่ต้องมีรันไทม์พิเศษหรือการตั้งค่าซับซ้อน
- ตอนนี้นักพัฒนาสามารถ รันสแตก Unkey ทั้งชุดบนเครื่องโลคัลได้ภายในไม่กี่วินาที ทำให้การดีบักและการทดสอบง่ายขึ้นมาก
- ความเร็วของการพัฒนาและดีบักบนเครื่องโลคัลดีขึ้นอย่างก้าวกระโดด
-
ปรับปรุงประสบการณ์นักพัฒนา
- ข้อจำกัดที่พบในเซิร์ฟเวอร์เลส:
- ต้องคำนึงถึงข้อจำกัดในการรันฟังก์ชัน
- รักษาสถานะข้ามการเรียกใช้ได้ยาก
- การดีบักล็อกแบบกระจายมีความซับซ้อน
- สภาพแวดล้อมทดสอบบนเครื่องโลคัลไม่สะดวก
- เมื่อย้ายไปสู่เซิร์ฟเวอร์แบบมีสถานะ ก็ช่วยลด ภาษีความซับซ้อน (Complexity Tax) เหล่านี้ลง
-
ได้มาซึ่งความเป็นอิสระจากแพลตฟอร์ม
- ไม่ต้องผูกติดกับ ecosystem ของ Cloudflare อีกต่อไป
- นำไป deploy ที่ไหนก็ได้
- ใช้ฐานข้อมูลใดก็ได้
- ผสานเข้ากับบริการภายนอกได้โดยไม่ต้องกังวลเรื่องความเข้ากันได้ของ runtime
กลยุทธ์การย้ายระบบและบทเรียนที่ได้
- ใช้การย้ายระบบครั้งนี้เป็นโอกาสในการแก้ไข ปัญหาการออกแบบ API ที่สะสมมานาน
- API v2 ใหม่ทำงานควบคู่ไปกับ v1 เดิม ทำให้ลูกค้าสามารถใช้ทั้งสองเวอร์ชันได้ในช่วงก่อนเลิกซัพพอร์ต
- ข้อดีอย่างหนึ่งของการคง v1 ไว้บนเซิร์ฟเวอร์เลสคือ แม้ปริมาณการใช้งานจะลดเหลือศูนย์ ก็ยังมีค่าใช้จ่ายไม่มากนัก จึงเปิดช่วงย้ายระบบได้ฟรี
-
สิ่งที่ยังคงไว้
- ไม่ได้ทิ้งทุกอย่างจากแนวทางเซิร์ฟเวอร์เลส
- การกระจาย edge ทั่วโลก: ใช้ AWS Global Accelerator เพื่อคง latency ต่ำทั่วโลก
- การสเกลอัตโนมัติ: ใช้ Fargate เพื่อจัดการการสเกลโดยไม่มีข้อจำกัดแบบเซิร์ฟเวอร์เลส
-
ปรับปรุงประสิทธิภาพของ rate limiter
- ในโมเดลเซิร์ฟเวอร์เลส ต้องยอมรับ trade-off อย่างมากระหว่าง ความเร็ว ความแม่นยำ และต้นทุน และด้วยธรรมชาติแบบกระจายจึงแทบเป็นไปไม่ได้ที่จะได้ครบทั้งสามอย่าง
- ด้วยเซิร์ฟเวอร์แบบมีสถานะและสถานะในหน่วยความจำ จึงสามารถสร้าง rate limiter ที่ เร็วขึ้น แม่นยำขึ้น และลดต้นทุนการดำเนินงานได้ด้วย
เมื่อใดที่เซิร์ฟเวอร์เลสเหมาะสม (และเมื่อใดที่ไม่เหมาะ)
-
กรณีที่เซิร์ฟเวอร์เลสเหมาะสม
- เวิร์กโหลดที่เกิดเป็นครั้งคราว: ถ้าไม่ได้รันต่อเนื่อง ความคุ้มค่าจากการสเกลลงเหลือศูนย์จะโดดเด่นมาก
- แพตเทิร์นคำขอ/คำตอบแบบเรียบง่าย: เมื่อไม่ต้องการสถานะคงอยู่หรือดาต้าไปป์ไลน์ที่ซับซ้อน
- สถาปัตยกรรมแบบ event-driven: เหมาะมากกับการตอบสนองต่อ event โดยไม่ต้องดูแลโครงสร้างพื้นฐาน
-
กรณีที่เซิร์ฟเวอร์เลสไม่เหมาะ
- เมื่อต้องการ latency ต่ำอย่างสม่ำเสมอ: การพึ่งพาเครือข่ายภายนอกทำให้ประสิทธิภาพลดลง
- เมื่อต้องการสถานะคงอยู่: การหาวิธีอ้อมข้อจำกัดของระบบไร้สถานะจะเพิ่มความซับซ้อน
- เวิร์กโหลดความถี่สูง: โมเดลคิดค่าบริการต่อการเรียกใช้อาจไม่คุ้มค่า
- เมื่อต้องการการควบคุมอย่างละเอียด: การ abstraction ของแพลตฟอร์มอาจกลายเป็นข้อจำกัด
-
ต้นทุนของความซับซ้อน
- บทเรียนสำคัญที่สุดจากการย้ายระบบครั้งนี้คือการเข้าใจ ต้นทุนของความซับซ้อนจากการพยายามหาทางอ้อมข้อจำกัดของแพลตฟอร์ม
-
สิ่งที่ต้องสร้างบนเซิร์ฟเวอร์เลส
- ไลบรารีแคชที่ซับซ้อนเพื่อเลี่ยงข้อจำกัดของความไร้สถานะ
- บริการเสริมหลายตัวสำหรับการประมวลผลข้อมูลแบบแบตช์
- ล็อกไปป์ไลน์ที่ซับซ้อนสำหรับการเก็บ metric
- วิธีอ้อมที่ซับซ้อนสำหรับการพัฒนาบนเครื่องโลคัล
-
บนเซิร์ฟเวอร์แบบมีสถานะ
- ทั้งหมดข้างต้น หายไป
- จากระบบกระจายที่มีชิ้นส่วนเคลื่อนไหวมากมาย กลายเป็น สถาปัตยกรรมแอปพลิเคชันที่เรียบง่ายกว่า
- บางครั้งทางออกที่ดีที่สุดคือ เลือกฐานที่ต่างออกไป แทนการพยายามหาทางอ้อมข้อจำกัด
ขั้นตอนถัดไป
- ปัจจุบันรันอยู่บน AWS Fargate หลัง Global Accelerator แต่เป็นเพียงทางออกชั่วคราว
- ปีหน้ามีแผนเปิดตัวแพลตฟอร์ม deployment ของตัวเองชื่อ "Unkey Deploy" ที่จะช่วยให้ลูกค้า (รวมถึง Unkey เอง) สามารถรัน Unkey ได้ทุกที่ที่ต้องการ
- การย้ายไปสู่เซิร์ฟเวอร์ Go แบบมีสถานะเป็นก้าวแรกในการทำให้ Unkey พกพาได้จริงและโฮสต์เองได้จริง
- รายละเอียดการใช้งานเผยแพร่เป็นโอเพนซอร์สใน GitHub repository
3 ความคิดเห็น
ครั้งหนึ่งผมก็เคยแทบจะเป็นสาวก serverless ถึงขั้นยัดสถาปัตยกรรม serverless เข้าไปทุกที่ แต่ช่วงนี้ผมกลับชอบโครงสร้างที่มี
ec2หนึ่งตัวกับrdsหนึ่งตัวมากกว่า แล้วค่อย ๆ แยกสิ่งที่จำเป็นออกมาทีละอย่าง และทำให้การนำ serverless มาใช้เป็นเรื่องที่ต้องคิดหนักมากขึ้นมีหลายเหตุผล แต่ถ้าในทีมมีแค่คนเดียวที่ไม่มีความรู้เรื่อง serverless ต้นทุนด้านการสื่อสาร/การบำรุงรักษาก็เพิ่มขึ้นมากจริง ๆ แล้วพอกลับมารันเซิร์ฟเวอร์อีกครั้ง ก็ยิ่งรู้สึกอีกครั้งว่า serverless ไม่ได้ถูกอย่างที่คิดหรือสะดวกอย่างที่คิดนัก
ความคิดเห็นจาก Hacker News
ในฐานะคนที่ใช้สภาพแวดล้อม serverless มาหลายปี (ส่วนใหญ่คือ amazon lambda และตัวอื่น ๆ ด้วย) ผมเห็นด้วยกับผู้เขียนอย่างมาก
serverless ช่วยลดงานบางส่วนก็จริง แต่ขณะเดียวกันกลับเพิ่มงานในด้านอื่นเพราะต้องคอยแก้ "ปัญหาที่ถูกสร้างขึ้นมาแบบประดิษฐ์"
ตัวอย่างหนึ่งของผมคือปัญหาเรื่องข้อจำกัดขนาดอัปโหลด
ตอนย้ายแอปเดิมไปเป็น serverless ผมคิดว่าแค่ทำ API endpoint สำหรับ import ข้อมูลลูกค้าขนาดใหญ่ แล้วต่อเข้ากับ background worker ก็น่าจะพอ
แต่ที่ "api gateway" (พร็อกซีที่เรียกโค้ด) กลับอัปโหลดไฟล์เกิน 100MB ไม่ได้ พอถามว่าสามารถเปลี่ยนลิมิตได้ไหม ก็ได้รับคำแนะนำง่าย ๆ ว่าให้บอกลูกค้าแบ่งไฟล์เป็นไฟล์เล็กลงแล้วค่อยอัปโหลด
ในเชิงเทคนิคมันอาจฟังดูสมเหตุสมผล แต่ในโลกจริงลูกค้าไม่มีทางเปลี่ยนวิธีอัปโหลดของตัวเองหรอก
มันให้ความรู้สึกแบบ "ใช้งานได้ดีในสุญญากาศ" มากกว่า คือฟังดูดีในทางทฤษฎี แต่พอลงมือทำจริง เวลาและต้นทุนที่ประหยัดได้จากการย้ายมา serverless สุดท้ายก็ต้องเอากลับไปจ่ายกับการแก้ปัญหาเฉพาะทางของ serverless อยู่ดี
ถ้าจะจัดการปัญหานี้ ต้องให้ presigned S3 URL
จะให้ผู้ใช้อัปโหลดเข้า S3 โดยตรงแล้วค่อยส่งผลการอัปโหลดกลับมา หรือจะผูกไฟล์ด้วย request id ก็ทำได้
ในฐานะคนที่ใช้ AWS มานาน ผมก็ยอมรับว่ามันน่ารำคาญ แต่ถ้าเปิด API ให้อัปโหลดไฟล์อะไรก็ได้ไว้ตรง ๆ ความเสี่ยงโดน DoS ก็สูงมาก เลยพอเข้าใจได้ว่าทำไมถึงมีลิมิต 100MB
แต่ถ้ามองจากความเร็วอินเทอร์เน็ตสมัยนี้ เกณฑ์ 100MB ก็ให้ความรู้สึกล้าสมัยไปหน่อย
ถึงอย่างนั้นผมก็คิดว่าสุดท้ายแล้วควรมีลิมิตอยู่ดี
บริษัทของเราเคยเป็นลูกค้ารายใหญ่ระดับตัวแทนของฝ่ายใบรับรอง SSL ของ AWS
เพราะต้องรองรับ Vanity URL (custom domain) เลยต้องใช้ใบรับรอง SSL แยกตามแต่ละโดเมน ซึ่งมีเป็นหลักพันใบ
เครื่องมือจัดการใบรับรองของ AWS ใช้งานได้ลื่นแค่ระดับหลักร้อยเท่านั้น เลยใช้เวลาประมาณ 3 เดือนกว่าปัญหานี้จะถูกแก้
น่าแปลกใจที่บางบริการของ AWS ตอบสนองความต้องการของลูกค้ากลุ่มเล็กมาก ๆ ได้ไม่เร็วเท่าไร
ตอนแรก Lambda ดูมีศักยภาพมากจนเราเอามาใช้ แต่สุดท้ายก็เลิกทุกโปรเจกต์ Lambda แล้วค่อย ๆ ย้ายไปสภาพแวดล้อมแบบคอนเทนเนอร์ตามความจำเป็น
Lambda เองก็ต้องอัปเกรด Node runtime ทุก 1-2 ปีอยู่ดี แต่ถ้าใช้คอนเทนเนอร์เราจะเป็นคนกำหนดรอบนั้นเองได้ เลยยืดหยุ่นกว่า
ปัญหาที่ยากที่สุดในวิทยาการคอมพิวเตอร์คือการคัดลอกไฟล์จากคอมพิวเตอร์เครื่องหนึ่งไปอีกเครื่องหนึ่ง
ทิ้งข้อมูลอ้างอิงไว้ให้ผู้อ่านในอนาคต
ถ้าใช้ตัวอัปโหลดกับ endpoint ของ "tus" จะได้ฟีเจอร์อย่างการอัปโหลดแบบแบ่งส่วน การอัปโหลดต่อเนื่องหลังหลุด ฯลฯ ซึ่งเหมาะกับการหลบข้อจำกัดแบบนี้
https://tus.io/
อย่างที่อธิบายในบทความ serverless มีกรณีใช้งานของมันแน่นอน
แต่ผมคิดว่ามันไม่เหมาะกับแอปส่วนใหญ่
ถ้าไม่ใช่กรณีพิเศษ ผมไม่คิดจะใช้ serverless เป็นโครงสร้างพื้นฐานหลัก
ในทางปฏิบัติมันกลับทำให้การจัดการอินฟราซับซ้อนขึ้นมากกว่า
แต่ละแพลตฟอร์มมีข้อกำหนดต่างกัน วิธีทดสอบ/พัฒนาก็ไม่เหมือนกัน ดูคลุมเครือไปหมดและทดสอบบนเครื่องตัวเองก็ยาก
ชั้น abstraction ของแต่ละแพลตฟอร์มก็มีหลุมพรางของมัน และไม่มีมาตรฐานจริง ๆ
การแพ็ก executable เป็น Docker image กลับสะดวกกว่า แถมตั้งค่าสภาพแวดล้อมได้แบบ abstract พอเหมาะ จึงเป็นสภาพแวดล้อมการพัฒนาที่เป็นธรรมชาติกว่าสำหรับผม
ผมรู้สึกว่าการใช้ abstraction ขั้นต่ำในระดับ Linux environment และ filesystem มีประสิทธิภาพที่สุด
ถ้าจำเป็นก็เอา image นั้นไปรันเป็นเซิร์ฟเวอร์ แล้วเปิดแบบ on-demand หรือให้พร้อมรอตลอดเวลาก็ได้
ถ้ามองเทรนด์เทคโนโลยีหลายอย่างในช่วง 10 ปีที่ผ่านมา จะเห็นว่าบ่อยครั้งเป็นเทคโนโลยีที่บริษัทใหญ่นำมาใช้เพื่อแก้ปัญหาที่มีอยู่จริงเฉพาะในสเกลของตัวเอง แล้วกลายเป็นของฮิต
ตัวอย่างก็อย่าง GraphQL, react, Tailwind, NextJS และอีกหลายอย่าง
ไม่มีเครื่องมือไหนเป็นคำตอบสารพัดนึกสำหรับทุกปัญหา สิ่งสำคัญคือการเลือกโดยอิงจากประสบการณ์และความเข้าใจต่อสถานการณ์กับปัญหาของตัวเอง
ตอนนี้ผมแทบอยากเล่าเลยว่าผมกำลังพยายามรัน amazon lambda app บนเครื่องตัวเองแบบ "สนุกแค่ไหน"
การจะทดสอบก่อน deploy นี่แทบเป็นชาเลนจ์ในตัวมันเอง
Knative (Serverless สำหรับ Kubernetes) รับคอนเทนเนอร์โดยตรง
เพราะเป็นฟอร์แมตแพ็กเกจมาตรฐาน เลยย้ายไปหลายแพลตฟอร์มได้ง่าย
ทีมนั้นจริง ๆ แล้วกำลังพัฒนาไลบรารีที่จะเอาไปผสานเข้ากับ server application แบบมี state ไม่ใช่ตัวแอปพลิเคชันเอง
ข้อดีด้านประสิทธิภาพก็มาจากการทำ authentication ใกล้กับสภาพแวดล้อมของลูกค้า ไม่ได้เกิดจาก serverless เอง
ที่จริงแพลตฟอร์ม "serverless" แทบทั้งหมดก็รับ Docker image ไม่ใช่เหรอ?
เท่าที่รู้ Cloud Run ก็รองรับ
จริง ๆ ความกังวลคือ “serverless function” มัน abstract อะไรมากเกินไปต่างหาก
ClickHouse ไม่ชอบ insert เล็ก ๆ หลายพันครั้ง เราเลยใช้บริการ Go ชื่อ chproxy มารวบรวม event แล้วส่งเป็น batch ก้อนใหญ่
แต่ละ Cloudflare Worker จะส่ง analytic event ไปที่ chproxy แล้ว chproxy ค่อยรวบไปส่ง ClickHouse ทีเดียว
สำหรับข้อมูล ClickHouse โดยเฉพาะ ผมสงสัยว่าทำไมถึงไม่ใช้ฟีเจอร์ asynchronous insert แทนที่จะต้องแยกอีกบริการออกมา
https://clickhouse.com/docs/optimize/asynchronous-inserts
นักพัฒนาทุกวันนี้เหมือนกำลังจมอยู่กับเครื่องมือที่อ้างว่าจะ "ทำให้ง่าย"
ทั้งที่จริงแล้วปัญหาส่วนใหญ่แก้ได้ง่ายด้วยเครื่องมือพื้นฐานที่สุดอย่างคอมไพเลอร์, bash script, ไลบรารี
การหมกมุ่นกับเครื่องมือแบบนี้บางทีกลับให้ผลเสียกับทั้งบริษัทและนักพัฒนาเอง
สำหรับผม Docker ในปี 2013 น่าจะเป็นเครื่องมือตัวสุดท้ายที่สร้างการเปลี่ยนแปลงเชิงบวกแบบใช้ได้ทั่วไปจริง ๆ
หลังจากนั้นสิ่งต่าง ๆ อาจช่วยบางบริษัทได้ แต่พอพยายามยัดให้ทุกบริษัทใช้ กลับกลายเป็นทำลาย productivity หรือทำให้ระบบพังมากกว่า
ทุกวันนี้ที่อย่าง Cloudflare กำลังผลักดัน v8 isolates เหมือนจะเป็น 'docker รุ่นถัดไป' แต่มันก็เหมาะกับแค่งานบางแบบ ไม่ได้เหมาะกับทุกอย่าง
รูปแบบ "รับ docker image แล้วเอาไปรันบนอินเทอร์เน็ต" มันทรงพลังมากจนผมคิดว่าต่อให้ถึงปี 2040 ก็น่าจะยังเป็นวิธีที่แข็งแรงที่สุดอยู่ดี
อีกปัญหาหนึ่งคือคนที่รู้พื้นฐานจริง ๆ มีน้อยลงเรื่อย ๆ
งานจำนวนมากกลายเป็นการเอาส่วนใหญ่ของสแตกไปจ้างบริการภายนอก แล้วสิ่งที่เราสร้างเองจริง ๆ ก็เหลือแค่บางส่วนของแอปหลัก
แม้แต่ส่วนนั้นก็อาจถูก AI เขียนแทนได้
ทั้งอุตสาหกรรมเหมือนกำลังสร้าง "ภาวะไร้พลังที่ถูกเรียนรู้" กันอยู่
คนจำนวนมากกว่าที่คิด ไม่ค่อยรู้เรื่องคอมไพเลอร์, bash script, ไลบรารี
AWS lambda ถูกเกินไปด้วยซ้ำ
แต่มันช่วยเรื่องภาพลักษณ์ได้นะ!
ผมไม่เคยเห็นใครเขียน bash script แล้วได้เลื่อนถึงระดับ Staff เลย
ผมอยากถาม "vercel security checkpoint" หน่อย
เวลาผมใช้ Proton VPN กับ Firefox Focus บน iPhone แล้วออกผ่าน exit node ที่แคลิฟอร์เนียหรือแคนาดา มันขึ้น code 99 "Failed to verify your browser" ซ้ำ ๆ
มันมีปัญหาอะไรเหรอ?
เธรดนี้ยิ่งทำให้ผมมั่นใจในความคิดตัวเอง
คำว่า serverless เองก็เป็นคำที่นิยามคลุมเครือมาก จนรู้สึกว่าชื่อมันไร้สาระ
เพราะสุดท้ายเซิร์ฟเวอร์ก็ยังมีอยู่ดี
มันคล้ายกับคำว่า "ไม่มีไฟฟ้า" ทั้งที่จริงก็ยังใช้ไฟฟ้าอยู่
เหมือนแค่เปลี่ยนชื่อ แต่ไม่ได้บอกเลยว่าข้างในเกิดอะไรขึ้นจริง ๆ
ผมไม่คิดว่าบทสรุปจะมีแค่ "serverless แย่"
บทเรียนที่สำคัญกว่าคือ ถ้าบริการของคุณมี dependency อยู่ ต่อให้ย้ายบริการไปใกล้ client มากขึ้น แต่ถ้าไม่ได้ย้าย dependency นั้นไปด้วย end-to-end experience ก็อาจช้ากว่าที่คิดได้
โดยธรรมชาติแล้วการ build ให้อยู่ใกล้ dependency มักดีกว่า และถ้ายังไม่พอ ก็ต้องย้ายหรือซิงก์ dependency ทั้งหมดไปไว้ใกล้ client แต่ในโลกจริงกระบวนการนั้นมักซับซ้อนมาก
มันขึ้นอยู่กับว่าคุณใช้ dependency อย่างไร และบ่อยแค่ไหน ถ้าเป็น DB ก็ต้องดูว่าใน use case นั้นควรอยู่ใกล้ server หรือใกล้ client มากกว่า
บางสถานการณ์ต้องการการตอบสนองเร็วมาก บางอย่างช้าหน่อยก็ได้
และยังแยกออกได้อีกตามว่าจะ cache อะไรไว้ฝั่ง server หรือ client ได้บ้าง
ผมไม่คิดว่ามันจำเป็นต้องมองแบบสองขั้วเสมอไป
ถ้าต้องการความคุ้มค่าต่อประสิทธิภาพสูงสุด คำตอบคือเซ็ตอินสแตนซ์เองแล้วจัดการสิ่งที่ต้องการด้วยตัวเอง
ไม่จำเป็นต้องปล่อยให้ผู้ให้บริการคลาวด์กลายเป็นพ่อมดแห่งการคิดเงินเกิน
ตอนนี้สิ่งที่เราไปถึงในฐานะ "local maximum" คือการใช้ Docker container เป็นมาตรฐานของ environment/deployment artifact แล้วค่อย inject secret เท่าที่จำเป็น
แบบนี้ทำให้ทดสอบบนเครื่องได้ง่าย และยังรักษาข้อดีสำคัญอย่าง infrastructure automation, reproducibility ฯลฯ ได้เกือบทั้งหมด
serverless เป็นแนวทางที่เกินจำเป็นสำหรับแอปส่วนใหญ่ แต่ก็เหมาะกับบางแอป
โดยเฉพาะ utility ง่าย ๆ หรือบริการแบบ on-demand ที่ไม่ต้องมีอินฟราเป็นของตัวเอง รวมถึงแอป stateless ขนาดใหญ่ serverless อาจเหมาะกว่า
ไม่ได้แปลว่า serverless ใช้ได้แค่งานง่าย ๆ เท่านั้น แต่ผมคิดว่ามีความขัดแย้งเชิงพื้นฐานระหว่างโมเดล "เว็บแอปแบบดั้งเดิม" กับแพลตฟอร์ม serverless
ดูเหมือนว่าตอนนี้คุณพร้อมจะรับ misterio แล้ว
https://github.com/daitangio/misterio
มันคือ wrapper สำหรับคลัสเตอร์ Docker แบบ stareless ที่เรียบง่าย
เริ่มจากในโฮมแล็บของผม แล้วก็มีคนสนใจกันมากขึ้น
Docker ก็คล้ายกับ microservices
เหมาะกับบางแอป แต่ก็ถูกโหมเกินจริงจนดูเหมือนเป็นมาตรฐานของทั้งอุตสาหกรรม
การใช้ Docker มากเกินไปทำให้มีภาระเรื่อง security patch และการปฏิบัติการ ถ้าใช้กับทุกโปรเจกต์แบบไม่คิดก็เป็นการจัดการความเสี่ยงที่ล้มเหลว
ทุกวันนี้ปัญหา dependency ก็ถูกแก้ได้แล้วสำหรับนักพัฒนาส่วนใหญ่ด้วยแนวทางที่ไม่ต้องติดตั้งแบบ global
Docker เลยไม่ถึงกับจำเป็นเหมือนเมื่อก่อน แม้จะยังเป็นกระแสหลักอยู่
ถ้ามองจากฝั่งผู้ให้บริการโฮสติ้ง ผมคาดว่าการนำ Docker เข้ามาจะเพิ่มมาร์จินได้อย่างน้อย 10%
ผมรู้สึกว่า Docker เองก็เข้าข่าย "ความหมกมุ่นกับเครื่องมือ" ที่มีคนข้างล่างพูดถึงเหมือนกัน
ประเด็นไม่ใช่ว่า serverless ใช้ไม่ได้ แต่เป็นเพราะผู้เขียนไม่เข้าใจพื้นฐานที่ตัวเองกำลังสร้างอยู่
การเอา API ที่ไวต่อ latency ไปวางบน stateless edge runtime เป็นความผิดพลาดระดับมือใหม่ และความเจ็บปวดที่เจอก็เป็นสิ่งที่คาดเดาได้เต็มที่
จากประสบการณ์ของผม ปัญหาส่วนใหญ่ของบริการคลาวด์เกิดจากการใช้สถาปัตยกรรมผิดหรือเข้าใจผิด
มันเป็นปัญหาที่เกิดจากคน ซึ่งหลีกเลี่ยงได้ด้วยการออกแบบที่รอบคอบกว่านี้
แต่ปัญหาคือผู้ขายคลาวด์ส่วนใหญ่มักโปรโมตผลิตภัณฑ์ด้วยการตลาดสวยหรูและซ่อนตัวเลขประสิทธิภาพจริงไว้
เราไม่มีทางรู้จริงว่า Lambdas จะเร็วพอสำหรับ workload ของเราหรือว่า external replication ของ AWS RDS เหมาะไหม จนกว่าจะได้ลองทดสอบ
ผมเรียนรู้จากประสบการณ์ว่า performance จริงของ AWS ต้อง benchmark เอง
ประเด็นไม่ใช่ว่าผู้เขียนไม่เข้าใจ แต่เป็นการที่มีคนเอาข้อมูลมาแชร์ต่างหากที่มีคุณค่า
ผมไม่คิดว่าจะมองว่านี่เป็นแค่ "ความผิดพลาดของมือใหม่" ได้
ในความเป็นจริงวิศวกรมักรู้ว่าวิธีบางอย่างไม่เหมาะกับงานจริง แต่ผู้จัดการก็มักผลักดันเพราะ "ตอนนี้เขาฮิตกันแบบนี้"
หรือบางทีก็จำเป็นต้องเลือกทางที่ด้อยกว่าเพราะข้อจำกัดเรื่องเวลาและงบประมาณ
หรือจะเป็นไปได้จริง ๆ ว่าทีมเดิมไม่เข้าใจมันดีพอ แต่ไม่ว่าแบบไหน ผมคิดว่าการแชร์เรื่องพวกนี้ช่วยให้ทั้งคอมมูนิตี้พัฒนาได้มาก
จากประสบการณ์ของผม ถ้าต้องการอะไรที่แน่นอนจริง ๆ (auto scaling ที่เร็ว, latency ต่ำ, CPU, ดิสก์, ความเร็วเครือข่าย ฯลฯ) การดูแล EC2 instance เองคือทางที่ชัวร์ที่สุด
ถ้ายอมปล่อยการควบคุมไปแล้วหวังว่าจะได้ performance ดีขึ้น สุดท้ายมักเจอคอขวดที่แก้ไม่ได้
สุดท้ายแล้วก็คือผู้เขียนได้ยอมรับว่าตัวเองเป็น "หนึ่งในหนึ่งหมื่นของวันนี้"
https://xkcd.com/1053/
ส่วนตัวผมรู้สึกขอบคุณที่แชร์ข้อมูลและความผิดพลาดแบบนี้ออกมา
วิศวกรรมคือการต่อสู้เรื่องต้นทุนอยู่เสมอ
ในช่วงแรกก็ใช้เพื่อลดเวลาในการทำต้นแบบหรือสร้างธุรกิจ
ภายหลังก็ต้องลดต้นทุนด้วยการปรับให้เหมาะสม
ตัวบทความแบบนี้เองก็พิสูจน์ว่าตัวเองไม่ใช่วิศวกรมากแค่ไหน
แบบนี้นี่แหละ