Technology

10 ข้อแนะนำ สำหรับการทำ Microservices ที่ดี (และ ทำ API ที่ดี)

By iFew

September 29, 2021

เคยเขียนเรื่อง 6 ข้อที่จะบอกว่าระบบคุณเป็น Microservices หรือไม่ และมีประโยชน์อย่างไร นานแล้ว เพื่อบอกว่า Microservices ควรมีคุณสมบัติอย่างไรบ้าง แต่ค่อนข้าง Technical และเจาะจงกับ Microservices ไปหน่อย

เพิ่งไปอ่านเจอบทความหนึ่ง เห็นน่าสนใจดี เป็นการอธิบายคุณสมบัติ Microservices คล้ายกับบทความแรกที่ผมเขียน แต่เห็นประโยชน์แต่ละข้อชัดเจนมาก และคิดว่าดัดแปลงไปใช้กับ API Service ทั่วไปได้ด้วย เลยขอบันทึกไว้สักหน่อย

1. ยึดหลักการ ทำหน้าที่เดียวเท่านั้น

แต่ละ Service หรือ API ควรออกแบบขอบเขตการทำงานให้ดี เพื่อไม่ให้เกิดความซับซ้อน และความเกี่ยวข้องกัน เพราะธุรกิจมีความเปลี่ยนแปลงอยู่ตลอดเวลา การไม่ผูกมัดกันไว้ จะง่ายกับการเปลี่ยนแปลงในอนาคต ไม่ว่าจะเปลี่ยนในเชิง Business หรือ Technical ก็ตาม

ตัวอย่าง เช่น Microservices ของแอพสั่งอาหาร ที่สามารถออกแบบตามหน้าที่การทำงานแต่ละส่วนได้ (functionality) เช่น InventoryService, OrderService, PaymentsService, UserProfileService, DeliveryNotificationService

2. แยกการเก็บ Data ออกจากกัน

การใช้ Database ร่วมกัน ก็หมายถึงการพังพร้อมกันด้วยเช่นกัน และการทำงานของแต่ละ Service อาจต้องการความเหมาะสมของ Database ต่างกันด้วย หรือลักษณะ Configuration ที่ต่างกันด้วย

ตัวอย่าง เช่น ProductService จะถูกเก็บลง MongoDB, ส่วน OrderService จะถูกเก็บด้วย PostgreSQL

ปล. ผมเคยมีคำถามในใจว่า ทำไมต้องแบ่งให้ยุ่งยาก และรู้สึกไม่เป็นประโยชน์เท่าไร, คำตอบที่ตอบตัวเองคือ ระบบเราไม่ใหญ่พอ คนใช้ไม่มากพอ ที่ควรจะต้องไปลงทุนแยก Database หรือแม้แต่ทำ Microservices

3. สื่อสารกันด้วย Asynchronous

นอกจากการเขียนโค้ดที่ลดผลกระทบต่อกันแล้ว (Loose Coupling) การสื่อสารระหว่าง Service เป็นอีกจุดที่ต้องทำ เพื่อเป็นการลดความผูกมัดกันของแต่ละ Service ได้

ตัวอย่างเช่น เมื่อบันทึก Order ลงฐานข้อมูลเสร็จแล้ว ระบบไม่จำเป็นต้องรอส่งอีเมลให้เสร็จ ถึงจะ return ว่าการทำงานนี้สำเร็จ

4. ปิดปัญหาให้ไวด้วย Circuit Breaker

ถ้า Microservice ของเราต้องไปเกี่ยวข้องกับ Service ภายนอก และจะทำงานต่อได้ ก็ต่อเมื่อต้องรอ return ผลลัพธ์จากระบบนั้นด้วย ประเด็นคือ ถ้าทำการเชื่อมต่อไม่ได้ จะต้องรอนานแค่ไหน หรือทำอย่างไรต่อ

สิ่งที่ควรทำไว้ คือ กำหนดเวลา Timeout และมีการ return ค่าเริ่มต้น (default) หรือแจ้ง error เพื่อเป็นการตัดจบ และทำ rollback, recovery ต่อไป และ Service ลักษณะแบบนี้ ควรแยกออกไปจาก Service อื่นๆ ให้ได้มากที่สุด เพื่อลดผลกระทบที่จะพังต่อกันเป็นทอดๆ

5. เรียกใช้ Microservice ผ่าน API Gateway

การใช้ API Gateway มาคั่น แทนการเชื่อมต่อโดยตรงไปที่ Microservice มีข้อดีหลายข้อนะ

ปล. มีข้อดีมากก็จริง แต่ข้อเสียก็มี เพราะเป็น Single point of Failure นะ, อาจเกิดคอขวดได้นะ, เป็นระบบที่ใช้ทุกคน ดังนั้น ใครดูแลล่ะ และต้องดูแลให้ดีด้วยนะ

6. ต้องแน่ใจว่า Service สามารถใช้งานกับผู้ใช้เก่าๆ ได้ (Backwards Compatible)

ถึงแม้ว่าจะออกแบบระบบเป็น Microservice และมี CI/CD ที่สามารถ deploy ระบบขึ้นไปเมื่อไรก็ได้ แต่ต้องมั่นใจว่า ผู้ใช้เก่ายังคงต้องทำงานได้อยู่ ซึ่งวิธีแก้ปัญหาที่นิยมทำกันคือ แจ้งการเปลี่ยนแปลงให้แก่ Client ทั้งหมด ว่าจะเกิดขึ้นวันไหน และตัดการรองรับใน Version เก่า เมื่อไรม แต่การแก้ปัญหาแบบนี้ จะใช้เวลาประสานงาน และบาง Client ก็อาจไม่พร้อมเปลี่ยนแปลงในเร็ววัน ทำให้ระบบเราต้องเลื่อนการ deploy ออกไปอีก

สิ่งที่แนะนำให้ทำนอกเหนือจากประสานงานข้างต้น คือการทำ Test ในส่วนของ Service เราเองให้มีส่วนที่ Coverage ทั้งการเรียกใช้แบบเก่าและแบบใหม่ รวมไปถึงการทำ Stub Data และแจ้งให้ฝั่ง Client ทำ Contract Test กับระบบเขาดูว่าสามารถใช้งานได้ปกติหรือไม่

7. ปลดระวาง Version ที่ไม่ได้ใช้ออกไป

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

ปล. โดยปกติ Open API ใหญ่ๆ อย่าง Facebook จะแจ้งล่วงหน้า 1-2 ปี เพื่อให้เวลาในการ Migrate เป็น Version ซึ่งถ้าตรวจพบว่าไม่ทำ จะไม่สามารถเชื่อมต่อได้เลย

8. แยก Infrastructure Hosting ของแต่ละ Microservice ออกจากกัน

นอกเหนือจาก Database ที่ต้องแยกของแต่ละ Service แล้ว เราควรแยกการ Host ด้วย, ด้วยเหตุผลเดียวกันคือ ถ้าพัง ไม่ควรจะต้องพังไปด้วยกันหมด เช่น HDD เต็ม, HDD พัง, Memory หมด, CPU สูง เป็นต้น

และข้อดีอีกประการหนึ่งคือ บาง Service มีการเรียกใช้งานสูง เราจะสามารถ Scale ออกไปหรือเพิ่ม Spec สำหรับให้บริการเฉพาะ Service นั้นได้ เท่าที่จำเป็น

ปล. หลายที่ใช้เป็น Container เพื่อใช้งานในแต่ละ Service แต่อย่าลืมว่า 1Service 1Container ก็อาจจะพังได้ ดังนั้น เพื่อให้เกิด Availability ในระบบเรา Kubernetes อาจจะต้องเข้าแล้วหนึ่ง สภาพพพ!!

9. การ Deploy ต้องไม่เกี่ยวข้องกัน

ใน Service แต่ละตัว เมื่อไม่ผูกมัดกัน ไม่ส่งผลกระทบต่อกันแล้ว ก็จะต้องสามารถ Deploy ได้โดยที่ไม่เกี่ยวข้องกันด้วย กล่าวคือ ไม่จำเป็นว่า ต้อง Deploy A ก่อน แล้วถึงจะ Deploy B ต่อไปได้

10. สร้างประสิทธิภาพการทำงานในองค์กร

เมื่อ Microservices มีอิสระต่อกันทั้งทีมพัฒนา ทั้งการโค้ด ทั้งการออกแบบ แม้กระทั้งการ Deploy แล้ว สิ่งหนึ่งที่จะต้องปรับด้วยคือมาตรฐานในการออกแบบและเขียนโค้ด (Standards) เพื่อไม่ให้แต่ละทีมต้องเสียเวลาในการออกแบบและสร้าง Solution ของตนเองโดยไม่จำเป็น และยากกับการดูแลรักษาในอนาคตด้วย และเสียรูปแบบการทำงานร่วมกันด้วย

เรื่องนี้สำคัญมากๆนะ เพราะ Service แต่ละตัวถูกสร้างจากคนละทีม แต่ต้องทำงานร่วมกัน ดังนั้น ควรคุยกันก่อนเริ่มทำงาน เช่น API security, log aggregation, monitoring, API documentation, secrets management, config management, distributed tracing ฯลฯ

สรุป

หากเราได้ออกแบบและทำ Service ตามคำแนะนำทั้ง 10 ข้อ แล้วหละก็ เราจะได้ใช้คุณประโยชน์ของ Microservices ตามที่มันถูกคิดขึ้นมา นั่นคือ ไม่ส่งผลกระทบกัน (loosely coupled), กระจายการทำงาน (distributed) และ อิสระจากกัน (independent)

Reference