Công nghệ IT
Hướng dẫn cài đặt Kubernetes Master-Node trên Ubuntu 24.04.2
imageimage
Thoại Kỳ
08-07-2025
313 lượt xem

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-masterControl 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.36 thông qua repo mới pkgs.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 containerd làm container runtime.
  • Cấu hình SystemdCgroup = true cho containerd.
  • Dùng Flannel release cố định thay vì trỏ vào nhánh master củ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.35 hoặc v1.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

HostnameVai tròIP ví dụ
k8s-masterControl Plane192.168.76.10
k8s-node-1Worker Node192.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