Containers vs Serverless: Hướng dẫn ra quyết định kiến trúc
Khung tư duy kỹ thuật để lựa chọn giữa mô hình tính toán container và serverless dựa trên quyền kiểm soát tiến trình, độ trễ cold start, mô hình chi phí và backpressure.
Bản đồ học tập phát triển phần mềm bởi Tran Trong Thuc · Về dự án Atlas · Cập nhật lần cuối: 22 thg 9, 2026
Tóm tắt nhanh (TL;DR)
Trong một đêm săn sale cao điểm, một công ty công nghệ tài chính (fintech) bàng hoàng chứng kiến tỷ lệ thanh toán thành công rơi tự do: các hàm serverless chưa được làm ấm (un-warmed) mất tới 3 giây "Cold Start" chỉ để nạp thư viện mã hóa và mở kết nối database—vượt quá thời gian chờ (timeout) của cổng thanh toán. Vài tháng sau, khi lượng giao dịch đi vào quỹ đạo ổn định 24/7 với 3,500 request/giây, bộ phận tài chính phát hoảng khi hóa đơn Lambda tính theo từng mili-giây tăng vọt gấp 10 lần so với chi phí thuê một cụm container cố định. Lựa chọn mô hình tính toán đám mây không phải là câu chuyện chạy theo công nghệ thời thượng—đó là bài toán tối ưu tổng chi phí sở hữu (TCO), kiểm soát ngân sách độ trễ và hiểu rõ bản chất ranh giới giữa đóng gói (Packaging) và vận hành (Operating).
💡 Quy tắc bỏ túi: Tách bạch ranh giới đóng gói (Packaging) khỏi ranh giới vận hành (Operating). Container quy định những gì bạn đóng gói trong runtime artifact, còn Serverless quy định mức độ ủy thác việc cấp phát tài nguyên và vòng đời máy chủ cho nhà cung cấp đám mây. Chọn serverless cho các tác vụ biến thiên đột biến, hướng sự kiện hoặc có thể co về 0 để tiết kiệm công sức vận hành; chọn container cố định khi hệ thống cần tối ưu chi phí cho lưu lượng đều đặn, yêu cầu kết nối TCP trường tồn hoặc bắt buộc đảm bảo độ trễ P99 tính bằng mili-giây.
- Ranh giới đóng gói khác biệt với mô hình vận hành: Container image đóng gói mã nguồn và môi trường thực thi; nền tảng serverless tự động quản lý hạ tầng và co giãn instance. Một ứng dụng hoàn toàn có thể vừa chạy container vừa mang tính chất serverless (như Google Cloud Run hoặc AWS Fargate).
- Độ trễ Cold Start quyết định tính khả thi trên luồng request: Serverless phải khởi tạo môi trường sandbox mới khi có đột biến tải, tạo ra độ trễ Cold Start có thể làm đứt gãy trải nghiệm người dùng trừ khi có giải pháp giữ ấm (warm instances) hoặc provisioned concurrency chủ động.
- Hình thái lưu lượng quyết định điểm giao thoa chi phí (TCO): Serverless cực kỳ tiết kiệm cho lưu lượng biến động ngắt quãng nhờ khả năng co về 0 (Scale-to-Zero), nhưng sẽ đắt gấp nhiều lần so với container cố định khi xử lý tải cao liên tục suốt 24/7.
- Đánh giá hợp đồng kỹ thuật cụ thể của nền tảng được chọn: Tên gọi "serverless" không tự động đảm bảo hiệu năng; bắt buộc phải rà soát kỹ các cơ chế co giãn, khởi động, độ đồng thời (concurrency), vòng đời tiến trình và mô hình tính cước của nhà cung cấp.
- Cạm bẫy chết người: Hiện tượng Serverless co giãn mất kiểm soát đánh sập cơ sở dữ liệu phía sau. Việc các serverless function tự động scale vọt từ 10 lên 1,500 instance đồng thời chỉ trong vài giây sẽ vắt kiệt connection pool của PostgreSQL/MySQL ngay lập tức. Bắt buộc phải đặt proxy quản lý kết nối (như RDS Proxy/PgBouncer) hoặc siết giới hạn concurrency tối đa của function.
Hai ranh giới cốt lõi: Đóng gói vs Vận hành
| Phương án | Code / zip | OCI image |
|---|---|---|
| VM tự quản lý | Triển khai process | Container runtime |
| Cluster được quản lý | Ít phổ biến | Container được quản lý |
| Serverless execution | Functions | Dịch vụ container serverless |
Ma trận so sánh tiêu chí kỹ thuật
| Tiêu chí | Container truyền thống (K8s/ECS) | Serverless (Lambda/Cloud Run) |
|---|---|---|
| Khả năng kiểm soát Runtime & OS | Toàn quyền cấu hình OS kernel flags, sidecars, file system nền | Bị giới hạn trong sandbox do nhà cung cấp đám mây quy định |
| Độ trễ khởi động (Startup Latency) | Instance luôn sẵn sàng (0ms trễ khởi động) | Tiềm ẩn Cold Start khi có đột biến lưu lượng mới |
| Mô hình thanh toán | Trả phí theo tài nguyên cấp phát tĩnh (Provisioned Capacity) | Chỉ trả tiền theo thời gian xử lý thực tế (Pay-per-request) |
| Quản lý trạng thái & Kết nối dài hạn | Hỗ trợ tuyệt vời cho WebSockets, hàng đợi bộ nhớ, nền tảng stateful | Ưu tiên kiến trúc Stateless; ngắt kết nối khi hết thời gian chờ |
Sự cố thực tế: Cơn bão kết nối đánh sập Cơ sở dữ liệu
Một công ty thương mại điện tử chuyển đổi webhook xử lý đơn hàng từ cụm container cố định sang các serverless functions để "tiết kiệm chi phí và tự động co giãn":
- Traffic burstSự kiện vào tăng 40×
- 1.500 workersPlatform scale thành công
- 150 kết nối DBGiới hạn downstream cố định
- Chuỗi timeoutCạn connection pool
- Hậu quả: Trong đợt Flash Sale, lưu lượng tăng vọt 40 lần. Nền tảng Serverless co giãn mượt mà lên 1,500 functions đồng thời — và ngay lập tức làm tràn ngưỡng giới hạn 150 kết nối tối đa của PostgreSQL chính. Toàn bộ cơ sở dữ liệu rơi vào tình trạng treo nghẽn, kéo sập cả luồng thanh toán chính của website.
- Nguyên nhân cốt lõi: Đội ngũ kỹ thuật quên rằng: khả năng co giãn của compute không đồng nghĩa với khả năng chịu tải của database hạ tầng. Trong mô hình container, connection pool được duy trì cố định (ví dụ 10 container x 10 connection = 100 connection). Khi chuyển sang serverless, mỗi invocation độc lập mở một kết nối mới trực tiếp.
- Cách khắc phục chuẩn: Bắt buộc phải đặt một tầng proxy quản lý kết nối (như AWS RDS Proxy, PgBouncer) hoặc giới hạn Concurrency tối đa của function để áp dụng áp lực ngược (Backpressure).
Điểm giao thoa chi phí (Cost Crossover Point)
Rèn luyện mô hình tư duy
Tình huống: Một đội ngũ xây dựng dịch vụ thông báo nội bộ. Ứng dụng web của client duy trì kết nối WebSocket thường trực để nhận thông báo đẩy thời gian thực. Lưu lượng dự kiến chỉ vài tin nhắn mỗi giờ trên mỗi kết nối, với 50,000 client kết nối ở trạng thái chờ (idle) vào giờ cao điểm.
Kỹ sư trưởng đề xuất: "Hãy dùng serverless functions theo nhu cầu mà không cần hạ tầng container, vì lượng tin nhắn rất ít và chúng ta chỉ trả tiền khi tin nhắn thực sự được xử lý."
Mô hình compute này có phù hợp với khối lượng công việc này không?
Xem giải thích chi tiết
Tại sao đề xuất này chứa rủi ro lớn:
Dù việc chuyển phát tin nhắn mang tính rải rác và hướng sự kiện (event-driven), nhưng mô hình kết nối lại mở liên tục và có trạng thái (stateful):
- Thời lượng kết nối: WebSocket yêu cầu kết nối TCP trường tồn dài hạn. Môi trường thực thi serverless on-demand thường giới hạn thời gian chạy tối đa (thường dưới 15 phút) và tính phí theo thời gian thực thi tích cực. Việc duy trì 50,000 môi trường serverless chạy liên tục chỉ để giữ socket TCP chờ sẽ cực kỳ tốn kém và bất khả thi về mặt kỹ thuật.
- Định danh tiến trình & Routing: Để đẩy thông báo tới một client cụ thể, hệ thống phải chuyển tiếp tin nhắn tới đúng tiến trình đang giữ kết nối socket của client đó. Serverless functions không có định danh tiến trình ổn định hay địa chỉ mạng cố định để nhận push peer-to-peer, trừ khi kết hợp với một managed gateway chuyên biệt (như AWS API Gateway WebSocket API hoặc pub/sub proxy).
Kiến trúc phù hợp hơn:
- Dùng worker chạy bằng container được tối ưu cho I/O concurrency cao (nơi hàng nghìn socket chờ chỉ tiêu tốn lượng RAM/CPU rất nhỏ trong Node.js hoặc Go).
- Hoặc tách riêng tầng kết nối bằng cách sử dụng dịch vụ quản lý kết nối chuyên dụng (như API Gateway WebSockets, Ably, Pusher) và chỉ kích hoạt serverless function ngắn hạn khi có sự kiện thực sự được phát hành.
Checklist rà soát kiến trúc cho Kỹ sư
- Mô hình kết nối: Ứng dụng có cần kết nối TCP trường tồn (như WebSocket, Socket.IO, gRPC server streaming) không? Nếu có, ưu tiên Container.
- Bảo vệ hệ thống hạ tầng (Backpressure): Đã thiết lập giới hạn concurrency hoặc connection pooler giữa serverless compute và database chưa?
- Thời gian xử lý tối đa (Timeout): Tác vụ có nguy cơ chạy vượt quá giới hạn timeout tối đa của serverless (thường là 15 phút) không?
- Độ nhạy cảm với Cold Start: Các endpoint API then chốt có chịu được độ trễ phát sinh vài trăm mili-giây khi scale từ 0 lên không?
- Khả năng quan sát (Observability): Đã thiết lập hệ thống trace phân tán (Distributed Tracing) để theo dõi các function có vòng đời ngắn chưa?
Monolith, Modular Monolith hay Microservices: Chọn ranh giới triển khai phù hợp
Lựa chọn ranh giới triển khai và ranh giới nghiệp vụ dựa trên quyền sở hữu của đội ngũ, nhu cầu giao dịch, cô lập lỗi và năng lực vận hành thay vì danh tiếng kiến trúc.
Message Queue hay Event Stream: Cách chọn đúng công nghệ nhắn tinNew
Chọn giữa hàng đợi công việc và luồng sự kiện được lưu giữ dựa trên quyền sở hữu công việc, phân phối tới nhiều bên, phát lại, thứ tự, ngữ nghĩa chuyển giao, áp lực ngược, thời gian lưu giữ và chi phí vận hành.