5 คะแนน โดย GN⁺ 2025-12-14 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • Twilio Segment เคยดูแล โครงสร้างไมโครเซอร์วิส หลายร้อยบริการ แต่ได้เปลี่ยนกลับไปใช้ บริการเดียว (โมโนลิท) เนื่องจากความซับซ้อนและภาระในการบำรุงรักษา
  • ในช่วงแรกมีการแยก Destination API แต่ละตัวออกจากกันเพื่อให้แยกผลกระทบจากปัญหาและรองรับการขยายระบบได้ แต่เมื่อจำนวนบริการเพิ่มเกิน 140 บริการ ภาระด้านการปฏิบัติการ ก็พุ่งสูงขึ้นอย่างมาก
  • การจัดการ รีโพซิทอรีจำนวนมากและไลบรารีที่ใช้ร่วมกัน กลายเป็นเรื่องยาก และยังเกิดปัญหาที่การทดสอบและการดีพลอยส่งผลกระทบต่อทุกบริการ
  • เพื่อแก้ปัญหานี้ บริษัทจึงนำ ระบบ Centrifuge และ โครงสร้าง monorepo มาใช้ พร้อมสร้าง Traffic Recorder สำหรับทำให้การทดสอบเป็นอัตโนมัติ
  • ผลลัพธ์คือความเร็วในการพัฒนาและเสถียรภาพดีขึ้นอย่างมาก และ Twilio Segment ยังคงรักษาโครงสร้างแบบโมโนลิทไว้เพื่อ ประสิทธิภาพการทำงานและประสิทธิภาพด้านปฏิบัติการ

การนำไมโครเซอร์วิสมาใช้และข้อจำกัด

  • Twilio Segment เลือกใช้ สถาปัตยกรรมไมโครเซอร์วิส สำหรับโครงสร้างพื้นฐานข้อมูลลูกค้า โดยออกแบบให้บริการแต่ละตัวประมวลผลอีเวนต์อย่างอิสระตามจุดประสงค์
    • ส่งข้อมูลไปยัง server-side destinations หลายร้อยรายการ (เช่น Google Analytics, Optimizely เป็นต้น)
    • ในช่วงแรกใช้คิวเดียว แต่เมื่อ destination ใด destination หนึ่งมีปัญหา ก็เกิดปัญหา head-of-line blocking ที่ทำให้ทั้งระบบหน่วงตามไปด้วย
  • เพื่อแก้ปัญหานี้ จึงแยกเป็น บริการและคิวเฉพาะสำหรับแต่ละ destination ทำให้สามารถแยกความเสียหายและขยายระบบแบบอิสระได้
  • อย่างไรก็ตาม เมื่อจำนวนบริการเพิ่มขึ้น ความซับซ้อนในการปฏิบัติการและต้นทุนการบำรุงรักษา ก็เพิ่มขึ้นอย่างรวดเร็ว ส่งผลให้ความเร็วในการพัฒนาลดลงและอัตราการเกิดข้อบกพร่องสูงขึ้น

ปัญหาของรีโพซิทอรีแยกกันและไลบรารีที่ใช้ร่วมกัน

  • แต่ละ destination ใช้รูปแบบ API ที่ต่างกัน จึงต้องมี โค้ดแปลงข้อมูลแบบคัสตอม
    • เดิมทีจัดการในรีโพเดียว แต่เมื่อการทดสอบล้มเหลวแล้วกระทบทั้งหมด จึงตัดสินใจ แยกรีโพ
  • หลังจากนั้นเมื่อมี destination ใหม่เพิ่มมากกว่า 50 รายการ ก็ทำให้เกิด รีโพซิทอรีมากกว่า 50 แห่ง
    • แม้จะนำ ไลบรารีที่ใช้ร่วมกัน มาใช้สำหรับฟังก์ชันทั่วไป แต่ก็ทำให้ภาระจากความไม่ตรงกันของเวอร์ชันและการดีพลอยเพิ่มขึ้น
  • เนื่องจากรูปแบบโหลดของแต่ละบริการแตกต่างกัน จึงตั้งค่า auto-scaling ได้ยาก และในบางกรณีผู้ปฏิบัติการต้องปรับด้วยตนเอง

การเปลี่ยนเป็นโมโนลิทและการนำ Centrifuge มาใช้

  • มีการตัดสินใจ รวมบริการมากกว่า 140 ตัวให้เป็นบริการเดียว
    • เพื่อทดแทนคิวแยกแต่ละตัว จึงพัฒนา ระบบ Centrifuge เพื่อส่งอีเวนต์ทั้งหมดเข้าสู่บริการเดียว
    • ต่อมา Centrifuge ได้พัฒนากลายเป็น โครงสร้างพื้นฐานแบ็กเอนด์ Connections ของ Twilio Segment
  • การเปลี่ยนเป็นโครงสร้างบริการเดียวช่วยให้ ภาระด้านการปฏิบัติการลดลง และ การรับมือเหตุขัดข้องง่ายขึ้น

monorepo และการทำให้การทดสอบเป็นอัตโนมัติ

  • รวมโค้ด destination ทั้งหมดเข้าไว้ใน รีโพซิทอรีเดียว และทำให้ dependency มากกว่า 120 รายการใช้เวอร์ชันเดียวกัน
    • ช่วยให้การจัดการเวอร์ชันง่ายขึ้นและเพิ่มประสิทธิภาพในการบำรุงรักษา
  • นำ Traffic Recorder มาใช้เพื่อทำให้การทดสอบเป็นอัตโนมัติ
    • บันทึกและเล่นซ้ำคำขอและคำตอบ HTTP จริง เพื่อตัดการพึ่งพาเครือข่ายภายนอก
    • ความเร็วในการทดสอบลดจากระดับหลายนาทีเหลือเพียง ระดับมิลลิวินาที พร้อมเสถียรภาพที่ดีขึ้นมาก
  • อัตราการทดสอบล้มเหลวลดลง และผลิตภาพของนักพัฒนาดีขึ้นอย่างชัดเจน

ผลของโครงสร้างโมโนลิทและ trade-off

  • หลังรวมเป็นบริการเดียว ความเร็วในการดีพลอยและประสิทธิภาพการพัฒนา ดีขึ้นอย่างมาก
    • ภายใน 1 ปี จำนวนครั้งที่ปรับปรุงไลบรารีที่ใช้ร่วมกันเพิ่มจาก 32 ครั้งเป็น 46 ครั้ง
    • วิศวกรเพียงคนเดียวสามารถดีพลอยได้ภายในไม่กี่นาที
  • ประสิทธิภาพด้านการปฏิบัติการ ก็ดีขึ้นเช่นกัน โดยแม้โหลดจะพุ่งสูงก็ยังรองรับได้ผ่าน worker pool ขนาดใหญ่
  • อย่างไรก็ตาม ยังมีข้อเสีย เช่น การแยกข้อบกพร่องทำได้ยาก, ประสิทธิภาพแคชลดลง, และ ความเสี่ยงจากการอัปเดต dependency
    • แต่การสูญเสียบางส่วนถูกชดเชยด้วย ความเรียบง่ายในการปฏิบัติการและผลิตภาพที่ดีขึ้น

บทสรุป

  • ไมโครเซอร์วิสช่วยแก้ปัญหาด้านประสิทธิภาพในระยะแรก แต่ไม่เหมาะกับ การขยายขนาดใหญ่และการอัปเดตแบบยกชุด
  • การเปลี่ยนเป็นโมโนลิทช่วยปรับปรุงทั้ง เสถียรภาพในการปฏิบัติการและความเร็วในการพัฒนา
  • เพื่อให้การเปลี่ยนผ่านประสบความสำเร็จ จำเป็นต้องมี ระบบการทดสอบที่แข็งแรง และ การยอมรับ trade-off
  • Twilio Segment ยังคงใช้ไมโครเซอร์วิสกับโครงสร้างพื้นฐานบางส่วนอยู่ แต่สำหรับ server-side destinations มองว่าโมโนลิทเป็นโครงสร้างที่เหมาะสมกว่า

2 ความคิดเห็น

 
yangeok 2025-12-16

การแยกทุกอย่างออกเป็นส่วนย่อยและทำให้เป็นมาตรฐานทั้งหมดดูเหมือนจะมีความเสี่ยงนะ

 
GN⁺ 2025-12-14
ความคิดเห็นใน Hacker News
  • เมื่อรวบรวมโค้ดของทุกปลายทางไว้ใน repo เดียว ก็สามารถรวมเป็นบริการเดียวได้
    ผลลัพธ์คือประสิทธิภาพการพัฒนาเพิ่มขึ้นมาก ตอนนี้ไม่ต้อง deploy บริการมากกว่า 140 ตัวทุกครั้งที่แก้ shared library หนึ่งตัว
    วิศวกรคนเดียวสามารถ deploy ได้ภายในไม่กี่นาที
    ถ้าการเปลี่ยน library ทำให้ต้อง deploy ทุกบริการใหม่ทั้งหมด นั่นก็ไม่ใช่ service ที่แท้จริง แต่เป็น distributed monolith
    แนวคิดที่ว่าต้องบังคับ sync shared library ให้ตรงกันทั้งระบบ ขัดกับปรัชญาของสถาปัตยกรรมแบบ service เอง

    • ที่คุณพูดก็มีเหตุผล แต่ในความเป็นจริงสถานการณ์มัน ซับซ้อนกว่านั้นมาก
      นี่ใกล้เคียงกับระบบ build/deploy แบบใช้ร่วมกัน สไตล์ Amazon มากกว่า ไม่ใช่ “redeploy ทั้งหมดทุกครั้งที่อัปเดต library”
      มีการดึง library จากแหล่งกลางที่บริหารจัดการจากศูนย์เดียว และถ้าเวอร์ชันต่างกันก็ต้อง migration ทั้งหมดเพราะปัญหาความเข้ากันได้
      ถ้าต้องถอดบางเวอร์ชันออกเพราะช่องโหว่ด้านความปลอดภัย ก็จำเป็นต้อง redeploy ทั้งหมด แต่ข้อดีของการจัดการแบบรวมศูนย์มีมากกว่าเยอะ
      ระบบแบบนี้ยังจัดเป็น microservices ได้อยู่ แต่ในแง่ต้นทุนและประสิทธิภาพการปฏิบัติการมันทำงานคล้ายสภาพแวดล้อมที่ใช้ร่วมกัน
      การเรียกสิ่งนี้ว่า distributed monolith ถือว่าตีความเกินไป
    • พูดน่ะง่าย แต่ในทางปฏิบัติมันทำให้เกิด บั๊กละเอียดอ่อนหรือความไม่เข้ากันได้ ระหว่างบริการได้ง่ายมาก
      เมื่อใช้แพตเทิร์น microservices ความเสี่ยงในการ deploy จะเพิ่มขึ้น และช่วงแรกมักมองไม่เห็น
      ตัวอย่างเช่น ถ้าแก้บั๊กใน library เกี่ยวกับเงิน คุณก็ต้องชั่งใจตามความเป็นจริงว่าจะ deploy ทุกบริการใหม่ทั้งหมดหรือไม่
    • การที่ต้องอัปเกรด library ทั้งระบบ ไม่ได้แปลว่าเป็น การ coupling ที่ผิดเสมอไป
      library ที่มีช่องโหว่ด้านความปลอดภัย ต่อให้ระบบออกแบบแบบไหนก็ต้องเปลี่ยนทั้งชุดอยู่ดี
      ในกรณีแบบนี้ monolith กลับจัดการได้ง่ายกว่า
    • แนวคิดของ shared library เองทำให้ทุกบริการ couple กันแน่น
      ถ้าเป็น microservices จริงก็ควรคุยกันด้วยข้อความและใช้ JSON
      ควรต้องรู้แค่ API ไม่ใช่ตัวโค้ด แบบนั้นแต่ละส่วนถึงจะ deploy และ scale แยกกันได้อย่างอิสระ
    • งั้นหมายความว่าต้องเขียน โค้ด logging ใหม่ในแต่ละบริการทั้ง 140 ตัวเลยหรือ?
      การใช้ shared module ไม่สมเหตุสมผลกว่าหรือ?
  • ที่บริษัทก่อนหน้านี้ ทุกอย่างรันแบบ microservices ส่วนบริษัทก่อนหน้านั้นเป็น AWS serverless
    ทั้งสองกรณี ปัญหาใหญ่ที่สุดคือการสื่อสารระหว่างบริการ ซิงก์ contracts ยาก และการ deploy ก็ซับซ้อน
    ช่วงแรกเคลื่อนไหวได้เร็ว แต่พอเวลาผ่านไปความซับซ้อนก็ระเบิด เกิด การพัฒนาที่ขับเคลื่อนด้วยความกลัว และมีประชุมมากเกินไป
    บริษัทปัจจุบันเป็น monolith และจัดการง่ายกว่ามาก type ชัดเจน และ refactor ง่าย
    การได้เห็น AI agent ที่สร้างอยู่บนแพลตฟอร์มภายใน ค่อยๆ ปรับปรุงตัวเองจากใน codebase เป็นเรื่องน่าสนใจ
    ข้อเสียมีแค่ build ใช้เวลานาน แต่ด้วยความก้าวหน้าของ toolchain ก็คาดว่าในปี 2026 จะ deploy ได้เร็วขึ้น 10 เท่า
    ข้อสรุปของฉันคือ เราเติบโตและขยายได้เร็วขึ้นมากเพราะสถาปัตยกรรมแบบ monolith

    • ประสบการณ์ของผมตรงกันข้ามเลย ผมทำงานที่ SendGrid มา 10 ปี เห็นการขยายจาก 12 คนเป็น 500 คน และมันเกิดขึ้นได้เพราะ สถาปัตยกรรมแบบ service
      ใน monolith นั้น การแยก concerns มักพังอยู่เสมอ และมี coupling ระหว่างทีมสูงมาก
      ความเร็วและการขยายที่แท้จริงเกิดขึ้นได้ก็ต่อเมื่อทีมแยกจากกัน
      การย้ายจาก ORM ไปเป็น DTO ใช้เวลา 2 ปี 50 ทีม และคนมากกว่า 150 คน
      งานซับซ้อนแบบนี้คงเป็นไปไม่ได้ถ้าไม่มี microservices
  • อ่านบทความนี้แล้ว รู้สึกว่าประเด็นสำคัญไม่ใช่การเลือกระหว่าง microservices vs monolith ในเชิงเทคนิค
    แต่อยู่ที่ คุณภาพและโครงสร้างขององค์กรวิศวกรรม มากกว่า
    โครงสร้างของ code repository และการทดสอบสะท้อนระดับขององค์กรนั้นออกมาตรงๆ

    • หลายทีมขาด วินัยทางเทคนิค
      ถ้าไม่มีคนที่กล้าพูดว่า “อย่าทำแบบนั้น” ความซับซ้อนก็จะระเบิด
      ต้องมีผู้นำที่มีอำนาจพอให้ทีมหยุดและคิดได้
    • ผมเคยทำโปรเจกต์กับ Twilio และมัน เละเทะมากจริงๆ
      เวลามีปัญหา API ก็ไม่วิเคราะห์สาเหตุ แต่ไปแก้ข้อมูลแล้วปิด ticket
      ต่อให้ปัญหาเดิมเกิดซ้ำ ก็ไม่แก้ที่ต้นเหตุ
    • กฎของ Conway ถูกพิสูจน์อีกครั้ง
      ถึงขั้นที่แค่สัมภาษณ์งานก็พอจะเดาโครงสร้าง codebase ของบริษัทได้ระดับหนึ่ง
  • นี่ไม่ใช่การ เปลี่ยนไปเป็น monolith จริงๆ แต่ยังเป็น สถาปัตยกรรมแบบ SOA อยู่
    แค่ขอบเขตของ service ใหญ่ขึ้นเท่านั้น
    ถ้าทีมเดียวดูแล 140 services, SOA ก็เป็นโครงสร้างเพื่อให้ทีมขยายได้ ไม่ใช่เพื่อให้ service ขยายได้
    ถ้าทีมเดียวดูแล shared libraries ทั้งหมด ก็จะเกิดเวอร์ชันไม่ตรงกันและความสับสนของ API
    สุดท้ายแล้ว โครงสร้างองค์กรเป็นตัวกำหนดสถาปัตยกรรม ทีมเดียวรวมทุกอย่างเข้าด้วยกันเพื่อลดความซับซ้อน
    นี่ไม่ใช่ “monolith” แต่เป็น ระดับ service ที่ปรับขอบเขตให้เหมาะกับระดับทีม
    ผมคิดว่าโครงสร้างแบบนี้เหมาะที่สุดแล้ว และถ้าทีมใหญ่ขึ้นก็ค่อยแยกออกอีกครั้ง

  • ผมไม่ได้เป็น สายสนับสนุน microservices แต่เห็นชัดว่ามี การสร้างทางเลือกสองขั้วปลอมๆ ระหว่าง “monorepo vs microservices”
    เครื่องมือมากเกินไปตั้งสมมติฐานว่า service กับ repo ต้องเป็น 1:1
    แต่จริงๆ คุณเก็บทุกอย่างไว้ใน repo เดียวและยัง deploy แยกอิสระได้
    คงดีถ้าใน GitHub หรือที่คล้ายกันจะมองแต่ละโฟลเดอร์เป็น service อิสระได้

    • บริษัทก่อนหน้าผมทำสิ่งนี้ขึ้นมาเอง
      ใช้ Bazel จัดการ dependency tree และใช้ bazel query หา target ที่ได้รับผลกระทบเพื่อรันเทสต์อัตโนมัติ
      แล้วเชื่อมกับ GitHub Actions เพื่อทำ workflow ที่บล็อก PR ได้
      มันทำงานดี แต่ใช้เวลาหลายเดือนกว่าจะสร้างเสร็จ
    • คำพูดที่ว่า “พอเปลี่ยนจาก microservices เป็น monolith ปัญหาก็หายไป” ทำให้รู้สึกขัดใจ
      ปัญหาที่แท้จริงคือ การปฏิบัติการและเครื่องมือที่ไม่พร้อม — ทั้ง CI, autoscaling และระบบ on-call ล้วนไม่ดีพอ
  • ทั้งสองแนวทางล้มเหลวได้ทั้งคู่
    ในสภาพแวดล้อมอย่าง Node.js หรือ Python ปริมาณโค้ดที่ event loop รับมือได้มีขีดจำกัด
    ผมเคยดูแล 200 services ด้วยคน 6~8 คน และก็เคยดูแล monolith หนึ่งตัวด้วยคน 80 คน
    microservices เหมาะกับการเปลี่ยนแปลงเล็กๆ แต่เปลี่ยนทั้งระบบยาก
    ส่วน monolith ก็ตรงกันข้าม
    สุดท้ายสิ่งสำคัญไม่ใช่สถาปัตยกรรม แต่คือ วิธีทำ abstraction, testing และ decoupling

    • “ถ้าเป็นซอฟต์แวร์ที่แก้ปัญหาทางธุรกิจเดียวกัน ก็ควรรวมเป็น microservice เดียว”
      คำว่า micro ไม่ได้วัดจากเทคโนโลยี แต่จาก หน่วยทางธุรกิจ
      ถ้าแยกย่อยต่ำกว่านั้นก็จะกลายเป็น nanoservices
    • การให้เหตุผลแบบนี้สุดท้ายก็ดูเหมือนเป็นแค่ทางออกชั่วคราวที่ใช้กลบ ข้อจำกัดของ runtime
      ในสภาพแวดล้อมอย่าง Beam, JVM, Rust, Go ปัญหานี้ถูกแก้ไปนานแล้ว
    • อยากรู้เหมือนกันว่าขีดจำกัดของปริมาณโค้ดที่ event loop รับมือได้ วัดกันเป็นหน่วยอะไรแน่
      เป็นปัญหาเรื่อง CPU cache หรือเปล่า?
    • สงสัยว่าระบบขนาดใหญ่เขาใช้ Node.js หรือ Python กันจริงหรือ
      ปกตินึกว่าจะใช้ Go, Java, C# มากกว่า
  • ในบริษัทส่วนใหญ่ microservices กลับเป็นต้นตอของปัญหา 90% เสียมากกว่า
    ถ้าไม่ใช่องค์กรยักษ์อย่าง AWS, Google, Netflix ก็มักไม่เหมาะ
    แค่การแยกระบบออกเป็นหน่วยที่ประกอบกันได้ก็ยากพออยู่แล้ว การเพิ่มขอบเขตเครือข่ายเข้าไปอีกยิ่งไม่ฉลาด
    เทรนด์ถัดไปน่าจะเป็นการถอยจาก React หรือ SPA ไปสู่ แนวทางที่ยึด server เป็นศูนย์กลาง

  • เหตุผลที่เปลี่ยนไปใช้ microservices เพราะ “เทสต์พังบ่อย” ฟังดูเป็น วิธีคิดที่กลับด้านมาก
    แค่เพราะเทสต์พังแล้วจะยกเครื่องโครงสร้าง codebase ทั้งหมดก็ดูแปลก

    • เราก็เคยเจอปัญหาคล้ายกัน แต่แก้ด้วยการให้แต่ละทีมมี สภาพแวดล้อมพัฒนาแยกของตัวเอง
      พอแยก VM และการตั้งค่า CI/CD รายทีมแล้ว ปัญหาเทสต์ชนกันก็หายไป
      ข้อเสียคือจะไม่เห็นปัญหาการชนกันระหว่างฟีเจอร์ทันที แต่เพราะความเป็นเจ้าของโค้ดชัดเจน ก็ไม่ได้เป็นปัญหาใหญ่
  • มีคนขอให้เพิ่ม [2018] ในชื่อเรื่อง

    • แอบสงสัยว่าตอนนี้เขา หันกลับไปใช้ microservices อีกรอบหรือยัง
    • ผมว่าการขุดบทความเมื่อ 7 ปีก่อนกลับมาพูดใหม่ก็ดูแปลกๆ ในโลกเทคโนโลยีมันแทบจะเป็น ประวัติศาสตร์โบราณ แล้ว
  • เขาบอกว่าแยก repo เพราะ “ถ้าเทสต์พัง ก็ต้องไปแก้โค้ดที่ไม่เกี่ยวด้วย”
    แต่ดูเหมือนน่าจะมี ทางแก้อื่น เช่น เปลี่ยนวิธีรันเทสต์ หรือยอมให้ deploy แบบแมนนวลได้
    การแยก repo ไม่ใช่คำตอบเดียว