

Hướng dẫn cài đặt Kubernetes Master-Node trên Ubuntu 24.04.2 bằng Script
Hướng dẫn cài đặt Kubernetes Control Plane + Worker Node trên Ubuntu 24.04.2
Tài liệu này dùng cho môi trường lab/dev với 2 máy ảo Ubuntu 24.04.2: 1 node Control Plane và 1 Worker Node.
Kubernetes hiện không còn khuyến nghị dùng từ “Master” trong tài liệu chính thức; tuy nhiên trong bài này có thể hiểu
k8s-masterlà Control Plane node.
1. Thông tin cập nhật
Bản hướng dẫn cũ dùng Kubernetes v1.29, hiện đã lỗi thời. File README này đã được chỉnh theo hướng:
- Dùng Kubernetes
v1.36thông qua repo mớipkgs.k8s.io. - Không disable/purge AppArmor mặc định.
- Không khuyến nghị disable UFW cho production/staging; chỉ disable nếu đang lab và bạn chủ động bật biến
LAB_DISABLE_UFW=true. - Dùng
containerdlàm container runtime. - Cấu hình
SystemdCgroup = truecho containerd. - Dùng Flannel release cố định thay vì trỏ vào nhánh
mastercủa GitHub. - Bổ sung bước kiểm tra, join node và cleanup rõ ràng hơn.
Ghi chú: Nếu muốn dùng version Kubernetes khác, chỉ cần đổi biến
K8S_VERSION, ví dụv1.35hoặcv1.34. Không nên dùng version đã hết support cho môi trường thật.
2. Mô hình lab đề xuất
| Hostname | Vai trò | IP ví dụ |
|---|---|---|
k8s-master | Control Plane | 192.168.76.10 |
k8s-node-1 | Worker Node | 192.168.76.11 |
Bạn cần thay IP ví dụ bằng IP thật của máy ảo của bạn.
3. Yêu cầu trước khi cài
Trên cả 2 máy Ubuntu:
- Ubuntu
24.04.2. - Tối thiểu 2 CPU, 2 GB RAM cho lab.
- 2 máy ping được nhau bằng IP.
- Có quyền
sudo. - Đã cấu hình IP tĩnh hoặc DHCP reservation.
- Hostname của các node không được trùng nhau.
- Swap phải được tắt.
Kiểm tra nhanh:
free -h
nproc
hostname
ip addr
cat /sys/class/dmi/id/product_uuid
Nếu bạn clone VM, hãy đảm bảo hostname và product_uuid không bị trùng giữa các node.
4. Các file cần tạo
Bạn sẽ tạo 3 file:
common_setup.sh # Chạy trên cả Control Plane và Worker Node
master_setup.sh # Chỉ chạy trên Control Plane node
node_join.sh # Chỉ chạy trên Worker Node
5. Cấu hình hostname và /etc/hosts
Trên Control Plane node
sudo hostnamectl set-hostname k8s-master
Trên Worker Node
sudo hostnamectl set-hostname k8s-node-1
Trên cả 2 máy
Mở file:
sudo nano /etc/hosts
Thêm nội dung, nhớ thay IP theo môi trường thật của bạn:
192.168.76.10 k8s-master
192.168.76.11 k8s-node-1
Kiểm tra:
ping -c 3 k8s-master
ping -c 3 k8s-node-1
6. File common_setup.sh
Chạy file này trên cả Control Plane và Worker Node.
Tạo file:
nano common_setup.sh
Dán nội dung sau:
#!/usr/bin/env bash
set -euo pipefail
# ============================================================
# Common setup for Kubernetes nodes on Ubuntu 24.04.2
# Run on both Control Plane and Worker nodes
# ============================================================
K8S_VERSION="${K8S_VERSION:-v1.36}"
LAB_DISABLE_UFW="${LAB_DISABLE_UFW:-false}"
log() {
echo ""
echo "==> $1"
}
log "Starting common Kubernetes setup"
log "1. Updating system packages"
sudo apt-get update -y
sudo apt-get upgrade -y
log "2. Disabling swap"
sudo swapoff -a
sudo sed -i.bak '/ swap / s/^/#/' /etc/fstab
log "3. Configuring firewall policy"
if command -v ufw >/dev/null 2>&1; then
if [ "${LAB_DISABLE_UFW}" = "true" ]; then
echo "LAB_DISABLE_UFW=true, disabling UFW for lab environment only."
sudo ufw disable || true
else
echo "UFW will NOT be disabled."
echo "If UFW is enabled, make sure required Kubernetes ports are allowed."
echo "For quick lab only, you may run: LAB_DISABLE_UFW=true ./common_setup.sh"
fi
fi
log "4. Keeping AppArmor enabled"
echo "AppArmor is not disabled or purged. Only change AppArmor if you have a specific runtime/CNI error."
log "5. Loading kernel modules and sysctl settings"
cat <<'EOF_K8S_MODULES' | sudo tee /etc/modules-load.d/k8s.conf >/dev/null
overlay
br_netfilter
EOF_K8S_MODULES
sudo modprobe overlay
sudo modprobe br_netfilter
cat <<'EOF_K8S_SYSCTL' | sudo tee /etc/sysctl.d/k8s.conf >/dev/null
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF_K8S_SYSCTL
sudo sysctl --system
log "6. Installing containerd"
sudo apt-get install -y ca-certificates curl gpg apt-transport-https
sudo install -m 0755 -d /etc/apt/keyrings
if [ ! -f /etc/apt/keyrings/docker.gpg ]; then
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
fi
UBUNTU_CODENAME="$(. /etc/os-release && echo "${VERSION_CODENAME}")"
ARCH="$(dpkg --print-architecture)"
cat <<EOF_DOCKER_REPO | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
deb [arch=${ARCH} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu ${UBUNTU_CODENAME} stable
EOF_DOCKER_REPO
sudo apt-get update -y
sudo apt-get install -y containerd.io
log "7. Configuring containerd with systemd cgroup driver"
sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/config.toml >/dev/null
# This works for containerd config generated by the package.
# Kubernetes requires kubelet and container runtime to use compatible cgroup drivers.
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml
# Make sure CRI plugin is not disabled.
sudo sed -i 's/^disabled_plugins = \["cri"\]/disabled_plugins = []/' /etc/containerd/config.toml || true
sudo systemctl restart containerd
sudo systemctl enable containerd
log "8. Adding Kubernetes apt repository: ${K8S_VERSION}"
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL "https://pkgs.k8s.io/core:/stable:/${K8S_VERSION}/deb/Release.key" \
| sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
cat <<EOF_K8S_REPO | sudo tee /etc/apt/sources.list.d/kubernetes.list >/dev/null
deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/${K8S_VERSION}/deb/ /
EOF_K8S_REPO
log "9. Installing kubelet, kubeadm, kubectl"
sudo apt-get update -y
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
sudo systemctl enable kubelet
log "10. Version check"
containerd --version || true
kubeadm version || true
kubectl version --client || true
log "Common setup completed"
echo "Please reboot this node before running master_setup.sh or node_join.sh."
echo "Command: sudo reboot"
Cấp quyền chạy:
chmod +x common_setup.sh
Chạy trên cả 2 máy:
./common_setup.sh
Nếu chỉ dùng lab và muốn tắt UFW tự động:
LAB_DISABLE_UFW=true ./common_setup.sh
Sau khi chạy xong, reboot cả 2 máy:
sudo reboot
7. Nếu không disable UFW, mở port cần thiết
Nếu UFW đang bật, bạn cần mở port Kubernetes.
Trên Control Plane node
sudo ufw allow 6443/tcp
sudo ufw allow 2379:2380/tcp
sudo ufw allow 10250/tcp
sudo ufw allow 10257/tcp
sudo ufw allow 10259/tcp
Trên Worker Node
sudo ufw allow 10250/tcp
sudo ufw allow 10256/tcp
sudo ufw allow 30000:32767/tcp
sudo ufw allow 30000:32767/udp
Flannel VXLAN
Flannel VXLAN thường cần UDP 8472 giữa các node:
sudo ufw allow 8472/udp
8. File master_setup.sh
Chỉ chạy file này trên Control Plane node.
Tạo file:
nano master_setup.sh
Dán nội dung sau:
#!/usr/bin/env bash
set -euo pipefail
# ============================================================
# Kubernetes Control Plane setup
# Run only on the Control Plane node
# ============================================================
POD_CIDR="${POD_CIDR:-10.244.0.0/16}"
MASTER_IP="${MASTER_IP:-$(hostname -I | awk '{print $1}')}"
FLANNEL_VERSION="${FLANNEL_VERSION:-v0.28.5}"
log() {
echo ""
echo "==> $1"
}
log "Checking swap status"
if swapon --show | grep -q .; then
echo "ERROR: Swap is still enabled. Please disable swap before continuing."
swapon --show
exit 1
fi
log "Initializing Kubernetes Control Plane"
echo "MASTER_IP=${MASTER_IP}"
echo "POD_CIDR=${POD_CIDR}"
sudo kubeadm init \
--apiserver-advertise-address="${MASTER_IP}" \
--pod-network-cidr="${POD_CIDR}" \
--cri-socket=unix:///run/containerd/containerd.sock \
| tee kubeadm-init.log
log "Configuring kubectl for current user"
mkdir -p "${HOME}/.kube"
sudo cp -f /etc/kubernetes/admin.conf "${HOME}/.kube/config"
sudo chown "$(id -u):$(id -g)" "${HOME}/.kube/config"
log "Installing Flannel CNI ${FLANNEL_VERSION}"
kubectl apply -f "https://github.com/flannel-io/flannel/releases/download/${FLANNEL_VERSION}/kube-flannel.yml"
log "Control Plane setup completed"
echo ""
echo "Check cluster status:"
echo " kubectl get nodes -o wide"
echo " kubectl get pods -A"
echo ""
echo "Save the kubeadm join command printed above."
echo "If you lost it, regenerate it with:"
echo " kubeadm token create --print-join-command"
Cấp quyền chạy:
chmod +x master_setup.sh
Chạy:
./master_setup.sh
Nếu máy có nhiều network interface, nên truyền IP rõ ràng:
MASTER_IP=192.168.76.10 ./master_setup.sh
Sau khi chạy xong, kiểm tra:
kubectl get nodes -o wide
kubectl get pods -A
Control Plane node có thể chưa Ready ngay lập tức. Đợi Flannel chạy xong rồi kiểm tra lại.
9. Lấy lệnh join node
Sau khi kubeadm init thành công, output sẽ có lệnh dạng:
kubeadm join 192.168.76.10:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash>
Nếu quên hoặc token hết hạn, chạy trên Control Plane:
kubeadm token create --print-join-command
Copy lệnh này để dùng cho Worker Node.
10. File node_join.sh
Chỉ chạy file này trên Worker Node.
Tạo file:
nano node_join.sh
Dán nội dung sau:
#!/usr/bin/env bash
set -euo pipefail
# ============================================================
# Join Worker Node to Kubernetes cluster
# Run only on Worker Node
# ============================================================
# Replace this value with the real command from kubeadm init output.
# Example:
# KUBEADM_JOIN_COMMAND='kubeadm join 192.168.76.10:6443 --token abcdef.0123456789abcdef --discovery-token-ca-cert-hash sha256:xxxxxxxxxx'
KUBEADM_JOIN_COMMAND='<PASTE_REAL_KUBEADM_JOIN_COMMAND_HERE>'
log() {
echo ""
echo "==> $1"
}
log "Checking kubeadm join command"
if [ "${KUBEADM_JOIN_COMMAND}" = "<PASTE_REAL_KUBEADM_JOIN_COMMAND_HERE>" ]; then
echo "ERROR: Please replace KUBEADM_JOIN_COMMAND with the real kubeadm join command from Control Plane."
exit 1
fi
log "Checking swap status"
if swapon --show | grep -q .; then
echo "ERROR: Swap is still enabled. Please disable swap before continuing."
swapon --show
exit 1
fi
log "Joining Worker Node to cluster"
sudo ${KUBEADM_JOIN_COMMAND} --cri-socket=unix:///run/containerd/containerd.sock
log "Worker Node joined"
echo "Please go back to Control Plane and run:"
echo " kubectl get nodes -o wide"
Cấp quyền chạy:
chmod +x node_join.sh
Sửa dòng này bằng lệnh thật:
KUBEADM_JOIN_COMMAND='<PASTE_REAL_KUBEADM_JOIN_COMMAND_HERE>'
Ví dụ:
KUBEADM_JOIN_COMMAND='kubeadm join 192.168.76.10:6443 --token abcdef.0123456789abcdef --discovery-token-ca-cert-hash sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
Chạy:
./node_join.sh
11. Kiểm tra cluster trên Control Plane
Chạy trên k8s-master:
kubectl get nodes -o wide
kubectl get pods -A
Kết quả mong muốn:
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane ... v1.36.x
k8s-node-1 Ready <none> ... v1.36.x
Kiểm tra runtime:
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.nodeInfo.containerRuntimeVersion}{"\n"}{end}'
12. Deploy thử một ứng dụng Nginx
Chạy trên Control Plane:
kubectl create deployment nginx-demo --image=nginx:stable
kubectl expose deployment nginx-demo --type=NodePort --port=80
kubectl get pods -o wide
kubectl get svc nginx-demo
Lấy NodePort:
kubectl get svc nginx-demo
Truy cập thử:
curl http://<NODE_IP>:<NODE_PORT>
Ví dụ:
curl http://192.168.76.11:30080
Xóa demo:
kubectl delete svc nginx-demo
kubectl delete deployment nginx-demo
13. Troubleshooting nhanh
Node ở trạng thái NotReady
Kiểm tra CNI:
kubectl get pods -n kube-flannel
kubectl get pods -A
Kiểm tra kubelet:
sudo systemctl status kubelet --no-pager
sudo journalctl -u kubelet -xe --no-pager
Lỗi container runtime hoặc CRI
Kiểm tra containerd:
sudo systemctl status containerd --no-pager
sudo journalctl -u containerd -xe --no-pager
sudo crictl info
Kiểm tra socket:
ls -l /run/containerd/containerd.sock
Lỗi swap
swapon --show
free -h
Nếu swap còn bật:
sudo swapoff -a
sudo sed -i.bak '/ swap / s/^/#/' /etc/fstab
sudo reboot
Lỗi token join hết hạn
Chạy trên Control Plane:
kubeadm token create --print-join-command
Kiểm tra port API server từ Worker Node
Chạy trên Worker Node:
nc -vz k8s-master 6443
Nếu không có nc:
sudo apt-get install -y netcat-openbsd
nc -vz k8s-master 6443
14. Xóa sạch Kubernetes và dữ liệu liên quan
Thực hiện trên tất cả node bạn muốn xóa Kubernetes.
Cảnh báo: Các lệnh dưới đây sẽ xóa trạng thái cluster trên node. Nếu chạy trên Control Plane và xóa
/var/lib/etcd, dữ liệu cluster sẽ mất.
Bước 1: Reset kubeadm
sudo kubeadm reset -f
Bước 2: Xóa cấu hình CNI
sudo rm -rf /etc/cni/net.d
sudo rm -rf /var/lib/cni
Bước 3: Xóa cấu hình Kubernetes và kubelet
sudo rm -rf /etc/kubernetes
sudo rm -rf /var/lib/kubelet
sudo rm -rf /var/lib/dockershim
sudo rm -rf /var/run/kubernetes
Bước 4: Xóa dữ liệu etcd trên Control Plane nếu muốn xóa hoàn toàn cluster
Chỉ chạy trên Control Plane cũ nếu bạn muốn xóa sạch cluster state:
sudo rm -rf /var/lib/etcd
Bước 5: Gỡ package Kubernetes
sudo apt-mark unhold kubelet kubeadm kubectl || true
sudo apt-get purge -y kubeadm kubectl kubelet kubernetes-cni || true
sudo apt-get autoremove -y
Bước 6: Xóa kubeconfig của user hiện tại
rm -rf "${HOME}/.kube"
Bước 7: Xóa containerd nếu muốn xóa cả container runtime
Nếu bạn muốn giữ containerd cho việc khác, bỏ qua bước này.
sudo systemctl stop containerd || true
sudo apt-get purge -y containerd.io || true
sudo rm -rf /var/lib/containerd
sudo rm -rf /etc/containerd
Bước 8: Xóa iptables rule do Kubernetes/CNI tạo ra
Chỉ chạy khi bạn chắc chắn node này không còn dùng iptables rule quan trọng cho dịch vụ khác.
sudo iptables -F
sudo iptables -t nat -F
sudo iptables -t raw -F
sudo iptables -t mangle -F
sudo iptables -X
sudo iptables -t nat -X
sudo iptables -t raw -X
sudo iptables -t mangle -X
Nếu hệ thống dùng nftables, có thể kiểm tra thêm:
sudo nft list ruleset
Không flush toàn bộ nftables nếu máy đang dùng firewall cho dịch vụ khác.
Bước 9: Tắt swap lại nếu cần
sudo swapoff -a
sudo sed -i.bak '/ swap / s/^/#/' /etc/fstab
Bước 10: Reboot
sudo reboot
15. Tài liệu tham khảo
- Kubernetes kubeadm install: https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/
- Kubernetes container runtimes: https://kubernetes.io/docs/setup/production-environment/container-runtimes/
- Kubernetes ports and protocols: https://kubernetes.io/docs/reference/networking/ports-and-protocols/
- Kubernetes releases: https://kubernetes.io/releases/
- Flannel: https://github.com/flannel-io/flannel