1. Kubernetes Overview
Mục lục
- 1. Containers Overview
- 2. Container Orchestration
- 3. Kubernetes Architecture
- 4. Docker vs ContainerD
- 5. Vì sao vẫn nói về Docker dù đã bị Deprecate?
1. Containers Overview
Kubernetes (K8s) ra đời từ chính kinh nghiệm nhiều năm Google vận hành container ở quy mô production — giờ đã là dự án mã nguồn mở và là một trong những cái tên phổ biến nhất khi nhắc tới container orchestration. Nhưng trước khi hiểu Kubernetes thực sự làm gì, cần trả lời 2 câu hỏi nền tảng: container là gì, và orchestration đang giải quyết vấn đề gì?
Vì sao cần Docker?
Tác giả khóa học kể lại đúng trải nghiệm của chính mình trong 1 dự án trước đây: nhận nhiệm vụ dựng 1 stack ứng dụng end-to-end — web server chạy Node.js, database MongoDB, messaging qua Redis, và orchestrate bằng Ansible. Nghe qua tưởng chỉ là công việc thường ngày. Rắc rối chỉ lộ ra khi bắt tay vào làm thật.
- Tương thích OS: phải đảm bảo tất cả service tương thích với đúng phiên bản OS đang dùng, có lúc phải đổi hẳn OS khác để tương thích.
- Tương thích thư viện/dependency: service này cần version A của 1 thư viện, service khác lại cần version B.
- Mỗi lần kiến trúc ứng dụng thay đổi (nâng cấp version, đổi database...) lại phải kiểm tra lại toàn bộ ma trận tương thích giữa các thành phần.
💡 Hình dung: vấn đề ma trận tương thích này thường được gọi là "matrix from hell".
Chưa hết. Mỗi khi có developer mới gia nhập team, họ lại phải tự chạy hàng trăm lệnh chỉ để dựng lại đúng môi trường — đúng OS, đúng version từng thành phần. Môi trường dev, test, production lại thường khác nhau, mỗi developer lại quen dùng 1 OS khác nhau. Kết quả là không ai dám chắc ứng dụng sẽ chạy giống hệt nhau ở mọi nơi.
Đây chính là bài toán Docker giải quyết: chạy mỗi thành phần trong 1 container riêng, có dependency/library riêng, nhưng tất cả vẫn nằm trên cùng 1 VM/OS, cách ly nhau hoàn toàn. Chỉ cần build cấu hình Docker đúng 1 lần — mọi developer sau đó chỉ cần gõ docker run là chạy được, bất kể OS nền bên dưới, miễn có Docker cài sẵn.
Container là gì?
Đơn giản nhất, container là 1 môi trường cách ly hoàn toàn — có process/service riêng, network interface riêng, mount riêng, giống hệt VM — nhưng có 1 điểm khác biệt cốt lõi: mọi container đều dùng chung OS kernel.
Container không phải công nghệ Docker phát minh ra. Nó đã tồn tại khoảng 10 năm trước khi Docker xuất hiện, dưới dạng LXC, LXD, LXCFS... Thực chất Docker vẫn dùng LXC container bên dưới — cái Docker mang lại là 1 lớp công cụ high-level, biến việc setup vốn rất low-level và khó chịu trở nên dễ dàng hơn hẳn.
Vậy "chia sẻ kernel" nghĩa là gì? Một OS (Ubuntu, Fedora, SUSE, CentOS...) thực ra gồm 2 phần: OS kernel (tương tác trực tiếp với hardware) và lớp software phía trên (giao diện, driver, compiler, dev tools...). Kernel Linux thì dùng chung giữa mọi distro — phần software phía trên mới là thứ tạo nên sự khác biệt giữa các OS.
Chính vì Docker container chia sẻ chung kernel, Docker trên host Ubuntu hoàn toàn có thể chạy container dựa trên Debian, Fedora, SUSE, hay CentOS — vì tất cả đều đứng trên cùng 1 kernel Linux. Nhưng không chạy được container Windows trên Docker host Linux, vì khác kernel hoàn toàn — muốn vậy phải có Docker trên chính Windows Server.
⚠️ Lưu ý: khi cài Docker trên Windows và chạy được container Linux, thực chất Windows đang chạy container đó bên trong 1 máy ảo Linux ẩn bên dưới — không phải chạy trực tiếp trên kernel Windows.
Nói cách khác, mục đích của Docker không phải để ảo hóa nhiều OS/kernel khác nhau trên cùng phần cứng như hypervisor vẫn làm — mục đích cốt lõi của nó là đóng gói (containerize), vận chuyển (ship), và chạy ứng dụng ở bất kỳ đâu, bất kỳ lúc nào, bao nhiêu lần tùy ý.
Container vs Virtual Machine
Với Docker: hardware → OS → Docker → các container (chỉ chứa library/dependency riêng của ứng dụng).
Với VM: hardware → hypervisor (ví dụ ESXi) → các VM, mỗi VM có OS riêng + dependency riêng + ứng dụng riêng bên trong.
💡 Hình dung như thế này: container giống các phòng trong 1 chung cư — dùng chung hạ tầng điện nước nền (kernel), mỗi phòng chỉ cần trang bị riêng đồ đạc của mình (library/dependency) nên xây nhanh, nhẹ. VM giống các căn nhà riêng biệt — mỗi nhà tự có hẳn hệ thống điện nước riêng từ đầu (OS/kernel riêng), cách ly tốt hơn hẳn nhưng xây chậm và tốn diện tích hơn nhiều.
Nhìn ra sự khác biệt rõ nhất qua bảng so sánh sau:
| Container (Docker) | Virtual Machine | |
|---|---|---|
| Sử dụng tài nguyên | Thấp hơn — chia sẻ chung kernel | Cao hơn — mỗi VM chạy 1 kernel/OS ảo riêng |
| Dung lượng | Nhẹ, thường tính bằng megabyte | Nặng, thường tính bằng gigabyte |
| Thời gian khởi động | Vài giây | Vài phút (phải boot cả OS) |
| Mức cách ly | Thấp hơn — chia sẻ kernel | Hoàn toàn cách ly nhau |
| Chạy khác loại OS trên cùng máy | Không (bị giới hạn bởi kernel host) | Có (VM không phụ thuộc OS/kernel host) |
Không hẳn phải chọn 1 trong 2. Thực tế thường kết hợp cả container và VM: dùng lợi ích ảo hóa để dễ dàng provision/decommission host, đồng thời dùng lợi ích Docker để provision ứng dụng và scale nhanh. Khi đó số lượng VM cần dựng ít hơn hẳn so với trước — trước đây mỗi ứng dụng 1 VM riêng, giờ 1 VM có thể gánh hàng trăm, hàng nghìn container.
Không tự build thì lấy image ở đâu?
Không cần tự build lại mọi thứ từ đầu. Rất nhiều ứng dụng đã được đóng gói sẵn dưới dạng Docker image, công khai trên Docker Hub (hay Docker Store) — có thể tìm thấy image cho hầu hết OS, database, service, tool phổ biến.
docker run nginx
Chạy xong, bạn có ngay 1 instance nginx sống trên Docker host — không cần cài đặt gì thêm. Tương tự với MongoDB, Redis, Node.js... Muốn chạy nhiều instance cho 1 web service, chỉ cần thêm instance và cấu hình load balancer phía trước; nếu 1 instance lỗi, đơn giản là hủy nó và khởi chạy instance mới.
Image là 1 package hoặc template (tương tự VM template) — dùng để tạo ra một hoặc nhiều container. Container là instance đang chạy của 1 image, cách ly với môi trường và tập process riêng.
Docker thay đổi mối quan hệ Dev–Ops như thế nào?
Hình dung bạn ở vai trò Ops: nhận bàn giao ứng dụng kèm 1 bộ hướng dẫn setup dài dằng dặc — host cần cấu hình thế nào, cần cài prerequisite gì, dependency ra sao. Vì không tự tay viết ứng dụng, bạn dễ vấp trục trặc ngay từ bước dựng môi trường, và mỗi lần gặp lỗi lại phải quay sang hỏi developer.
Với Docker, câu chuyện đổi khác: developer và Ops cùng nhau chuyển đổi code thành 1 Dockerfile, gộp đủ yêu cầu của cả hai bên. Dockerfile này dùng để build ra image cho ứng dụng — image chạy được trên bất kỳ host nào có Docker, và đảm bảo chạy giống hệt nhau ở mọi nơi. Ops lúc này chỉ cần deploy đúng image đó. Vì image đã hoạt động đúng ngay từ lúc developer build, và Ops không chỉnh sửa gì thêm, nó tiếp tục chạy đúng khi lên production. Không còn màn đá bóng trách nhiệm qua lại giữa 2 team.
2. Container Orchestration
Container đã đóng gói xong ứng dụng — nhưng đưa nó lên production thì cần thêm gì? Ứng dụng có thể phụ thuộc container khác (database, messaging service...), số lượng user có thể tăng cần scale lên, hoặc giảm tải cần scale xuống.
Để làm được những việc này cần 1 platform bên dưới có khả năng: orchestrate kết nối giữa các container, và tự động scale lên/xuống theo tải. Quá trình tự động deploy và quản lý container này gọi là container orchestration. Kubernetes chính là 1 công nghệ container orchestration.
Kubernetes, Docker Swarm, hay Mesos — chọn ai?
Hiện có nhiều công nghệ orchestration: Docker Swarm (của Docker), Kubernetes (của Google), Mesos (của Apache).
| Công nghệ | Độ khó setup | Tính năng |
|---|---|---|
| Docker Swarm | Rất dễ | Thiếu 1 số tính năng nâng cao cho ứng dụng phức tạp |
| Mesos | Khó | Hỗ trợ nhiều tính năng nâng cao |
| Kubernetes | Khó vừa phải | Nhiều tùy chọn customize deployment, hỗ trợ kiến trúc phức tạp — phổ biến nhất trong 3 công nghệ |
Kubernetes hiện được hỗ trợ trên mọi nhà cung cấp cloud lớn (GCP, Azure, AWS), và là 1 trong những dự án top trên GitHub.
Vậy container orchestration mang lại gì trong thực tế?
Không chỉ dừng ở việc "chạy được nhiều container" — orchestration còn giải quyết hàng loạt vấn đề vận hành mà nếu tự làm tay sẽ rất tốn công:
- Tính sẵn sàng cao: hardware lỗi không làm sập ứng dụng vì có nhiều instance chạy trên các node khác nhau.
- Load balancing: traffic được cân bằng tải qua các container.
- Scale nhanh: khi nhu cầu tăng, deploy thêm instance chỉ trong vài giây, ngay ở service level.
- Scale hạ tầng: khi hết tài nguyên hardware, scale số node bên dưới lên/xuống mà không cần tắt ứng dụng.
- Tất cả những việc trên thực hiện dễ dàng chỉ bằng 1 bộ file cấu hình khai báo (declarative object configuration files).
Nói ngắn gọn: Kubernetes không tạo ra container, nó biến hàng trăm, hàng nghìn container rời rạc trong 1 môi trường clustered thành 1 hệ thống thực sự vận hành được.
3. Kubernetes Architecture
Node, Cluster, Master
Bắt đầu từ đơn vị nhỏ nhất: Node là 1 máy (vật lý hoặc ảo) có cài Kubernetes — đây là worker machine, nơi container được Kubernetes khởi chạy. Trước đây còn gọi là Minion.
Nhưng nếu chỉ có 1 node và node đó lỗi, ứng dụng sập theo ngay lập tức. Vì vậy cần nhiều hơn 1 node — 1 cluster là tập hợp các node gộp lại với nhau, giúp ứng dụng vẫn truy cập được từ node khác khi 1 node lỗi, đồng thời chia tải giữa các node.
Câu hỏi đặt ra: ai quản lý cluster? Thông tin thành viên cluster lưu ở đâu? Node được giám sát thế nào? Khi 1 node lỗi, workload của nó được chuyển sang node khác ra sao?
Đó là vai trò của master — 1 node khác cũng cài Kubernetes nhưng được cấu hình làm master. Master theo dõi các node trong cluster và chịu trách nhiệm orchestrate việc chạy container trên các worker node.
💡 Hình dung như thế này: cluster giống 1 công ty vận hành theo ca. Master là phòng điều phối — theo dõi ai đang làm gì, ai vắng mặt (etcd lưu hồ sơ, scheduler phân việc, controller-manager theo dõi bất thường). Worker node là nhân viên trực tiếp thực thi công việc (kubelet nhận lệnh, container runtime thực sự "làm việc"), rồi báo cáo tình trạng ngược lại phòng điều phối.
Cài Kubernetes thực chất là cài những gì?
Khi cài Kubernetes lên 1 hệ thống, bạn không chỉ cài 1 phần mềm duy nhất — mà đang cài cả 1 tập hợp thành phần: API server, etcd, kubelet, container runtime, controllers, scheduler. Sáu mảnh ghép này, mỗi mảnh giữ đúng 1 vai trò:
- API server: đóng vai trò front-end của Kubernetes. User, management tool, command-line interface đều giao tiếp qua API server để tương tác với cluster.
- etcd: 1 key-value store phân tán, đáng tin cậy, dùng để lưu toàn bộ dữ liệu quản lý cluster. Khi có nhiều node và nhiều master, etcd lưu thông tin này trên tất cả master theo cách phân tán, đồng thời chịu trách nhiệm implement lock trong cluster để đảm bảo không có xung đột giữa các master.
- Scheduler: chịu trách nhiệm phân phối công việc/container qua nhiều node — tìm container mới được tạo và gán chúng vào node phù hợp.
- Controllers: là "bộ não" đứng sau orchestration — phát hiện và phản ứng khi node, container, hoặc endpoint bị sập, đưa ra quyết định khởi chạy container mới trong các trường hợp đó.
- Container runtime: phần mềm nền dùng để chạy container — trong tài liệu này là containerd, nhưng còn có các lựa chọn khác.
- kubelet: agent chạy trên mỗi node trong cluster, chịu trách nhiệm đảm bảo container chạy đúng như mong đợi trên node đó.
Ai làm master, ai làm worker?
Vậy các thành phần trên phân bổ thế nào giữa các loại server? Điều gì khiến 1 server trở thành master, server còn lại là worker?
Worker node (hay minion) là nơi container thực sự được host (ví dụ container containerd). Để chạy container cần cài container runtime — đây là lý do container runtime nằm trên worker node.
Master server có kube-apiserver — chính thành phần này khiến nó trở thành master. Tương tự, worker node có kubelet agent, chịu trách nhiệm giao tiếp với master để báo cáo thông tin sức khỏe của worker node, đồng thời thực hiện các hành động do master yêu cầu.
Master còn có key-value store (dựa trên etcd), controller-manager, và scheduler.
Nắm được bộ khung Master–Worker này, phần lớn thuật ngữ Kubernetes sau này chỉ là biến thể của cùng 1 bức tranh: ai ra lệnh, ai thực thi, và ai ghi nhớ trạng thái.
Vậy làm sao để thực sự ra lệnh cho cluster?
Đó là việc của kubectl (hay "kube control") — command-line tool dùng để deploy và quản lý ứng dụng trên Kubernetes cluster, lấy thông tin cluster, trạng thái node, và nhiều việc khác.
kubectl run # deploy 1 ứng dụng lên cluster
kubectl cluster-info # xem thông tin về cluster
kubectl get nodes # liệt kê tất cả node thuộc cluster
kubectl get nodes trả về bảng gồm tên node, trạng thái (Ready/NotReady), role (control-plane/worker), và version Kubernetes đang chạy trên node đó — đây thường là lệnh đầu tiên chạy để kiểm tra cluster có "sống" hay không.
4. Docker vs ContainerD
Đọc tài liệu/blog cũ, bạn sẽ thấy Docker được nhắc tới cùng Kubernetes ở khắp nơi. Đọc blog mới hơn, cái tên xuất hiện lại là containerd. Rồi bạn còn gặp thêm hàng loạt CLI tool khác nhau: ctr, crictl, nerdctl — vậy rốt cuộc nên dùng cái nào?
Câu chuyện này thường được kể theo đúng thứ tự lịch sử — nhưng kể theo thứ tự đó lại dễ gây rối, vì containerd chỉ được "lộ diện" ở cuối, trong khi nó đã đóng vai trò quan trọng ngay từ đầu. Nên lật ngược lại: nhìn thẳng vào bên trong Docker trước, rồi phần lịch sử phía sau sẽ tự nhiên sáng tỏ.
Docker thực chất gồm những gì?
Cái tên "Docker" không phải 1 khối duy nhất — nó là 1 bộ nhiều tool gộp lại:
- Docker CLI và Docker API — giao diện bạn gõ lệnh hằng ngày (
docker run,docker ps...). - Build tool — dùng để build Dockerfile thành image.
- Hỗ trợ volume, network, security.
- Và nằm sâu nhất bên dưới tất cả: 1 container runtime tên runc — phần thực sự tạo và chạy container — cùng 1 daemon quản lý runtime đó tên containerd.
Nói cách khác, containerd không phải một runtime mới mọc lên để cạnh tranh với Docker — nó chính là bộ phận đã âm thầm làm việc bên trong Docker từ trước tới giờ. Mọi lần bạn gõ docker run, Docker CLI chỉ đứng ra nhận lệnh rồi chuyển xuống cho containerd + runc thực thi thật sự. Nhớ kỹ chi tiết này trước khi đọc phần lịch sử tiếp theo — nó là chìa khóa để hiểu vì sao Docker gỡ được khỏi Kubernetes mà container vẫn chạy bình thường.
Lịch sử: từ Docker-only tới CRI
Ban đầu chỉ có Docker (và vài tool khác như rkt — container runtime của CoreOS), nhưng trải nghiệm người dùng vượt trội giúp Docker gần như độc chiếm thị trường. Kubernetes ra đời để orchestrate Docker — ban đầu Kubernetes gọi thẳng vào API riêng của Docker, chưa hề có khái niệm "chuẩn chung" nào cả.
Rắc rối lộ ra khi Kubernetes phổ biến hơn: rkt và các runtime khác cũng muốn được Kubernetes hỗ trợ. Nếu cứ viết riêng 1 đoạn code tích hợp cho từng runtime, Kubernetes sẽ phải bảo trì hàng chục nhánh tích hợp khác nhau mãi mãi. Giải pháp là Container Runtime Interface (CRI) — 1 chuẩn giao tiếp chung, để bất kỳ runtime nào tuân thủ đúng đều cắm được vào Kubernetes mà không cần Kubernetes biết chi tiết bên trong runtime đó. Đi kèm CRI là chuẩn OCI (Open Container Initiative), gồm 2 phần:
- Image spec: quy định cách 1 image phải được build.
- Runtime spec: quy định chuẩn để phát triển 1 container runtime.
rkt và các runtime tuân OCI khác lập tức cắm được vào CRI. Nhưng Docker thì không — không phải vì containerd bên trong nó kém, mà vì lớp CLI/API của Docker ra đời trước khi CRI tồn tại, và chưa từng được thiết kế để nói chuyện qua chuẩn này. Kubernetes vẫn phải tiếp tục hỗ trợ Docker (lúc đó vẫn là công cụ thống trị), nên giới thiệu Dockershim — 1 lớp dịch tạm thời, đứng giữa để kubelet (nói chuyện qua CRI) và Docker Engine (chỉ hiểu API riêng của nó) hiểu được nhau.
💡 Hình dung như thế này: CRI giống 1 chuẩn ổ cắm điện quốc tế — bất kỳ thiết bị nào (containerd, CRI-O, rkt...) có phích cắm đúng chuẩn (tuân OCI) đều dùng được ngay. Docker ra đời trước khi có chuẩn ổ cắm này, dùng phích cắm kiểu cũ — nên cần 1 bộ chuyển đổi tạm thời (Dockershim) để vẫn cắm được vào ổ chuẩn mới, cho tới khi bộ chuyển đổi đó bị tháo bỏ hẳn.
Vậy vì sao cuối cùng Kubernetes vẫn gỡ được Docker?
Câu trả lời nằm ngay ở phần "Docker thực chất gồm những gì" phía trên: containerd — thứ thực sự chạy container bên dưới Docker — vốn đã tuân CRI và hoạt động độc lập được, không cần thông qua lớp CLI/API của Docker. Nghĩa là kubelet hoàn toàn có thể nói chuyện thẳng với containerd, bỏ qua toàn bộ Docker Engine và lớp Dockershim ở giữa, mà container vẫn chạy y hệt như trước.
Một khi điều này được nhận ra rõ ràng, việc tiếp tục duy trì Dockershim chỉ còn là gánh nặng bảo trì không cần thiết — mỗi lần Docker đổi API nội bộ, Kubernetes lại phải chạy theo sửa lớp dịch cho khớp. Vì vậy, ở phiên bản Kubernetes 1.24, Dockershim bị gỡ bỏ hoàn toàn khỏi kubelet — Docker, với vai trò container runtime được khai báo trực tiếp, không còn được Kubernetes hỗ trợ nữa.
💡 Không có nghĩa là Docker biến mất, và cũng không có nghĩa container ngừng chạy. Phần "runtime" nằm trong Docker — chính là containerd — vẫn tiếp tục chạy dưới các node Kubernetes như bình thường, chỉ khác là giờ kubelet nói chuyện thẳng với containerd, không còn đi vòng qua lớp CLI/API/Dockershim của Docker nữa.
⚠️ Lưu ý: tất cả image đã build bằng Docker trước đó vẫn tiếp tục hoạt động, vì Docker tuân theo image spec của chuẩn OCI — image build bởi Docker vẫn theo đúng chuẩn nên tiếp tục chạy được với containerd. Chỉ có lớp Docker Engine (CLI/API/Dockershim) bị gỡ khỏi Kubernetes, không phải image do Docker build, và cũng không phải containerd.
Hiện tại containerd, dù ban đầu là 1 phần của Docker, đã là 1 dự án riêng biệt, thành viên của CNCF với trạng thái graduated — có thể cài containerd độc lập mà không cần cài Docker.
Không có Docker thì dùng CLI tool nào?
ctr — đi kèm sẵn khi cài containerd. Tool này chỉ dành cho việc debug containerd, không thân thiện người dùng, chỉ hỗ trợ 1 tập tính năng giới hạn. Muốn tương tác gì khác ngoài tập tính năng giới hạn đó phải tự gọi API trực tiếp.
ctr images pull <địa chỉ image> # ví dụ pull image Redis
ctr run <địa chỉ image> # chạy container
nerdctl — CLI giống Docker dành cho containerd, dùng cho mục đích thông thường (tạo container...), hỗ trợ cùng hoặc nhiều tính năng hơn Docker CLI (image mã hóa, lazy pulling, P2P image distribution, ký/verify image, namespace trong Kubernetes — tính năng Docker không có). Chỉ cần thay docker bằng nerdctl trong hầu hết các lệnh:
nerdctl run ... # thay cho docker run
nerdctl run -p ... # option -p (port mapping) hoạt động tương tự docker run
crictl (cri control) — công cụ command-line từ cộng đồng Kubernetes (khác ctr/nerdctl vốn từ cộng đồng containerd), dùng để tương tác với bất kỳ container runtime nào tương thích CRI (không chỉ containerd). Phải cài riêng, dùng để inspect/debug container runtime, hoạt động song song với kubelet.
⚠️ Lưu ý: đừng dùng
crictlđể tạo container thay Docker/nerdctl trong thực tế — nếu bạn tự tạo container bằng crictl, cuối cùng kubelet sẽ xóa chúng đi, vì kubelet không biết về những container/pod được tạo ngoài phạm vi quản lý của nó (kubelet đảm bảo đúng số lượng container/pod mong đợi trên node).
Cú pháp crictl khá giống Docker:
crictl ps # tương tự docker ps — liệt kê container
crictl exec -i -t <id> ... # tương tự docker exec, chạy lệnh trong container
crictl logs # tương tự docker logs
crictl pods # liệt kê pod — khái niệm Docker không có
Cũng như docker ps, crictl ps trả về danh sách container kèm ID, image, trạng thái, thời gian tạo — chỉ khác là danh sách này lấy trực tiếp từ container runtime (containerd/CRI-O...) chứ không qua Docker daemon. Riêng crictl pods là lệnh Docker hoàn toàn không có, vì Docker không có khái niệm pod — pod là đơn vị của Kubernetes.
Rất nhiều lệnh khác (attach, exec, images, info, inspect, logs, ps, stats, version, create, remove, start, stop...) hoạt động gần như giống hệt giữa Docker và crictl.
crictl kết nối tới runtime tương thích CRI qua socket; nếu không cấu hình gì, thứ tự thử kết nối mặc định hiện tại là: containerd → CRI-O → cri-dockerd (dockershim đã bị loại khỏi danh sách mặc định kể từ khi bị gỡ ở Kubernetes 1.24). Tài liệu chính thức khuyến cáo không nên dựa vào default — mỗi lần thử 1 endpoint không tồn tại sẽ tốn vài giây timeout trước khi thử tiếp endpoint kế, ảnh hưởng hiệu suất. Nên luôn set tường minh qua option --runtime-endpoint khi chạy crictl, biến môi trường CONTAINER_RUNTIME_ENDPOINT, hoặc file /etc/crictl.yaml.
Rốt cuộc, dùng công cụ nào cho đúng việc?
| Tool | Cộng đồng phát triển | Mục đích | Phạm vi |
|---|---|---|---|
ctr | containerd | Debug only, tính năng giới hạn — hầu như không cần dùng | Chỉ containerd |
nerdctl | containerd | Dùng thông thường, giống Docker CLI, khuyến khích dùng thay Docker | Chỉ containerd |
crictl | Kubernetes | Debug/inspect, hoạt động cùng kubelet | Mọi runtime tương thích CRI |
Nhớ đơn giản thế này: ctr để debug, nerdctl để làm việc, crictl để soi khi có sự cố — và soi được ở bất kỳ runtime nào, không riêng containerd. Các lab trước đây dùng Docker cài trên toàn bộ node nên dùng lệnh Docker để troubleshoot; giờ mọi thứ chuyển sang containerd nên cần dùng crictl để troubleshoot thay thế.
5. Vì sao vẫn nói về Docker dù đã bị Deprecate?
Một câu hỏi phổ biến: vì sao tài liệu này vẫn nhắc tới Docker nếu nó đã bị deprecate khỏi Kubernetes?
Docker vốn là container runtime gốc và duy nhất được Kubernetes hỗ trợ. Để mở Kubernetes ra cho các runtime khác, Container Runtime Interface (CRI) được giới thiệu. Docker thực chất gồm nhiều tool gộp lại: Docker CLI, API, build tool giúp build image, hỗ trợ volume/auth/security, và cuối cùng là container runtime runc, cùng daemon quản lý nó là containerd.
Khi điều này được làm rõ, Kubernetes không còn cần các tool khác của Docker nữa (mọi việc đã được chính Kubernetes tự đảm nhiệm), nên Kubernetes deprecate luôn phần hỗ trợ Docker.
💡 Không có nghĩa là Docker biến mất. Docker vẫn là giải pháp container phổ biến nhất, vẫn được dùng rộng rãi trong công việc dev/build hằng ngày — chỉ là Kubernetes không còn yêu cầu Docker làm runtime nữa.
Vì vậy, xuyên suốt tài liệu này, mỗi khi nói về container, ví dụ vẫn dùng Docker — hoàn toàn ổn, vì đôi khi cần học cách container hoạt động trước, rồi mới tới cách nó vận hành trong thế giới container orchestration của Kubernetes. Nếu máy bạn không có Docker, hoặc chỉ dùng containerd, vẫn có thể chạy đúng các ví dụ đó bằng cách thay lệnh docker bằng lệnh nerdctl trong hầu hết trường hợp. Công cụ có thể đổi, nhưng khái niệm container đằng sau thì không đổi — đó mới là thứ đáng học trước.
Nguồn tham khảo
Nguồn gốc: Module "Kubernetes Overview" (5 bài: Containers Overview, Container Orchestration, Kubernetes Architecture, Docker-vs-ContainerD, A Note on Docker Deprecation) — khóa "Kubernetes for the Absolute Beginners - Hands-on Tutorial", nền tảng KodeKloud.
Đính chính/cập nhật (fact-check):
- Thứ tự default runtime endpoint của
crictlhiện tại là containerd → CRI-O → cri-dockerd, dockershim đã bị loại khỏi danh sách mặc định từ Kubernetes 1.24 — 2026, cri-tools/crictl.md - Dockershim chính thức bị gỡ khỏi kubelet ở Kubernetes 1.24 — 2022, Kubernetes 1.24 Release Announcement