Một hệ thống đang chạy ổn định.
Đột nhiên máy chủ MySQL gặp sự cố.
Nếu muốn ứng dụng hoạt động trở lại, đội vận hành phải:
Nếu quy trình này nghe quen thuộc, rất có thể ứng dụng của bạn đang phụ thuộc chặt (Tightly Coupled) vào hạ tầng.
Đây chính là điều mà Yếu tố thứ 4 của 12-Factor App muốn loại bỏ:
Nói cách khác, mọi dịch vụ bên ngoài chỉ nên là "tài nguyên gắn kèm", không phải một phần cố định của ứng dụng.
Backing Service là gì?
Hầu như mọi ứng dụng hiện đại đều phải kết nối tới các dịch vụ bên ngoài.
Ví dụ:
Đây đều được gọi là Backing Services.
Ứng dụng cần chúng để hoạt động, nhưng không nên phụ thuộc vào một máy chủ hay một nhà cung cấp cụ thể.
Điều gì nên nằm trong Configuration?
Mỗi Backing Service thường yêu cầu các thông tin như:
Những thông tin này không nên xuất hiện trong source code.
Thay vào đó, chúng nên được lưu trong:
Điều này cho phép cùng một ứng dụng kết nối tới các dịch vụ khác nhau chỉ bằng cách thay đổi cấu hình.
Thay Database mà không cần sửa Code
Hãy tưởng tượng hôm nay ứng dụng kết nối tới:
MySQL Server nội bộ
Ngày mai doanh nghiệp quyết định chuyển sang:
Amazon RDS MySQL
Hoặc:
Azure Database for MySQL
Hoặc một cụm MySQL High Availability mới.
Nếu ứng dụng được thiết kế đúng theo 12-Factor App, việc chuyển đổi chỉ cần cập nhật:
Ứng dụng vẫn sử dụng cùng một giao thức, cùng một driver và không cần sửa một dòng source code nào.
Đây là một lợi ích rất lớn khi doanh nghiệp di chuyển hạ tầng từ On-Premises lên Cloud hoặc thay đổi nhà cung cấp dịch vụ.
Tightly Coupled vs Loosely Coupled
Hãy xem hai cách thiết kế khác nhau.
Thiết kế phụ thuộc chặt (Tightly Coupled):
Application
│
▼
MySQL Server A
Nếu MySQL Server A gặp sự cố:
Thiết kế phụ thuộc lỏng (Loosely Coupled):
Application
│
Configuration
│
▼
Backing Service
Ứng dụng chỉ biết đọc thông tin kết nối từ cấu hình. Việc thay đổi máy chủ hoặc nhà cung cấp dịch vụ chỉ cần cập nhật cấu hình mà không phải thay đổi logic của chương trình.
High Availability trở nên đơn giản hơn
Giả sử Database chính gặp sự cố.
Nếu ứng dụng phụ thuộc trực tiếp vào địa chỉ của máy chủ đó, bạn sẽ phải:
Trong khi đó, nếu tuân thủ nguyên tắc Backing Services, bạn chỉ cần:
Ứng dụng tiếp tục hoạt động mà không cần thay đổi mã nguồn. Điều này giúp giảm thời gian gián đoạn và tăng khả năng sẵn sàng của hệ thống.
Cloud Native được xây dựng trên nguyên tắc này
Hầu hết các nền tảng Cloud hiện đại đều giả định rằng ứng dụng có thể kết nối linh hoạt tới các dịch vụ bên ngoài.
Ví dụ:
Ứng dụng không cần biết dịch vụ đang chạy ở đâu, miễn là thông tin kết nối được cung cấp qua cấu hình.
Lợi ích trong DevSecOps
Khi Backing Services được coi là các tài nguyên gắn kèm thay vì thành phần cố định của ứng dụng, doanh nghiệp sẽ nhận được nhiều lợi ích:
Đây là một nguyên tắc cốt lõi của Cloud Native và DevSecOps: xây dựng ứng dụng theo hướng loosely coupled, để hạ tầng có thể thay đổi mà không kéo theo việc sửa đổi ứng dụng. Khi ứng dụng được thiết kế theo cách này, việc mở rộng, thay thế dịch vụ hay chuyển đổi giữa On-Premises và Cloud sẽ trở nên nhanh chóng, an toàn và ít rủi ro hơn.
Đột nhiên máy chủ MySQL gặp sự cố.
Nếu muốn ứng dụng hoạt động trở lại, đội vận hành phải:
- Sửa source code.
- Build lại ứng dụng.
- Triển khai phiên bản mới.
- Khởi động lại toàn bộ hệ thống.
Nếu quy trình này nghe quen thuộc, rất có thể ứng dụng của bạn đang phụ thuộc chặt (Tightly Coupled) vào hạ tầng.
Đây chính là điều mà Yếu tố thứ 4 của 12-Factor App muốn loại bỏ:
Treat Backing Services as Attached Resources.
Nói cách khác, mọi dịch vụ bên ngoài chỉ nên là "tài nguyên gắn kèm", không phải một phần cố định của ứng dụng.
Backing Service là gì?
Hầu như mọi ứng dụng hiện đại đều phải kết nối tới các dịch vụ bên ngoài.
Ví dụ:
- MySQL
- PostgreSQL
- Microsoft SQL Server
- Redis
- Memcached
- RabbitMQ
- Kafka
- SMTP Server (Postfix)
- LDAP
- Auth0
- Azure Entra ID
- Amazon S3
- Elasticsearch
Đây đều được gọi là Backing Services.
Ứng dụng cần chúng để hoạt động, nhưng không nên phụ thuộc vào một máy chủ hay một nhà cung cấp cụ thể.
Điều gì nên nằm trong Configuration?
Mỗi Backing Service thường yêu cầu các thông tin như:
- URL hoặc Hostname
- Port
- Username
- Password
- API Key
- Access Token
Những thông tin này không nên xuất hiện trong source code.
Thay vào đó, chúng nên được lưu trong:
- Environment Variables
- Secret Manager
- Configuration Service
- Vault
Điều này cho phép cùng một ứng dụng kết nối tới các dịch vụ khác nhau chỉ bằng cách thay đổi cấu hình.
Thay Database mà không cần sửa Code
Hãy tưởng tượng hôm nay ứng dụng kết nối tới:
MySQL Server nội bộ
Ngày mai doanh nghiệp quyết định chuyển sang:
Amazon RDS MySQL
Hoặc:
Azure Database for MySQL
Hoặc một cụm MySQL High Availability mới.
Nếu ứng dụng được thiết kế đúng theo 12-Factor App, việc chuyển đổi chỉ cần cập nhật:
- Host
- Port
- Username
- Password
Ứng dụng vẫn sử dụng cùng một giao thức, cùng một driver và không cần sửa một dòng source code nào.
Đây là một lợi ích rất lớn khi doanh nghiệp di chuyển hạ tầng từ On-Premises lên Cloud hoặc thay đổi nhà cung cấp dịch vụ.
Tightly Coupled vs Loosely Coupled
Hãy xem hai cách thiết kế khác nhau.
Thiết kế phụ thuộc chặt (Tightly Coupled):
Application
│
▼
MySQL Server A
Nếu MySQL Server A gặp sự cố:
- Ứng dụng có thể ngừng hoạt động.
- Việc chuyển sang máy chủ khác có thể yêu cầu sửa mã nguồn, cấu hình lại và triển khai lại.
Thiết kế phụ thuộc lỏng (Loosely Coupled):
Application
│
Configuration
│
▼
Backing Service
Ứng dụng chỉ biết đọc thông tin kết nối từ cấu hình. Việc thay đổi máy chủ hoặc nhà cung cấp dịch vụ chỉ cần cập nhật cấu hình mà không phải thay đổi logic của chương trình.
High Availability trở nên đơn giản hơn
Giả sử Database chính gặp sự cố.
Nếu ứng dụng phụ thuộc trực tiếp vào địa chỉ của máy chủ đó, bạn sẽ phải:
- Dừng ứng dụng.
- Sửa cấu hình trong mã nguồn hoặc gói triển khai.
- Build lại nếu cần.
- Triển khai lại.
- Khởi động lại hệ thống.
Trong khi đó, nếu tuân thủ nguyên tắc Backing Services, bạn chỉ cần:
- Chuyển sang một Database Replica hoặc Cluster khác.
- Cập nhật cấu hình hoặc để hệ thống High Availability tự động chuyển đổi.
Ứng dụng tiếp tục hoạt động mà không cần thay đổi mã nguồn. Điều này giúp giảm thời gian gián đoạn và tăng khả năng sẵn sàng của hệ thống.
Cloud Native được xây dựng trên nguyên tắc này
Hầu hết các nền tảng Cloud hiện đại đều giả định rằng ứng dụng có thể kết nối linh hoạt tới các dịch vụ bên ngoài.
Ví dụ:
- Kubernetes sử dụng ConfigMap và Secret để quản lý cấu hình.
- Docker truyền thông tin qua Environment Variables.
- AWS cung cấp RDS, ElastiCache, SQS, Secrets Manager.
- Azure cung cấp Azure SQL, Azure Cache for Redis, Service Bus, Key Vault.
Ứng dụng không cần biết dịch vụ đang chạy ở đâu, miễn là thông tin kết nối được cung cấp qua cấu hình.
Lợi ích trong DevSecOps
Khi Backing Services được coi là các tài nguyên gắn kèm thay vì thành phần cố định của ứng dụng, doanh nghiệp sẽ nhận được nhiều lợi ích:
- Cùng một artifact có thể triển khai trên Development, Testing, Staging và Production.
- Thay đổi nhà cung cấp dịch vụ hoặc hạ tầng mà không cần sửa mã nguồn.
- Hỗ trợ High Availability và Disaster Recovery tốt hơn.
- Giảm sự phụ thuộc giữa ứng dụng và hạ tầng, giúp kiến trúc linh hoạt hơn.
- Đơn giản hóa quy trình CI/CD vì Pipeline chỉ cần thay đổi cấu hình trong quá trình triển khai.
Đây là một nguyên tắc cốt lõi của Cloud Native và DevSecOps: xây dựng ứng dụng theo hướng loosely coupled, để hạ tầng có thể thay đổi mà không kéo theo việc sửa đổi ứng dụng. Khi ứng dụng được thiết kế theo cách này, việc mở rộng, thay thế dịch vụ hay chuyển đổi giữa On-Premises và Cloud sẽ trở nên nhanh chóng, an toàn và ít rủi ro hơn.