# Containers vs Serverless: Hướng dẫn ra quyết định kiến trúc (/vi/docs/engineering-judgment/decision-guides/containers-vs-serverless)



## Tóm tắt nhanh (TL;DR) [#tóm-tắt-nhanh-tldr]

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).

> 💡 &#x2A;*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:** &#x2A;*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 [#hai-ranh-giới-cốt-lõi-đóng-gói-vs-vận-hành]

<AtlasIllustration id="packaging-vs-operating-boundary" />

<TermBox term="Cold start">
  **Cold start (Khởi động lạnh)** là khoảng thời gian trễ phát sinh khi nền tảng phải cấp phát sandbox mới, tải image/code bundle và khởi tạo runtime trước khi có thể xử lý request đầu tiên.
</TermBox>

<AtlasIllustration id="cold-vs-warm-start" />

***

## Ma trận so sánh tiêu chí kỹ thuật [#ma-trận-so-sánh-tiêu-chí-kỹ-thuật]

<DecisionMatrix
  caption="Ma trận so sánh Containers và Serverless"
  criterionLabel="Tiêu chí"
  options="['Container truyền thống (K8s/ECS)', 'Serverless (Lambda/Cloud Run)']"
  rows="[
  {
    criterion: 'Khả năng kiểm soát Runtime & OS',
    values: [
      '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',
    ],
  },
  {
    criterion: 'Độ trễ khởi động (Startup Latency)',
    values: [
      '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',
    ],
  },
  {
    criterion: 'Mô hình thanh toán',
    values: [
      '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)',
    ],
  },
  {
    criterion: 'Quản lý trạng thái & Kết nối dài hạn',
    values: [
      '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 [#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":

<AtlasIllustration id="serverless-downstream-avalanche" />

* **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) [#điểm-giao-thoa-chi-phí-cost-crossover-point]

<AtlasIllustration id="tco-crossover" />

***

## Rèn luyện mô hình tư duy [#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: &#x2A;"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?**

<details>
  <summary>
    Xem giải thích chi tiết
  </summary>

  **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):

  1. **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.
  2. **Đị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.
</details>

***

## Checklist rà soát kiến trúc cho Kỹ sư [#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?
