Microservices - What, Why and How?

Microservices - What, Why and How?

Trong bài viết này tôi sẽ bắt đầu bằng cách giải thích kiến trúc Monolith là gì, những thách thức của Monolith và tại sao ngành phần mềm lại chuyển dần sang kiến trúc Microservice. Sau đó chúng ta sẽ xem chính xác thì microservice hoặc kiến trúc microservice là gì cũng như các phương pháp thực hành hay nhất mang lại lợi ích và cách giao tiếp giữa các microservice để chúng thực sự hoạt động. Chúng ta cũng sẽ thấy các cách khác nhau để quản lý mã nguồn phát triển cho ứng dụng microservices và nói về monorepo so với polyrepo cũng như ưu điểm và nhược điểm của cả 2, vì vậy hãy bắt đầu.

  1. Monolith là gì?

  2. Những thách thức của Monolith và tại sao lại là Microservices?

  3. Microservices là gì?

  4. Lợi ích và các cách thực hành tốt nhất?

  5. Những thách thức của microservices?

  6. Giao tiếp giữa các MS

  7. Monorepo vs Polyrepo

Monolith là gì?

Trước microservice, monolithic architecture là tiêu chuẩn cho mọi ứng dụng. Có nghĩa là tất cả các components (toàn bộ code) về cơ bản là 1 phần của 1 đơn vị duy nhất. Ví dụ chúng ta có 1 online shop application, tất cả các phần của nó như user-auth, shopping-cat, product-catalog, notifications, payment...Tất cả code cho các chức năng này sẽ nằm trong 1 codebase vì là 1 phần của 1 monolithic application. Mọi thứ đều được phát triển và mở rộng quy mô thành 1 đơn vị, điều này có nghĩa là ứng dụng phải được viết bằng 1 ngôn ngữ duy nhất, 1 tech stack duy nhất, 1 runtime duy nhất và nếu bạn có các team khác nhau làm việc trên các phần khác nhau của ứng dụng, họ sẽ cần phối hợp để đảm bảo rằng họ không gây ảnh hưởng xấu đến công việc của nhau cũng như nếu họ thay đổi code cho chức năng payment mà bạn sẽ cần để build toàn bộ app và deploy nó như 1 package, bạn không thể chỉ update và deploy chỉ thay đổi chức năng thanh toán 1 cách riêng lẻ, thay vào đó bạn phải redeploy lại toàn bộ ứng dụng.

Đó cũng là cách phát triển 1 ứng dụng tiêu chuẩn. Nhưng khi ứng dụng ngày càng tăng về quy mô và độ phức tạp, điều này dẫn đến những thách thức khác nhau.

Những thách thức của Monolith và tại sao lại là Microservices?

Trước hết là sự phối hợp giữa các team trở nên phức tạp, khó khăn hơn vì mã nguồn lớn hơn nhiều và các phần của ứng dụng cũng phức tạp hơn. Nếu đột nhiên bạn có mức sử dụng giỏ hàng (shopping-cart) tăng đột biến, chẳng hạn như vào những ngày nghỉ lễ và bạn chỉ muốn mở rộng phần đó của ứng dụng mà bạn có thể. Để làm điều đó, bạn cần phải mở rộng quy mô của toàn bộ ứng dụng, điều này có nghĩa là chi phí cơ sở hạ tầng cao hơn và kém linh hoạt hơn trong việc mở rộng quy mô ứng dụng của bạn.

Chẳng hạn như một vấn đề khác là nếu 1 khoản thanh toán (pay-ment) có chức năng sử dụng module của bên thứ 3 với version 1.8 trong khi cần có noti (notification) của cùng 1 module nhưng yêu cầu version 1.7 thay vào đó trong 1 ứng dụng monolithic, bạn sẽ phải chọn cái này hoặc cái kia vì đó là 1 ứng dụng duy nhất và bạn chỉ có thể có 1 phần phụ thuộc của cùng 1 module.

Một vấn đề lớn khác với các ứng dụng monolithic là quá trình release của các ứng dụng như vậy tốn nhiều thời gian hơn vì đối với những thay đổi trong bất kỳ phần nào của ứng dụng, trong bất kỳ tính năng nào, bạn cần test và build lại toàn bộ ứng dụng để triển khai những thay đổi đó và câu trả lời cho tất cả các vấn đề này là kiến trúc microservice.

Microservices là gì?

Vậy chính xác thì microservices là gì? Với microservices mà tôi phân tích, ứng dụng về cơ bản là nhiều ứng dụng nhỏ hơn nên tôi có 1 số ứng dụng small hoặc micro tạo nên 1 ứng dụng lớn này.

Bây giờ tôi có 1 số câu hỏi rất quan trọng khi tạo kiến trúc microservices.

Trước hết làm cách nào để tôi quyết định cách chia nhỏ ứng dụng, mã nguồn nào sẽ đi đâu và bao nhiêu micro app hoặc microservices như vậy mà chúng ta tạo ra, những microservice này lớn hay nhỏ như thế nào và cuối cùng là làm thế nào để các dịch vụ này giao tiếp với nhau?

Trước hết cách tốt nhất là chia ứng dụng thành các thành phần (component) hoặc các microservice dựa trên chức năng kinh doanh (business functional) chứ không phải chức năng kỹ thuật (technical funtional) nên microservice của ứng dụng online shop sẽ là product, shopping-cart, user, checkout...vì tất cả những điều này về cơ bản đều là chức năng kinh doanh.

Các chức năng kinh doanh xét về quy mô, mỗi microservice chỉ phải thực hiện 1 chức năng riêng biệt. Vì vậy bạn nên có 1 microservice chịu trách nhiệm về business logic shopping-cart và 1 ms chịu trách nhiệm về business logic checkout, bạn phải luôn cố gắng duy trì 1 service thực hiện 1 công việc cụ thể.

Một đặc điểm rất quan trọng của mỗi microservice là chúng phải khép kín và độc lập (self-contained & independent) với mỗi microservice khác. Điều này có nghĩa là mỗi service phải có khả năng được triển khai và mở rộng quy mô một cách riêng biệt mà không có bất kỳ sự phụ thuộc chặt chẽ nào vào bất kỳ service nào khác mặc dù chúng là 1 phần của cùng 1 ứng dụng.

Và điều này được gọi là mất liên kết (loosely coupled), vì vậy hãy áp dụng phương pháp thực hành tốt nhất này nếu bạn thay đổi gì đó trong payment service, bạn sẽ chỉ xây dựng và triển khai payment service, không có gì khác bị ảnh hưởng và điều này có nghĩa là các service có version riêng không phụ thuộc vào service khác, vì vậy nếu tôi phát hành 1 service, tôi không cần phát hành bất kỳ service nào khác. Vì vậy chu kỳ phát hành này phải hoàn toàn độc lập.

Giao tiếp giữa các MS

Các service này hoàn toàn độc lập vì chúng đã bị cô lập và khép kín, làm cách nào để chúng kết nối với nhau vì rõ ràng payment service sẽ cần thứ gì đó từ user-account service để xử lý thanh toán hoặc checkout service sẽ cần thứ gì đó từ shopping-cart?

Một cách rất phổ biến để giao tiếp với MS là sử dụng cách gọi API. Mỗi service có 1 endpoint chấp nhận request từ các service khác để các service có thể giao tiếp với nhau bằng cách gửi cho nhau cách http request trên các endpoint này.

Đây là 1 giao tiếp đồng bộ (synchronous communication) trong đó 1 service gửi request đến service khác và chờ response, để service user-account có thể gửi http request đến payment service trên api endpoint của nó và ngược lại.

Một cách giao tiếp phổ biến khác giữa các MS là sử dụng message broker, đây là giao tiếp bất đồng bộ (asynchronous communication). Tại đây các service trước tiên sẽ gửi message đến service message trung gian hoặc một broker như rabbitmq chẳng hạn và sau đó message broker sẽ chuyển tiếp message đó đến service tương ứng để xử lý.

Ví dụ user-account sẽ gửi message đến broker và nói rằng vui lòng chuyển message này vào payment. Sau đó message broker sẽ chuyển tiếp message đó đến payment service.

Cách giao tiếp thứ 3 giữa các MS đang trở lên khá phổ biến, đặc biệt là trong lĩnh vực kubernetes đang sử dụng service mesh.Với service mesh bạn có 1 loại service trợ giúp đảm nhận logic giao tiếp hoàn chỉnh nên bạn không cần phải phát triển logic giao tiếp này vào các MS và loại logic giao tiếp này được ủy quyền cho service bên ngoài (external service).

Đó là các tùy chọn giao tiếp khác nhau và vì các service đều bị cô lập và giao tiếp với nhau bằng call api hoặc sử dụng các service bổ sung, bạn thậm chí có thể phát triển các service với các ngôn ngữ lập trình khác nhau và bạn có thể có các team tương ứng cho các service với các tech stack của riêng họ và làm việc trên service của họ mà không bị ảnh hưởng hoặc bị ảnh hưởng bởi các service teams khác.

Và điều này chính là ưu điểm quan trọng nhất của kiến trúc microservice so với kiến trúc monolithic.

Những thách thức của MS

Tuy nhiên những lợi ích này cũng đi kèm với giá thành. Vì vậy mặc dù MS giúp việc phát triển và triển khai ứng dụng dễ dàng hơn về nhiều mặt nhưng nó cũng đặt ra 1 số thách thức khác chưa từng có trước đây.

Khi bạn chia nhỏ ứng dụng thành nhiều phần sẽ gây ra rất nhiều sự phức tạp và thách thức, một trong những vấn đề phức tạp chính có thể là việc định cấu hình phần giao tiếp giữa các service. Một MS có thể không work hoặc work không tốt và chưa trả response trong khị 1 service khác bắt đầu gửi request đến api của nó và mong nhận đc response đầy đủ. Trong trường hợp đó, bạn cũng có thể nhận đc kết quả không mong muốn.

Khi các MS được deploy và mở rộng quy mô riêng biệt, việc giữ cái nhìn tổng quan và tìm ra khị nào 1 MS không work hoặc MS nào thực sự không work khi có thứ gì đó trong ứng dụng không work bình thường, vì vậy bạn chắc chắn cần 1 cấu hình phù hợp cho thiết lập ứng dụng của bạn và các phần của nó để đảm bảo toàn bộ ứng dụng của bạn hoạt động tốt.

Có nhiều công cụ khác nhau để thực hiện tất cả những điều này dễ dàng hơn nên mặc dù kiến trúc MS phức tạp nhưng vẫn có rất nhiều công cụ vẫn đang được phát triển thường xuyên để giúp việc chạy các ứng dụng MS trở nên dễ dàng hơn. Ứng dụng phổ biến nhất có thể bạn đã biết là kubernetes, một nền tảng hoàn hảo để chạy các ứng dụng MS lớn.

CI/CD pipeline cho MS

Yếu tố quan trọng của việc triển khai MS là ci/cd pipeline. Trển thực tế, có nhiều công ty deploy MS nhiều lần trong ngày. Các công ty như AMZ, GG, NF họ có ứng dụng với hàng trăm MS mà họ deploy hàng nghìn lần mỗi ngày nên bạn có thể tưởng tượng độ phức tạp ci/cd pipeline của họ. Vì vậy hiện tại rất có thể bạn sẽ làm việc với MS và trong trường hợp này bạn cần biết cách cấu hình process bằng ci/cd pipeline cho MS.

Monorepo vs Polyrepo

MS là chia ứng dụng thành nhiều service nhỏ hơn, được phát triển và deploy như những ứng dụng riêng biệt. Câu hỏi đặt ra là làm cách nào để quản lý code cho ứng dụng MS trong repository như git chẳng hạn. Với 1 project, thật đơn giản tôi chỉ có 1 ứng dụng và nó có 1 git repository. Với MS, tôi có 2 tùy chọn về cách quản lý mã nguồn. Monorepo (single repository) và polyrepo có nhiều repository.

Monorepo có 1 git repository cho tất cả các services, vì vậy tôi sẽ tạo 1 dự án cho 1 monorepo. Vậy sự khác biệt là gì, ở đây hoặc làm cách nào để chúng tôi cấu trúc tốt nhiều MS bên trong 1 repository?

Monorepo

Một cách phổ biến là sử dụng các folder để bạn có các folder cho từng service như shopping-cart, paymment, notification...và tất cả code cho các service đó đều nằm trong các folder tương ứng. Có 1 repo đơn có nghĩa là tất cả các service vẫn nằm trong 1 repo giúp việc quản lý và phát triển mã nguồn dễ dàng hơn vì bạn chỉ phải clone và làm việc với 1 repo để đơn giản hóa mọi thứ, cộng thêm nếu bạn có 1 số code được share giữa các service như file config, util, common...về cơ bản tất cả các service đều có thể tái sử dụng chúng nhưng cũng có 1 số thách thức. Tiêu chí quan trọng nhất của MS là hoàn toàn độc lập nên không có sự kết hợp chặt chẽ giữa các service bên trong code và việc phá vỡ tiêu chí này trở nên dễ dàng khi bạn có monorepo.

Bạn có các junior developer có ít exp hơn trong việc setup các monorepo. Việc mắc những lỗi như vậy sẽ dễ dàng hơn và phát triển tightly coupled, logic hoặc code được liên kết chặt chẽ trong service của bạn. Một nhược điểm khác của monorepo là khi ứng dụng trở nên thực sự lớn, clone fetching và pushing trở nên chậm vì project của bạn rất lớn và ci/cd pipeline trong hầu hết các nền tảng ci/cd như gitlab ci/cd hoặc jenkins, bạn chỉ có thể tạo 1 pipeline cho 1 project nên bạn đang xây dựng nhiều service với 1 single project pipeline và điều đó có nghĩa là bạn cần thêm logic bổ sung vào code pipeline của bạn để đảm bảo chỉ build và deploy service đã thay đổi. Vì vậy nếu bạn thực hiện thay đổi code trong payment service, pipeline code sẽ phát hiện điều đó và chỉ service đó nên được build test và deploy và có thể thực hiện điều đó nhưng sẽ khó khăn hơn 1 chút.

Một vấn đề nữa với monorepo là vì bạn chỉ có 1 branch main trên repository. Nếu developer của 1 trong các service phá vỡ main branch, các service khác và pipeline của họ cũng sẽ bị chặn nhưng có rất nhiều công ty, bao gồm cả những công ty rất lớn như GG sử dụng monorepo cho app của họ.

Polyrepo

Tùy chọn thứ 2 có lẽ được ưu tiên hơn 1 chút là polyrepo hoặc multiple repository.

Với cách tiếp cận này đối với mỗi service, tôi tạo 1 project git riêng để code được tách biệt hoàn toàn, bạn có thể clone và làm việc với chúng 1 cách riêng biệt vì hiện tại chúng nằm trong các repo riêng biệt.

Mặc dù là repo riêng biệt nhưng chúng vẫn là 1 phần của ứng dụng lớn hơn này nên tất nhiên bạn sẽ vẫn muốn có 1 số loại kết nối giữa những repo này để dễ dàng quản lý và tổng quan. Vì vậy, nếu bạn đang lưu repo của mình trên gitlab chẳng hạn, bạn có thể sử dụng tính năng groups của gitlab để nhóm code cho tất cả MS thuộc về cùng 1 ứng dụng trong 1 nhóm để quản lý các repo đó dễ dàng hơn nên về cơ bản bạn sẽ tạo 1 gitlab repository groups cho ứng dụng của mình có tên là my online shop.

Và trong group này, bạn có thể tạo từng repo tương ứng cho từng MS thuộc ứng dụng đó nếu công ty bạn có nhiều. Điều này sẽ giúp giữ cái nhìn tổng quan về những dự án nào thuộc về nhau nhưng cũng trong group, bạn thực sự có thể tạo các secrets hoặc các ci variables có thể được share bởi tất cả các project trong groups đó. Còn ci/cd pipeline cho polyrepo thì sao?

Đối với polyrepo, cấu hình ci/cd đơn giản hơn vì bạn chỉ có pipeline riêng cho từng repo nên không cần thêm logic để phân biệt giữa các service. Tất nhiên mọi thứ đều có ưu điểm và nhược điểm, vì vậy đối với polyrepo cũng có 1 số nhược điểm như có code trong nhiều repo có thể khiến việc thực hiện dự án trở nên khó khăn hơn, đặc biệt nếu bạn cần thay đổi 2 hoặc nhiều service cùng 1 lúc vì 1 tính năng hoặc sửa lỗi ảnh hưởng đến nhiều service.

Nếu bạn cần chuyển đổi giữa các service thường xuyên, điều này cũng có thể tẻ nhạt cộng với những thứ như việc tìm kiếm thứ gì đó trên nhiều project từ code editor có thể khó hoặc không thể . Ngoài ra nó cũng gây khó khăn cho việc debug và testing.

Trong polyrepo bạn thực sự không thể share các files trong project như kubernetes hoặc hell manifest, docker compose...bạn sẽ phải duplicate chúng trong repo của mỗi project hoặc phải tạo 1 project chuyên dụng để lưu những file đó và reference chúng.

Từ đó để bạn thấy cả 2 tùy chọn đều có ưu và nhược điểm nhưng nguyên tắc chung là nếu bạn có 1 project nhỏ chỉ với 1 vài MS bạn nên sử dụng monorepo để tiết kiệm chi phí cho việc tạo, quản lý và checkout nhiều repo.

Nếu bạn có nhiều team riêng biệt cho từng service, nếu bạn muốn cách ly hoàn toàn code base, chia chúng thành những phần nhỏ hơn để clone trên các pipeline thì tất nhiên polyrepo sẽ là 1 lựa chọn tốt hơn.

Tôi hy vọng bài viết này cung cấp cho bạn phần giới thiệu tuyệt vời về MS và giờ bạn cũng hiểu nó là gì cũng như lý do mọi người sử dụng nó.

Nguồn : TechWorld with Nana