Từ Ruby on Rails Monolith đến kiến trúc Microservices: Cách GitHub tái cấu trúc để phát triển bền vững

Trong giới phát triển phần mềm, câu chuyện về việc chuyển đổi từ kiến trúc monolith sang microservices không còn mới, nhưng không phải lúc nào chúng ta cũng có cơ hội nhìn vào GitHub thực hiện điều đó. Bắt đầu là một khối monolith vững chắc bằng Ruby on Rails, GitHub đã phục vụ hàng chục triệu developer. Tuy nhiên, khi quy mô công ty và đội ngũ kỹ sư tăng vọt, khối monolith từng là niềm tự hào bắt đầu bộc lộ những hạn chế.

e6d3fbe0-e54d-4f81-bf86-1984489691d1

Khi Monolith không còn là lựa chọn tối ưu

GitHub ra đời năm 2008 và nhanh chóng trở thành một phần không thể thiếu của cộng đồng mã nguồn mở. Được xây dựng trên Ruby on Rails, kiến trúc monolith của họ đã chứng tỏ sự hiệu quả đáng kinh ngạc, có khả năng scale để phục vụ 50 triệu developer, hơn 100 triệu repository và xử lý hơn 1 tỷ API call mỗi ngày.

Tuy nhiên, vấn đề bắt đầu nảy sinh khi chính GitHub phát triển. Trong vòng 18 tháng, số lượng kỹ sư đã tăng gấp đôi và hơn 70% trong số họ làm việc remote trên khắp các múi giờ. Việc yêu cầu mọi kỹ sư mới, dù chuyên về mảng nào, cũng phải học Ruby và làm quen với một codebase khổng lồ đã trở thành một nút thắt cổ chai, làm chậm quá trình phát triển và onboard nhân sự. Đây chính là lúc đội ngũ kỹ thuật của GitHub nghiêm túc xem xét kiến trúc microservices.

Cân nhắc giữa Monolith và Microservices

Trước khi đưa ra quyết định, GitHub đã phân tích kỹ lưỡng ưu và nhược điểm của cả hai kiến trúc trong bối cảnh của chính họ.

Ưu điểm của kiến trúc Monolith:

  • Infrastructure Simplicity: Một kỹ sư mới có thể thiết lập và chạy toàn bộ hệ thống GitHub trên máy local chỉ trong vài giờ.
  • Code Simplicity: Việc phát triển trong một codebase duy nhất giúp loại bỏ các logic phức tạp để xử lý timeout, network latency hay các sự cố mạng giữa các service.
  • Tổ chức đơn giản: Mọi người đều quen thuộc với cùng một codebase, giúp việc điều chuyển nhân sự giữa các team, các tính năng trở nên dễ dàng hơn.

Ưu điểm của kiến trúc Microservices:

  • System Ownership: Các team có ranh giới chức năng rõ ràng thông qua các API contract. Điều này trao cho họ quyền tự chủ cao hơn, được tự do lựa chọn stack công nghệ phù hợp nhất cho service của mình miễn là tuân thủ API contract.
  • Separation of Concerns: Kỹ sư mới không cần phải hiểu toàn bộ hệ thống đồ sộ mà chỉ cần tập trung vào service của team mình, giúp rút ngắn đáng kể thời gian onboarding.
  • Scaling độc lập: Các service có thể được scale riêng lẻ tùy theo nhu cầu sử dụng thực tế, giúp tối ưu hóa tài nguyên và chi phí.

Sau khi cân nhắc, GitHub quyết định chuyển đổi sang một kiến trúc hướng microservices. Tuy nhiên, đây không phải là đập đi xây lại. Họ xác định sẽ duy trì một môi trường hybrid, nơi monolithmicroservices cùng tồn tại trong tương lai gần. Vì vậy, việc tiếp tục cải tiến và bảo trì codebase monolith vẫn là một ưu tiên.

Chiến lược phân rã Monolith một cách bài bản

Đây là phần quan trọng nhất trong chiến lược của GitHub. Họ không vội vàng tách code thành các service riêng biệt mà tiếp cận một cách có phương pháp.

Bước 1: Bắt đầu từ Data, không phải từ Code

Sai lầm phổ biến nhất khi chuyển sang microservices là tạo ra một distributed monolith, một hệ thống vừa mang sự phức tạp của microservices, vừa có sự thiếu linh hoạt của monolith. Để tránh cái bẫy này, GitHub khẳng định rằng mỗi service phải sở hữu và kiểm soát hoàn toàn dữ liệu của nó. Mọi truy cập dữ liệu từ bên ngoài đều phải thông qua API.

Họ đã thực hiện điều này bằng cách:

  1. Xác định ranh giới chức năng: Phân tích database schema của monolith để tìm ra các cụm dữ liệu có liên quan đến nhau theo chức năng. Ví dụ: nhóm tất cả các table liên quan đến repository (pull requests, issues, comments) vào một nhóm, các table liên quan đến user vào một nhóm khác. Họ gọi các nhóm này là schema domains.
  2. Giám sát và thực thi: GitHub đã triển khai một công cụ gọi là query watcher bên trong monolith. Công cụ này sẽ phát hiện và cảnh báo mỗi khi có một query truy vấn dữ liệu từ nhiều schema domain khác nhau.
  3. Refactor query: Khi một query vi phạm bị phát hiện, đội ngũ kỹ sư sẽ viết lại nó thành nhiều query nhỏ hơn, mỗi query chỉ truy cập vào một schema domain duy nhất. Sau đó, họ sẽ thực hiện các thao tác join cần thiết ở tầng application.

Bước 2: Tách Service

Khi tách các service ra khỏi monolith, GitHub tuân thủ một nguyên tắc cốt lõi: Dependency direction phải luôn đi từ trong monolith ra ngoài microservice, không bao giờ được theo chiều ngược lại. Nếu một microservice gọi ngược vào trong monolith, nó sẽ lại rơi vào anti-pattern distributed monolith.

  • Ví dụ thực tế: Service cốt lõi đầu tiên mà GitHub tách ra là Authentication và Authorization. Khối monolith (Rails) giao tiếp với service này thông qua Twirp, một framework giao tiếp service-to-service tương tự gRPC. Luồng phụ thuộc rất rõ ràng: monolith (bên trong) gọi đến service xác thực (bên ngoài).

Bước 3: Chú trọng trải nghiệm Developer và dọn dẹp hệ thống

Để khuyến khích các kỹ sư phát triển trên kiến trúc mới, GitHub nhận ra rằng họ cần cung cấp các công cụ hỗ trợ tương đương với những gì có sẵn trong monolith.

  • Shared Tooling: Một ví dụ điển hình là hệ thống feature flags, giúp developer trong monolith dễ dàng kiểm soát việc ra mắt tính năng mới. Hệ thống này cần được cung cấp cho cả các developer làm việc với microservices.
  • Loại bỏ code cũ: Sau khi một service mới đã ổn định và 100% traffic đã được chuyển qua, việc lên kế hoạch xóa bỏ code path cũ trong monolith là bắt buộc. Nếu không, đội ngũ sẽ phải gánh chịu chi phí bảo trì cả hai hệ thống song song.

Hành trình chuyển đổi của GitHub cung cấp một case study giá trị cho bất kỳ tổ chức nào đang đứng trước ngưỡng cửa tương tự. Họ đã chọn một chiến lược phân rã có phương pháp, thận trọng và tập trung vào nền tảng.

Chia sẻ bài viết:
Theo dõi
Thông báo của
0 Góp ý
Được bỏ phiếu nhiều nhất
Mới nhất Cũ nhất
Phản hồi nội tuyến
Xem tất cả bình luận