Skip to main content

8. Kubeadm

Mục lục


Kubeadm

1. Tổng quan về kubeadm

Kubeadm là công cụ dùng để bootstrap một Kubernetes cluster. Kubeadm giúp thiết lập một cluster multi-node theo đúng các best practice của Kubernetes.

Kubernetes cluster gồm nhiều thành phần như kube-apiserver, etcd, các controller,... và có các yêu cầu về bảo mật, certificate để các thành phần này giao tiếp được với nhau. Việc cài đặt riêng lẻ từng thành phần trên các node khác nhau, chỉnh sửa file cấu hình để các thành phần trỏ đúng vào nhau, và thiết lập certificate để mọi thứ hoạt động, là một công việc tốn nhiều công sức. Kubeadm giúp lo hết các việc đó.

💡 Hình dung như thế này: Dựng một Kubernetes cluster bằng tay giống như tự lắp một dàn âm thanh rời — phải tự nối đúng dây tín hiệu, đặt đúng cấu hình cho từng thiết bị, và đảm bảo mọi thứ nói chung một "ngôn ngữ". Kubeadm giống một bộ dàn lắp sẵn theo đúng chuẩn nhà sản xuất — vẫn là những thành phần đó (kube-apiserver, etcd, certificate...), nhưng phần đấu nối và cấu hình đã được tự động hóa theo đúng thứ tự.

Các bước cài đặt Kubernetes cluster bằng kubeadm ở mức tổng quan:

  1. Phải có nhiều máy (system) hoặc virtual machine đã được provision sẵn để cấu hình cluster.
  2. Sau khi các máy đã được tạo, chỉ định một node làm master, các node còn lại làm worker node.
  3. Cài container runtime trên tất cả các host.
  4. Cài công cụ kubeadm trên tất cả các node. Kubeadm giúp bootstrap giải pháp Kubernetes bằng cách cài đặt và cấu hình tất cả các thành phần cần thiết trên đúng node, theo đúng thứ tự.
  5. Khởi tạo (initialize) master server. Trong quá trình này, tất cả các thành phần cần thiết được cài đặt và cấu hình trên master server.
  6. Trước khi join các worker node vào master, phải đảm bảo network prerequisite đã được đáp ứng. Kết nối mạng thông thường giữa các máy là chưa đủ — Kubernetes yêu cầu một giải pháp networking đặc biệt giữa master và worker node, gọi là pod network.
  7. Bước cuối cùng là join các worker node vào master node.
kubeadm Bootstrap Flow1. Provision VMs / Nodes2. Designate 1 Master + N Worker Nodes3. Install Container Runtime (all nodes)4. Install kubeadm, kubelet, kubectl (all nodes)5. kubeadm init (Master / Control Plane)6. Install Pod Network (CNI plugin)7. Join Worker Nodes to MasterCluster Ready

Sau khi hoàn tất, cluster đã sẵn sàng để launch ứng dụng lên môi trường Kubernetes.


2. Chuẩn bị 3 VM trên Windows bằng VirtualBox và Vagrant

Để cài một Kubernetes cluster, cần ít nhất 2-3 Kubernetes node — về cơ bản là các Linux server. Thay vì mua thêm máy vật lý, dùng virtualization để tự dựng "máy trong máy" ngay trên máy Windows hiện có, cần 3 Linux server.

Cần 2 công cụ:

Windows: VirtualBox + Vagrant tạo 3 VMVagrantvagrant up (đọc Vagrantfile)VirtualBox (Hypervisor)control plane192.168.86.32node1192.168.86.33node2192.168.86.34Windows Host

Hypervisor — VirtualBox:

Hypervisor là công cụ quản lý các virtual machine. Ở đây dùng VirtualBox — miễn phí, chạy được trên nhiều platform (Windows, macOS, Linux).

Lưu ý riêng cho Apple Silicon (M1, M2, M3, M4): VirtualBox có một số vấn đề khi chạy trên các chip này — phần macOS sẽ dùng công cụ khác là Multipass để tránh vấn đề đó (xem mục 3). Với máy Mac dùng chip Intel đời cũ thì vẫn dùng VirtualBox bình thường được.

Cài đặt VirtualBox: vào virtualbox.org → Download → chọn bản cho Windows hosts → chạy installer → Next → Accept the terms in the license agreement → Next → giữ nguyên default → có cảnh báo tạm mất kết nối mạng (vì hypervisor cần chỉnh lại networking) → Yes → Yes → chọn các thành phần muốn cài (giữ default) → Install → Finish.

VirtualBox là hypervisor, cho phép tạo VM Linux, Windows, hay cả macOS bằng cách bấm New và đi qua từng bước cài đặt — tương tự cài một OS mới trên một server mới. Việc này khá tốn thời gian, nhất là khi cần tới 3 server.

Automation tool — Vagrant:

Thay vì tạo từng VM thủ công, dùng Vagrant — công cụ tự động hóa việc provision VM. Vagrant cho phép định nghĩa toàn bộ cấu hình VM mong muốn trong 1 file, chạy 1 lệnh, và Vagrant sẽ tự spin up nhiều VM theo đúng cấu hình đó.

Cài đặt Vagrant: vào developer.hashicorp.com/vagrant (hoặc search "Vagrant" trên Google) → Install → chọn bản cho Windows (có 2 kiến trúc AMD64 và i686, chọn bản phù hợp) → chạy installer → Accept the terms in the license agreement → Install → Finish → cần restart máy để có hiệu lực → Finish → Yes, restart now.

Sau khi máy khởi động lại, mở terminal (Command Prompt) và kiểm tra cài đặt bằng vagrant --help hoặc vagrant --version:

vagrant --version
# Output minh họa
Vagrant 2.4.6

Lấy Vagrantfile và tạo 3 VM:

Trong repo GitHub của khóa học, đi vào thư mục kubeadm-clusters, rồi vào thư mục VirtualBox — trong đó có sẵn Vagrantfile, sẽ tự động provision các VM: 1 VM cho master node, và các VM cho worker node, mỗi VM có IP address riêng.

git clone <URL-repo>
cd kubeadm-clusters
cd VirtualBox

Trước khi chạy tiếp, phải đảm bảo VirtualBox đang chạy — vì Vagrant sẽ tự kết nối tới VirtualBox để tạo VM giúp bạn, dựa theo đúng những gì đã khai báo trong Vagrantfile.

Khi đứng đúng thư mục chứa Vagrantfile, chỉ cần chạy:

vagrant up

Lệnh này provision 3 VM tự động: control plane, node one, node two (có thể xuất hiện pop-up xác nhận — chọn Yes).

Nếu có lỗi trong quá trình build, xem log để debug; nếu cần rebuild lại từ đầu:

vagrant destroy -f
vagrant up

Sau khi hoàn tất, xem danh sách VM đã tạo:

vagrant status

Kết quả hiển thị 3 node: control plane, node one, node two. Mở VirtualBox GUI cũng thấy đúng 3 VM được tạo.

Kết nối tới các VM:

vagrant ssh <tên-VM>

Ví dụ, kết nối vào control plane:

vagrant ssh controlplane

Kết nối vào 2 worker node (mở thêm terminal cho mỗi node):

vagrant ssh node1
vagrant ssh node2

Khuyến nghị dùng 3 terminal riêng biệt, mỗi terminal cho 1 node (control plane, node1, node2), đặt tên tab tương ứng để dễ theo dõi.

Kiểm tra IP addressing:

Trên mỗi node, chạy:

ip addr

Sẽ thấy 2 network interface. Interface đầu tiên (enp0s3) có IP 10.0.2.15 — giống nhau trên tất cả các node, đây là interface nội bộ do Vagrant dùng, bỏ qua interface này. Interface còn lại mới dùng để giao tiếp giữa các node — ví dụ trong demo: control plane 192.168.86.32, node1 192.168.86.33, node2 192.168.86.34.

Xóa VM khi không cần nữa:

vagrant destroy -f

Tóm lại: vagrant up để dựng môi trường, vagrant destroy -f để xóa toàn bộ.


3. Chuẩn bị 3 VM trên macOS bằng Multipass

Phần này dành cho máy Mac dùng chip Apple Silicon (dòng chip M).

Lưu ý về RAM: nếu máy Mac chỉ có 8GB RAM, script triển khai sẽ tự động phát hiện và chỉ tạo 1 server duy nhất thay vì 3, do giới hạn bộ nhớ. Khi đó, Kubernetes sẽ được cài trên 1 node duy nhất thay vì 3 node.

Công cụ dùng cho macOS: Multipass — cho phép tạo các VM Ubuntu-based dễ dàng.

macOS: Multipass tạo 3 VM./deploy-virtual-machines.shdùng Multipass để provision VMRAM ≤ 8GB máy Mac→ chỉ tạo 1 nodeMultipass (VM manager)control plane192.168.86.40node01192.168.86.41node02192.168.86.42macOS Host (Apple Silicon)

Cài đặt Multipass:

Vào canonical.com/multipass → Install → chọn bản cho macOS, tải "Multipass for macOS" → mở installer → Continue → Continue → agree Terms of Service → Install → nhập password → cài đặt thành công → đóng lại (có thể chuyển file installer vào Trash).

Kiểm tra cài đặt: mở Terminal app (hoặc iTerm) và chạy:

multipass version
# Output minh họa
multipass 1.15.1
multipassd 1.15.1

Clone repo và chạy script deploy VM:

Trong repo GitHub, vào thư mục kubeadm-clusters, rồi vào thư mục Apple Silicon — có sẵn script chịu trách nhiệm tạo VM (dùng Multipass để cấu hình network và provision VM):

git clone <URL-repo>
cd kubeadm-clusters
cd "Apple Silicon"
./deploy-virtual-machines.sh

Sau khi script chạy xong, xác nhận bằng:

multipass list

Kết quả hiển thị 3 node: control plane, node one, node two — kèm IP address và image được dùng (Ubuntu 22.04 LTS).

Kết nối tới VM:

multipass shell <tên-VM>

Kết nối vào 2 worker node:

multipass shell node01
multipass shell node02

Khuyến nghị mở 3 terminal, mỗi cái gán cho 1 node (control plane, node01, node02).

Kiểm tra IP addressing:

ip addr

Mỗi node luôn có 2 interface — bỏ qua interface đầu tiên (không dùng để giao tiếp giữa các node). Interface còn lại (enp0s2 trong demo) dùng cho giao tiếp giữa các node, ví dụ IP 192.168.86.40 cho control plane.

Script cũng tự động thiết lập sẵn host entry cho từng server:

cat /etc/hosts

Trong demo: control plane ứng với .40, node1 ứng với .41, node2 ứng với .42. Có thể dùng IP address trực tiếp hoặc dùng host entry khi làm việc với kubeadm.

Xóa VM khi không cần nữa:

Trong repo, vào kubeadm-clustersApple SiliconDocs, mục Compute resources có hướng dẫn tear down môi trường:

cd "Apple Silicon"
./delete-virtual-machines.sh

Sau khi chạy xong, còn 1 bước thủ công cuối cùng mà script không tự làm được: dọn các stale DHCP lease — vào file được chỉ ra trong tài liệu Compute resources và xóa hết các entry trong file đó.


4. Cài container runtime containerd và kubeadm, kubelet, kubectl

Sau khi có 3 server sẵn sàng (control plane, node1, node2 — tiếp nối từ mục 2 hoặc 3), bắt đầu cài Kubernetes bằng kubeadm.

Tài liệu tham chiếu:

Trong repo của khóa học, vào thư mục kubeadm-clusters, rồi vào thư mục generic — có sẵn các file hướng dẫn từng bước cài Kubernetes cluster bằng kubeadm.

Các hướng dẫn này được tổng hợp từ nhiều trang trong tài liệu chính thức của Kubernetes, theo đường dẫn: Getting Started → Production Environment → Installing Kubernetes with Deployment Tools → Bootstrapping Clusters with kubeadm → Creating a Cluster with kubeadm.

Nếu tạo cluster với nhiều control plane node, có một trang tài liệu riêng là Creating Highly Available Clusters with kubeadm — các bước gần như giống hệt, chỉ thêm 1 bước khi thêm control plane node thứ 2. Ở đây chỉ dùng 1 control plane node duy nhất nên theo trang Creating a Cluster with kubeadm.

Cần mở thêm trang Installing kubeadm — hướng dẫn cài các package kubeadm, kubelet, kubectl.

Cài container runtime + kubeadm trên cả 3 nodeapt update+ curl, ca-certapt installcontainerdSystemdCgroup= trueconfig.tomlsystemctl restartcontainerdapt installkubeadm kubeletkubectlChạy trên cả 3 node — control plane, node1, node2

⚠️ Lưu ý khi thi chứng chỉ: trong phòng thi sẽ không truy cập được vào repo GitHub của khóa học, chỉ có tài liệu chính thức của Kubernetes — nên cần nhớ rõ các trang tài liệu nêu trên nằm ở đâu.

Bước 1 — Cài container runtime (containerd):

Yêu cầu đầu tiên là cài container runtime trên tất cả các node. Có nhiều container runtime được hỗ trợ, ở đây dùng containerd.

Trước khi cài, cần cấu hình một số thứ, ví dụ bật IPv4 packet forwarding.

sudo apt update

Cài thêm một số tool cần thiết (thường đã có sẵn trên hệ thống, nhưng cứ cài để chắc chắn) — ví dụ curl, ca-certificates,... Chạy trên cả 3 node.

Tiếp theo, thiết lập kernel module và kernel parameter (ví dụ bật IP forwarding) — chạy trên cả 3 node. Sau đó set các kernel parameter cần thiết (bật IPv4 và một vài thứ khác).

Cài containerd:

sudo apt install containerd

Chạy trên cả 3 node.

Bước 2 — Cấu hình systemd cgroup driver cho containerd:

Có 2 loại cgroup driver: cgroupfssystemd. Nếu hệ thống dùng systemd (init system), container runtime phải dùng systemd cgroup driver để khớp với nhau. Việc này phải cấu hình ở 2 chỗ: container runtime, và kubelet.

💡 Hình dung như thế này: cgroup driver giống như cách 2 người cùng ghi chép vào chung một cuốn sổ quản lý tài nguyên của node — nếu mỗi người dùng một định dạng ghi khác nhau (container runtime dùng cgroupfs, còn hệ thống lại vận hành bằng systemd), sổ sách sẽ nhanh chóng lệch nhau. Thống nhất cả 2 phía cùng dùng systemd giống như bắt cả 2 người dùng chung 1 mẫu sổ.

Về phía kubelet: bắt đầu từ Kubernetes 1.22 trở đi, khi tạo cluster bằng kubeadm, nếu không set field cgroupDriver trong KubeletConfiguration, giá trị mặc định sẽ là systemd — đúng như mong muốn, nên không cần cấu hình gì thêm cho kubelet.

Về phía containerd: file cấu hình mặc định nằm ở /etc/containerd/config.toml. Cần thêm dòng sau vào dưới mục plugins tương ứng:

SystemdCgroup = true

Có thể tự tạo file cấu hình mặc định bằng lệnh:

containerd config default

rồi pipe kết quả vào file config.toml, sau đó chỉnh giá trị SystemdCgroup theo ý muốn. Có thể gộp toàn bộ việc này trong 1 bước — tạo thư mục /etc/containerd, chạy containerd config default, và đổi giá trị SystemdCgroup từ false sang true:

sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

Chạy lệnh này trên cả 3 node. Sau khi sửa xong, restart containerd để áp dụng thay đổi:

sudo systemctl restart containerd

Kiểm tra lại cấu hình đã áp dụng đúng chưa:

cat /etc/containerd/config.toml | grep -i cgroup

Kết quả mong đợi:

SystemdCgroup = true

Bước 3 — Cài kubeadm, kubelet, kubectl:

Trước tiên, lấy version Kubernetes mới nhất muốn cài (hoặc chỉ định version cụ thể, ví dụ 1.33). Có thể lưu version mới nhất vào 1 biến môi trường tên kube_latest:

echo $kube_latest
# 1.33

Quay lại trang Installing kubeadm trong tài liệu Kubernetes (bản 1.30 trong demo) — hướng dẫn cài kubeadm, kubelet, kubectl: thiết lập apt repository, thiết lập public signing key, sau đó:

sudo apt install kubelet kubeadm kubectl

Về mặt kỹ thuật, không bắt buộc phải cài kubectl trên tất cả các node — chỉ cần có trên control plane node (hoặc trên máy nào muốn dùng để quản lý cluster) là đủ. Nhưng kubeletkubeadm thì bắt buộc phải cài trên cả 3 node.

Tùy chọn: cài thêm crictl để xem trực tiếp các container đang chạy trên node — hữu ích khi troubleshoot lúc kube-apiserver bị down (khi đó kubectl sẽ không hoạt động được, nhưng vẫn có thể kiểm tra container bằng crictl).


5. Xử lý interface mạng đặc thù và khởi tạo control plane node

Vấn đề interface mạng trong môi trường demo:

Mỗi node (dù dùng Vagrant hay Multipass) đều có 2 network interface, kiểm tra bằng:

ip addr

Interface đầu tiên chỉ dùng nội bộ cho automation tool (Vagrant/Multipass), không dùng để giao tiếp giữa các node trong cluster. Kiểm tra route mặc định:

ip route

Sẽ thấy route mặc định đang trỏ ra qua interface đầu tiên — đây là vấn đề, vì khi cài Kubernetes (kubelet, kube-apiserver,...), các thành phần này sẽ mặc định dùng interface mà hệ thống dùng để ra Internet, tức interface đầu tiên — trong khi ta muốn chúng dùng interface thứ 2 để giao tiếp.

💡 Hình dung như thế này: Mỗi node có 2 "cửa" ra vào: một cửa nội bộ do Vagrant/Multipass tạo ra chỉ để công cụ đó quản lý VM, và một cửa thật để các node nói chuyện với nhau. Nếu không chỉ rõ, kubeadm sẽ "gõ nhầm cửa" — dùng cửa nội bộ vì đó là cửa quen thuộc mà hệ thống dùng để ra Internet. PRIMARY_IP chính là địa chỉ của cửa đúng, còn --apiserver-advertise-address--node-ip là cách dán nhãn rõ ràng để mọi thành phần đều gõ đúng cửa đó.

Tất cả các VM trong môi trường demo đều có sẵn 1 biến môi trường tên PRIMARY_IP, trỏ tới IP của interface thứ 2:

echo $PRIMARY_IP
# Output minh họa
192.168.86.40

(Giá trị cụ thể tùy môi trường — ví dụ trên lấy theo IP control plane trong demo Multipass ở mục 3.)

Cần cấu hình để kubelet dùng đúng interface thứ 2 này, bằng cách truyền flag --node-ip khi kubelet khởi động, trỏ tới giá trị của PRIMARY_IP — ví dụ trong /etc/default/kubelet hoặc drop-in tương ứng:

KUBELET_EXTRA_ARGS=--node-ip=$PRIMARY_IP

Chạy cấu hình này trên tất cả các node (control plane lẫn worker) trước khi khởi tạo cluster.

Node có 2 NIC — kubeadm phải trỏ đúng NIC #2Node (VM)enp0s3 — NIC #110.0.2.15 (Vagrant/Multipass nội bộ)enp0s2 — NIC #2PRIMARY_IP (vd 192.168.86.40)dùng để giao tiếp giữa các nodeRoute mặc định / Internetkubeadm init dùng nếu không set flag--apiserver-advertise-address--node-ip = $PRIMARY_IP❌ sai (mặc định)✅ đúng (chỉ định flag)Phải trỏ cả 2 flag về đúng NIC #2 (PRIMARY_IP) trước khi kubeadm init

Khởi tạo control plane node:

Luôn khởi tạo control plane node trước tiên. Lệnh khởi tạo cơ bản:

kubeadm init

Nếu để mặc định, kubeadm init sẽ tự bind vào interface đầu tiên (dùng để ra Internet) — không đúng ý muốn trong môi trường demo này. Cần truyền thêm flag để chỉ định đúng interface thứ 2, đó là flag:

--apiserver-advertise-address=$PRIMARY_IP

Flag này báo cho tất cả các node khác biết địa chỉ IP mà kube-apiserver sẽ lắng nghe.

Kubeadm còn có 1 flag khác dễ nhầm với flag trên là --control-plane-endpoint — đây không phải cùng một thứ: --control-plane-endpoint dùng để khai báo 1 endpoint cố định (DNS name hoặc IP) cho control plane, chủ yếu hữu ích khi có kế hoạch nâng cấp cluster lên nhiều control plane node (HA) sau này. Với 1 control plane node như trong bài này, flag cần dùng để bind đúng interface là --apiserver-advertise-address.

Ngoài ra còn 2 flag để chỉ định dải CIDR mong muốn cho pod network và cho service:

--pod-network-cidr=<pod-CIDR>
--service-cidr=<service-CIDR>

Pod CIDR dùng trong demo là 10.244.0.0/16 (giá trị này cũng sẽ được dùng lại khi cấu hình Calico ở mục 6). Nếu không truyền --service-cidr, kubeadm mặc định dùng 10.96.0.0/12 cho dải IP của Service.

Lệnh khởi tạo đầy đủ:

kubeadm init \
--apiserver-advertise-address=$PRIMARY_IP \
--pod-network-cidr=10.244.0.0/16 \
--service-cidr=<service-CIDR>

Chạy lệnh này chỉ trên control plane node. Quá trình này mất một khoảng thời gian.

Sau khi hoàn tất, kubeadm init sẽ in ra:

  • Câu lệnh để join worker node vào cluster — cần copy và lưu lại (ví dụ lưu vào notes) để dùng sau. Nếu làm mất câu lệnh này, có thể generate lại (xem mục 7 bên dưới).
  • Hướng dẫn để bắt đầu sử dụng cluster: tạo thư mục .kube trong home directory, và copy file kubeconfig (được kubeadm tạo ra trong quá trình init) vào đó để kubectl dùng được.

Kiểm tra:

kubectl get node
# Output minh họa
NAME STATUS ROLES AGE VERSION
controlplane NotReady control-plane 45s v1.33.0

(Output minh họa — cấu trúc và tên field đúng theo lệnh thật, số liệu cụ thể chỉ mang tính ví dụ.)

Trạng thái NotReady là vì chưa cài CNI plugin. Sau khi cài CNI plugin (mục 6), node sẽ chuyển sang Ready. Cột ROLES hiển thị control-plane; chưa thấy worker node nào vì chưa join.


6. Cài CNI plugin Calico

Có thể dùng bất kỳ CNI plugin nào tuân theo chuẩn Kubernetes — ở đây dùng Calico.

💡 Hình dung như thế này: kube-apiserver, etcd, kubelet,... giao tiếp được với nhau qua network sẵn có của các node — nhưng Pod thì chưa có "con đường" nào để nói chuyện với Pod trên node khác. CNI plugin như Calico giống như việc xây thêm một hệ thống đường nội bộ chỉ dành riêng cho Pod đi lại, tách biệt khỏi network vật lý bên dưới. Đó là lý do trước khi cài CNI, node đã "sống" nhưng vẫn báo NotReady.

Tài liệu Calico: docs.tigera.io/calico → Installing and UpgradingInstalling on-premises deployments.

Cài CNI plugin CalicoControl PlaneNotReadyInstall Operator+ CRDsEdit YAMLIPPool podCIDR10.244.0.0/16kubectl createcustom-resources.yamlNodeReady ✅Pod CIDR trong custom-resources.yaml phải khớp --pod-network-cidr lúc kubeadm init

Bước 1 — cài operator và CustomResourceDefinitions, chạy trên control plane node.

Bước 2 — tải file cấu hình CNI về máy:

custom-resources.yaml

File này chứa cấu hình mặc định của Calico. Giữ nguyên hầu hết cấu hình mặc định, chỉ cần chỉnh 1 chỗ: mục IPPool — khai báo dải pod CIDR mà Calico sẽ dùng. Vì đã set --pod-network-cidr=10.244.0.0/16 khi kubeadm init, nên sửa IPPool trong custom-resources.yaml khớp thành 10.244.0.0/16.

Với cluster dựng bằng kubeadm, bước sửa IPPool này thực ra không bắt buộc — Calico tự phát hiện đúng pod CIDR đã cấu hình lúc kubeadm init. Sửa tay như trên vẫn đúng và không hại gì, chỉ là không phải bước bắt buộc riêng cho kubeadm.

Áp dụng cấu hình:

kubectl create -f custom-resources.yaml

Việc deploy Calico cần một khoảng thời gian — nhiều thành phần control plane sẽ được restart lại. Trong lúc đó:

kubectl get nodes

có thể bị treo (hang) một lúc do API server đang restart. Đợi vài phút rồi thử lại — lúc này sẽ thấy 1 node ở trạng thái Ready.

Kiểm tra pod ở tất cả namespace:

kubectl get pods -A

Sẽ thấy nhiều pod của Calico đã được deploy. Có thể thấy một số pod ở trạng thái "evicted" — không cần lo, các pod này sẽ tự được dọn dẹp sau đó.


7. Join worker node vào cluster

Sau khi CNI đã cài xong trên control plane, tiếp tục join 2 worker node còn lại.

Theo tài liệu Creating a Cluster with kubeadm: sau bước cài pod network add-on, có thể thêm control plane node khác (không cần trong demo này vì chỉ dùng 1 control plane), rồi tới bước join worker node — có hướng dẫn riêng cho Linux và Windows, ở đây chọn Linux.

Nếu đã lưu lại câu lệnh join từ output của kubeadm init (ở mục 5), dùng lại câu lệnh đó. Nếu bị mất, generate lại bằng lệnh sau (chạy trên control plane node):

kubeadm token create --print-join-command

💡 Hình dung như thế này: Token sinh ra từ lệnh trên giống như một mã mời có hạn dùng (mặc định 24 giờ) — worker node dùng mã đó để chứng minh với control plane "tôi được phép gia nhập cluster này", chứ không phải cứ biết địa chỉ IP của control plane là join được.

Copy câu lệnh join được in ra, chạy trên node1node2 với sudo (cần quyền sudo để join).

Join worker node vào clusterControl Planetoken create --print-join-commandjoin command (sudo)node1kubeadm join ...join command (sudo)node2kubeadm join ...kubectl get nodes → 3 node, chuyển dần sang Ready

Sau khi join xong cả 2 node, quay lại control plane node, kiểm tra:

kubectl get nodes

Sẽ thấy 3 node. 2 node join trước ở trạng thái Ready; node vừa join xong cần thêm một chút thời gian để chuyển sang Ready (đợi Calico pod được deploy lên node đó để networking hoạt động).


8. Kiểm tra cluster hoạt động

Deploy thử một pod NGINX để xác nhận mọi thứ hoạt động đúng: tạo một deployment dùng image NGINX, sau đó tạo một NodePort service để có thể truy cập từ bên ngoài.

Kiểm tra IP của các node:

kubectl get nodes -o wide
# Output minh họa (rút gọn)
NAME STATUS ROLES INTERNAL-IP VERSION
controlplane Ready control-plane 192.168.86.40 v1.33.0
node1 Ready <none> 192.168.86.41 v1.33.0
node2 Ready <none> 192.168.86.42 v1.33.0

(Output minh họa — cấu trúc và tên field đúng theo lệnh thật, số liệu cụ thể chỉ mang tính ví dụ.)

Kiểm tra port được NodePort service gán:

kubectl get service
# Output minh họa
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx NodePort 10.100.5.20 <none> 80:30143/TCP 40s

Ví dụ trong demo, port được gán là 30143 — nghĩa là port 80 bên trong Pod được map ra port 30143 trên tất cả các node. Test truy cập bằng curl tới IP của một node, ở port đó:

curl 192.168.86.41:30143
# Output minh họa (rút gọn)
<!DOCTYPE html>
<html>
<head><title>Welcome to nginx!</title></head>
...

(Output minh họa — cấu trúc và tên field đúng theo lệnh thật, số liệu cụ thể chỉ mang tính ví dụ.)

Thấy được response HTML mặc định của NGINX — xác nhận Service hoạt động đúng. Thử lại với IP của node còn lại (192.168.86.42) cũng cho kết quả tương tự — xác nhận toàn bộ networking của cluster (Calico, kube-proxy, Service) đang hoạt động đúng.

Kiến trúc cluster sau khi hoàn tấtControl Planekube-apiserver · etcd · schedulercontroller-managerCalico (CNI control)node1 (Worker)kubelet · kube-proxyCalico agentPod: nginxnode2 (Worker)kubelet · kube-proxyCalico agentPod: nginxService (NodePort) — 80:30143/TCPcurl <node-ip>:30143

9. Lưu ý khi ôn thi và khi copy lệnh từ repo

Khi ôn thi chứng chỉ, cần nhớ trang tài liệu chính: Bootstrapping Clusters with kubeadm → Creating a Cluster with kubeadm — chứa toàn bộ hướng dẫn và các link liên quan cần dùng. Nếu cần cài một CNI cụ thể, phải tự mở tài liệu riêng của CNI đó.

Đường dẫn trong tài liệu Kubernetes chính thứcSetup → Production EnvironmentInstalling Kubernetes withDeployment ToolsBootstrapping Clusters with kubeadmInstalling kubeadmcài kubeadm/kubelet/kubectlCreating a Clusterwith kubeadm★ theo trang này (1 control plane)Creating a HA Clusterwith kubeadmnếu nhiều control planeCopy lệnh từ repo GitHub? Nhớ copy cả cặp dấu ngoặc nhọn { }đi kèm để sudo không bị mất hiệu lực giữa các lệnh gộp

Nếu copy lệnh trực tiếp từ repo GitHub của khóa học để làm theo: ở đầu trang có một lưu ý là tất cả các lệnh được cung cấp đều được nhóm trong dấu ngoặc nhọn { }. Lý do: trong các trường hợp cần chạy nhiều lệnh gộp với sudo, lệnh sudo sẽ ngừng hoạt động sau lệnh đầu tiên trong nhóm nếu không có cặp ngoặc nhọn bao ngoài. Vì vậy, bất cứ khi nào thấy một nhóm lệnh cần chạy, phải copy luôn cả cặp dấu ngoặc nhọn đi kèm. Nếu dùng nút Copy code trên trang markdown của GitHub, dấu ngoặc nhọn sẽ tự động được copy theo, giúp tránh vấn đề này.


Nguồn tham khảo

Nguồn gốc: Khóa "Kubernetes for the Absolute Beginners" — bài "Kubeadm" (tổng quan kubeadm; chuẩn bị VM trên Windows bằng VirtualBox và Vagrant; chuẩn bị VM trên macOS bằng Multipass; demo cài đặt cluster bằng kubeadm), nền tảng KodeKloud.

Fact-check (Mode R):

  • Tên flag --apiserver-advertise-address, --pod-network-cidr, --service-cidr của kubeadm init — 2026-08-09, kubeadm init reference
  • cgroup driver mặc định systemd từ Kubernetes v1.22 trở đi nếu không set cgroupDriver trong KubeletConfiguration — 2026-08-09, Container runtimes
  • Cấu hình --node-ip cho kubelet qua KUBELET_EXTRA_ARGS trong /etc/default/kubelet (hệ Debian/Ubuntu) — 2026-08-09, Configuring kubelet with kubeadm
  • Path cấu hình containerd /etc/containerd/config.tomlSystemdCgroup = true — 2026-08-09, Container runtimes
  • Tên trang tài liệu chính thức là "Creating Highly Available Clusters with kubeadm" (số nhiều) — 2026-08-09, Creating a Cluster with kubeadm
  • Với cluster dựng bằng kubeadm, Calico tự động detect pod CIDR — sửa IPPool trong custom-resources.yaml là không bắt buộc — 2026-08-09, Calico on-premises install
  • kubectl create -f custom-resources.yaml là lệnh được khuyến nghị chính thức để cài Calico operator — 2026-08-09, Calico on-premises install
  • --service-cidr mặc định là 10.96.0.0/12 nếu không truyền flag — 2026-08-09, kubeadm init reference
  • Token sinh bởi kubeadm token create có hiệu lực mặc định 24 giờ — 2026-08-09, kubeadm join reference
  • --control-plane-endpoint là flag riêng biệt dùng để khai báo endpoint cố định cho control plane khi có kế hoạch nâng cấp lên HA, khác mục đích với --apiserver-advertise-address — 2026-08-09, Creating a Cluster with kubeadm