The Twelve Factor App: A Complete Guide

10 min read

12 factor app

Trong kỷ nguyên số, khi các doanh nghiệp đẩy mạnh quá trình chuyển đổi số lên đám mây, việc phát triển ứng dụng đã không còn đơn thuần như trước. Đặc biệt, sự trỗi dậy của kiến trúc Microservice đã đặt ra nhiều thách thức mới cho các nhà phát triển. Để đối phó với những vấn đề này, một tập hợp các nguyên tắc mạnh mẽ đã ra đời: 12-Factor App. Bộ quy tắc này mang đến một lộ trình rõ ràng để xây dựng các ứng dụng linh hoạt và mạnh mẽ trong môi trường điện toán đám mây.

Các ứng dụng hoạt động trong môi trường đám mây phải đối mặt với những ràng buộc đặc thù như độ trễ mạng cao hơn, băng thông và tốc độ có thể bị giới hạn.12-Factor App được thiết kế để giúp các ứng dụng đáp ứng những ràng buộc này, từ cách chúng ta quản lý mã nguồn, triển khai, cho đến cách chúng ta tận dụng các dịch vụ hệ thống. Mặc dù ban đầu được áp dụng cho các ứng dụng đám mây nói chung, nhưng do microservice cũng có thể được coi là một dạng ứng dụng đám mây, nên các nguyên tắc này cực kỳ phù hợp và thường được áp dụng chung cho cả hai.

1. Codebase

Nguyên tắc đầu tiên và cũng là cơ bản nhất là Codebase. Mỗi ứng dụng hoặc microservice cần sở hữu một codebase riêng biệt, được quản lý chặt chẽ bằng hệ thống kiểm soát phiên bản (version control), với Git là lựa chọn phổ biến hàng đầu hiện nay. Codebase này sau đó sẽ được sử dụng để triển khai xuyên suốt các môi trường khác nhau như kiểm thử, staging và production.

Trong kiến trúc microservice, nơi nhiều dịch vụ độc lập kết hợp tạo nên một hệ thống phân tán, việc mỗi microservice sở hữu codebase riêng là tối quan trọng. Việc hai hoặc nhiều ứng dụng/microservice cùng chia sẻ một codebase hoặc repository là điều cần tránh, vì nó có thể dẫn đến xung đột mã không cần thiết và cản trở sự phát triển độc lập của từng team phát triển.

2. Dependencies: Tự Chủ Trong Quản Lý Thư Viện

Dependencies quy định rằng mỗi microservice phải tự mình quản lý các gói (packages) và thư viện mà nó sử dụng. Trong quá trình phát triển ứng dụng, việc sử dụng các thư viện sẵn có là luôn xảy ra, và chúng thường được quản lý bởi một trình quản lý thư viện, sau đó được tải về và đóng gói cùng với ứng dụng khi thực hiện quá trình Build.

Trách nhiệm quản lý và sử dụng các thư viện này thuộc về chính microservice đó. Phương pháp này giúp ngăn chặn các xung đột phiên bản không đáng có, vốn có thể xảy ra nếu hai ứng dụng khác nhau cùng sử dụng một thư viện nhưng lại yêu cầu các phiên bản khác nhau.

3. Configuration

Nguyên tắc thứ ba, Configuration, nhấn mạnh rằng thông tin cấu hình của một ứng dụng cần được quản lý bên ngoài microservice. Việc nhúng trực tiếp cấu hình vào mã nguồn là điều không nên.

Lý do là các microservice có thể được tự động tải xuống và thực thi một cách tự động. Nếu cấu hình nằm bên trong, bạn sẽ phải điều chỉnh thủ công, điều này cực kỳ tốn thời gian và không thể tự động hóa. Thay vào đó, thông tin cấu hình nên được truy cập thông qua các biến môi trường (environment variables), các tham số khi thiết lập container, hoặc các dịch vụ quản lý cấu hình trên các nền tảng đám mây như Azure hay AWS.

4. Backing Services

Backing Services là những dịch vụ bên ngoài mà ứng dụng của bạn cần để hoạt động. Các ví dụ điển hình bao gồm SQL Server, MySQL, Redis.

Nguyên tắc này khuyến nghị rằng các dịch vụ hỗ trợ nên được tách biệt và chạy bên ngoài microservice. Mặc dù bạn có thể cài đặt và sử dụng các dịch vụ này ngay bên trong microservice, nhưng điều này sẽ hạn chế khả năng truy cập của các dịch vụ khác một cách không cần thiết. Cách tốt nhất là để các dịch vụ này chạy độc lập, được cung cấp bởi các dịch vụ đám mây hoặc như các microservice khác, và được truy cập thông qua cấu hình bên ngoài.

5. Build, Release, Run: Quy Trình Triển Khai Ba Bước

Nguyên tắc Build, Release, Run định nghĩa một quy trình rõ ràng và tách biệt để đưa một mã nguồn vào sử dụng:

  • Build: Bước này chuyển đổi mã nguồn thành mã chạy được, đóng gói tất cả các tệp dữ liệu, tệp cấu hình và thư viện cần thiết.
  • Release: Lấy kết quả từ bước Build và kết hợp với thông tin cấu hình cụ thể để tạo ra một bản release. Mỗi release phải có một ID duy nhất. Bất kỳ thay đổi nào cũng đòi hỏi tạo một bản phát hành mới.
  • Run: Thực thi bản release đã tạo ra.

Điều quan trọng là nguyên tắc này không có khái niệm “undo” (xóa bỏ một bản phát hành). Điều đó có nghĩa là thông tin về một bản phát hành có vấn đề vẫn được giữ lại để tiện cho việc điều tra, gỡ lỗi. Tuy nhiên, nó hỗ trợ “rollback”. Rollback cho phép bạn quay trở lại một phiên bản cũ hơn, ổn định hơn nếu bản phát hành mới gặp sự cố, mà không làm mất thông tin về bản lỗi.

6. Processes

Nguyên tắc Processes quy định rằng mỗi microservice phải được thực thi trong một tiến trình riêng biệt và phải không có trạng thái (stateless). “Stateless” có nghĩa là tiến trình đó không lưu trữ bất kỳ dữ liệu hoặc thông tin trạng thái nào bên trong nó. Ví dụ, bạn không bao giờ nên lưu dữ liệu trực tiếp vào một tệp tin bên trong microservice đang chạy.

Điều này cực kỳ quan trọng vì các microservice có thể được khởi tạo, dừng lại hoặc mở rộng (scale) bất cứ lúc nào. Nếu trạng thái được lưu trữ nội bộ, nó sẽ không đáng tin cậy và không thể chia sẻ giữa nhiều instance. Thay vào đó, tất cả trạng thái phải được lưu trữ trong các dịch vụ hỗ trợ bên ngoài, như MongoDB, SQL Server hoặc Redis. Việc lưu trữ trạng thái bên ngoài đảm bảo rằng nó có thể truy cập được bởi tất cả các instance của microservice và không bị mất nếu một microservice đang chạy bị kết thúc.

7. Port Binding

Nguyên tắc Port Binding đề cập đến cách các microservice chia sẻ các dịch vụ của mình ra bên ngoài. Nó quy định rằng các dịch vụ do một microservice cung cấp sẽ được truy cập thông qua cơ chế liên kết cổng (port binding). Cơ chế này cho phép bạn ánh xạ một cổng nội bộ bên trong microservice ra một cổng bên ngoài mà các dịch vụ khác có thể kết nối đến. Giá trị của cổng nội bộ và cổng bên ngoài có thể khác nhau, mang lại sự linh hoạt trong cấu hình mạng.

8. Concurrency

Nguyên tắc Concurrency hướng dẫn cách mở rộng khả năng phục vụ của ứng dụng. Mở rộng theo chiều ngang có nghĩa là bạn triển khai thêm nhiều instance của một microservice để cùng xử lý công việc, phân tán tải. Điều này trái ngược với mở rộng theo chiều dọc, tức là nâng cấp tài nguyên (RAM, CPU, ổ đĩa) của một máy chủ duy nhất.

Mở rộng theo chiều ngang mang lại khả năng mở rộng (cả tăng và giảm) một cách dễ dàng, chỉ bị giới hạn bởi ngân sách. Ngược lại, mở rộng theo chiều dọc bị hạn chế nghiêm trọng bởi giới hạn công nghệ của một máy tính. Để ứng dụng có khả năng mở rộng theo chiều ngang, điều này cần được cân nhắc ngay từ khi thiết kế.

9. Disposability

Nguyên tắc Disposability nhấn mạnh rằng các tiến trình của microservice phải được thiết kế để có thể khởi động nhanh chóng và tắt một cách gọn gàng. Điều này có nghĩa là một microservice phải sẵn sàng nhận các yêu cầu ngay lập tức sau khi khởi động và có thể dừng lại mà không làm mất dữ liệu hay ảnh hưởng đến các giao dịch đang diễn ra.

Trong môi trường điện toán đám mây, các ứng dụng thường được khởi tạo hoặc kết thúc một cách tự động để đáp ứng sự thay đổi về tải trọng hoặc để phục hồi sau sự cố. Nếu một microservice mất nhiều thời gian để khởi động hoặc không thể tắt một cách sạch sẽ, nó sẽ làm giảm khả năng phục hồi và mở rộng của toàn bộ hệ thống.

10. Dev/Prod Parity

Nguyên tắc Dev/Prod Parity khuyến khích giữ cho môi trường phát triển (development), kiểm thử (staging), và sản xuất (production) càng giống nhau càng tốt. Mục tiêu là giảm thiểu sự khác biệt giữa các môi trường để tránh những lỗi “hoạt động tốt trên máy tôi” (it works on my machine).

Sự khác biệt giữa các môi trường có thể dẫn đến các vấn đề không lường trước được khi ứng dụng được triển khai lên production. Để đạt được sự đồng nhất này, các đội phát triển nên sử dụng cùng một loại cơ sở dữ liệu, dịch vụ cache, message queues và các dịch vụ hỗ trợ khác ở tất cả các môi trường. Việc sử dụng các công cụ ảo hóa như Docker hoặc các dịch vụ đám mây có thể giúp ích rất nhiều trong việc duy trì tính đồng nhất này, đảm bảo rằng mã nguồn và cấu hình hoạt động nhất quán ở mọi giai đoạn của vòng đời ứng dụng.

11. Logs

Nguyên tắc này quy định rằng các ứng dụng không nên tự mình quản lý việc lưu trữ hoặc định tuyến Logs. Thay vào đó, logs cần được xem như một event stream và được xuất ra stdout hoặc stderr.

Logs là nguồn thông tin quan trọng để theo dõi hành vi của ứng dụng, gỡ lỗi và phân tích hiệu suất. Khi nhật ký được xuất ra stdout/stderr, hệ thống quản lý nhật ký tập trung có thể thu thập, tổng hợp, lưu trữ và phân tích chúng một cách hiệu quả. Điều này cho phép các nhà phát triển dễ dàng tìm kiếm và phân tích nhật ký từ hàng trăm hoặc hàng nghìn microservice mà không cần phải truy cập từng máy chủ riêng lẻ.

12. Admin Processes

Cuối cùng, nguyên tắc Admin Processes đề cập đến các one-off admin tasks. Các tác vụ này, chẳng hạn như chạy migration cơ sở dữ liệu, chạy script tùy chỉnh, hoặc kiểm tra dữ liệu, nên được thực thi trong môi trường tương tự như các tiến trình ứng dụng thông thường.

Điều này có nghĩa là các tác vụ quản trị nên được thực hiện thông qua các công cụ hoặc script đi kèm với codebase của ứng dụng, và được triển khai cùng với ứng dụng. Chúng nên truy cập các dịch vụ hỗ trợ và cấu hình giống như ứng dụng, đảm bảo tính nhất quán và giảm thiểu rủi ro lỗi do khác biệt môi trường.

Tóm Lược

Các nguyên tắc này là nền tảng vững chắc để thiết kế các ứng dụng phù hợp với môi trường đám mây và đặc biệt là kiến trúc microservice. Hẹn gặp lại các bạn ở các bài viết tiếp theo.

Avatar photo

Leave a Reply

Your email address will not be published. Required fields are marked *