Service Discovery in a Microservices Architecture
สวัสดีผู้อ่านทุกท่านครับ นี่เป็นบทความแรกในปี 2020 ของผม ไม่ได้เขียนมานานเลย พอดีหลายวันก่อน มีพี่ DevOps ที่รู้จักมาขอให้ช่วย POC เกี่ยวกับการทำ Service Discovery โดยใช้ Spring Cloud Netflix Eureka ให้ No loss transaction ไหน ๆ ก็ทำเสร็จไปแล้ว ก็เลยจะขอถือโอกาสมาพูดถึง Service Discovery กันหน่อย (ที่เค้าให้ทำมันไม่ได้อยู่ในบทความนี้นะ อันนั้นมัน Advance กว่านี้)ในบทความนี้ผมจะมาอธิบายเกี่ยวกับเรื่องของ Service Discovery ว่ามันคืออะไร ทำไมเราถึงจะต้องใช้มัน (อันที่จริง Service Discovery ไม่ใช่ของใหม่อะไร มันมีมานานแล้วนะ) โดยที่จะไม่ไปยึดติดกับ Framework
Service Discovery
ในโลกที่ทุกสิ่งเริ่มกลายไปเป็น Container ตั้งแต่การเข้ามาของ Docker ไปจนถึง หน่วยเล็ก ๆ อย่าง POD ใน Kubernetes หลายองค์กรหันมาใช้สถาปัตยกรรม Microservices กันทั้งนั้น นั่นหมายความว่า เราจะมี Service ยุบยับกันเต็มไปหมด มีการ “เกิดขึ้น ตั้งอยู่ และ ดับไป” อยู่ตลอดเวลา
ทุกสิ่ง เกิดขึ้น ตั้งอยู่ และ ดับไป
วันนี้ผมไม่ได้มาอธิบายเรื่อง Microservices, Docker หรือ K8S ฉะนั้น ผมจะไม่อธิบายอะไรให้มากความนะครับ จะพูดถึงแค่ Service Discovery เท่านั้น
Why should we use service discovery?
ทำไมเราถึงต้องใช้ Service Discovery ด้วยล่ะ ลองจินตนาการว่าเราเขียนโปรแกรมขึ้นมาหลาย ๆ ตัว โดยใช้การคุยกันผ่าน REST API เวลาที่เราต้องการให้ Service ต่าง ๆ นั้นคุยกัน สิ่งที่เราต้องรู้เลยก็คือ!! IP address และ Port ของ Service นั้น ๆ ถูกไหมครับ ใน Application สมัยก่อน เรามักจะใช้ Physical hardware กัน ซึ่งนั่นมันค่อนข้างที่จะสามารถ Fix IP address และ Port ได้
แต่ด้วยความ Container ใน Microservices ตัว Service Instances มีความยืดหยุ่นใน Network locations ยิ่งไปกว่านั้นพวก Service Instances ยังสามารถสับเปลี่ยนตัวไปได้เรื่อย ๆ เนื่องจากมีการทำ Autoscale, Failures หรือ Update ดังนั้น Client ที่จะเข้ามาใช้บริการ Services จึงจำเป็นที่จะต้องทำให้รู้ให้ได้ว่า Service ที่เกิดขึ้น ตั้งอยู่ และ ดับไปนั้น มันยังอยู่ไหม ถ้าของเดิมที่มีอยู่มันหายไปแล้ว ต้องรู้ และ ต้องรู้ของใหม่ด้วย แหละนั่นจึงทำให้เกิดสิ่งที่เรียกว่า Service Discovery ขึ้นมานั่นเอง เย้!!
The two main service discovery patterns
Service discovery นั้นมี Pattern สองสิ่งหลัก ๆ ที่เราต้องทำความเข้าใจคือ Client-side discovery และ Server-side discovery
The Client-side discovery
Client-side discovery จะมีหน้าที่รับผิดชอบในการกำหนด Network locations โดยทำการ Queries service registry เพื่อหา Instance ที่พร้อมใช้งาน จากนั้น Client จะใช้ Load-balancing algorithm เพื่อเลือกหนึ่งใน Instance ที่พร้อมใช้งาน และ ทำการ Request ต่อไป
ขั้นตอนการทำงานคร่าว ๆ Instance จะทำการ Register ไปที่ Service registry เมื่อ Service start up แล้วมันก็จะทำการถอด Service registry เมื่อ Instance terminate โดยทั่วไปแล้ว การ Registration จะทำการ Refresh ไปเรื่อย ๆ เป็นระยะ ๆ โดยใช้ Heartbeat mechanism
มาดูข้อดี ข้อเสียกัน
ข้อดี มันเข้าใจไม่ยาก มีรูปแบบตรงไปตรงมาไม่ซับซ้อน ตัว Client รู้ว่ามี Service instances ตัวไหนที่พร้อมให้บริการ จึงทำให้สามารถตัดสินใจได้อย่างชาญฉลาด
ข้อเสีย เราต้องจับคู่ Client กับ Service registry และ เราก็ต้อง Implement client-side service discovery logic ในทุก ๆ Service ของคุณ ซึ่งถ้าก่อนหน้านี้ไม่เคยทำ ก็คือต้องไปไล่ทำให้หมด (จริง ๆ มันก็ไม่ใช่ว่าจะเป็นข้อเสียเท่าไหร่นะ แต่มันเยอะ)
The Server‑Side Discovery
The Server‑Side Discovery เมื่อ Client ทำการ Request ไปที่ Service โดยผ่าน Load-balance ตัว Load-balance จะทำการ Queries service registry และ ทำการ Routes ทุก ๆ Request ไปยัง Service instance ที่พร้อมให้บริการ
AWS Elastic Load Balancer (ELB) ก็ถือเป็น Server-side discovery router ตัวนึง NGINX Plus และ NGINX ก็สามารถใช้เป็น Server-side discovery load balancer ได้ รวมถึง Proxy ใน Environments อย่าง Kubernetes และ Marathon เอง ก็สามารถเป็น Server-side discovery load balancer ได้
ข้อดี
การทำ Server-side discovery นั้นแยกออกจากฝั่ง Client ตัว Client ทำหน้าที่ยิงไปหา Load-balance เท่านั้น ที่เหลือจะจัดการให้เอง โดยที่ Client ไม่จำเป็นต้องทำการ Implement ใด ๆ เพิ่มเติม
The Service Registry
ส่วนสุดท้าย Service Registry ซึ่งถือเป็นส่วนสำคัญส่วนหลักเลย ของ Service Discovery จากข้างบน จะเห็นได้ว่า ผมพูดถึง Service registry บ่อยมาก เรามาดูกัน
ใน Service registry ให้เรามองว่ามันเป็น Database ตัวนึง ที่มีข้อมูลเป็น Network locations ของ Service instances ต่าง ๆ ที่มา Register เอาไว้ โดยที่ตัว Clients สามารถ Cache network locations ที่ Service registry provide มาให้ได้ แต่ทั้งนี้ทั้งนั้น มันก็ไม่ได้อยู่แบบคงทนถาวรนะครับ เนื่องจาก ในโลกที่ทุกสิ่งเป็น Container ฉะนั้น ตัว Clients ก็จะจำเป็นต้องวิ่งเข้ามา Query ที่ Service register อยู่เป็นระยะ ๆ
เรามาดูตัวอย่างกันครับ
จากภาพตัวอย่างด้านบน เป็นการ Implement ด้วย Spring cloud netflix eureka โดยมี 2 Service และ 1 Eureka server
Service eureka server จะทำหน้าที่เป็น Service registry ให้ Service อื่น ๆ เข้ามาทำการ Register และ ทำหน้าที่แจกจ่าย Location address ให้คนที่เข้ามา Register
Eureka client begin และ Eureka client A ทำหน้าที่ส่ง ข้อมูลของตัวเองไปหา Service eureka server หรือ Register instance นั่นเอง แล้วก็ทำการ Query location address ไปเก็บไว้ที่ Cache
โดยปกติแล้ว Eureka client จะทำการ Refresh registration ทุก ๆ 30 วินาที เพื่อ Update ข้อมูลให้ Eureka server
เมื่อ Eureka client begin ทำการ Request ไปหา Eureka client A ซึ่งเป็น Service ที่มีอยู่หลาย Container ก็ไม่ต้องกังวล ว่า Eureka client A จะมี IP address หรือ Port อะไร เนื่องจากไป Query location address มาจาก Service eureka server แล้ว
Conclusion
ปัจจุบันก็มีผู้ที่สร้าง Library ที่สนับสนุนการทำ Service Discovery ขึ้นมาหลายตัวครับ เช่น Consul, Zookeeper, Eureka และ อื่น ๆ ที่ไม่ได้กล่าวถึง ก็ขึ้นอยู่กับผู้ใช้ ว่าต้องการใช้ Service Discovery ตัวไหน เนื่องจากแต่ละตัว ก็จะมีข้อดี ข้อเสีย ที่แตกต่างกันออกไป
ก็หวังว่าท่านผู้อ่านทุกท่านจะมีความเข้าใจเกี่ยวกับ Service Discovery กันมากขึ้นนะครับ และ เหมือนเดิมครับ ผิดพลาดตรงไหน รบกวนชี้แนะ เพื่อพัฒนาวงการกันต่อไปครับ ^^