จากงาน QCon Plus 2020 :: The Journey from Monolith to Microservices at GitHub

ระบบของ GitHub นั้นมีอายุมากกว่า 12 ปี
โดยพัฒนาในรูปแบบของ Monolith architecture เป็นหลัก
พัฒนาด้วย RoR หรือ Ruby on Rails
ทำการ deploy หลายครั้งต่อวัน
ระบบต้อง scale เพื่อรองรับจำนวน request มากกว่า 1,000 ล้านครั้งต่อวัน
โดยระบบสามารถทำงานได้ตามที่ต้องการเป็นอย่างดี

แต่ในช่วงหลัง ๆ มานั้นทีมของ GitHub โตขึ้นแบบก้าวกระโดด

นั่นคือภายใน 18 เดือนหรือปีครึ่ง จำนวนคนมากกว่า 2,000 คน
ซึ่งมีจำนวนมากกว่าเดิม 2 เท่าตัว
โดยจำนวนพนักงานที่เพิ่มมา มีสองส่วนคือ

  • การรับคนแบบปกติ (Organic)
  • จากบริษัทที่ซื้อเข้ามา ทั้ง NPM, Dependabot, Pull panda และ Semmle เป็นต้น

อีกอย่าง พนักงานกว่า 70% ทำงานแยกกันไปตามแต่ละที่
ต่าง timezone กันไป (Distributed team)

จากที่กล่าวมาข้างต้น

ทำให้เทคโนโลยีที่ใช้ภายในของ GitHub มีความหลากหลายมาก ๆ
จะให้ทุกคนมาเรียนรู้ภาษา Ruby และ RoR ก็ไม่น่าเหมาะสม
ทั้งในแง่ของ productivity, efficient และ scale

รวมทั้ง code ของ GitHub ก็ใหญ่ขึ้นมาก
จะทำอะไรสักอย่างประชุมวางแผนก็เยอะ
คนที่เกี่ยวข้องก็เยอะ การตัดสินใจในแต่ละเรื่อง ก็ลำบากซับซ้อนขึ้นเรื่อย ๆ
ผลกระทบมากมายทั้ง logic และ data ที่ใช้งานร่วมกัน
ดังนั้นการแก้ไขแต่ละครั้งต้องระมัดระวังอย่างมาก
นี่คือปัญหาหรือไม่นะ ?

จึงเป็นที่มาของ Monolith vs Microservices ?

ซึ่งทั้งคู่มีข้อดีและข้อเสียต่างกันไป
จะ migrate ไปทั้งหมดเลย ?
ถ้าจะทำ ต้องทำอย่างไรบ้าง ?
ซึ่งต้องรู้และเข้าใจก่อน

ก่อนที่ GitHub จะทำการเปลี่ยนแปลงระบบนั้น

ต้องใช้เวลาในการคิด วิเคราะห์ ไตร่ตรองว่า
ทำไมต้องเปลี่ยนแปลง
เป้าหมายของการเปลี่ยนแปลงคืออะไร


ในการเปลี่ยนแปลงนั้น เรื่อง culture สำคัญมาก ๆ
แน่นอนว่า มันไม่ง่าย มีงานต่าง ๆ ให้ทำมากมาย

โดยจะทำอะไรนั้น ต้องให้ความสนใจ ใส่ใจ
ในเรื่องของปัญหา และ pain point เป็นหลัก

เป้าหมายหลักที่ GitHub ต้องการคือ การ enable ไม่ใช่การ replace

ซึ่งต้องยอมรับก่อนว่าในอนาคตจะมีทั้ง monolith และ microservices ทำงานร่วมกัน
ดังนั้นเรื่อง environment จะต้องมีทั้งสองแบบ
การปรับปรุง code ใน monolith ก็ยังไม่ความสำคัญ
ไม่แพ้กับการสร้าง microservices ขึ้นมา

Good Architecture start with Modularity

ในขั้นตอนแรกของการแยกการทำงานของ monolith ออกมา
คือการแยกส่วนการทำงานต่าง ๆ ทั้ง business logic, data ออกมา
ในรูปแบบของ module (Logical)
ก่อนที่จะแยกกันจริง ๆ ในแง่ physical ในรูปแบบ microservices ต่อไป

ดังนั้นสิ่งที่สำคัญมาก ๆ คือ
ต้องทำให้ code มันจัดการได้ง่าย
ก่อนที่จะไปคิดเรื่องอื่น ๆ
ถ้าพื้นฐานมันแย่แล้ว ต่อไปมันจะแย่ลงเรื่อย ๆ

จากนั้นเรื่อง data หรือข้อมูลที่ต้องใช้งาน

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

ซึ่งช่วยทำให้แต่ละ service มีการกำหนด API หรือ contract
ระหว่างกันได้ชันเจนมากขึ้น

สิ่งหนึ่งที่เห็นบ่อยมาก ๆ และไม่ถูกต้องคือ

มักแยกเพียง logic ออกมาเป็น service เท่านั้น
แต่ data ยัง share กัน !!
เหมือนใน monolith เลย !!
นำไปสู่ปัญหาที่เรียกกันว่า Distributed Monolith

ผลที่ตามมาคือ แย่ทั้ง monolith และ microservices นั่นเอง
ทำให้เราได้ microservices ที่มีความซับซ้อน
แต่ไม่ได้ประโยชน์อะไรจากมันเลย

การแยก data ที่ GitHub ทำอย่างไร ?

มีขั้นตอนดังนี้

ขั้นตอนที่ 1

ทำการระบุ boundary ของ function การทำงานต่าง ๆ
พร้อมระบุว่ามี table อะไรบ้างที่ใช้งาน
จะเห็นว่ามี table อะไรที่ใช้ร่วมกัน และ แยกกันไปในแต่ละ function การทำงาน
และยังมีการพัฒนาเครื่องช่วย monitor ด้วยว่า
มี query อะไร ที่ทำการดึงข้อมูลจาก table จากกลุ่มการทำงานอื่น ๆ บ้าง
เพื่อทำให้เห็นว่าต้องแก้ไขตรงไหนบ้าง

ขั้นตอนที่ 2

แยก schema ออกมาในแต่ละกลุ่มการทำงานให้ชัดเจน
พร้อมกับใส่ partition key เข้ามาด้วยเพื่อให้ง่ายต่อการ scale
เมื่อแยก schema ไว้แล้ว
ทำให้ง่ายต่อการแยกในระดับ database server ต่อไป

เนื่องจากระบบงานมีขนาดใหญ่ จะเริ่มตรงไหนดี ?

สิ่งที่ GitHub เริ่มคือ

  • Core service หรือ service หลักของระบบ เช่น Authentication and Authorization
  • Share resource หรือ อะไรก็ตามที่ใช้งานร่วมกันเสมอ เช่น เครื่องมือในการพัฒนาให้ง่ายขึ้น เช่น feature flag เป็นต้น

การติดต่อสื่อสารระหว่าง service จะใช้งาน gRPC
อีกอย่างเมื่อแยกการทำงานอะไรออกไปแล้ว
code ในระบบเดิมต้องเอาออกไปด้วยเสมอ
รวมทั้งยังสร้างเครื่องมือ
สำหรับการดูว่า traffic วิ่งไปยังที่ใหม่ทั้งหมดหรือไม่อีกด้วย
เป็นการสร้างเครื่องมือ เพื่อตอบโจทย์ความต้องการ

เรื่องของ Operation ก็เปลี่ยนไปเช่นกัน ขาดไม่ได้เลย

ทั้งเรื่องเดิม ๆ คือ Monitoring, CI/CD และ containerization
ต้องเปลี่ยนไปเพื่อให้รองรับ microservices ด้วย
เพราะว่ามีจำนวน service เยอะขึ้น
ความหลากหลายของ technology มากขึ้น
ต้องมีการ customize pipeline ให้เหมาะสมกับ monolith และ microservices

ระบบ Monitoring ก็ต้องเพิ่มอีก
ทั้งเรื่องของ metric, contract interface ของ API ระหว่าง service

สิ่งที่ทาง GitHub ทำขึ้นมาคือ template ของ pipeline
เพื่อให้สามารถ reuse ได้ง่าย
แต่เกิดมาจากการทำงานจริง ๆ ถึงจะมี template ได้

ยกตัวอย่างเช่น

Self-service runtime platform สำหรับการ deliver microservices
ช่วยลด overhead ของการทำงานในฝั่ง operation
ซึ่งประกอบไปด้วย

  • Kubernetes manefest template
  • Ingress สำหรับ load balancing
  • การเก็บ log
  • การ integrate กับส่วนการทำงานอื่น ๆ

ทำให้ทีมง่ายต่อการทดลองสร้าง microservices ขึ้นมาซึ่งเป็นสิ่งที่ดีมาก ๆ

ปิดท้ายด้วยประโยคนี้

Start Small and Think about product/business value

น่าจะพอมีประโยชน์สำหรับใครก็ตามที่กำลังจะทำ
เป็นอีกหนึ่งแนวทางที่น่าสนใจครับ
ก่อนจะทำอะไรก็ตาม ให้เริ่มด้วยคำว่า why ไม่ใช่ how