Microservices là một trong những thuật ngữ phổ biến nhất trong lĩnh vực kiến trúc phần mềm. Bạn có thể tìm thấy khá nhiều tài liệu giới thiệu và nói về những nguyên tắt cũng như lợi ích của microservices, tuy nhiên khá ít tài liệu hướng dẫn cách áp dụng microservices trong những hoàn cảnh thực tế.
Ta sẽ tìm hiểu về các khái niệm kiến trúc chính của Kiến trúc Microservices (MSA) và cách bạn có thể sử dụng các nguyên tắc kiến trúc đó trong thực tế qua bài viết này.
Kiến trúc nguyên khối
Các ứng dụng phần mềm doanh nghiệp được thiết kế để hỗ trợ nhiều yêu cầu kinh doanh; một ứng dụng phần mềm nhất định cung cấp hàng trăm chức năng và tất cả các chức năng đó được xếp thành một ứng dụng nguyên khối duy nhất (monolithic). Ví dụ, ERP, CRM và các hệ thống phần mềm khác được xây dựng như một khối nguyên khối với hàng trăm chức năng. Việc triển khai, khắc phục sự cố, mở rộng và nâng cấp các ứng dụng phần mềm khổng lồ như vậy là một cơn ác mộng.
Kiến trúc hướng dịch vụ (Service Oriented Architecture - SOA) được thiết kế để khắc phục một số hạn chế đã đề cập ở trên bằng cách giới thiệu khái niệm “dịch vụ” (service) - một nhóm tập các chức năng tương tự được cung cấp từ một ứng dụng. Vậy nên trong SOA, một ứng dụng phần mềm được thiết kế như một sự kết hợp của các dịch vụ. Tuy nhiên, với SOA, phạm vi của một dịch vụ rất rộng và được định nghĩa khá “thô” (coarse-grained). Việc này khiến các services cũng có thể trở nên quá to và phức tạp.
Hình 1. Monolithic architecture
Trong phần lớn trường hợp, dịch vụ trong SOA là độc lập nhưng chúng lại được triển khai chung. Tương tự như ứng dụng monolithic, những dịch vụ này to và phức tạp lên theo thời gian vì thường xuyên thêm các tính năng. Và thế là những ứng dụng này lại trở thành một mớ các dịch vụ monolithic, cũng không còn khác mấy so với kiến trúc nguyên khối thông thường.
Hình trên thể hiện một ứng dụng gồm nhiều services. Những services này được triển khai cùng một lúc vào 1 ứng dụng lớn. Dù bên trong có gồm các services thì đây là một ứng dụng monolithic. Một số tính chất của kiến trúc nguyên khối (Monolithic Architecture):
- Các ứng dụng nguyên khối được thiết kế, phát triển và triển khai như một đơn vị duy nhất.
- Các ứng dụng nguyên khối rất phức tạp; điều này dẫn đến khó khăn trong việc bảo trì, nâng cấp và thêm các tính năng mới.
- Rất khó để thực hành các phương pháp kiểu Agile.
- Cần phải triển khai lại toàn bộ ứng dụng dù chỉ cập nhật hay nâng cấp một phần.
- Mở rộng: phải mở rộng cả khối ứng dụng, gặp khó khăn nếu có các yêu cầu về tài nguyên khác nhau (ví dụ một service yêu cầu thêm CPU, service khác lại yêu cầu nhiều memory).
- Độ tin cậy: một service không ổn định có thể sập cả hệ thống.
- Khó đổi mới: ứng dụng monolithic phải sử dụng chung công nghệ nên khó thay đổi hay áp dụng công nghệ mới.
Kiến trúc Microservices
Mục tiêu nền tảng của kiến trúc microservices là xây dựng một ứng dụng mà ứng dụng này là tổng hợp của nhiều services nhỏ và độc lập có thể chạy riêng biệt, phát triển và triển khai độc lập.
Trong hầu hết các định nghĩa của kiến trúc microservices, nó đều được giải thích là quá trình tách biệt các dịch vụ có sẵn trong nguyên khối thành một tập hợp các dịch vụ độc lập. Tuy nhiên, bạn có thể hiểu rằng Microservices không chỉ là việc tách các dịch vụ có sẵn trong nguyên khối thành các dịch vụ độc lập.
Ý tưởng quan trọng chính là nhìn vào các tính năng trong một ứng dụng monolithic, ta có thể nhận biết, xác định các yêu cầu và khả năng cần thiết để đáp ứng một nghiệp vụ. Sau đó từng năng lực nghiệp vụ này sẽ được xây dựng thành những service nhỏ, độc lập. Những services này có thể sử dụng các nền tảng công nghệ khác nhau và phục vụ một mục đích cụ thể và có giới hạn.
Theo đó, ví dụ về hệ thống trong hình 1 có thể được chia theo microservices như trong hình 2. Đây có thể là một ứng dụng phần mềm bán hàng, với kiến trúc microservices, mỗi chức năng kinh doanh hay trong doanh nghiệp được tách thành một microservices. Trong kiến trúc microservices, một service mới được tạo ra từ những services gốc trong kiến trúc monolithic.
Hình 2. Microservices Architecture
Tiếp theo, chúng ta sẽ nói về các nguyên tắc chính của kiến trúc microservices và quan trọng hơn chính là cách chúng được sử dụng trong thực tế.
Thiết kế Microservices: Kích thước, phạm vi và tính năng
Bạn có thể xây dựng ứng dụng mới với microservices hoặc chuyển đổi ứng dụng sẵn có sang microservices. Với cách nào thì việc quyết định kích cỡ, phạm vi và tính năng của microservices rất quan trọng. Có thể đây chính là phần khó nhất bạn gặp phải khi phát triển hệ thống microservices trong thực tế.
Hãy thảo luận về một số vấn thực tiễn và quan niệm sai lầm liên quan đến kích thước, phạm vi và khả năng của microservices:
- Số dòng code/ kích cỡ của một đội lập trình là chỉ số không chính xác: có vài cuộc bàn luận về kích thước của một service dựa vào số lượng dòng code hay kích thước của đội phát triển service đó (ví dụ two-pizza team). Tuy nhiên, những cách đo đếm này không thực tiễn và không chính xác, vì ta có thể phát triển services với ít dòng code hoặc với một đội nhỏ nhưng hoàn toàn đảm bảo được các nguyên lý trong kiến trúc microservices.
- "Micro" là một thuật ngữ dễ gây nhầm lẫn: Hầu hết các nhà lập trình viên có xu hướng nghĩ rằng họ nên cố gắng tạo ra service càng nhỏ càng tốt. Đây là một quan niệm sai lầm.
- SOA: Trong bối cảnh SOA, các dịch vụ thường được triển khai dưới dạng các quả cầu nguyên khối với sự hỗ trợ cho hàng chục hoạt động/ chức năng. Vì vậy, việc có các dịch vụ giống SOA và đổi thương hiệu chúng thành microservices sẽ không mang lại cho bạn bất kỳ lợi ích nào của kiến trúc microservices.
Vì vậy, ta nên thiết kế các dịch vụ trong Microservices Architecture như thế nào thì phù hợp và đúng đắn?
Hướng dẫn thiết kế Microservices
- Single Responsibility Principle (SRP): một service với phạm vi và chức năng giới hạn, tập trung vào một nhiệm vụ giúp quá trình phát triển và triển khai dịch vụ trở nên nhanh chóng hơn.
- Trong quá trình thiết kế, ta nên xác định và giới hạn các services theo chức năng nghiệp vụ thực tế (theo Domain-Driven-Design).
- Đảm bảo microservices có thể phát triển và triển khai độc lập.
- Mục tiêu của thiết kế là phạm vi của microservices phục vụ một nghiệp vụ chứ không chỉ đơn giản làm các dịch vụ nhỏ hơn. Kích thước hợp lý của một service là kích thước đủ để đáp ứng yêu cầu của một chức năng trong hệ thống.
- Khác với services trong SOA, một microservice không nên có quá nhiều hàm hay chức năng hỗ trợ xung quanh và định dạng thông báo/ gửi tin (messaging) phải đơn giản.
- Một cách tốt là có thể bắt đầu với services to có phạm vi rộng rồi chia nhỏ dần (dựa theo nghiệp vụ thực tế của hệ thống).
Trong ví dụ về hệ thống bán hàng, bạn có thể thấy ứng dụng monolithic được tách thành bốn microservices là “inventory”, “accounting”, “shipping” và “store”. Chúng giải quyết một yêu cầu giới hạn nhất định nhưng tập trung vào chức năng đó. Theo đó, mỗi service tách biệt hoàn toàn khỏi nhau và đảm bảo tính linh hoạt và độc lập.
Nếu tách các services ra độc lập, vậy làm sao liên lạc và trao đổi thông tin/dữ liệu giữa các services với nhau?
Liên lạc giữa Microservices
Trong ứng dụng monolithic, các chức năng khác nhau nằm trong các component khác nhau được kết nối bằng cách gọi hàm hay phương thức. Trong SOA, việc này được chuyển sang một chế độ tách rời hơn với kiểu nhắn tin qua các dịch vụ web (web service messaging), phần lớn dùng SOAP trên nền giao thức HTTP, JMS. Những webservices này khá phức tạp. Như đã nói ở trên, microservices yêu cầu là phải có một cơ chế truyền tin đơn giản và nhẹ.
Vậy đó là những cơ chế nào?
Gửi tin đồng bộ - REST, Tiết kiệm
Đối với nhắn tin đồng bộ (khách hàng mong đợi phản hồi kịp thời từ dịch vụ và đợi cho đến khi nhận được) trong Kiến trúc Microservices, REST là sự lựa chọn hàng đầu vì nó cung cấp hệ thống truyền tin đơn giản qua giao thức HTTP dạng request – response. Do đó, nhiều microservices sử dụng HTTP với API. Mỗi chức năng sẽ xuất ra (expose) API.
Hình 3. Dùng REST API để hiển thị microservices
Thrift được sử dụng (trong đó bạn có thể xác định định nghĩa giao diện cho microservice của mình) thay thế cho nhắn tin đồng bộ REST/ HTTP.
Gửi tin không đồng bộ - AMQP, STOMP, MQTT
Đối với một số tình huống microservices, bắt buộc phải sử dụng kỹ thuật gửi tin không đồng bộ (máy khách không mong đợi phản hồi ngay lập tức hoặc hoàn toàn không chấp nhận phản hồi). Trong các tình huống như vậy, các giao thức nhắn tin không đồng bộ như AMQP, STOMP hoặc MQTT được sử dụng rộng rãi.
Định dạng thông báo - JSON, XML, Thrift, ProtoBuf, Avro
Quyết định định dạng tin nhắn tốt nhất cho Microservices là một yếu tố quan trọng khác. Các ứng dụng nguyên khối truyền thống sử dụng các định dạng nhị phân phức tạp, các ứng dụng dựa trên dịch vụ SOA/ Web sử dụng các thông điệp văn bản dựa trên các định dạng thông điệp phức tạp (SOAP) và lược đồ (xsd). Trong hầu hết các ứng dụng dựa trên microservices, chúng sử dụng các định dạng thông báo dựa trên văn bản đơn giản, chẳng hạn như JSON và XML trên đầu trang của kiểu API tài nguyên HTTP. Trong trường hợp chúng ta cần các định dạng tin nhắn nhị phân (tin nhắn văn bản có thể dài dòng trong một số trường hợp sử dụng), microservices có thể tận dụng các định dạng tin nhắn nhị phân như binary Thrift, ProtoBuf hoặc Avro.
Hợp đồng dịch vụ (service contracts) - Xác định các giao diện dịch vụ - Swagger, RAML
Khi bạn có một công việc được xây dựng như một dịch vụ, bạn cần định nghĩa và thông báo hợp đồng dịch vụ (service contract thể hiện giao kèo của service).
Bởi vì chúng ta xây dựng microservices trên kiểu kiến trúc REST, ta có thể sử dụng cùng kiểu REST API để định nghĩa hợp đồng của microservices. Do đó, microservices sử dụng các ngôn ngữ định nghĩa REST API tiêu chuẩn như Swagger, RAML để định nghĩa hợp đồng dịch vụ.
Đối với trường hợp microservices không được triển khai dựa trên HTTP/REST (cụ thể là dùng Thrift), chúng ta có thể dùng mức độ giao thức ‘Interface Definition Languages’ (ví dụ: Thrift IDL)
Tích hợp Microservices (Giao tiếp giữa các dịch vụ/ quy trình)
Trong kiến trúc microservices, ứng dụng phần mềm được cấu thành từ các dịch vụ độc lập. Để hoàn thành một tác vụ của người dùng trên phần mềm, kết nối và giao tiếp giữa các microservices là cần thiết vì tác vụ gồm nhiều tác động khác nhau lên các services. Vì vậy, giao tiếp giữa các microservices là một vấn đề cực kì quan trọng.
Khi áp dụng SOA, giao tiếp giữa các services được tiến hành bởi một Enterprise Service Bus (ESB) và phần lớn logic nằm trong tầng trung gian này (định tuyến cho tin nhắn, chuyển đổi và điều phối tin nhắn). Tuy nhiên, kiến trúc microservices thúc đẩy việc loại trừ một tầng giao tiếp trung gian tập trung/ ESB và chuyển phần ‘smart-ness’ hay business logic sang các services (nó được gọi là ‘Smart Endpoints’).
Bởi vì microservices sử dụng giao thức tiêu chuẩn như HTTP/REST, JSON, …, Cho nên các giao tiếp giữa các microservices được cố gắng thống nhất về giao thức và hạn chế tối đa việc tích hợp với một giao thức khác. Một lựa chọn khác cho giao tiếp giữa microservices là sử dụng một message bus nhẹ hay gateway với khả năng định tuyến tối thiểu hoạt động như những đường truyền đơn thuần không xử lý business logic gì hết (dump pipe). Dựa vào những phong cách này, có một số kiểu mẫu giao tiếp trong microservices như dưới đây.
Point-to-point - Kết nối trực tiếp giữa các service
Theo phong cách điểm tới điểm, toàn bộ logic định tuyến thông báo nằm trên mỗi điểm cuối và các dịch vụ có thể giao tiếp trực tiếp. Mỗi microservice lộ ra một REST API và một microservice nhất định hoặc một khách hàng bên ngoài có thể gọi một microservice khác thông qua REST API của nó.
Hình 4. Giao tiếp giữa các dịch vụ với kết nối point-to-point
Rõ ràng, mô hình này hoạt động đối với các ứng dụng dựa trên microservices tương đối đơn giản, nhưng khi số lượng dịch vụ tăng lên, điều này sẽ trở nên phức tạp. Sau cùng, đó là lý do chính xác để sử dụng ESB trong triển khai SOA truyền thống: để loại bỏ các liên kết tích hợp point-to-point lộn xộn. Hãy thử tóm tắt những nhược điểm chính của phong cách điểm-điểm đối với giao tiếp microservices:
- Những yêu cầu như xác thực người dùng, điều tiết, giám sát,…phải được thực hiện (implement) ở mọi microservices.
- Việc trên dẫn đến các tính năng phổ biến bị trùng lặp, việc thực hiện mỗi microservices có thể trở nên phức tạp.
- Không có cách quản lý, kiểm soát giao tiếp giữa các services (chẳng hạn như muốn theo dõi, truy tìm hay lọc các giao hành động giữa các services).
- Thường việc kết nối trực tiếp trong microservices được coi là anti-pattern cho việc triển khai các ứng dụng microservice có quy mô lớn.
Vì vậy, với những trường hợp phức tạp hơn, thay vì kết nối trực tiếp hay dùng một ESB trung tâm, chúng ta có thể sử dụng 1 messaging bus trung tâm gọn nhẹ. Nó cung cấp một lớp rút gọn các microservices (an abstraction layer). Cách này được gọi là API Gateway.
API-Gateway
Ý tưởng chính của API Gateway là sử dụng một cổng truyền tin gọn nhẹ như một điểm vào chính cho tất cả khách hàng/ người dùng và triển khai những chức năng chung mà không liên quan đến nghiệp vụ đặc thù ở cấp Gateway này.
Nhìn chung, một API Gateway cho phép bạn sử dụng một API được quản lý qua HTTP/REST. Vì thế, chúng ta có thể cung cấp tất cả các chức năng nghiệp vụ được phát triển thành microservices qua API Gateway như một APIs tập trung được quản lý.
Hình 5. Tất cả các microservices được giao tiếp thông qua API Gateway
Trong ví dụ bán hàng của chúng ta, theo Hình 5 tất cả microservices đều được xuất ra thông qua API Gateway và đây là một chốt đầu vào duy nhất cho người dùng. Nếu một microservice muốn giao tiếp với một microservice khác thì cần đi qua API Gateway.
API Gateway cung cấp các lợi thế dưới đây:
- Cung cấp một lớp trừu tượng hóa các microservices. Ví dụ thay vì cung cấp một API cho tất cả khách hàng, API gateway có thể xuất ra hay hiển thị API khác nhau cho mỗi khách hàng.
- Định tuyến và chuyển đổi tin nhắn gọn nhẹ ở cấp gateway.
- Một điểm tập trung cho các chức năng chung không mang tính nghiệp vụ kinh doanh như bảo mật, giám sát và điều tiết.
- Với API Gateway, microservices trở nên càng gọn nhẹ vì các chức năng chung không mang tính nghiệp vụ đều chuyển sang Gateway.
API Gateway có thể là kiểu mẫu được sử dụng rộng rãi nhất trong triển khai microservices.
Người truyền tin trung gian (Message Broker)
Microservices có thể được kết nối qua truyền tin bất đồng bộ như yêu cầu một chiều (requests) hay thông báo/ đăng kí nhận thông báo (publish/ subscribe) qua queues hay topics. Với publish/ subscribe, một service có thể tạo tin nhắn và gửi bất đồng bộ đến một hàng chờ (queue hay topic). Sau đó service khác có thể nhận tin này từ queue hay topic. Phong cách này tách biệt người gửi và người nhận, và người truyền tin trung gian sẽ lưu tin nhắn đến khi người nhận có thể xử lý. Người gửi hoàn toàn không biết gì về người nhận.
Hình 6. Tích hợp gửi tin nhắn bất đồng bộ dùng pub-sub
Giao tiếp giữa người gửi/ người nhận được tạo điều kiện thông qua message broker dựa trên các tiêu chuẩn nhắn tin không đồng bộ như AMQP, MQTT, v.v.
Quản lý cơ sở dữ liệu phân tán
Trong một kiến trúc nguyên khối, ứng dụng lưu trữ dữ liệu trong một cơ sở dữ liệu duy nhất và tập trung để triển khai các chức năng/ khả năng khác nhau của ứng dụng.
Hình 7. Ứng dụng Monolithic dùng cơ sở dữ liệu tập trung cho tất cả các tính năng của nó
Trong kiến trúc Microservices, các chức năng được phân tán trên nhiều microservices và nếu chúng ta sử dụng cùng một cơ sở dữ liệu tập trung thì các microservices sẽ không còn độc lập với nhau nữa (ví dụ: nếu lược đồ cơ sở dữ liệu đã thay đổi từ một microservice nhất định, điều đó sẽ bị hỏng một số dịch vụ khác). Do đó, mỗi microservice phải có cơ sở dữ liệu riêng.
Hình 8. Microservices có database riêng và chúng không thể truy cập trực tiếp đến database của microservices khác
Dưới đây là vài đặc điểm chính khi phát triển việc quản lý dữ liệu phân tán trong kiến trúc microservices.
- Mỗi service có thể có một CSDL riêng biệt để lưu trữ thông tin cần thiết cho nghiệp vụ của service đó.
- Một microservice chỉ có thể truy xuất vào CSDL riêng của nó mà không có quyền truy xuất vào CSDL của microservices khác.
- Trong một số hoàn cảnh, để hoàn thành một tác vụ bạn cần phải cập nhật nhiều CSDL. Khi đó CSDL của microservices khác chỉ nên được cập nhật qua API của những dịch vụ này (không trực tiếp thay đổi CSDL).
Việc phân tách quản lý dữ liệu cho phép bạn hoàn toàn phân tách các microservices và có thể chọn các công nghệ quản lý dữ liệu khác nhau (SQL hay NoSQL,…nhiều CSDL khác nhau cho các microservices). Tuy nhiên, với các giao dịch phức tạp liên quan đến nhiều microservices, giao dịch phải được thực hiện qua API.
Quản trị phân tán
Kiến trúc Microservices ủng hộ quản trị phân tán.
Nhìn chung, quản trị nghĩa là thiết lập và thực thi phương thức mà con người và các giải pháp làm việc cùng nhau để đạt được mục tiêu. Trong ngữ cảnh SOA, quản trị SOA (SOA governance) hướng dẫn việc phát triển các services có thể tái sử dụng, thiết lập cách services được thiết kế, phát triển và cách chúng thay đổi theo thời gian. Nó thiết lập một giao kèo giữa người cung cấp services và người sử dụng services, thông báo cho người sử dụng những thứ họ có thể nhận được và người sử dụng những thứ họ phải cung cấp. Trong quản trị SOA, có hai kiểu quản trị phổ biến:
- Quản trị khi thiết kế – định nghĩa và kiểm soát việc tạo services, thiết kế và thực thi các chính sách của services.
- Quản trị khi triển khai/ chạy – khả năng đảm bảo các chính sách của services trong quá trình hoạt động.
Vậy trong microservices thì quản trị là gì?
Microservices được xây dựng động lập và phân tán với nền tảng công nghệ khác nhau. Do đó, việc thiết lập một tiêu chuẩn thiết kế và phát triển chung là không cần thiết. Chúng ta có thể kết luận về khả năng quản trị phân tán trong kiến trúc microservices như sau:
- Không cần phải có một hệ quản trị tập trung khi thiết kế.
- Microservices có thể tự quyết định thiết kế và phát triển của nó.
- Kiến trúc ủng hộ và hỗ trợ việc chia sẻ các services chung hay có thể tái sử dụng.
- Một số mặt của quản trị trong quá trình hoạt động như SLAs, điều phối, giám sát, bảo mật hay tìm kiếm dịch vụ (service discovery) có thể được phát triển ở cấp API Gateway.
Đăng ký và truy tìm dịch vụ
Đăng ký dịch vụ
Cơ quan đăng ký dịch vụ nắm giữ các phiên bản microservices và vị trí của chúng. Các phiên bản microservice được đăng ký với sổ đăng ký dịch vụ khi khởi động và hủy đăng ký khi tắt máy. Người tiêu dùng có thể tìm thấy các microservices có sẵn và vị trí của chúng thông qua đăng ký dịch vụ.
Truy tìm dịch vụ
Để tìm các microservices có sẵn và vị trí của chúng, chúng ta cần có cơ chế khám phá dịch vụ. Có hai loại cơ chế khám phá dịch vụ, Khám phá phía máy khách và Khám phá phía máy chủ. Chúng ta hãy xem xét kỹ hơn các cơ chế khám phá dịch vụ này:
Khám phá phía máy khách - Trong cách tiếp cận này, máy khách hoặc API-GW có được vị trí của phiên bản dịch vụ bằng cách truy vấn Service Registy
Hình 9. Khám phá phía khách hàng
Khám phá phía máy chủ - Với cách tiếp cận này, máy khách/ API-GW sẽ gửi yêu cầu đến một thành phần (chẳng hạn như bộ cân bằng tải) chạy trên một vị trí nổi tiếng. Thành phần đó gọi sổ đăng ký dịch vụ và xác định vị trí tuyệt đối của microservice.
Hình 10. Khám phá phía khách hàng
Các giải pháp triển khai microservices, chẳng hạn như Kubernetes (http://kubernetes.io/v1.1/docs/user-guide/services.html), cung cấp cơ chế khám phá phía dịch vụ
Triển khai (Deployment)
Việc triển khai các dịch vụ có một trò trọng yếu và gồm các yêu cầu sau:
- Khả năng triển khai/ gỡ xuống độc lập mà không ảnh hưởng đến dịch vụ khác.
- Có thể mở rộng theo cấp microservices, chỉ mở rộng microservices cần thiết.
- Phát triển và triển khai microservices nhanh chóng.
- Một microservice ngắt kết nối hay sập thì không ảnh hưởng các dịch vụ khác.
Docker (một công cụ mã nguồn mở cho phép lập trình viên và quản trị viên hệ thống triển khai các ứng dụng thành các containers trên môi trường Linux) cung cấp một công cụ tuyệt vời để triển khai microservices đáp ứng đủ các yêu cầu trên. Các bước chính gồm:
- Đóng gói mỗi microservice thành một ảnh Docker (docker image).
- Triển khai mỗi thực thể của service là một Docker container.
- Mở rộng dựa vào số lượng thực thể.
- Phát triển, triển khai và khởi động microservices trở nên nhanh hơn với Docker (nhanh hơn nhiều các máy ảo thông thường – VM).
Kubernetes mở rộng khả năng của Docker bằng cách quản lý một cụm các Linux container như một hệ thống duy nhất, quản lý và chạy các Docker containers trên nhiều hosts, cung cấp service discovery và kiểm soát việc nhân rộng. Như bạn có thể thấy, những tính năng này cần thiết cho kiến trúc microservices. Vì vậy sử dụng Kurbernetes (trên nền Docker) để triển khai microservices đang trở thành một phương pháp cực kì hữu dụng, đặc biệt với ứng dụng lớn.
Hình 11: Xây dựng và triển khai microservices như các containers
Mỗi thực thể của microservices được triển khai thành một container và có hai containers trên mỗi host. Bạn có thể thay đổi số lượng containers chạy trên một host theo ý mình.
Bảo mật (Security)
Bảo mật microservcies là một yêu cầu phổ biến trong thực tế. Trước khi nói đến bảo mật microservices, hãy nhìn lại cách chúng ta thường thực hiện bảo mật của ứng dụng monolithic.
- Bảo mật của một ứng dụng monolithic thường là về xác định xem “Ai là người gọi đến”, “Người này có quyền hạn gì” và “Ứng dụng thông báo về những thông tin này thế nào”.
- Một component phụ trách bảo mật chung thường nằm ở đầu chuỗi xử lý yêu cầu và component sẽ trả về thông tin cần thiết.
Vậy chúng ta có thể chuyển đổi trực tiếp mô hình này sang kiến trúc microservices không?
Có nhưng điều này yêu cầu phát triển bảo mật ở từng microservices và kết nối với một cơ sở trung tâm về người dùng để lấy thông tin cần thiết. Đây là một cách khá rối rắm. Thay vào đó, chúng ta có thể tận dụng các API bảo mật tiêu chuẩn như OAuth2 và OpenID Connect để tìm một giải pháp tốt hơn cho vấn đề bảo mật. Trước khi nói sâu hơn, chúng ta sẽ tóm tắt mục tiêu của mỗi tiêu chuẩn và cách sử dụng chúng.
- OAuth2 – một phương thức chứng thực kiểu ủy quyền. Client xác thực với server cấp quyền (authorization server) và nhận một token gọi là “Access token”. Access token không chứa bất kì thông tin gì về client. Nó chỉ là một tấm vé tham chiếu đến thông tin người dùng mà server cấp quyền có thể truy xuất đến. Do đó, đây cũng được gọi là token kiểu tham chiếu “by-reference token” và an toàn để sử dụng trên mạng lưới mở và internet.
- OpenID Connect hoạt động tương tự OAuth nhưng ngoài Access Token, server cấp quyền còn phát một ID token chứa thông tin người dùng. Token này thường dạng JWT (JSON Web Token) và được kí bởi server cấp quyền. JWT token do đó được coi là token kiểu tham trị “by-value token” bởi vì nó chứa thông tin về người dùng và có thể trở nên không an toàn.
Bây giờ, hãy xem xét những tiêu chuẩn này có thể bảo mật microservices trong ví dụ bán hàng của chúng ta như thế nào.
Hình 12: Bảo mật microservices với OAuth2 và OpenID Connect
- Như hình trên, các bước chính để thực hiện bảo mật microservices gồm:
- Chuyển việc xác thực cho server dạng OAuth và OpenID Connect (Authorization server).
- Sử dụng API Gateway để có một điểm đầu vào duy nhất cho yêu cầu từ clients.
- Client kết nối với server cấp quyền và nhận Access token. Sau đó gửi token này đến API Gateway cùng với yêu cầu.
- Dịch token tại Gateway – API Gateway lấy ra access token và gửi đến server cấp quyền để lấy JWT.
- Gateway truyền JWT cùng với yêu cầu đến microservices.
- JWT chứa thông tin người dùng cần thiết để lưu user sessions,….
- Ở mỗi lớp microservice, bạn có thể có một component xử lý JWT, việc này khác đơn giản.
Các giao dịch (Transactions)
Làm thế nào về hỗ trợ giao dịch trong microservices? Trên thực tế, hỗ trợ các giao dịch phân tán trên nhiều microservices là một nhiệm vụ đặc biệt phức tạp. Bản thân kiến trúc microservice khuyến khích sự phối hợp ít giao dịch giữa các dịch vụ.
Ý tưởng là một dịch vụ nhất định hoàn toàn khép kín và dựa trên nguyên tắc trách nhiệm duy nhất. Nhu cầu có các giao dịch phân tán trên nhiều microservice thường là dấu hiệu của một lỗ hổng thiết kế trong kiến trúc microservice và thường có thể được sắp xếp bằng cách tái cấu trúc phạm vi của microservice. Tuy nhiên, nếu có yêu cầu bắt buộc phải có các giao dịch phân tán trên nhiều dịch vụ, thì các kịch bản như vậy có thể được thực hiện với việc giới thiệu 'hoạt động bù đắp' ở mỗi cấp dịch vụ nhỏ. Ý tưởng chính là, một dịch vụ vi mô nhất định dựa trên nguyên tắc trách nhiệm duy nhất và nếu một dịch vụ vi mô cụ thể không thực hiện được một hoạt động nhất định, chúng ta có thể coi đó là sự thất bại của toàn bộ dịch vụ vi mô đó. Sau đó, tất cả các hoạt động (ngược dòng) khác phải được hoàn tác bằng cách gọi hoạt động bù tương ứng của các microservices đó.
Thiết kế cho sự thất bại (Design for Failures)
Kiến trúc microservices tăng khả năng xảy ra thất bại tại microservices. Một microservice có thể sập do một vấn đề mạng, nguồn tài nguyên bị thiếu hụt hoặc không sẵn sàng,… Một microservice không đáp ứng lại thì không nên dẫn đến toàn bộ ứng dụng microservices sập. Do đó, mỗi microservices nên có khả năng chịu lỗi và hồi phục khi có thể. Người dùng phải có trải nghiệm tốt, nhẹ nhàng và không nhận ra lỗi bên trong hệ thống.
Thêm nữa, vì các dịch vụ có thể gặp lỗi bất cứ lúc nào, việc phát hiện (giám sát thời gian thực) lỗi nhanh chóng và nếu có thể tự động phục hồi dịch vụ là cực kì quan trọng.
Có một vài kiểu mẫu xử lý lỗi trong microservices phổ biến.
Circuit Breaker
Khi có lời gọi từ bên ngoài đến một microservice, bạn có thể cấu hình một component giám sát lỗi với mỗi lời gọi. Component này đếm số yêu cầu thành công và thất bại, khi lỗi đạt một giới hạn nhất định thì component này sẽ dừng hoạt động của service (ngắt giao mạch).
Cách này hữu dụng để tránh tiêu tốn tài nguyên không cần thiết, yêu cầu bị chậm vì timeouts, cách này cũng giúp giám sát hệ thống.
Bulkhead
Vì một ứng dụng kiến trúc microservices bao gồm nhiều microservices, một service dừng hoạt động không nên ảnh hưởng đến phần còn lại. Kiểu mẫu bulkhead tách biệt các phần của ứng dụng để khi có lỗi tại một nơi nào đó thì không ảnh hưởng hệ thống.
Timeout
Kiểu mẫu timeout là một quy trình cho phép dừng một yêu cầu khi thời gian chờ đợi vượt quá hạn mức. Bạn có thể cấu hình khung thời gian tùy ý.
Vậy khi nào chúng ta sử dụng những kiểu mẫu này?
Trong phần lớn trường hợp, những kiểu mẫu này được áp dụng ở lớp Gateway. Khi microservices không đáp lại hay không sẵn sàng, ở cấp Gateway chúng ta có thể quyết định có nên gửi yêu cầu đến microservices sử dụng circuit breakers hay timeout. Hơn nữa, sử dụng bulkhead ở Gateway khá quan trọng vì đây là một điểm đầu vào cho tất cả yêu cầu từ clients, nên nếu lỗi một service cũng không ảnh hưởng đến việc gửi yêu cầu đến các services khác.
Thêm vào đó, Gateway có thể được sử dụng như một điểm trung tâm ta có thể lấy thông tin về trạng thái và theo dõi các microservices hoạt động.
Microservices, Tích hợp doanh nghiệp, Quản lý API và các ứng dụng khác.
Chúng ta đã trao đổi các đặc tính của kiến trúc Microservices và cách bạn có thể thực hiện chúng. Tuy nhiên, bạn nên lưu ý rằng Microservices không phải toa thuốc chữa bách bệnh. Việc áp dụng mù quáng những khái niệm đang nổi sẽ không xử lý vấn đề thực tế của bạn. Nếu bạn đã đọc toàn bộ bài này thì bạn sẽ thấy Microservices có nhiều lợi ích và chúng ta nên tận dụng. Tuy nhiên, bạn cần hiểu rằng mong chờ microservices giải quyết tất cả các vấn đề là không thực tiễn.
Lời kết
Qua bài này chúng ta có thể hiểu được Microservices là gì, microservices giúp bạn được những gì, cách để chia nhỏ các microservices, cách để tích hợp và tương tác giữa các microservices với nhau, cách để bảo mật khi triển khai ứng dụng với microservices, những công cụ hỗ trợ để giúp bạn triển khai ứng dụng microservices dễ hơn.
Hy vọng điều này cung cấp cho bạn một ý tưởng rõ ràng hơn về cách bạn có thể sử dụng Microservices trong doanh nghiệp của mình.
Tổng hợp việc làm IT - Software trên VietnamWorks
VietnamWorks InTECH
Theo Dzone
TẠO TÀI KHOẢN MỚI: XEM FULL “1 TÁCH CODEFEE” - NHẬN SLOT TƯ VẤN CV TỪ CHUYÊN GIA - CƠ HỘI RINH VỀ VOUCHER 200K