Khác nhau giữa server, serverless và container — tầng App

Bài viết này nằm trong khóa Kiến thức nền tảng về CNTT, xây dựng từ đầu những kiến thức CNTT thực tế tối thiểu mà bạn cần để lập trình và vibe coding.
Khác biệt nằm ở thời gian mà chương trình xử lý đang chạy. Các sơ đồ giúp bạn phân biệt hình thức chạy sẵn rồi chờ, hình thức chỉ chạy khi có request, và container — thứ chạy kèm cả môi trường chạy trên 1 OS.

Bài viết này nói về 3 hình thức chạy tầng App.

Đó là chạy thường trực, tức để nguyên 1 server đang chạy; serverless, chỉ chạy khi có request tới; và container.

Khác biệt nằm ở thời gian mà chương trình xử lý đang chạy.

Vì thời gian chạy khác nhau nên cách tăng số lượng khi request nhiều lên, và những thứ bạn phải tự chuẩn bị, cũng khác nhau.

Cách chạy chia thành 3 nhóm theo thời gian đang chạy
Chạy tầngApp serverChạy cả khikhông có requestChỉ chạy khicó request tớiServer thường trựcContainerServerless
Từ 1 nút bên trái, sơ đồ tách trên dưới theo việc có cần chạy cả trong lúc không có request hay không. Phía trên để nguyên đang chạy, phía dưới chỉ chạy khi được gọi.

Server chạy thường trực luôn chạy và chờ nhận request

Bạn không biết request tới lúc nào, nên bạn chạy server.js trước và để nó chờ ở đó dưới dạng một tiến trình (process, một chương trình đã khởi động và đang chạy).

Hình thức để nguyên tiến trình đó thay vì kết thúc nó là chạy thường trực (always-on, giữ chương trình ở trạng thái đang chạy để lúc nào cũng nhận được request).

Server listening on port 3000
Press Ctrl+C to stop

Dòng thứ 1 cho thấy trạng thái đang lắng nghe request ở cổng đã chọn.

Đúng như dòng thứ 2, tiến trình không kết thúc cho tới khi bạn thao tác dừng nó.

Thứ tới vào thời điểm đóTiến trình server.jsNội dung trả về tầng Web server
9:00 thao tác khởi độngKhởi động và bắt đầu lắng ngheChưa trả về gì
9:05 request /reservationsNhận luôn và tra cứu các lượt đặt chỗ3 lượt đặt chỗ
9:06 không có gì tớiKhông kết thúc, tiếp tục chờKhông trả về gì
Những gì có bên trong một server luôn chạy
Máy tính giữ cho chạy liên tục
Tiến trình node (khởi động bằng node server.js)
  • Lắng nghe request ở cổng 3000
  • Không kết thúc cho tới khi bạn thao tác dừng
server.js — file viết phần xử lý của ứng dụng đặt chỗ
  • Chứa phần xử lý cho request tới /reservations
  • Hỏi tầng DB số lượt đặt chỗ
OS, và node do bạn tự cài
  • node thì bạn cài từ trang chính thức
  • Bản cập nhật OS cũng do bạn tự làm
Khung ngoài là máy tính bạn giữ cho chạy liên tục. Bên trong đó tiến trình node lắng nghe, và bên trong tiến trình đó là server.js do bạn viết được nạp vào.

Thứ bạn tự chuẩn bị không chỉ có server.js ở trong cùng.

Máy tính giữ cho chạy liên tục, OS trên đó và node cũng do bạn tự chuẩn bị.

Đây chính là khác biệt lớn so với serverless sẽ xem tiếp theo.

Bên trong khung là 1 máy do bạn tự chuẩn bị và giữ cho chạy liên tục.

Tiến trình không kết thúc kể cả trong lúc không có request tới; nó mở sẵn cổng và chờ.

Chạy thường trực nghĩa là chạy mãi cho tới khi bạn dừng

Server chạy thường trực là hình thức bạn chạy trước rồi để nó chờ request tới.

Thứ khởi động nó là node server.js, tiến trình không kết thúc cho tới khi bạn thao tác dừng, còn máy tính giữ cho chạy liên tục, OS và node đều do bạn tự chuẩn bị.

Serverless chỉ chạy phần xử lý khi có request tới

Với serverless (hình thức mà chương trình xử lý chỉ chạy khi có request tới và dừng khi xử lý xong), bạn không tự chạy một tiến trình đứng chờ.

Thứ bạn đăng ký là 1 file duy nhất như handler.js, và đơn vị đăng ký đó được gọi là "hàm".

Với serverless, phạm vi nhà cung cấp chuẩn bị và thứ bạn đặt vào
Hệ thống của nhà cung cấp cloud
Phần nhận request và gọi hàm
  • Nhận các request tới
  • Quyết định số lượng chạy đồng thời
Môi trường chạy được chuẩn bị cho mỗi request
  • OS và node do nhà cung cấp chuẩn bị
  • Dừng khi xử lý xong
handler.js — file đặt phần xử lý của ứng dụng đặt chỗ
  • Chỉ xử lý 1 lượt request tới /reservations
  • Thời gian chạy được có giới hạn trên
Khung ngoài là hệ thống của nhà cung cấp cloud. Khung trên là phần nhận request, luôn chạy. Khung dưới là môi trường chạy được chuẩn bị cho mỗi request, và handler.js của bạn nằm bên trong đó.

Thứ bạn động tới chỉ là handler.js ở trong cùng.

Các khung bên ngoài do nhà cung cấp chuẩn bị, việc cập nhật OS và node cũng do họ làm.

Ai chuẩn bị tới đâu thì bài "On-premises, server cho thuê, cloud" sẽ nói tới.

Trong lúc chưa có request tới, handler.js không chạy.

Môi trường chạy được chuẩn bị theo từng request và dừng lại khi trả lời xong.

Đây là khác biệt so với chạy thường trực, vốn chạy mãi cho tới khi bạn dừng.

Serverless chỉ chạy khi được gọi

Serverless là hình thức chạy phần xử lý sau khi request tới, rồi dừng khi trả lời xong.

Thứ bạn đặt vào chỉ là handler.js, còn môi trường chạy nó thì nhà cung cấp chuẩn bị cho mỗi request.

Cách tăng số lượng khi request đồng thời nhiều lên là khác nhau

Số request tới cùng lúc thay đổi theo khung giờ.

Việc điều chỉnh theo đó là scaling (tăng hoặc giảm số lượng bên xử lý cho khớp với lượng request).

Hãy bắt đầu từ trường hợp 3 request tới serverless cùng lúc.

3 request tới cùng lúc thì 3 môi trường chạy cũng khởi động
A gọi/reservationsB gọi/reservationsC gọi/reservationsPhần nhận requestcủa nhà cung cấpMôi trường 1handler.js chạyMôi trường 2handler.js chạyMôi trường 3handler.js chạy
3 request bên trái đều là request tới cùng một /reservations. Sau khi ở giữa nhận vào, mỗi request được chuẩn bị 1 môi trường chạy.

Cho tới số lượng chạy đồng thời mà nhà cung cấp đã định, không request nào phải chờ.

Bạn cũng không tự thao tác để tăng số lượng.

Server chạy thường trực cũng cần những vai trò đó, nhưng bên đảm nhiệm thì khác.

Những thứ giữ cùng vai trò ở chạy thường trực và ở serverless
===Chờ nhậnrequestTiến trình nodechờ ở cổng 3000Phần nhận củanhà cung cấpChạy phần xử lýChạy server.jscùng tiến trìnhDựng môi trườngchạy handler.jsTăng số lượngBạn tự tăng tiếntrình hoặc số máyHệ thống nhà cungcấp tăng giúp bạn
Cột trái là vai trò, cột giữa là server chạy thường trực, cột phải là serverless. Hai thứ nối bằng đường kép làm cùng một việc, chỉ khác ở bên đảm nhiệm.

Thứ thay đổi là bạn tự quyết định việc tăng, hay giao cho hệ thống của nhà cung cấp.

Sau khi tăng lên thì request của cùng một người chưa chắc lần nào cũng tới đúng một bên, nên cần stateless (một lượt xử lý không giữ giá trị đã nhớ ở lượt xử lý trước).

Việc lượt thứ 2 có đọc được giá trị đã nhớ ở lượt thứ 1 hay không phụ thuộc vào chỗ bạn đặt giá trị đó.

Giá trị mà request kế tiếp cũng dùng thì hãy lưu vào tầng DB rồi đọc ra từ đó.

Server chạy thường trực cũng vậy: khi bạn tăng số tiến trình, giá trị không còn lại ở các tiến trình khác.

Cách tăng số lượng, và chỗ đặt giá trị

Khi request đồng thời nhiều lên, ở chạy thường trực bạn tự tăng số lượng, còn ở serverless thì hệ thống của nhà cung cấp tăng giúp.

Sau khi tăng lên, 2 request của cùng một người chưa chắc do cùng một bên trả lời, nên giá trị bạn còn muốn dùng tiếp thì lưu vào tầng DB chứ không phải bộ nhớ tại chỗ.

Container chạy trên 1 OS, và bạn chọn trong 3 hình thức theo việc có cần chạy thường trực hay không

Hình thức thứ 3, container (định dạng gói ứng dụng, môi trường chạy và các file cần thiết vào làm một, rồi chạy trên máy tính khác vẫn giữ nguyên cấu hình đó), có thời gian đang chạy giống server chạy thường trực.

Chỗ khác là môi trường chạy không được cài vào máy tính mà được gói chung với ứng dụng.

Cùng 3 ứng dụng đó: chạy trên 3 máy và chạy bằng container trên 1 máy
Chuẩn bị 1 máy cho mỗi ứng dụng
Thứ phải chuẩn bị
  • 3 máy tính
  • 3 OS
  • Cài Node.js 18 / Python 3.11 / Node.js 20 lên các máy riêng
1 máy chia nhau bằng container
1 máy tính, 1 OS — 3 container chạy trên đó
Container 1
  • 1 tiến trình
  • Node.js 18
  • server.js
Container 2
  • 1 tiến trình
  • Python 3.11
  • batch.py
Container 3
  • 1 tiến trình
  • Node.js 20
  • admin.js
Phía trên là hình thức chuẩn bị 1 máy cho mỗi ứng dụng, cần 3 bộ thiết bị và 3 OS. Phía dưới là hình thức 3 container chạy trên 1 OS, mỗi container là 1 tiến trình, nên 1 thiết bị và 1 OS là đủ.

OS chỉ có 1, và mỗi container chạy trên đó như 1 tiến trình.

Môi trường chạy nằm riêng bên trong từng container, nên container bên cạnh dùng gì cũng không ảnh hưởng.

Phần thiết bị dùng để chạy, như CPU và bộ nhớ, là tài nguyên (resource).

Phía trên chạy 3 OS, còn phía dưới chia nhau 1 OS, nên cùng một thiết bị chứa được nhiều ứng dụng hơn.

Cùng số máy mà chạy được nhiều hơn, chi phí giữ cho chạy liên tục cũng ít hơn.

Vì không khởi động OS mới nên thời gian tới lúc bắt đầu chạy cũng ngắn hơn.

Chạy 2 thứ trên cùng 1 máy: đặt môi trường chạy ở đâu thì kết quả khác nhau
Muốn chạy Node 18và Node 20 trêncùng 1 máyCài môi trườngchạy vào OS1 máy chỉ càiđược 1 nodeChỉ còn cáchchọn 1 trong 2Gói cả môi trườngvào containerMỗi container cónode riêngChạy song songcả 2 như vậy
Bên trái là việc bạn muốn chạy. Đường trên là trường hợp cài môi trường chạy vào máy tính, đường dưới là gói vào container. Cùng 1 máy nhưng chỗ đặt khác nhau thì kết quả khác nhau.

1 OS chỉ cài được 1 node, nhưng với container thì 2 bên vẫn giữ node riêng và chạy song song.

Vì môi trường chạy tách theo từng ứng dụng nên nâng phiên bản một bên thì bên kia vẫn chạy.

Bộ file đã gói lại là container image (gói ứng dụng, môi trường chạy và các file cần thiết vào làm một), và phần mềm tiêu biểu để tạo rồi chạy nó là Docker.

Bên chạy chỉ việc nhận image này rồi khởi động.

Cùng 1 image cho ra cùng một cấu hình dù chạy ở đâu
Ứng dụng, môi trườngchạy và file cần thiếtContainer imagegói lại làm mộtMáy tính của bạnServer công khaiMáy đồng nghiệp
Container image là 3 thứ bên trái gói lại làm một. 3 nơi bên phải khởi động thẳng image đó, nên không cần làm lại việc cài môi trường chạy.

Dù nơi chạy có thay đổi, thứ được khởi động vẫn là cùng 1 image.

Vì không cần cài lại môi trường chạy trên server công khai nên bạn công khai được đúng cấu hình đã chạy ở máy mình.

Ranh giới khi chọn là có cần chạy cả trong lúc không có request hay không.

Ranh giới còn lại là 1 thiết bị chứa được bao nhiêu.

Việc có cần chạy thường trực hay không chia ra 3 cách chạy
Xử lý muốn chạyCần chạy cả khikhông có requestCài môi trườngchạy vào máy tínhServer chạythường trựcGói cả môi trườngvào làm mộtContainerChỉ cần trả lờirequest là đủChỉ chạy khiđược gọiServerless
Từ 1 nút bên trái, sơ đồ tách theo việc có cần chạy trong lúc không có request hay không. 2 nhánh trên khởi động rồi chạy tiếp, còn 1 nhánh dưới bắt đầu chạy sau khi request tới.

Ở 2 nhánh trên, tiến trình vẫn chạy cả trong lúc không có request.

2 nhánh đó tách nhau ở chỗ cài môi trường chạy vào máy tính, hay gói cả môi trường chạy vào làm một.

2 hình thức chạy thường trực phải chạy suốt cả phần thời gian không có request.

Với serverless, thứ được đếm chỉ là thời gian đã chạy và số lần được gọi.

Khi request còn thưa, khác biệt này hiện ra ngay.

Chọn theo thời gian đang chạy

Trước hết chia làm 2 theo việc có cần chạy cả trong lúc không có request hay không.

Nếu không cần thì chọn serverless, nếu cần thì chọn server chạy thường trực hoặc container.

Container chia 1 OS giữa các ứng dụng nên bạn xếp được nhiều ứng dụng lên cùng thiết bị và giảm được chi phí giữ cho chạy liên tục.

Khi muốn cấu hình ở máy mình và ở nơi công khai giống nhau, bạn cũng chọn container.

QUIZ

Kiểm tra kiến thức

Hãy trả lời từng câu hỏi một.

Câu 1Giữa server chạy thường trực và serverless, thời gian chương trình xử lý đang chạy khác nhau thế nào?

Câu 2Bạn dùng container trong trường hợp nào?

Câu 3Khi muốn dùng lại ở request kế tiếp một giá trị đã đặt vào bộ nhớ giữa chừng phần xử lý, bạn làm gì?