Tại sao ứng dụng hiện đại không thể chỉ chạy trên một server? Góc nhìn DevOps về kiến trúc ứng dụng phân tán.
Có một thời việc triển khai ứng dụng khá đơn giản. Chúng ta viết code, build thành một chương trình binary, cài cái ứng dụng đó lên một server, cho người dùng kết nối vào là xong. Mô hình này gọi là monolithic application. Với những ứng dụng nhỏ hoặc ít quan trọng, cách tiếp cận này vẫn hoạt động ổn. Nhưng với hệ thống hiện đại, đây gần như là một giới hạn lớn.
Vấn đề đầu tiên là điểm chết đơn - single point of failure. Nếu máy chủ server duy nhất đó bị lỗi phần cứng, sập hệ điều hành, đầy ỏ cứng harddisk, hoặc tiến trình ứng dụng treo, toàn bộ dịch vụ sẽ dừng hoạt động. Giải pháp của chúng ta không có phương án dự phòng. Không có failover. Không có tính sẵn sàng cao. Đây là lý do ngành công nghệ chuyển sang mô hình distributed application architecture.
Một bước tiến đầu tiên là kiến trúc three-tier. Thay vì nhét mọi thứ vào cùng một máy, hệ thống được chia thành nhiều tầng rõ ràng. Presentation tier xử lý giao diện hoặc còn gọi là frontend. Business tier chứa logic xử lý nghiệp vụ. Data tier quản lý cơ sở dữ liệu. Việc tách này không chỉ giúp tổ chức hệ thống tốt hơn mà còn cải thiện hiệu năng đáng kể. (Các bạn quen thuộc với Web-App-Database, nhưng bài viết này chọn giữ nguyên các thuật ngữ hàn lâm một chút).
Hãy tưởng tượng nếu database và application cùng chạy trên một server. Khi database tăng tải, CPU, RAM và I/O sẽ bị tranh chấp trực tiếp với ứng dụng. Kết quả là toàn hệ thống chậm đi. Khi tách riêng từng tầng, mỗi thành phần có thể scale độc lập. Ví dụ khác là nếu frontend cần thêm công suất? Scale thêm web nodes. Business logic bị bottleneck? Thêm application instances. Database quá tải? Xem xét replication hoặc clustering. Đây chính là tư duy nền tảng của kiến trúc phân tán hiện đại.
Một bước tiến tiếp theo là microservices architecture. Thay vì business logic là một khối lớn, ứng dụng được chia thành nhiều service nhỏ. Authentication service, payment service, inventory service, notification service… mỗi thành phần có lifecycle riêng, deploy riêng, scale riêng. Nhưng khi số lượng instance tăng lên, một câu hỏi xuất hiện: traffic sẽ đi về đâu? Cách đơn giản đầu tiên là chúng ta có thể dùng DNS load balancing. DNS trả về nhiều địa chỉ IP cho cùng một hostname như app.company.com. Client tự chọn IP để kết nối. Ban đầu, khi nghe thì chúng ta thấy có vẻ hợp lý, nhưng thực tế có vấn đề. Nếu một backend server chết thì sao? Liệu client có đủ thông minh để bỏ qua IP đó và thử IP khác? Không phải client nào cũng xử lý tốt tình huống này. Đó là lý do load balancer trở thành thành phần cốt lõi trong kiến trúc hiện đại.
Load balancer đứng trước các backend servers, nhận toàn bộ traffic từ client rồi quyết định phân phối lưu lượng theo nhiều chiến lược như Round Robin, Least Connections, Fastest Response, Session Persistence (sticky session). Một số bạn trong cộng đồng chúng ta đã quen thuộc với các thuật toán chia tải này.
Quan trọng hơn, load balancer còn thực hiện health checks. Nếu một backend không phản hồi, nó tự động loại node đó khỏi pool.
Đây là thứ giúp người dùng “không cảm nhận được sự cố”. Load balancer có thể là thiết bị chuyên dụng hoặc software-based như:
Thông thường sẽ có Active-Active: nhiều node cùng xử lý traffic. Hoặc Active-Passive: một node chính hoạt động, node dự phòng chờ takeover khi có lỗi. Phần backend database cũng tương tự. Một database server đơn lẻ là rủi ro cực lớn. Vì vậy các hệ thống hiện đại dùng replication hoặc clustering với:
Nếu nhìn từ góc độ DevOps, đây chính là nền móng của các nền tảng cloud-native hiện nay. Kubernetes thực chất đang hiện thực hóa tư duy này:
Có một thời việc triển khai ứng dụng khá đơn giản. Chúng ta viết code, build thành một chương trình binary, cài cái ứng dụng đó lên một server, cho người dùng kết nối vào là xong. Mô hình này gọi là monolithic application. Với những ứng dụng nhỏ hoặc ít quan trọng, cách tiếp cận này vẫn hoạt động ổn. Nhưng với hệ thống hiện đại, đây gần như là một giới hạn lớn.
Vấn đề đầu tiên là điểm chết đơn - single point of failure. Nếu máy chủ server duy nhất đó bị lỗi phần cứng, sập hệ điều hành, đầy ỏ cứng harddisk, hoặc tiến trình ứng dụng treo, toàn bộ dịch vụ sẽ dừng hoạt động. Giải pháp của chúng ta không có phương án dự phòng. Không có failover. Không có tính sẵn sàng cao. Đây là lý do ngành công nghệ chuyển sang mô hình distributed application architecture.
Một bước tiến đầu tiên là kiến trúc three-tier. Thay vì nhét mọi thứ vào cùng một máy, hệ thống được chia thành nhiều tầng rõ ràng. Presentation tier xử lý giao diện hoặc còn gọi là frontend. Business tier chứa logic xử lý nghiệp vụ. Data tier quản lý cơ sở dữ liệu. Việc tách này không chỉ giúp tổ chức hệ thống tốt hơn mà còn cải thiện hiệu năng đáng kể. (Các bạn quen thuộc với Web-App-Database, nhưng bài viết này chọn giữ nguyên các thuật ngữ hàn lâm một chút).
Hãy tưởng tượng nếu database và application cùng chạy trên một server. Khi database tăng tải, CPU, RAM và I/O sẽ bị tranh chấp trực tiếp với ứng dụng. Kết quả là toàn hệ thống chậm đi. Khi tách riêng từng tầng, mỗi thành phần có thể scale độc lập. Ví dụ khác là nếu frontend cần thêm công suất? Scale thêm web nodes. Business logic bị bottleneck? Thêm application instances. Database quá tải? Xem xét replication hoặc clustering. Đây chính là tư duy nền tảng của kiến trúc phân tán hiện đại.
Một bước tiến tiếp theo là microservices architecture. Thay vì business logic là một khối lớn, ứng dụng được chia thành nhiều service nhỏ. Authentication service, payment service, inventory service, notification service… mỗi thành phần có lifecycle riêng, deploy riêng, scale riêng. Nhưng khi số lượng instance tăng lên, một câu hỏi xuất hiện: traffic sẽ đi về đâu? Cách đơn giản đầu tiên là chúng ta có thể dùng DNS load balancing. DNS trả về nhiều địa chỉ IP cho cùng một hostname như app.company.com. Client tự chọn IP để kết nối. Ban đầu, khi nghe thì chúng ta thấy có vẻ hợp lý, nhưng thực tế có vấn đề. Nếu một backend server chết thì sao? Liệu client có đủ thông minh để bỏ qua IP đó và thử IP khác? Không phải client nào cũng xử lý tốt tình huống này. Đó là lý do load balancer trở thành thành phần cốt lõi trong kiến trúc hiện đại.
Load balancer đứng trước các backend servers, nhận toàn bộ traffic từ client rồi quyết định phân phối lưu lượng theo nhiều chiến lược như Round Robin, Least Connections, Fastest Response, Session Persistence (sticky session). Một số bạn trong cộng đồng chúng ta đã quen thuộc với các thuật toán chia tải này.
Quan trọng hơn, load balancer còn thực hiện health checks. Nếu một backend không phản hồi, nó tự động loại node đó khỏi pool.
Đây là thứ giúp người dùng “không cảm nhận được sự cố”. Load balancer có thể là thiết bị chuyên dụng hoặc software-based như:
- NGINX
- HAProxy
- Envoy
- F5 BIG-IP
- AWS ALB / NLB
- Azure Load Balancer / Application Gateway
Thông thường sẽ có Active-Active: nhiều node cùng xử lý traffic. Hoặc Active-Passive: một node chính hoạt động, node dự phòng chờ takeover khi có lỗi. Phần backend database cũng tương tự. Một database server đơn lẻ là rủi ro cực lớn. Vì vậy các hệ thống hiện đại dùng replication hoặc clustering với:
- Primary / Replica
- Master / Standby
- Distributed database clusters
Nếu nhìn từ góc độ DevOps, đây chính là nền móng của các nền tảng cloud-native hiện nay. Kubernetes thực chất đang hiện thực hóa tư duy này:
- Pod = application instance
- Service = load balancing layer
- Deployment = scaling + rolling updates
- ReplicaSet = high availability
- StatefulSet = stateful distributed workloads