ข้ามไปยังเนื้อหา

Microservice Chassis

ทุก service ที่คุณ build ต้องมีเครื่องจักรพื้น ๆ ชุดเดียวกันก่อนถึงจะทำงานที่มีประโยชน์ได้ ต้องโหลด configuration ตั้ง structured logging พร้อม request correlation เปิด metric ในรูปแบบที่ระบบ monitoring คาดหวัง ตอบ health และ readiness probe ให้ orchestrator แล้ว register ตัวเองเพื่อให้ตัวอื่นค้นเจอ ไม่มีข้อไหนเป็น business logic เลย แต่ทุกข้อคือเงื่อนไขที่ทำให้ business logic รันได้อย่างปลอดภัย

ตอนคุณเขียน service เดียว คุณประกอบ plumbing นี้ครั้งเดียว พอถึง service ที่สิบ คุณสังเกตว่า code หลายร้อยบรรทัดเดียวกันถูก copy, paste และค่อย ๆ แตกต่างไปเงียบ ๆ: service หนึ่งเปิดเผย /healthz อีกตัว /health ตัวหนึ่ง log เป็น JSON อีกตัวเป็น plain text metric ถูกตั้งชื่อไม่สอดคล้องกัน cross-cutting baseline ถูก implement ใหม่ ต่างกันเล็กน้อย ในทุก service

คุณต้องการให้ทุก service ใหม่เริ่มต้นชีวิตด้วย configuration, logging, metric, health check และ discovery ที่เดินสายไว้แล้วและสอดคล้องกันทั้งกองทัพ — เพื่อให้ service เปิดเผย health endpoint เดียวกัน ปล่อย log ในรูปทรงเดียวกัน และรายงาน metric ด้วยชื่อเดียวกันกับทุก service อื่น คุณต้องการให้ developer ที่สร้าง service ใหม่เขียน business logic ตั้งแต่วันแรก ไม่ใช่ใช้เวลาหนึ่งสัปดาห์ประกอบ boilerplate ใหม่ และคุณต้องการ upgrade baseline นั้นในที่เดียวแทนที่จะใน repository ยี่สิบแห่ง

สิ่งที่ต้องการจึงเป็น operational baseline ชุดเดียวที่สอดคล้องกันและพร้อมใช้ในทุก service โดยลงแรงต่อ service ให้น้อยที่สุด และมีที่เดียวสำหรับพัฒนาต่อ

build microservice chassis ขึ้นมา ซึ่งคือ framework, library หรือฐานที่รวม cross-cutting concerns ไว้ให้ service ใหม่หยิบไปใช้ได้ทันที chassis รับหน้าที่ดูแล plumbing ทั้งหมด ทั้งอ่าน configuration ตั้ง structured logging ลงทะเบียน metric endpoint ติดตั้ง health และ readiness probe และจัดการ discovery registration แล้วเปิดผิวสัมผัสเล็ก ๆ ไว้ให้คุณเสียบ business logic เข้าไป

งานของ developer หดเหลือ: เริ่มจาก chassis เพิ่ม route และ domain logic แล้วส่งมอบ ทุกอย่างด้าน operation มีอยู่แล้วและเหมือนกับทุก service อื่นที่ build บน chassis chassis มักจับคู่กับ service template — repository ตั้งต้น, generator หรือโครงร่าง — เพื่อให้การสร้าง service ใหม่หมายถึงการ generate จาก template และเติม business code

เพราะ baseline อยู่ใน component ร่วมตัวเดียว การปรับปรุงอย่างเพิ่ม metric field ใหม่ ทำ log format ให้เข้มขึ้น หรือทำ health check ให้ดีขึ้น จึงเป็นการแก้ครั้งเดียว แล้ว roll out ด้วยการเลื่อนเวอร์ชัน chassis ในแต่ละ service แทนที่จะไล่แก้ทีละ repository ด้วยมือ

flowchart TB
  subgraph Service[A service built on the chassis]
    direction TB
    Biz[Business logic: routes and domain rules]
    subgraph Chassis[Microservice chassis]
      Cfg[Config loading]
      Log[Structured logging]
      Met[Metrics endpoint]
      Health[Health and readiness probes]
      Disc[Service discovery registration]
    end
    Biz --> Chassis
  end
  Chassis --> Plat[Platform: orchestrator, monitoring, registry]
chassis ห่อหุ้ม business logic โดยให้ config, logging, metric, health และ discovery; developer เพิ่มเพียง domain code

ตัวอย่างนี้คือ service ที่สร้างบน chassis การเรียก bootstrap ครั้งเดียวต่อสาย config, logging, metric, health และ discovery ให้ครบ สิ่งเดียวที่ developer ต้องเพิ่มคือ route ที่มีความหมายเชิงธุรกิจ แต่ละตัวอย่างเขียนตามสไตล์ของภาษานั้น ๆ

import { createService } from '@acme/chassis';
// The chassis loads config, sets up logging, metrics,
// /health, /ready, and registers the service for discovery.
const app = createService({ name: 'order-service' });
// Developer adds only the business logic.
app.get('/orders/:id', async (req, res) => {
const order = await orderRepo.find(req.params.id);
res.json(order);
});
app.start();

สิ่งที่คุณได้:

  • ได้ความสอดคล้องมาฟรี ๆ ทุก service เปิด health endpoint รูปแบบ log และชื่อ metric ชุดเดียวกัน เพราะได้มาจาก component ร่วมตัวเดียว observability และ operability เลิกเป็นเรื่องที่แต่ละทีมเดาเอาเอง
  • สร้าง service ได้เร็วขึ้น service ใหม่เริ่มต้นด้วย operational baseline ที่มีอยู่แล้ว ดังนั้น developer จึงเขียน domain code แทนการประกอบ plumbing ใหม่
  • ที่เดียวให้พัฒนา baseline การปรับปรุง logging, metric หรือ health ลงใน chassis และ roll out ด้วยการเลื่อนเวอร์ชัน ไม่ใช่การแก้ทุก repository

สิ่งที่คุณต้องเสีย:

  • ต้องลงแรงแยกตามภาษา ระบบแบบ polyglot ต้องมี chassis หนึ่งตัวต่อหนึ่งภาษา และการดูแลให้ทุกตัวมี feature เท่าเทียมกันคืองานที่ต้องทำต่อเนื่องไม่จบ
  • การ coupling และแรงเสียดทานด้านการทำเวอร์ชัน ทุก service พึ่งพา chassis การเปลี่ยนแบบ breaking บังคับให้ต้อง upgrade แบบประสานงานกัน และ chassis ที่เก่าค้างสามารถรั้ง service ไว้ได้
  • เสี่ยงบวม chassis ที่พยายามทำทุกอย่างจะหนักและบังคับสไตล์มากเกินไป ให้โฟกัสเฉพาะ cross-cutting concerns จริง ๆ ไม่ใช่ helper เชิงธุรกิจ

ข้อสังเกตที่ใช้ได้จริง: chassis จัดการเรื่อง in-process (config, logging, metric, health) ส่วน Service Mesh จัดการ networking between-process หลายทีมใช้ทั้งสองอย่าง

  • Externalized Configuration — chassis มักเป็นตัวที่อ่านและใช้ externalized config
  • Service Mesh — จัดการ networking cross-cutting concerns ที่ chassis ปล่อยให้ sidecar ทำ
  • Service per Container — service ที่ build บน chassis ก็ยังถูก package และรันแบบหนึ่ง service ต่อหนึ่ง container
ข้อดีข้อแลกเปลี่ยน
cross-cutting concerns (logging, health check, metrics) ทำครั้งเดียวchassis กลายเป็น dependency ที่ทุก service ผูกกัน
ทุก service มี baseline ของ observability และ security เหมือนกันอัปเดต chassis ต้อง rebuild และ redeploy ทุก service
new service เริ่มต้นได้เร็ว — clone chassis แล้วเพิ่ม business logicchassis อาจ bloat ถ้าใส่ฟีเจอร์มากเกิน
standard ชัดเจน — ทุกทีมทำงานแบบเดียวกันหลาย language stack = หลาย chassis

Fat Chassis — ใส่ business logic ลงใน chassis อาการ:

  • chassis มี auth logic, pricing logic, หรือ domain rule
  • ทุก service ได้รับ code ที่ส่วนใหญ่ไม่ต้องการ
  • chassis กลายเป็น monolith ที่แชร์ข้าม service

Chassis ที่ไม่ Update — chassis เก่าแต่ service ใหม่ยังคง base บน version เก่า อาการ:

  • vulnerability ใน logging library แก้ใน chassis ใหม่ แต่ service เก่ายังใช้ version เก่า
  • ไม่มี automated update mechanism

💡 ตัวอย่างจากของจริง

Netflix OSS:

  • สร้าง Spring Cloud Netflix เป็น chassis สำหรับ Java service
  • รวม Eureka (discovery), Hystrix (circuit breaker), Ribbon (load balancing) ไว้ใน chassis
  • ทีมใหม่ใช้ starter project แล้วได้ baseline observability ทันที

Uber:

  • ใช้ Fx (DI framework ของ Uber) เป็นฐานของ chassis สำหรับ Go service
  • ทุก service ใหม่เริ่มจาก template ที่มี logging, tracing, metrics พร้อม
microservice chassis คืออะไร?
chassis แก้ปัญหาอะไรเป็นหลัก?
chassis ต่างจาก service mesh อย่างไร?
ข้อใดคือต้นทุนจริงของ pattern chassis?