I. Lời nói đầu
Note lại quá trình triển khai 1 k8s cluster nho nhỏ với 1 controlplane + 2 worker.
Tôi ko triển khai ở quy mô lớn nên ko dùng kubespray hay ansible.
II. Tổng quan kiến trúc
Hệ điều hành: 3 máy đều sử dụng RHEL 9.x
Kubernetes: v1.36.
Runtime: containerd.
CNI: Cilium, VXLAN.
CSI: Ceph
Ingress: F5 NGINX Ingress
| Vai trò | Hostname | IP |
|---|---|---|
| Control Plane | k8s-sec-controlplane |
10.0.1.1 |
| Worker 1 | k8s-sec-worker-1 |
10.0.1.2 |
| Worker 2 | k8s-sec-worker-1 |
10.0.1.3 |
III. Chuẩn bị
3.1. Đặt hostname
Trên Control Plane:
1 | sudo hostnamectl set-hostname k8s-sec-controlplane |
Trên Worker 1:
1 | sudo hostnamectl set-hostname k8s-sec-worker-1 |
Trên Worker 2:
1 | sudo hostnamectl set-hostname k8s-sec-worker-2 |
Kiểm tra:
1 | hostnamectl --static |
3.2 Update RHEL và tắt swap
1 | sudo dnf update -y |
Tắt swap vĩnh viễn:
1 | sudo sed -ri '/\sswap\s/s/^#?/#/' /etc/fstab |
swapon --show không trả kết quả là đúng.
Nếu dnf update vừa nâng kernel, reboot node trước khi đi tiếp:
1 | sudo reboot |
3.3. Hardening với SELinux
Bật SELinux ở chế độ permissive:
1 | getenforce |
Kết quả mong muốn:
1 | Permissive |
Không đặt SELINUX=disabled; permissive vẫn ghi audit log và dễ troubleshoot hơn.
3.4 Kernel modules và sysctl
Chuẩn bị kernel networking cho Kubernetes giúp Pod, Service và CNI (Calico, Cilium…) có thể định tuyến và áp dụng firewall/network policy đúng cách.
3.4.1. Load kernel modules
1 | sudo tee /etc/modules-load.d/k8s.conf >/dev/null <<'EOF' |
Tác dụng:
overlay: hỗ trợ OverlayFS, cơ chế filesystem mà container runtime như containerd/Docker thường dùng để tạo layer image và filesystem cho container.br_netfilter: cho phép các gói tin đi qua Linux bridge được xử lý bởi cơ chế firewall của Linux (iptables/nftables).
Hai lệnh này load module ngay tại thời điểm chạy.
1 | sudo modprobe overlay |
File /etc/modules-load.d/k8s.conf giúp hai module này được tự động load lại sau mỗi lần reboot.
3.4.2. Cấu hình sysctl cho network Kubernetes
1 | sudo tee /etc/sysctl.d/k8s.conf >/dev/null <<'EOF' |
Dòng sudo sysctl --system đọc và áp dụng các file cấu hình sysctl. Dòng /etc/sysctl.d/k8s.conf giúp cấu hình có hiệu lực ngay và vẫn được giữ sau khi reboot.
Xác nhận hai kernel module đã được load.
1 | lsmod | grep -E 'overlay|br_netfilter' |
Kiểm tra thấy net.ipv4.ip_forward = 1 là cấu hình đã nhận, node đã được phép route/forward traffic IPv4.
Ý nghĩa:
| Cấu hình | Tác dụng |
|---|---|
net.bridge.bridge-nf-call-iptables = 1 |
Cho phép traffic IPv4 đi qua Linux bridge được kiểm tra bởi iptables. Quan trọng để kube-proxy, NetworkPolicy và firewall rule hoạt động đúng. |
net.bridge.bridge-nf-call-ip6tables = 1 |
Tương tự nhưng áp dụng cho traffic IPv6. |
net.ipv4.ip_forward = 1 |
Cho phép máy Linux chuyển tiếp gói tin giữa các network interface. Node Kubernetes cần điều này để chuyển traffic giữa Pod, Service, node khác và mạng ngoài cluster. |
Ví dụ: Pod A gửi traffic đến Pod B nằm trên node khác. Node phải có khả năng forward packet sang interface/network phù hợp. Nếu ip_forward = 0, traffic có thể bị chặn ngay tại node.
IV. Triển khai CRI
Sử dụng containerd làm container runtime. Tiến hành cài đặt containerd trên cả 3 node.
4.1 Kiểm tra xung đột package
1 | rpm -q podman runc containerd containerd.io || true |
Nếu node đang chạy Podman/container workload, không gỡ vội. Hãy chuyển workload sang node khác hoặc chọn runtime CRI-O thay vì containerd.
Với node mới, chuyên dụng cho Kubernetes:
1 | sudo dnf install -y dnf-plugins-core |
4.2 Cấu hình cgroup driver systemd
1 | sudo mkdir -p /etc/containerd |
Khởi động và kiểm tra:
1 | sudo systemctl enable --now containerd |
Phải thấy:
1 | SystemdCgroup = true |
Kiểm tra CRI socket:
1 | sudo test -S /run/containerd/containerd.sock && echo "containerd CRI socket OK" |
Không khởi động dịch vụ
docker; Kubernetes dùng trực tiếp containerd thông qua CRI socket/run/containerd/containerd.sock.
V. Cài kubeadm, kubelet, kubectl, crictl
5.1 Khai báo Kubernetes RPM repository
1 | cat <<'EOF' | sudo tee /etc/yum.repos.d/kubernetes.repo |
5.2 Cài package
1 | sudo dnf makecache |
Bật kubelet:
1 | sudo systemctl enable --now kubelet |
Kubelet có thể báo lỗi hoặc restart loop trước khi chạy kubeadm init/join; đây là trạng thái bình thường vì nó chưa có cấu hình cluster.
Cấu hình crictl để troubleshoot containerd:
1 | sudo tee /etc/crictl.yaml >/dev/null <<'EOF' |
Kiểm tra phiên bản:
1 | kubeadm version |
VI. Bootstrap Controlplane
6.1. Khởi tạo Control Plane
6.1.1. Pull image Kubernetes
1 | sudo kubeadm config images pull \ |
6.1.2. Khởi tạo cluster
1 | sudo kubeadm init \ |
Khi lệnh hoàn tất, lưu lại toàn bộ lệnh kubeadm join ... ở cuối output.
6.1.3. Cấu hình kubectl cho user đang đăng nhập
1 | mkdir -p "$HOME/.kube" |
Kiểm tra:
1 | kubectl get nodes |
Lúc này Control Plane thường ở trạng thái NotReady; CoreDNS có thể Pending do chưa cài CNI.
Nếu mất join command:
1 | kubeadm token create --print-join-command |
6.2. Cài Helm trên Control Plane
1 | curl -fsSL -o get_helm.sh \ |
Helm đã được copy vào /usr/local/bin/helm, nhưng thư mục /usr/local/bin chưa nằm trong biến môi trường PATH của user hiện tại.
Kiểm tra file đã có chưa:
1 | ls -l /usr/local/bin/helm |
Nếu thấy file helm, chạy ngay bằng đường dẫn đầy đủ:
1 | /usr/local/bin/helm version |
Sau đó thêm tạm thời vào PATH cho phiên shell hiện tại:
1 | export PATH=$PATH:/usr/local/bin |
Để giữ vĩnh viễn cho user root:
1 | echo 'export PATH=$PATH:/usr/local/bin' >> ~/.bashrc |
Hoặc cách gọn hơn, tạo symbolic link vào /usr/bin vì thư mục này thường đã có trong PATH:
1 | ln -s /usr/local/bin/helm /usr/bin/helm |
Chỉ cần dùng một trong hai cách trên. Tôi khuyên dùng cách thêm PATH:
1 | export PATH=$PATH:/usr/local/bin |
VII. Triển khai CNI
7.1. Mở firewall cho VXLAN giữa các node:
1 | sudo firewall-cmd --permanent --add-port=8472/udp |
Trong đó:
- 8472/UDP: bắt buộc cho VXLAN tunnel.
- 4240/TCP: phục vụ Cilium health check, nên mở giữa các node.
Cài Cilium bằng Helm
Ví dụ chọn Pod CIDR là 10.244.0.0/16, mỗi node nhận một subnet /24 tương đương tối đa khoảng 254 IP Pod/node:
1 | helm upgrade --install cilium oci://quay.io/cilium/charts/cilium \ |
Ý nghĩa chính:
routingMode=tunnel: chạy overlay network.tunnelProtocol=vxlan: đóng gói traffic Pod-to-Pod qua UDP 8472.cluster-pool: Cilium Operator tự cấp Pod CIDR cho từng node.10.244.0.0/16: dải IP dành cho Pod; không được trùng Node subnet, Service CIDR, VPN, LAN hoặc hệ thống khác.kubeProxyReplacement=false: giữ kube-proxy, phù hợp để triển khai ban đầu ổn định hơn.
1 | helm upgrade cilium oci://quay.io/cilium/charts/cilium \ |
7.1. Mô hình IP cần thống nhất trước khi thao tác
Trong lịch sử cấu hình, các IP được sử dụng như sau:
Control plane 1: 10.0.1.1
Worker 1: 10.0.1.2
Worker 2: 10.0.1.3
API VIP HAProxy: 10.16.61.62
API VIP port: 16443
Ingress LoadBalancer: 10.16.61.63
Mạng quản trị SSH: 10.2.15.0/24
Network interface: ens33
7.1. Mục tiêu
Triển khai HAProxy Ingress Controller trong Kubernetes để publish các ứng dụng ra mạng bên ngoài qua HTTP/HTTPS.
Thông tin sử dụng trong tutorial:
| Thành phần | Giá trị |
|---|---|
| Kubernetes API VIP | 10.16.61.62:16443 |
| HAProxy Ingress LoadBalancer VIP | 10.16.61.63 |
| Control-plane nodes | 10.16.61.30, 10.16.61.31, 10.16.61.32 |
| Mạng quản trị SSH | 10.2.15.0/24 |
| Network interface dùng để announce VIP | ens33 |
| Ingress Class | haproxy |
| Namespace triển khai | haproxy-ingress |
Luồng truy cập:
1 | Client |