Microservices có phải là “miền đất hứa”?

Thuật ngữ Microservices đã không còn xa lạ trong việc triển khai các ứng dụng và hệ thống phần mềm trong nhiều năm trở lại đây. Microservices sẽ là lựa chọn hàng đầu của developer khi bắt đầu xây dựng một hệ thống mới. Và cũng là mục tiêu chuyển đổi cho nhiều hệ thống monolithic đang hoạt động kém hiệu quả.

Trong bối cảnh hiện nay, trong khi các ứng dụng ngày càng trở nên phức tạp, thì hệ sinh thái cũng trở nên rất đa dạng về thiết bị và các phương thức giao tiếp, developer ngày càng phải xử lý những vấn đề phức tạp và business thay đổi liên tục. Ứng dụng monolithic khi vừa phát triển có lẽ sẽ không có vấn đề gì, nhưng càng phát triển sẽ càng bộc lộ những nhược điểm mà nếu không được đánh giá để chuyển đổi kịp thời sang microsevices thì đến một thời điểm doanh nghiệp sẽ nhận ra hệ thống hiện tại không thể đáp ứng được business. Nhưng microservices có phải chỉ có những hứa hẹn như cách mà chúng ta vẫn nghe về nó. Bài viết này sẽ đi vào những thách thức mà nhà phát triển sẽ phải đối mặt trước khi có thể đạt được những giá trị mà microservices mang lại.

Microservices và Monolithic

Ưu điểm lớn nhất của monolithic là tính dễ triển khai, chi phí thấp, phát triển nhanh khi ở quy mô nhỏ, còn với microservices ưu điểm sẽ là tính linh hoạt trong việc thay đổi và mở rộng. Nhà phát triển cần bỏ công sức ra nhiều hơn để phát triển, bảo trì và vận hành hệ thống microservices so với một hệ thống monolithic có cùng tính năng.

Những giá trị microservices mang đến thật sự đáng giá, ví dụ như khả năng phục hồi, khả năng chịu lỗi, khả năng co giãn đáp ứng tải. Nhưng những điều này không phải tự nhiên mà có. Câu chuyện với microservices không đơn giản như cài đặt một framework, vì là kiến trúc phần mềm, microservices được mô tả bằng các pattern, concept và principle. Và để đạt được những giá trị của microservices đòi hỏi nhà phát triển vừa triển khai theo các concept nhưng cũng phải cân đối chi phí cho việc phát triển theo từng giai đoạn.

Lợi ích của Microservice

Cùng điểm qua một số ưu điểm của microservice trước khi đi vào chi phí

  • Những dịch vụ có thể rất đơn giản, tập trung vào một số business nhất định
  • Hệ thống xây dựng theo hướng loosely coupling, xoay quanh business
  • Nhiều nhà phát triển hoặc các team khác nhau có thể đồng thời triển khai các tính năng tương đối độc lập với nhau
  • Phù hợp với nhu cầu delivery liên tục, cho phép release trong khi giữ phần còn lại của hệ thống vẫn ổn định

Chi phí phát triển

Microservices cần rất nhiều chi phí vận hành

Thông thường một ứng dụng trải qua rất nhiều các phase trước khi được publish hoàn toàn. Mỗi service phải trải qua build, test, deploy and run. Mỗi dịch vụ có thể được viết bằng các ngôn ngữ và chạy trên các môi trường/ hệ điều hành khác nhau, các kịch bản triển khai CI/CD, và các dependency cũng có thể khác nhau.

Mỗi ứng dụng lại cần được triển khai theo cụm (clustering) để đáp ứng khả năng chịu lỗi và phục hồi, dẫn đến x2, x3 số lượng runtime instance so với số lượng dịch vụ ban đầu. Nếu hệ thống có 15 service, số lượng runtime instance có thể lên tới 30-50. Cộng thêm với các thành phần Load balancer và Message broker, hệ thống đã trở nên khá lớn nếu so với ứng dụng monolithic cùng chức năng.

Do hệ thống bị phân tán , việc monitoring và tracing cũng cần được thêm vào để đảm bảo hệ thống hoạt động ổn đinh. Tránh tình trạng deadlock, hết dung lượng lưu trữ, compute bị quá tải…hay làm sao để biết được dịch vụ nào đang bị quá tải hoặc bị ngừng hoạt động để đưa ra quyết định phù hợp, hoặc làm sao để tracing được data/bug khi thực hiện một chức năng cross qua nhiều service và kênh messaging. Để ghi lại và truy vấn được log trong 30-50 instance cần một lượng tài nguyên không hề nhỏ.

Hiện tại đã có nhiều giải pháp đáp ứng các quá trình phát triển và triển khai các microservices, nhưng thường những ứng dụng này chỉ đáp ứng một phần các công việc cần làm . Gần như nhà phát triển sẽ cần triển khai hệ thống ở một mức độ nhất định trước khi có thể bắt tay vào code business.

Yêu cầu kĩ năng DevOps

Một hệ thống microservices thường được triển khai qua một Container Engine/ Container Orchestration như Docker, Docker Swarm, K8S, Openshift… Những công nghệ này yêu cầu kiến thức về docker, container, hệ điều hành và commandline, chưa kể đến kiến thức về mạng, storage, environment thậm chí các kiến thức nâng cao hơn về ảo hóa, clustering của mỗi công nghệ đặc thù. Hệ thống được tạo nên từ nhiều dịch vụ nhỏ, nên quá trình CI/CD là không thể thiếu để quá trình triển khai và tích hợp được tự động. Những kiến thức trên khá xa lạ với các developer chuyên về business như Frontend hay Backend, nên hệ thống microservices yêu cầu kĩ năng về DevOps đáng kể.

Interface Giao tiếp giữa các dịch vụ không rõ ràng

Vì hệ thống được chia nhỏ ra thành cách thành phần riêng biệt có thể cả về hướng kĩ thuật hoặc hướng theo domain business, các dịch vụ này cũng có thể được thực hiện bởi những cá nhân hay team khác nhau, dẫn đến việc phối hợp cùng nhau để định nghĩa interface giao tiếp giữa các service-service hoặc service-message broker trở nên phức tạp và không ổn định nếu giữa các team/ cá nhân không tuân thủ theo các rule về mặt giao tiếp. Function sẽ bị break nếu một bên thay đổi kiểu dữ liệu hoặc một dữ liệu mandatory không được đáp ứng giữa các service. Một số chuẩn giao tiếp hoặc công nghệ hỗ trợ cho chuẩn hóa format API và dữ liệu như OpenAPI hay Schema registry đã giúp ích rất nhiều cho việc kết hợp giữa các dịch vụ, nhưng về bản chất nhà phát triển cần có các biện pháp để giải quyết vấn đề này.

Multiply Effort

Hệ thống microservices có xu hướng triển khai hầu hết các dịch vụ chính trên cùng một công nghệ hoặc ngôn ngữ để giảm effort phát triển, nhưng ngay cả khi đã triển khai trên cùng một công nghệ thì các dịch vụ cũng thường được lưu trữ code hoàn toàn tách biệt về phạm vi project code và dependency.

Nhà phát triển thường xuyên phải đối mặt với vấn đề duplicate các đoạn code hoặc chức năng trên các service khác nhau, thậm chí ngay cả khi bắt đầu một dịch vụ mới, các cấu hình hệ thống, environment cũng thường được sử dụng lại trên các dịch vụ, các function util, secret, apikey… Điều gì sẽ xảy ra khi một đoạn code hay chức năng global cần được update tính năng hoặc fix bug, nhà phát triển cần thực hiện thay đổi đó ở tất cả các dịch vụ đang sử dụng. Vì đặc tính loose coupling của microservices nên việc duplicate các model hay code giữa các service là điều không thể tránh khỏi, nhưng cũng cần được tối ưu để giảm chi phí và rủi ro cho việc maintain hệ thống.

Xử lý hệ thống phân tán phức tạp

Microservices triển khai trên một hệ thống phân tán, các service thậm chí còn không cùng nằm trên một server, đây cũng chính là nguyên do dẫn đến rất nhiều vấn đề của microservices mà nếu ở hệ thống monolithic thậm chí chúng ta còn chưa nghe tới. Các vấn đề có thể kể đến như khả năng chịu lỗi, độ trễ của mạng, xử lý bất đồng bộ, transaction cross qua nhiều service, version của ứng dụng, tracing dữ liệu, khả năng tương thích ngược, cấu hình tập trung…

  • Khả năng chịu lỗi: Microservices cần có khả năng chịu lỗi khi có một thành phần trong hệ thống không hoạt động và đảm bảo business không bị ảnh hưởng khi có một dịch vụ bị downtime hoặc xảy ra lỗi trong giao tiếp.
  • Độ trễ của network: Với càng nhiều microservices, nguy cơ độ trễ của function càng lớn, vấn đề này rất quan trọng với các ứng dụng yêu cầu tốc độ cao và ổn định như chứng khoán, tài chính, ngân hàng.
  • Xử lý bất đồng bộ: Trong hệ thống có thể có những function không thể nào nhận được đáp ứng ngay lập tức, vậy hệ thống lại cần có cơ chế để bên request có thể nhận biết được kết quả của một request đã được đáp ứng thực sự.
  • Transaction cross qua nhiều dịch vụ: Đây là vấn đề thực sự quan trọng, Data consistency luôn là vấn đề quan trọng trong bất cứ hệ thống nào. Có lỗi trên một ứng dụng monolithic sẽ dễ dàng được giải quyết hơn do các framework hầu hết đã hỗ trợ Transactional trên các function khi thực thi. Nhưng điều này không thể lặp lại trên microservices, do các ứng dụng có nhu cầu gọi đến nhau để thực thi một business function, chưa kể đến các kênh giao tiếp khác Send and forget (messaging), dẫn đến các chức năng hoặc dữ liệu không thể rollback theo cách truyền thống.
  • Version của ứng dụng: Việc kết hợp giữa các team yêu cầu sự thống nhất về mặt interface giao tiếp và chức năng. Làm thế nào để biết một business function trong release mới đang cần code của những dịch vụ nào, version nào của dịch vụ đó, và nhu cầu rollback cả hệ thống.
  • Tracing dữ liệu: Việc debug trên hệ thống microservices không đầy đủ là khá khó khăn, nếu một function cross qua nhiều service và mỗi service chạy trên một vài instance. Điều này lại yêu cầu một hệ thống loging tập trung.
  • Khả năng tương thích ngược: Các ứng dụng khi phát triển cần được quản lý các version và thay đổi interface một cách cần trọng, cân nhắc đến khả năng tương thích ngược với các hệ thống hiện tại. Thận trọng trong các thay đổi về request/response hoặc các bussiness code. Bạn có thể phát triển thêm một tính năng những sẽ break business hiện tại.
  • Centralize configuration: Khi hệ thống phân tán mà không có giải pháp cho việc centralize các configuration, nhà phát triển sẽ phải thường xuyên thay đổi các config một các thủ công và trên nhiều dịch vụ. Việc này khá mất effort và không đảm bảo tính chính xác/ ổn định của hệ thống.

Những vấn đề này sẽ được đề cập chi tiết hơn trong các bài viết sau.

Khả năng kiểm thử

Do có nhiều dịch vụ nên việc kiểm thử trở nên khó khăn và tốn effort trên cả unit test/ manual test/ auto test/ intergration test. Thậm chí còn trở nên khó khăn hơn nữa với các chức năng sử dụng bất đồng bộ(async/callback) hoặc messaging(publish/subscribe).

Handle multi-instance

Nhà phát triển cần chú ý tới khả năng multi instance có ảnh hưởng đến các chức năng vốn có của các dịch vụ hay không. Nếu lúc đầu các dịch vụ luôn chỉ có 1 instance, nhưng không chắc chắn khi tăng số replica , mọi business sẽ hoạt động ổn định. Lấy ví dụ với các chức năng về cron-job hoặc subscribe messaging thường chỉ có nhu cầu được trigger/execute 1 lần trên cả hệ thống, nhưng với multi instance, các chức năng này cũng sẽ được repeat hoặc chạy song song nếu không thêm vào các kĩ thuật để xử lý.

Kết

Trên đây là những khó khăn mà tôi đã gặp phải trong quá trình phát triển hệ thống với microservice, các vấn đề sẽ được phân tích và thảo luận hướng giải quyết trong các bài viết tiếp theo. Tuy có nhiều khó khăn, thách thức nhưng những gì Microservice mang lại là rất đáng giá. Với cách tiếp cận đúng đắn khi nhìn vào cả cơ hội và thách thức của Microservice, tôi hi vọng sẽ giúp cho nhà phát triển có cái nhìn toàn diện hơn và tránh được những sai lầm trong quá trình phát triển microservice.

Bạn cần chuyên gia tư vấn giải pháp Cloud phù hợp?

Vui lòng để lại thông tin, chúng tôi sẽ liên hệ với bạn trong thời gian sớm nhất!