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

ชั่ง​น้ำ​หนักข้อ​แลกเปลี่ยน​ของ Microservices (Microservice Trade-Offs)

เกี่ยว​กับ​บท​แปล​นี้

บทความ​นี้​เป็น บท​แปล​ภาษา​ไทย​อย่าง​ไม่​เป็น​ทางการ ของ “Microservice Trade-Offs” โดย Martin Fowler เผยแพร่​ครั้ง​แรก​บน martinfowler.com เมื่อ 1 กรกฎาคม 2015

แปล​และ​เรียบเรียง​เพื่อ​การ​ศึกษา — ไดอะแกรม​เชิง​แนวคิด​วาด​ใหม่​เป็น​ภาษา​ไทย (label ของ node คง​ไว้​เป็น​ภาษา​อังกฤษ) ส่วน​ศัพท์​เทคนิค​คง​ไว้​เป็น​ภาษา​อังกฤษ​ตาม​ต้นฉบับ​พร้อม​คำ​อธิบาย​ไทย​เมื่อ​พบ​ครั้ง​แรก ลิขสิทธิ์​ของ​เนื้อหา​ต้นฉบับ​เป็น​ของ​ผู้​เขียน

หลาย​ทีม​พบ​ว่า​สถาปัตยกรรม​แบบ microservices ช่วย​ยก​ระดับ productivity (ผลิต​ภาพ/การ​ผลิต​งาน) ได้​เหนือ​สถาปัตยกรรม​แบบ monolith (application ก้อน​เดียว​ที่ deploy พร้อม​กัน​ทั้งหมด) แต่​ก็​มี​อีก​หลาย​ทีม​ที่​กลับ​พบ​ว่า​มัน​เป็น​ภาระ​ที่​ฉุด​ให้​ช้า​ลง

เช่น​เดียว​กับ​รูปแบบ​สถาปัตยกรรม​ทุก​แบบ microservices มา​พร้อม​ทั้ง​ข้อดี​และ​ข้อ​เสีย การ​จะ​ตัดสิน​ใจ​อย่าง​มี​เหตุผล​ได้ เรา​ต้อง​เข้าใจ trade-off (การ​แลก​ได้​แลก​เสีย) เหล่า​นี้ แล้ว​นำ​ไป​ปรับ​ใช้​กับ context (บริบท​เฉพาะ) ของ​เรา​เอง

บทความ​นี้​ตั้งใจ​จะ​ช่วย​ชี้​ให้​เห็น​ปัจจัย​ที่​ควร​พิจารณา มากกว่า​จะ​ฟัน​ธง​ว่า​ทาง​ไหน “ดี​กว่า” แบบ​ตายตัว

ต่อ​ไป​นี้​ผม​จะ​ไล่​ข้อดี (Pro) และ​ข้อ​เสีย (Con) สลับ​กัน​ไป เพราะ​ใน​ทาง​ปฏิบัติ​หลาย​อย่าง​เป็น​สอง​ด้าน​ของ​เหรียญ​เดียวกัน ข้อดี​ของ microservices ก็​มัก​ลาก​ต้นทุน​บาง​อย่าง​ติดตาม​มา​ด้วย​เสมอ

flowchart LR
  MS[Microservices]
  MS --> B[Benefits]
  MS --> C[Costs]
  B --> B1[Strong Module Boundaries]
  B --> B2[Independent Deployment]
  B --> B3[Technology Diversity]
  C --> C1[Distribution]
  C --> C2[Eventual Consistency]
  C --> C3[Operational Complexity]

ภาพ 1 — ภาพ​รวม​ของ​ข้อ​แลกเปลี่ยน: ข้อดี​สาม​ข้อ (module boundaries ที่​แข็งแรง, deploy อิสระ, ความ​หลากหลาย​ทาง​เทคโนโลยี) ต้อง​ถูก​ชั่ง​กับ​ต้นทุน​สาม​ข้อ (distribution, eventual consistency, ความ​ซับซ้อน​ด้าน​ปฏิบัติการ) การ​เลือก​ที่​ดี​คือ​การ​รู้​ว่า​ใน​ระบบ​ของ​เรา ด้าน​ไหน​หนัก​กว่า​กัน

ข้อดี​ข้อ​แรก​และ​สำคัญ​ที่สุด​ของ microservices คือ module boundaries (ขอบเขต​ของ module) ที่​แข็งแรง ฟัง​ดู​ย้อน​แย้ง​อยู่​บ้าง เพราะ​ใน​ทาง​ทฤษฎี​แล้ว microservices ไม่​ควร​มี boundary ที่​แข็งแรง​ไป​กว่า module ใน​โปรแกรม monolith ที่​ออกแบบ​มา​ดี ๆ เลย

เมื่อ​พูด​ถึง “boundary ที่​แข็งแรง” ผม​หมาย​ถึง​การ​ที่​ซอฟต์แวร์​ถูก​แบ่ง​เป็น​ก้อน ๆ ที่ decouple (ตัด​การ​พึ่งพา) กัน​ได้​ดี จน​เวลา​จะ​แก้​อะไร​สัก​อย่าง เรา​ต้อง​เข้าใจ​เพียง​ส่วน​เล็ก ๆ ที่​หา​เจอ​ได้​ง่าย​เท่านั้น modularity (ความ​เป็น module) นี้​ยิ่ง​สำคัญ​แบบ​ทวีคูณ​เมื่อ​ระบบ​และ​ทีม​งาน​เติบโต​ขึ้น

เหตุผล​หนึ่ง​ที่ modularity มี​น้ำหนัก​มาก​คือ Conway’s Law ที่​บอกว่า​โครงสร้าง​ของ​ซอฟต์แวร์​มัก​สะท้อน​โครงสร้าง​การ​สื่อสาร​ของ​องค์กร​ที่​สร้าง​มัน​ขึ้น​มา

เมื่อ​ทีม​กระจาย​ตัว​กัน​ใน​เชิง​ภูมิศาสตร์ การ​สื่อสาร​ข้าม​ทีม​ย่อม​กลาย​เป็น​แบบ​ทางการ​มาก​ขึ้น microservices จึง​มี​คุณค่า​ตรง​ที่​มัน​ช่วย​ให้​แต่ละ​ทีม​ดูแล​หน่วย​งาน​ที่​ค่อน​ข้าง​เป็น​อิสระ​ได้ สอดคล้อง​กับ​สาย​การ​สื่อสาร​ของ​ทีม​นั้น ๆ

ถึง monolith จะ​สามารถ​รักษา modularity เอา​ไว้​ได้​ใน​ทาง​ทฤษฎี แต่​ใน​ทาง​ปฏิบัติ​มัน​แทบ​ไม่​เคย​เป็น​เช่น​นั้น รูปแบบ​ที่​พบ​บ่อย​ที่สุด​กลับ​กลาย​เป็น Big Ball of Mud (ก้อน​โคลน​ขนาด​ใหญ่​ที่​ทุก​อย่าง​พัน​กัน​ยุ่งเหยิง)

ต้นตอ​ของ​ปัญหา​คือ monolith ทำให้การ​ลัด​ข้าม boundary ของ module เป็น​เรื่อง​ง่าย​เกิน​ไป

การ​ลัด​แบบ​นี้​ดูเหมือน​จะ​มี​ประโยชน์​เชิงกลยุทธ์​ใน​ระยะ​สั้น​เมื่อ​มอง​ที​ละ​ครั้ง แต่​เมื่อ​รวม​กัน​มาก ๆ เข้า​กลับ​บ่อนทำลาย​โครงสร้าง​และ productivity การ​แยก module ออก​ไป​เป็น service ต่างหาก​จึง​สร้าง​กำแพง​ที่​แข็งแรง​กว่า​มาก​ใน​การ​กัน​ไม่​ให้​เกิด​การ​ลัด​เช่น​นั้น

📝 หมายเหตุ: คำ​ว่า “monolith” ใน​ที่​นี้​ไม่​ได้​เป็น​คำ​ดูแคลน​แต่​อย่าง​ใด ผม​นิยาม​มัน​ตรง ๆ ว่า​คือ “application ที่​ถูก​สร้าง​ขึ้น​เป็น​หน่วย​เดียว” เท่านั้น

อีก​จุด​หนึ่ง​ที่​สำคัญ​ต่อ boundary ของ microservices คือ decentralized data management (การ​จัดการ​ข้อมูล​แบบ​กระจาย​ศูนย์) แต่ละ service ดูแล​ฐาน​ข้อมูล​ของ​ตัวเอง และ service อื่น​ต้อง​เข้าถึง​ข้อมูล​ผ่าน API เท่านั้น ไม่ใช่​ไป​อ่าน​เขียน​ฐาน​ข้อมูล​ร่วม​กัน​โดยตรง

วิธี​นี้​ช่วย​กำจัด integration database (ฐาน​ข้อมูล​กลาง​ที่​หลาย​ระบบ​ใช้​ร่วม​กัน) ซึ่ง​เป็น​ตัวการ​ก่อ coupling ที่​ร้ายแรง​ใน​ระบบ​ขนาด​ใหญ่

flowchart TB
  UI[Web UI] --> APP
  subgraph APP[Monolith Process]
    direction TB
    M1[Orders Module]
    M2[Billing Module]
    M3[Shipping Module]
  end
  M1 --> DB[(Shared Database)]
  M2 --> DB
  M3 --> DB

ภาพ 2 — โครงสร้าง​แบบ monolith: ทุก module อยู่​ใน process เดียว และ​มัก share integration database ร่วม​กัน จุด​นี้​เอง​ที่​ทำให้ boundary ระหว่าง module ถูกลัด​ข้าม​ได้​ง่าย เพราะ​ไม่มี​อะไร​มา​บังคับ​ให้​เคารพ​ขอบเขต

flowchart TB
  UI[Web UI] --> G[API Gateway]
  G --> S1[Orders Service]
  G --> S2[Billing Service]
  G --> S3[Shipping Service]
  S1 --> D1[(Orders DB)]
  S2 --> D2[(Billing DB)]
  S3 --> D3[(Shipping DB)]
  S1 -- API --> S2
  S2 -- API --> S3

ภาพ 3 — โครงสร้าง​แบบ microservices: แต่ละ service เป็น​เจ้าของ​ฐาน​ข้อมูล​ของ​ตัวเอง service อื่น​เข้าถึง​ได้​ผ่าน API เท่านั้น การ​แบ่ง service ที่​ดี​มัก​อิง​กับ bounded context — ขอบเขต​เชิง​ความหมาย​ใน domain ที่​ใช้​กำหนด​ว่า module หนึ่ง ๆ ครอบคลุม​อะไร​บ้าง

อย่างไร​ก็ตาม boundary ที่​แข็งแรง​ต้อง​อาศัย discipline (วินัย) ไม่​ว่า​จะ​ใน​สถาปัตยกรรม​แบบ​ไหน เพียง​แต่ microservices ทำให้การ​ทำ​ผิด​กฎ​ยาก​ขึ้น​เท่านั้น​เอง

ข้อ​จำกัด​สำคัญ​คือ​เรา​จะ​ประเมิน modularity ได้​จริง​ก็​ต่อ​เมื่อ​ระบบ​ทำงาน​มา​แล้ว​หลาย​ปี และ​คน​ที่​ลอง microservices เป็นกลุ่ม​แรก ๆ มัก​เป็น​ทีม​ที่​เก่ง​กว่า​ค่า​เฉลี่ย เรา​จึง​ยัง​สรุป​แบบ​ฟัน​ธง​ไม่​ได้​จนกว่า​จะ​เห็น​ทีม​ระดับ​กลาง ๆ สร้าง​ระบบ​ที่​อยู่​ยาว

หลักฐาน​เชิง​เล่า​ขาน​จาก​ผู้​ที่​มี​ประสบการณ์​ตอน​นี้​ชี้​ไป​ใน​ทาง​ที่​ว่า microservices ช่วย​รักษา modularity ได้​ดี​กว่า​จริง แต่​ก็​ยัง​เป็น​เพียง​หลักฐาน​เบื้องต้น

ข้อดี​นี้​จะ​กลาย​เป็น​ข้อ​เสีย​ทันที​ถ้า​ตีเส้น boundary ผิด​ที่ นี่​คือ​หนึ่ง​ใน​สอง​เหตุผล​หลัก​ของ​กลยุทธ์ Monolith First และ​เป็น​เหตุผล​ที่​แม้แต่​คน​ที่​โน้ม​เอียง​จะเริ่ม​ด้วย microservices ตั้งแต่​แรก ก็​ยัง​ย้ำ​ว่า​ทำได้​ก็​ต่อ​เมื่อ​เข้าใจ domain เป็น​อย่าง​ดี​เท่านั้น

📷 ภาพ 4 — ปก​หนังสือ Building Microservices ของ Sam Newman — แหล่ง​อ้างอิง​สำคัญ​หาก​ต้องการ​ศึกษา​วิธี​สร้าง​ระบบ microservices · ดู​ภาพ​ต้นฉบับ ↗

มี​กรณี​ศึกษา​หนึ่ง​ที่​น่า​สนใจ​ใน​เชิง​ตรง​ข้าม ทีม​หนึ่ง​ใช้ microservices ทั้ง​ที่​ระบบ​ยัง​ไม่​ซับซ้อน​พอ จน​สุดท้าย​ต้อง​เรียก​ทีม​กู้ภัย​เข้า​มา​ช่วย

ตอน​ที่​ต้อง​รีบ​เสริม​นัก​พัฒนา​จำนวน​มาก​เพื่อ​กู้ project กลับ​พบ​ว่า​สถาปัตยกรรม microservices ช่วย​ได้​มาก เพราะ​ทำให้​รับ​คน​ใหม่​เข้า​ทำงาน​ได้​ง่าย​กว่า monolith ถึง​สุดท้าย project นี้​จะ​ใช้​ชั่วโมง​คน​รวม​มากกว่า​ถ้า​ทำ​แบบ monolith แต่ microservices ก็​ช่วย​ให้​ขยาย​กำลัง​คน​ได้​ดี​กว่า

(หมายเหตุ​ผู้​แปล: จุด​นี้​น่า​สนใจ​เพราะ​ขัด​กับ​สัญชาตญาณ​ของ Brooks’s Law ที่ว่าการ​เพิ่ม​คน​เข้า project ที่​ล่าช้า​ยิ่ง​ทำให้​ล่าช้า​ลง​ไป​อีก — แต่ Fowler เอง​ไม่​ได้​อ้าง​ถึง​กฎ​นี้​ใน​บทความ​ต้นฉบับ)

microservices ใช้​ระบบ​แบบ​กระจาย (distributed systems) เพื่อ​ยก​ระดับ modularity แต่​การกระจาย​ตัวเอง​ก็​ลาก​ความ​ซับซ้อน​ก้อน​ใหญ่​เข้า​มา​ด้วย

ผม​ยอมรับ​ว่า​ชุมชน microservices มี​ความ​เข้าใจ​ใน​เรื่อง​นี้​ลึกซึ้ง​กว่า​ยุค​ของ​ขบวนการ distributed objects เมื่อ​ก่อน​อยู่​มาก แต่​ต่อ​ให้​เป็น​เช่น​นั้น ความ​ซับซ้อน​ของ​การกระจายก็​ยัง​ไม่​หาย​ไป​ไหน

อย่าง​แรก​คือ performance (สมรรถนะ/ประสิทธิภาพ) การ​เรียก​ข้าม​เครื่อง (remote call) นั้น​ช้า​กว่า​การ​เรียก function ภายใน process เดียวกัน​มาก และ​เมื่อ service หนึ่ง​เรียก service อื่น​ต่อ ๆ กัน​เป็น​ทอด ๆ latency (เวลา​หน่วง) ก็​ยิ่ง​สะสม​พอกพูน​ตลอด​สาย​การ​เรียก จน​กลาย​เป็น​ปัญหา performance ที่​ร้ายแรง​ได้

เรา​สามารถ​บรรเทา​ได้​ด้วย​การ​ปรับ granularity (ความ​หยาบ-ละเอียด​ของ​การ​เรียก) เช่น​รวบ​การ​สื่อสาร​ข้าม service ให้​เป็น​ก้อน​ใหญ่​ขึ้น​เพื่อ​ลด​จำนวน​ครั้ง​ที่​เรียก แต่​วิธี​นี้​ก็​ทำให้​ตรรกะ​ของ​โปรแกรม​ซับซ้อน​ขึ้น และ​ช่วย​ได้​จำกัด เพราะ​อย่างไร​เสีย service ก็​ต้อง​เรียก​ใช้ service อื่น​ที่​มัน​พึ่งพา​อย่าง​น้อยหนึ่ง​ครั้ง​อยู่ดี

อีก​วิธี​คือ​การ​ทำงาน​แบบ asynchronous (ไม่​ประสาน​เวลา) การ​ยิง​หลาย ๆ การ​เรียก​แบบ​ขนาน​พร้อม​กัน​จะ​ทำให้ latency รวม​เท่ากับ​การ​เรียก​ที่​ช้า​ที่สุด​เพียง​ตัว​เดียว แทนที่​จะ​เป็น​ผล​บวก​ของ​ทุก​ตัว

วิธี​นี้​ให้​ผล​ด้าน performance ที่​ดี​ขึ้น​อย่าง​มาก แต่​ก็​แลก​มา​ด้วย​ภาระ​ทาง​ความคิด​ที่​หนัก​ขึ้น การ​เขียน​โปรแกรม​แบบ asynchronous ให้​ถูกต้อง​นั้น​ทำได้​ยาก และ​ดี bug ยาก​เป็น​พิเศษ

ระบบ microservices ที่​จริงจัง​ส่วน​ใหญ่​ที่​ผม​เจอ​ล้วน​ต้อง​พึ่ง asynchrony เพื่อ​ให้​ได้ performance ที่​ยอมรับ​ได้

นอกจาก​เรื่อง​ความเร็ว ระบบ​กระจาย​ยัง​นำ​ปัญหา​ด้าน reliability (ความ​น่า​เชื่อถือ) เข้า​มา​ด้วย remote call ล้มเหลว​ได้​แบบ​คาด​เดา​ไม่​ได้ และ microservices ยิ่ง​เพิ่ม​จุด​ที่​อาจ​ล้มเหลว นัก​พัฒนา​ที่​ฉลาด​จึง​ต้อง​ออกแบบ​เผื่อ​ความ​ล้มเหลว (design for failure)

โชค​ดี​ที่​รูปแบบ​การ​ทำงาน​แบบ asynchronous นั้น​ช่วย​รองรับ​การ​จัดการ​กับ​ความ​ล้มเหลว​ได้​ดี จึง​ช่วย​เพิ่ม resilience (ความ​ทนทาน​ต่อ​ความ​ผิดพลาด) ให้​ระบบ แต่​ก็​หมายความ​ว่า​นัก​พัฒนา​ต้อง​คิด​ให้​ตกใน​ทุก ๆ การ​เรียก​ข้าม​เครื่อง​ว่า​ถ้า​มัน​ล้มเหลว​แล้ว​จะ​เกิด​ผลอย่างไร

และ​ทั้งหมด​นี้​ก็​เป็น​เพียง​สอง​ข้อ​แรก​จาก Fallacies of Distributed Computing (มายาคติ​แปด​ข้อ​เรื่อง​ระบบ​กระจาย​ที่​มีชื่อเสียง) เท่านั้น

ควร​กล่าว​ด้วย​ว่า​ปัญหา​เหล่า​นี้​จำนวน​มาก​ก็​โผล่​ขึ้น​มา​กับ monolith เมื่อ​มัน​เติบโต เพราะ​มี monolith น้อย​ระบบ​มาก​ที่​อยู่​ตัว​เดียว​ได้​จริง ๆ ส่วน​ใหญ่​ต้อง​คุย​กับ​ระบบ legacy หรือ​ระบบ​ภายนอก​ผ่าน​เครือข่าย​อยู่​แล้ว ซึ่ง​ก็​ก่อ​ปัญหา​แบบ​เดียวกัน

ทีม​ที่​มี​ประสบการณ์​มากกว่า​จะ​รับมือ​กับ​ความ​ซับซ้อน​ของ​การกระจาย​ได้​ดี​กว่า แต่​ไม่​ว่า​อย่างไร การกระจายก็​มี​ต้นทุน​เสมอ ผม​จึง​เตือน​ว่า​อย่า​รีบ​กระโดด​เข้า​สู่​สถาปัตยกรรม​แบบ​กระจาย​ก่อน​เวลา​อัน​ควร ซึ่ง​เป็น​สิ่ง​ที่​ผม​เห็น​เกิด​ขึ้น​บ่อย​เกิน​ไป

Eventual Consistency — ความ​สอดคล้อง​ที่​ตาม​มา​ทีหลัง (ข้อ​เสีย)

หัวข้อ​ที่​มีชื่อ​ว่า “Eventual Consistency — ความ​สอดคล้อง​ที่​ตาม​มา​ทีหลัง (ข้อ​เสีย)”

ลอง​นึก​ภาพ​ประสบการณ์​ที่​หลาย​คน​คง​เคย​เจอ เรา​กด​อัปเดต​ข้อมูล​บาง​อย่าง กด refresh แล้ว​การ​อัปเดต​นั้น​หาย​ไป รอ​สัก​ครู่​แล้ว refresh อีก​ที คราว​นี้​มัน​โผล่​กลับ​มา

นี่​คือ​อาการ​ของ eventual consistency (ความ​สอดคล้อง​ที่​ใน​ที่สุด​จะ​มา​ถึง) ข้อมูล​ถูก​จัดการ​โดย​หลาย node และ​มี​ช่วง​เวลา​หนึ่ง​ก่อน​ที่​ทุก node จะ sync ตรง​กัน หาก​เรา​ไป​โดน node ที่​ยัง​ไม่​อัปเดต​ก็​จะ​เห็น​ข้อมูล​เก่า

ความ​น่า​รำคาญ​ของ​ผู้​ใช้​ยัง​เป็น​เรื่อง​เล็ก ที่​หนัก​กว่า​คือ​ช่วง​เวลา​ที่​ข้อมูล​ยัง​ไม่​สอดคล้อง​กัน​นี้​อาจ​ทำให้​ตรรกะ​ทาง​ธุรกิจ (business logic) ตัดสิน​ใจ​บน​ข้อมูล​ที่​ล้าสมัย จน​ก่อ​ความ​ผิดพลาด​ร้ายแรง และ​ปัญหา​นี้ diagnose ยาก​มาก เพราะ​กว่า​จะ​มา​สืบสวน​ก็​มัก​เป็น​เวลา​นาน​หลัง​จาก​ช่วง inconsistency ปิด​ตัว​ลง​ไป​แล้ว

microservices เปิด​ประตู​ให้ eventual consistency เข้า​มา​ก็​เพราะ​การ​ยืนกราน​เรื่อง decentralized data management นั่นเอง

กับ monolith เรา​มัด​หลาย​การ​อัปเดต​ไว้​ใน transaction (การ​ทำ​ธุรกรรม​เป็น​ชุด​เดียว) เดียว​ได้ ซึ่ง transaction ให้การ​รับประกัน​เรื่อง consistency แต่​ต้อง​แลก​ด้วย​การ​ผูก resource หลาย​ตัว​ไว้​ด้วย​กัน

แต่ microservices ที่​ต้อง​อัปเดต resource หลาย​ตัว​โดย​ไม่​ใช้ distributed transaction (ซึ่ง​ชุมชน​ปฏิเสธ​ด้วย​เหตุ​อัน​สมควร) ทำให้​นัก​พัฒนา​ต้อง​ตระหนัก​ถึง​ประเด็น consistency เอง และ​ต้องหา​ทาง​ตรวจ​จับ​ว่า​ข้อมูล​ยัง​ไม่​ตรง​กัน​ก่อน​ที่ code จะ​ไป​ตัดสิน​ใจ​อะไร​ที่​ก่อ​ปัญหา

แน่นอน​ว่า monolith ก็​ไม่​ได้​รอดพ้น​เรื่อง​นี้​ไป​ทั้งหมด เมื่อ​ระบบ​โต​ขึ้น เรา​ก็​มัก​เจอ inconsistency เข้า​จน​ได้ เช่น

  • เรา​ใส่ cache เพื่อ​เพิ่ม performance แต่ cache invalidation (การ​ทำให้ cache หมด​อายุ​อย่าง​ถูก​จังหวะ) ก็​เป็น​หนึ่ง​ใน​ปัญหา​ที่​ขึ้นชื่อ​ว่ายาก​ที่สุด
  • เรา​ใช้ offline lock เพื่อ​เลี่ยง transaction ฐาน​ข้อมูล​ที่​ค้าง​ยาว
  • เรา​อัปเดต​ระบบ​ภายนอก​ที่​อยู่​นอก​การ​ประสาน transaction

นอกจาก​นี้ กระบวนการ​ทาง​ธุรกิจ​หลาย​อย่าง​ก็​ทน inconsistency ได้​มากกว่าที่​เรา​คิด เพราะ​ธุรกิจ​มัก​ให้​ความ​สำคัญ​กับ availability (ความ​พร้อม​ใช้งาน) มากกว่า consistency ซึ่ง​สะท้อน​สัญชาตญาณ​เชิง​ธุรกิจ​ที่​มี​ต่อ CAP theorem มา​นาน​แล้ว

เช่น​เดียว​กับ​ปัญหา​อื่น ๆ ของ​การกระจาย monolith ไม่​ได้​หนี​พ้น inconsistency ไป​ได้​หมด แต่​ก็​เจอ​น้อย​กว่า​มาก โดย​เฉพาะ​ใน​ระบบ​ขนาด​เล็ก

ใน​ศตวรรษ​ที่​ยี่สิบ การ​ปล่อย​ซอฟต์แวร์​ขึ้น production เป็น​เหตุการณ์​ที่​นาน ๆ เกิด​ที และ​เจ็บ​ปวด ต้อง​อาศัย​กะ​พิเศษ​ของ​ทีม​งาน​คอย​เฝ้า ตัด​กับ​วิธี​ปฏิบัติ​สมัย​ใหม่​ที่​ทีม​เก่ง ๆ deploy ขึ้น production ได้​หลาย​ครั้ง​ต่อ​วัน การ​เปลี่ยนแปลง​นี้​เป็น​ความ​เปลี่ยนแปลง​ครั้ง​ใหญ่​ของ​วงการ และ​มัน​เชื่อม​โยง​ลึกซึ้ง​กับ microservices

Microservices เป็น​สถาปัตยกรรม​ยุค​หลัง-DevOps-revolution ตัว​แรก — Neal Ford

หลาย​กรณี​ที่​หัน​มา​ใช้ microservices ก็​เพราะ​ความ​ยาก​ลำบาก​ใน​การ deploy monolith ก้อน​ใหญ่ ที่​การ​แก้​เพียง​เล็กน้อย​ก็​อาจ​ลุกลาม​จน​ล้ม​ทั้ง​ระบบ​ได้

microservices ซึ่ง​นิยาม​ว่า​เป็น​ชิ้น​ส่วน​ที่ deploy ได้​อย่าง​เป็น​อิสระ (independently deployable) ช่วย​ให้​นัก​พัฒนา​ทดสอบ​และ deploy การ​เปลี่ยนแปลง​เฉพาะ service เดียว​ได้ และ​แม้ service หนึ่ง​จะ​ล้ม​ไป​ทั้ง​ตัว ก็​ไม่​ควร​ทำให้​ทั้ง​ระบบ​ล่ม เพราะ​เรา​ออกแบบ​เผื่อ​ความ​ล้มเหลว​ไว้​แล้ว

ความ​สัมพันธ์​นี้​เป็น​แบบ​สอง​ทาง microservices เอื้อ​ให้ deploy ได้​บ่อย แต่​ความถี่​ใน​การ deploy ก็​เรียกร้อง​วุฒิ​ภาวะ​ด้าน​การ deploy กลับ​คืน​ด้วย

ความ​สามารถ​ใน​การ deploy application และ provision (จัด​เตรียม) โครงสร้าง​พื้นฐาน​ได้​อย่าง​รวดเร็ว​กลาย​เป็น​สิ่ง​จำเป็น ไม่ใช่​ของ​ฟุ่มเฟือย และ​เหนือ​กว่า​พื้นฐาน​นั้น continuous delivery (การ​ส่ง​มอบ​ต่อ​เนื่อง) ก็​กลาย​เป็น​สิ่ง​ที่​ขาด​ไม่​ได้

ประโยชน์​คือ cycle-time (เวลา​ตั้งแต่​คิด​จน​ได้​ซอฟต์แวร์​ขึ้น production) ที่​สั้น​ลง มอบ​ความ​ได้​เปรียบ​ใน​การ​แข่งขัน​ให้​องค์กร เพราะ​ตอบ​สนอง​ตลาด​ได้​เร็ว​ขึ้น​และ​ออก feature ได้​ถี่​ขึ้น

มี​ข้อ​ควร​ระวัง​สำคัญ หลาย​คน​อ้าง continuous delivery เป็น​เหตุผล​ใน​การ​ใช้ microservices แต่​ผม​ต้อง​ย้ำ​ว่า​แม้แต่ monolith ก้อน​ใหญ่​ก็​ทำ continuous delivery ได้ (ดู​ตัวอย่าง​อย่าง Facebook และ Etsy)

และ​ใน​ทาง​กลับ​กัน มี​ความ​พยายาม​ทำ microservices จำนวน​มาก​ที่​ล้มเหลว​เรื่อง​การ deploy อย่าง​เป็น​อิสระ เพราะ service ต้อง​ปล่อย​พร้อม ๆ กัน​แบบ​ประสาน​ล็อก​กัน

โดย​รวม​ผม​พบ​ว่า continuous delivery ทำได้​ง่ายกว่าด้วย microservices เมื่อ​เทียบ​กับ monolith แต่​ไม่​ได้​ต่าง​กัน​ดรามาติก​เท่ากับ​ข้อ​ได้​เปรียบ​ด้าน modularity

📝 หมายเหตุ: ความ​สามารถ​ใน​การ deploy อย่าง​เป็น​อิสระ​เป็น​ส่วน​หนึ่ง​ของ​นิยาม microservices เอง ดังนั้น service ชุด​หนึ่ง​ที่​ต้อง deploy พร้อม​กัน​แบ block กัน​จึง​มี​เหตุผล​ที่​จะ​บอกว่า​ไม่ใช่​สถาปัตยกรรม microservices จริง ๆ

การ deploy หน่วย​เล็ก ๆ ได้​อย่าง​รวดเร็ว​และ​เป็น​อิสระ​เป็น​พร​สำหรับ​ฝ่าย​พัฒนา แต่​กลับ​เพิ่ม​แรง​กดดัน​มหาศาล​ให้​ฝ่าย​ปฏิบัติการ (operations) ที่​ต้อง​ดูแล microservices หลาย​สิบ​หรือ​หลาย​ร้อย​ตัว​ที่​เปลี่ยนแปลง​ตลอด​เวลา หลาย​องค์กร​พบ​ว่าการ​จัดการ​ฝูง service ที่​วิวัฒน์​เร็ว​ขนาด​นี้​เป็น​ภาระ​ที่​แบก​ไม่​ไหว

จุด​นี้​เอง​ที่ continuous delivery เปลี่ยน​สถานะ​จาก “มี​ก็​ดี” สำหรับ monolith ไป​เป็น “จำเป็น​อย่าง​ยิ่ง” สำหรับ microservices

เรา​ไม่มี​ทาง​จัดการ service หลาย​สิบ​ตัว​ได้​เลย​หาก​ปราศจาก​ระบบ​อัตโนมัติ​และ​การ​ทำงาน​ร่วม​กัน​ที่ continuous delivery มอบ​ให้ ทั้ง​การ​จัดการ​และ monitoring เหล่า​นี้​เรียกร้อง​วุฒิ​ภาวะ​ระดับ​ที่ “มี​ประโยชน์” กับ monolith แต่ “จำเป็น” กับ microservices

ผู้​สนับสนุน microservices ชอบ​ชี้​ว่า service ที่​เล็ก​กว่า​ย่อม​เข้าใจ​ง่าย​กว่า​ที​ละ​ตัว ซึ่ง​จริง แต่​ความ​ซับซ้อน​ไม่​ได้​ถูก​กำจัด​ทิ้ง มัน​เพียง​ย้าย​ที่ ไป​โผล่​ตรง​จุด​เชื่อม​ต่อ​ระหว่าง service ต่าง ๆ และ​ปรากฏ​ออก​มา​ใน​รูป​ของ​ความ​ยาก​ด้าน​ปฏิบัติการ เช่น​การ debug ข้าม service

การ​เลือก service boundary (ขอบเขต​ของ service) ที่​ดี​จะ​ช่วย​ลด​ปัญหา​เหล่า​นี้ ใน​ทาง​กลับ​กัน​การ​เลือก​ที่​แย่​จะ​ทำให้​มัน​เลว​ร้าย​ลง​อย่าง​รุนแรง

การ​รับมือ​กับ​ความ​ซับซ้อน​ด้าน​ปฏิบัติการ​ต้อง​อาศัย​ทักษะ​และ​เครื่อง​มือใหม่ ๆ โดย​น้ำหนัก​ตก​อยู่​ที่ “ทักษะ” เป็น​พิเศษ เพราะ​เครื่องมือ​ยัง​ไม่​สุกงอม​นัก และ​แม้​เครื่องมือ​จะ​ดี​ขึ้น สภาพ​แวดล้อม​แบบ microservices ก็​ยัง​ต้องการ​ระดับ​ทักษะ​พื้นฐาน​ที่​สูง​กว่า​อยู่ดี

เหนือ​ไป​กว่า​ทักษะ​และ​เครื่องมือ ยัง​ต้องการ DevOps culture (วัฒนธรรม DevOps) นั่น​คือ​ความ​ร่วมมือ​อย่าง​ลึกซึ้ง​ระหว่าง​นัก​พัฒนา ฝ่าย​ปฏิบัติการ และ​ทุก​ฝ่าย​ที่​เกี่ยวข้อง​กับ​การ​ส่ง​มอบ

การ​เปลี่ยน​วัฒนธรรม​เป็น​เรื่อง​ยาก โดย​เฉพาะ​ใน​องค์กร​ใหญ่​ที่​มี​มา​นาน หาก​ขาด​การ​เปลี่ยน​วัฒนธรรม​นี้ application แบบ monolith จะ​แค่​ทำงาน​ได้​ไม่​เต็ม​ที่ แต่ application แบบ microservices จะ​เจอ​ความ​เจ็บ​ปวด​ขั้น​รุนแรง

เพราะ​แต่ละ microservice deploy ได้​อย่าง​เป็น​อิสระ แต่ละ​ตัว​จึง​ใช้​ภาษา library และ data store ที่​ต่าง​กัน​ได้ ทีม​สามารถ​เลือก​เครื่องมือ​ที่​เหมาะ​กับ​ปัญหา​เฉพาะ​หน้าที่สุด แทนที่​จะ​บังคับ​ใช้​มาตรฐาน​เดียวกัน​ทั้ง​องค์กร

บทสนทนา​มัก​โฟกัส​ที่ “เครื่องมือ​ที่​เหมาะ​กับ​งาน” (best tool for the job) แต่​ผม​กลับ​คิด​ว่า​ประโยชน์​เชิง​ปฏิบัติ​ที่​ใหญ่​ที่สุด​อาจ​เป็น​เรื่อง library versioning (การ​จัดการ version ของ library) มากกว่า

ใน monolith เรา​ใช้ library ได้​เพียง version เดียว จึง​เกิด​สถานการณ์​ปวด​หัว​ที่​ส่วน​หนึ่ง​ของ​ระบบ​ต้องการ​อัปเกรด library แต่​การ​อัปเกรด​นั้น​ดัน​ทำ​อีก​ส่วน​พัง ปัญหา​นี้​ยิ่ง​เลว​ร้าย​แบบ​ทวีคูณ​เมื่อ codebase โต​ขึ้น

แต่​ก็​ต้อง​ระวัง​ไม่​ให้​ความ​หลากหลาย​ทาง​เทคโนโลยี​มาก​เกิน​ไป​จน​องค์กร​รับ​ไม่​ไหว บริษัท​ส่วน​ใหญ่​ที่​ผม​รู้จัก​จึง​สนับสนุน​ให้​ใช้​ชุด​เทคโนโลยี​ที่​จำกัด​ไว้ เครื่องมือ monitoring ที่​ใช้​ร่วม​กัน​และ platform กลาง​ช่วย​ให้ service ต่าง ๆ ยัง​อยู่​ใน​กรอบ​เทคโนโลยี​ที่​บริหาร​จัดการ​ได้

microservices ยัง​เอื้อ​ต่อ​การ ทดลอง ซึ่ง​ทำได้​ยาก​ใน monolith ที่​การ​ตัดสิน​ใจ​เลือก​ภาษา​และ framework ตั้งแต่​แรก​กลาย​เป็น​สิ่ง​ที่​แก้​กลับ​ได้​ยาก​และ​แพง​เมื่อ​เวลา​ผ่าน​ไป​เป็น​สิบ​ปี เรา​สามารถ​ทดลอง​เทคโนโลยี​ใหม่​ใน service เดียว และ​ค่อย ๆ ย้าย​ทั้ง service ไป​สู่​เทคโนโลยี​ที่​ดี​กว่า​ได้ที​ละ​ตัว​แบบ​ค่อย​เป็น​ค่อย​ไป

นอกจาก​ปัจจัย​หลัก​ข้าง​ต้น ยัง​มี​ปัจจัย​รอง​ที่​มัก​ถูก​หยิบยก​ขึ้น​มา

Scaling ผู้​สนับสนุน​อ้าง​ว่า microservices ช่วย​ให้ scale แบบ​เลือก​เฉพาะ​จุด​ได้ (ขยาย​เฉพาะ service ที่​รับ​โหลด​หนัก) ซึ่ง​น่า​จะ​ประหยัด​กว่า​การ scale แบบ cookie-cutter (คือ duplicate ทั้ง application ขึ้น​มา​ทั้ง​ก้อน​เมื่อ​ต้องการ​กำลัง​เพิ่ม)

แต่​ผม​สารภาพ​ว่า​ยัง​หา​กรณี​ศึกษา​ที่​น่า​เชื่อ​มา​ยืนยัน​ไม่​ได้​ว่าการ scale แบบ​เลือก​จุด​นั้น​คุ้ม​ค่า​กว่า​การ duplicate ทั้ง​ก้อน​จริง

Security microservices ช่วย​ให้​แยก​ข้อมูล​อ่อนไหว (sensitive data) ออก​มา​ใส่​การ​ป้องกัน​ที่​เข้ม​ขึ้น และ​บังคับ​ให้การ​สื่อสาร​ระหว่าง service ทุก​เส้น​ต้อง​ปลอดภัย ซึ่ง​อาจ​ทำให้การ​เจาะ​ระบบ​เมื่อ​เกิด​ช่อง​โหว่​ทำได้​ยาก​ขึ้น

เมื่อ​ความ​มั่นคง​ปลอดภัย​ทวี​ความ​สำคัญ​มาก​ขึ้น​เรื่อย ๆ ปัจจัย​ข้อ​นี้​อาจ​กลาย​เป็น​เหตุผล​หลัก​ใน​การ​เลือก microservices ได้ ถึง​แม้​ระบบ​ที่​เป็น monolith เป็น​หลัก​ก็​มัก​แยก service สำหรับ​ข้อมูล​อ่อนไหว​ออก​มา​ต่างหาก​อยู่​แล้ว​เช่น​กัน

Testing ฝ่าย​วิจารณ์​ชี้​ว่า microservices ทดสอบ​ยาก​กว่า monolith ซึ่ง​ก็​จริง เพราะ​เป็น​ธรรมชาติ​ของ​ความ​ซับซ้อน​ใน​ระบบ​กระจาย แต่​ก็​มี​แนวทางการ​ทดสอบ​ที่​ดี​สำหรับ microservices อยู่​แล้ว ประเด็น​สำคัญ​คือ วินัย​ใน​การ​ทดสอบ ต่างหาก​ที่​ชี้ขาด​ความ​สำเร็จ ยิ่ง​กว่า​ตัว​เลือก​เชิง​สถาปัตยกรรม

คำ​แนะนำ​เชิง​สถาปัตยกรรม​แบบ​ทั่วไป​มี​ข้อ​จำกัด​ใน​ตัวเอง​เสมอ บทความ​อย่าง​นี้​จึง​ไม่​อาจ​สั่ง​การ​แทน​เรา​ได้​ว่า​ต้อง​เลือก​ทาง​ไหน ทำได้​เพียง​ช่วย​ชี้​ปัจจัย​ที่​ควร​พิจารณา​เท่านั้น

ข้อดี​และ​ต้นทุน​แต่ละ​ข้อ​มี​น้ำหนัก​ต่าง​กัน​ไป​ใน​แต่ละ​ระบบ บาง​ครั้ง​ถึง​กับ​พลิก​บทบาท​กัน​ได้ (เช่น boundary ที่​แข็งแรง​เป็น​ข้อดี​สำหรับ​ระบบ​ซับซ้อน แต่​กลับ​เป็น​ภาระ​ถ่วง​สำหรับ​ระบบ​ง่าย ๆ) การ​ตัดสิน​ใจ​จึง​ต้อง​นำ​เกณฑ์​เหล่า​นี้​ไป​ประเมิน​กับ context เฉพาะ​ของ​เรา แล้ว​ชั่ง​น้ำหนัก​ว่า​อะไร​สำคัญ​กว่า​อะไร

ประสบการณ์​กับ microservices ยัง​ค่อน​ข้าง​จำกัด การ​ตัดสิน​สถาปัตยกรรม​ทำได้​จริง​ก็​ต่อ​เมื่อ​ระบบ​เติบโต​เต็ม​ที่​และ​ผ่าน​การ​ใช้งาน​มา​หลาย​ปี ตอน​นี้​เรา​ยัง​มี​เรื่อง​เล่า​จาก​สถาปัตยกรรม microservices ที่​อยู่​ยาว​ไม่​มาก​พอ

ผม​อยาก​ย้ำ​ว่า monolith และ microservices ไม่ใช่​ทาง​เลือก​แบบ​ขาว-ดำ​สอง​ขั้ว มัน​เป็น​นิยาม​ที่​พร่า​เลือน มี​ขอบเขต​คลุมเครือ ระบบ​จำนวน​มาก​อยู่​ตรง​กลาง​ระหว่าง​สอง​แบบ และ​บาง​ระบบ​ก็​ไม่​เข้า​พวก​ทั้ง​คู่

ผม​มอง​มัน​เป็น “region” (อาณา​บริเวณ) ใน​พื้นที่​ของ​การ​ออกแบบ ที่​ควร​ค่า​แก่​การ​ตั้ง​ชื่อ​ไว้​เพื่อ​พูด​คุย​ถึง​คุณลักษณะ​ที่​น่า​สนใจ แต่​ไม่ใช่​การ​จัด​หมวด​หมู่​สถาปัตยกรรม​ที่​ครอบคลุม​ทุก​อย่าง

ข้อ​สรุป​หนึ่ง​ที่​ค่อน​ข้าง​เป็น​ฉันทา​มติ​คือ microservices มี microservice premium (ต้นทุน​ส่วน​เพิ่ม​ด้าน productivity ที่​ต้อง​จ่าย​เมื่อ​เลือก​ใช้ microservices) ซึ่ง​จะ​คุ้ม​ก็​ต่อ​เมื่อ​ระบบ​ซับซ้อน​มาก​พอ​เท่านั้น ระบบ​ที่​ยัง​บริหาร​ได้​ดี​ด้วย​สถาปัตยกรรม monolith ก็​ไม่​ควร​หัน​ไป​ใช้ microservices

ท้าย​ที่สุด ผม​ขอ​ปิด​ด้วย​ข้อสังเกต​ที่​มอง​ข้าม​ไม่​ได้ นั่น​คือ​ปัจจัย “อ่อน” (soft factors) อย่าง​คุณภาพ​ของ​คนใน​ทีม การ​ทำงาน​ร่วม​กัน​ของ​ทีม และ​ระดับ​การ​สื่อสาร​กับ domain expert (ผู้เชี่ยวชาญ​ใน domain) ล้วน​ส่ง​ผล​ต่อ​ความ​สำเร็จ​ของ project มากกว่า​การ​เลือก​ว่า​จะ​ใช้ microservices หรือ​ไม่

ใน​เชิง​เทคนิค การ​ใส่ใจ​กับ code ที่​สะอาด การ​ทดสอบ​ที่​รัดกุม และ evolutionary architecture (สถาปัตยกรรม​ที่​วิวัฒน์​ได้) ก็​มี​ความหมาย​มากกว่า​การ​เลือก​ใช้ microservices เสีย​อีก

หาก​ต้องการ​ศึกษา​ลึก​ลง​ไป​ว่า​จะ​สร้าง​ระบบ microservices อย่างไร แหล่ง​อ้างอิง​หลัก​คือ​หนังสือ Building Microservices ของ Sam Newman โดย​เฉพาะ​บท​ที่ 1 ซึ่ง​ไล่​เรียง​ข้อดี​ของ microservices ไว้​อย่าง​ครบถ้วน (ดู​ปก​หนังสือ​ได้ที่​ภาพ 4 ใน​หัวข้อ “Strong Module Boundaries” ด้าน​บน)

สำหรับ​มุมมอง​ด้าน​ข้อ​เสีย บทความ “Microservices - Not A Free Lunch!” ของ Benjamin Wootton บน High Scalability เป็น​หนึ่ง​ใน​บท​สรุป​ข้อ​เสีย​ของ microservices ที่​เก่า​แก่​และ​ดี​ที่สุด​ชิ้น​หนึ่ง

นอกจาก​นี้​ยัง​สามารถ​อ่าน Microservice Resource Guide ของ​ผม​เพื่อ​เจาะ​ลึก​คำถาม​ด้าน​การนำ​ไป​ใช้งาน​เพิ่มเติม​ได้

📷 ภาพ 5 — โลโก้ Microservice Resource Guide ของ Martin Fowler · ดู​ภาพ​ต้นฉบับ ↗

ขอบคุณ​สำหรับ​ความคิด​เห็น​และ​การ​พูด​คุย​ที่​ช่วย​ขัดเกลา​บทความ​นี้​จาก Brian Mason, Chris Ford, Rebecca Parsons, Rob Miles, Scott Robinson, Stefan Tilkov, Steven Lowe และ Unmesh Joshi


📄 อ่าน​ต้นฉบับ​ภาษา​อังกฤษ: Microservice Trade-Offs — Martin Fowler, martinfowler.com (1 กรกฎาคม 2015)