Container

Simplicity is the ultimate sophistication. – Leonardo da Vinci

5 min
Nội dung bài viết
Bài viết này cũng có bản English.
banner

Hi các bạn 👋. Mình là Hùng Anh.

Đây là bài thứ tư trong series Kubernetes Fundamentals. Ở bài trước, mình kết thúc bằng 1 câu hỏi: nếu phần lớn VM trên 1 server thực ra chỉ cần dùng chung 1 loại OS, thì có nhất thiết mỗi VM phải chứa riêng hẳn 1 kernel đầy đủ không? Cùng mình xem container trả lời câu hỏi đó như nào.

Bắt đầu thôi.

1. Container là gì

container-stack

Container đơn giản là 1 tiến trình bình thường trên host — không có gì huyền bí hơn thế. Thứ khiến nó trông như đang chạy trong 1 máy riêng biệt là kernel "nhốt" nó lại bằng đúng những cơ chế cô lập có sẵn: namespacecgroups — chính 2 thứ mình đã tự tay dựng lên ở bài đầu tiên của series này.

Khác với VM, container không có kernel riêng. Nó cũng không đi qua hypervisor — nó chạy thẳng trên kernel thật của host, chỉ khác là bị cô lập với mọi thứ khác cũng đang chạy ở đó.

2. Cách Container hoạt động

Khi 1 container khởi động, hệ thống làm 1 việc khá đơn giản: tạo 1 tiến trình mới trên host — nhanh, y hệt như khởi động bất kỳ chương trình nào khác — rồi áp 2 cơ chế của kernel vào đúng tiến trình đó:

  • Namespace — container không thấy được tiến trình, file, hay mạng của host hay của container khác.
  • cgroups — container bị giới hạn tối đa được dùng bao nhiêu CPU, RAM, disk I/O.

Tiến trình đó gọi syscall thẳng tới kernel thật của host — không qua lớp giả lập nào. Đây chính là khác biệt cốt lõi so với VM, và cũng là lý do container nhẹ và khởi động nhanh hơn hẳn.

3. Container đã giải quyết bài toán ở bài trước như nào

Vì container bỏ qua hoàn toàn hypervisor và guest OS, nó tránh được mọi thứ khiến VM trở nên nặng nề: không cần boot cả 1 hệ điều hành (khởi động 1 container chỉ là khởi động 1 tiến trình — dưới 1 giây, không phải vài phút), không tốn vài GB chỉ để nuôi sống 1 guest kernel, và số lượng container chạy được trên cùng 1 phần cứng nhiều hơn hẳn so với VM.

4. Nhược điểm của Container

  • Cô lập yếu hơn VM. Vì mọi container trên host đều dùng chung 1 kernel, 1 lỗ hổng ở kernel đó có thể ảnh hưởng tới mọi container đang chạy trên đó — khác với VM, nơi mỗi cái có hẳn 1 kernel tách biệt hoàn toàn.
  • Phụ thuộc vào kernel của host. 1 container Linux cần có Linux kernel bên dưới mới chạy được. Muốn chạy container Windows? Bạn cần đúng Windows kernel/host tương ứng.

Vậy Docker Desktop chạy container Linux trên Windows như nào? Tuyệt đại đa số image container ngoài đời (nginx, postgres, node...) đều là container Linux, mà Windows kernel thì đơn giản là không hiểu chúng. Vì vậy khi cài Docker Desktop trên Windows, nó âm thầm chạy 1 VM Linux nhẹ phía sau qua WSL2 — đúng mô hình đã nói ở bài trước — và mọi "container Linux" bạn chạy thực chất đang chạy bên trong chiếc VM ẩn đó, không phải trực tiếp trên Windows kernel. Docker chỉ khiến nó trông như native mà thôi.

5. Container Runtime

cri

Namespace và cgroups chỉ là cơ chế có sẵn trong kernel — vẫn cần 1 bên thực sự đứng ra gọi đúng các cơ chế đó để tạo, khởi động, dừng, xóa container. Đó chính là nhiệm vụ của container runtime.

Kubernetes không muốn bị khóa cứng vào 1 runtime cụ thể, nên nó định nghĩa sẵn 1 chuẩn giao tiếp trước — Container Runtime Interface (CRI) — rồi để các runtime khác nhau tự implement theo chuẩn đó:

  • containerd — tách ra từ Docker, hiện là runtime mặc định phổ biến nhất trong Kubernetes.
  • CRI-O — sinh ra ngay từ đầu để tuân theo đúng chuẩn CRI.

kubelet (agent của Kubernetes sẽ nói ở bài sau) chỉ nói chuyện theo đúng chuẩn CRI, việc đổi runtime bên dưới không bao giờ đòi hỏi phải sửa lại kubelet.

6. Tổng kết

Container chỉ đơn giản là 1 tiến trình bình thường, bị cô lập bằng namespace và cgroups, chạy thẳng trên chính kernel của host — không hypervisor, không guest OS. Đó là lý do nó nhẹ và nhanh, đổi lại là cô lập yếu hơn và phụ thuộc cứng vào kernel của host. Container runtime là phần mềm thực sự đứng ra tạo và quản lý chúng.

Giờ thì thư giãn một chút — bài tiếp theo: khi container rải rác trên hàng chục, hàng trăm máy, ai sẽ là người quyết định container nào chạy ở đâu? Đó chính xác là bài toán mà Kubernetes sinh ra để giải quyết.

Happy reading! 🍵

Tài liệu tham khảo

  1. What even is a container: namespaces and cgroups - Julia Evans
  2. Container Runtime Interface (CRI) - Kubernetes documentation

Bài viết liên quan

Các thuật ngữ Linux cơ bản

Trước khi chạm đến Kubernetes, câu chuyện phải bắt đầu từ thứ mà mọi thứ còn lại trong series này được xây trên nền của nó: Linux kernel. Nếu bạn chưa từng thực sự ngồi nghĩ kỹ kernel làm gì, driver là gì, hay "sandbox" trong Linux thực chất là gì, bài này dành cho bạn.

7 minKubernetes Fundamentals

Đừng bỏ lỡ bài mới nhé!