
最近花了一些時間在本地環境用 kubeadm 把 Kubernetes v1.28.3 叢集架起來,整個過程雖然不算太複雜,但細節蠻多的,稍有疏忽就可能卡在某個環節。這篇文章把我實際操作的完整流程記錄下來,希望對想要自己動手搭建 K8s 叢集的朋友有所幫助。
環境概述
這次部署採用的是 CentOS 衍生發行版,像是 Anolis OS 8.8、Rocky Linux 9.3、AlmaLinux 9.3 都適用。我自己用的是 Anolis OS 8.8,核心版本為 Linux Kernel 5.10.134-13.an8.x86_64。
以下是這次部署涉及的主要元件與版本:
| 元件名稱 | 版本 |
|---|---|
| containerd(容器運行時) | v1.7.8 |
| runc | v1.1.9 |
| crictl | v1.27.0 |
| kubeadm / kubectl / kubelet | v1.28.3 |
| kube-apiserver / kube-scheduler / kube-controller-manager | v1.28.3 |
| etcd | v3.5.9 |
| flannel(CNI 網路元件) | v0.23.0 |
| kubernetes-dashboard | v2.7.0 |
節點規劃
| 節點角色 | 主機名 | IP 位址 | Pod 網段 | Service 網段 |
|---|---|---|---|---|
| Master(Control Plane) | c1 | 192.168.31.31 | 10.15.0.0/16 | 10.16.0.0/16 |
| Worker Node | c2 | 192.168.31.32 | 10.15.0.0/16 | 10.16.0.0/16 |
| Worker Node | c3 | 192.168.31.33 | 10.15.0.0/16 | 10.16.0.0/16 |
系統初始化設定
在正式安裝 K8s 元件之前,需要先做好系統層面的基礎配置。這部分每個節點都要執行。
設定主機名解析
# 在所有節點上配置 hosts
cat >> /etc/hosts << EOF
192.168.31.31 c1
192.168.31.32 c2
192.168.31.33 c3
EOF
核心模組與參數調整
Kubernetes 需要載入 overlay 和 br_netfilter 這兩個核心模組,同時也需要開啟一些網路轉發相關的核心參數:
# 設定開機自動載入核心模組
cat << EOF > /etc/modules-load.d/99-k8s.conf
overlay
br_netfilter
EOF
# 調整核心參數
cat << EOF > /etc/sysctl.d/99-k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
user.max_user_namespaces=28633
EOF
sysctl -p /etc/sysctl.d/99-k8s.conf
停用 SELinux 與 Swap
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=Disabled/' /etc/selinux/config
sed -i '/swap/ s/^/# /' /etc/fstab
防火牆設定
這邊有兩種做法。正式環境建議只開放必要的連接埠;測試環境為了方便可以直接關閉防火牆。
方式一:精確開放連接埠(適用於正式環境)
# Master 節點
firewall-cmd --permanent --add-port={6443,2379,2380,10250,10251,10252,10257,10259,179}/tcp
firewall-cmd --permanent --add-port=4789/udp
firewall-cmd --reload
# Worker 節點
firewall-cmd --permanent --add-port={179,10250,30000-32767}/tcp
firewall-cmd --permanent --add-port=4789/udp
firewall-cmd --reload
方式二:關閉防火牆(適用於測試環境)
systemctl stop firewalld && systemctl disable firewalld
設定完成後,記得重啟所有節點讓配置生效。
安裝容器運行時 Containerd
Kubernetes 從 v1.24 開始已經移除了對 Docker 的直接支援,所以我們這邊採用 containerd 作為容器運行時。
下載並安裝
wget https://github.com/containerd/containerd/releases/download/v1.7.8/cri-containerd-cni-1.7.8-linux-amd64.tar.gz
tar -zxvf cri-containerd-cni-1.7.8-linux-amd64.tar.gz -C /
自訂 Containerd 配置
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
接著需要修改 /etc/containerd/config.toml 中的幾個關鍵配置:
# 將 cgroup driver 改為 systemd(使用 cgroup v2 時此項必須為 true)
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
# 替換 sandbox image 為國內鏡像源
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9"
修改完成後啟動 containerd:
systemctl enable --now containerd.service

安裝 kubeadm、kubectl、kubelet
透過 dnf 套件管理器來安裝 Kubernetes 核心元件:
# 新增 Kubernetes yum 倉庫
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.28/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.28/rpm/repodata/repomd.xml.key
exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
EOF
# 安裝元件
dnf install kubelet kubeadm kubectl --disableexcludes=kubernetes -y
dnf install iproute-tc ipvsadm -y
# 啟用 kubelet
systemctl enable --now kubelet
使用 kubeadm 初始化叢集
這是整個部署流程中最關鍵的一步。我習慣用 YAML 配置檔來自訂 kubeadm 的初始化參數,這樣比較好管理也方便日後回溯。
準備 kubeadm 配置檔
建立 kubeadm.yml,內容涵蓋了 API Server 監聽位址、叢集網段、cgroup driver 以及 KubeProxy 模式等重要配置:
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
bootstrapTokens:
- groups:
- system:bootstrappers:kubeadm:default-node-token
token: ********
ttl: 24h0m0s
usages:
- signing
- authentication
localAPIEndpoint:
advertiseAddress: 192.168.31.31
bindPort: 6443
nodeRegistration:
criSocket: unix:///var/run/containerd/containerd.sock
imagePullPolicy: IfNotPresent
name: c1
taints:
- effect: PreferNoSchedule
key: node-role.kubernetes.io/master
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
clusterName: kubernetes
kubernetesVersion: v1.28.3
imageRepository: registry.aliyuncs.com/google_containers
apiServer:
timeoutForControlPlane: 4m0s
certificatesDir: /etc/kubernetes/pki
networking:
dnsDomain: cluster.local
podSubnet: 10.15.0.0/16
serviceSubnet: 10.16.0.0/16
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
clusterDNS:
- 10.16.0.10
clusterDomain: cluster.local
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: ipvs
特別注意:
networking.podSubnet設定的 Pod 網段,務必與後續部署的 CNI 網路元件(如 Flannel)配置一致,否則 Pod 之間無法正常通訊。
執行初始化
kubeadm init --config=kubeadm.yml
初始化成功後,設定 kubectl 的存取權限:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
將 Worker 節點加入叢集
kubeadm join 192.168.31.31:6443 \
--token ******** \
--discovery-token-ca-cert-hash sha256:********
重置叢集(如需重來)
kubeadm reset
rm /etc/cni/net.d/* -rf
rm $HOME/.kube/config -rf
ipvsadm --clear
iptables -F -t filter
iptables -F -t nat
iptables -F -t mangle
iptables -F -t raw
部署 Flannel 網路元件
叢集初始化完成後,Pod 之間還沒辦法互相通訊,需要安裝一個 CNI 網路元件。我這邊選用 Flannel:
wget https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
kubectl apply -f kube-flannel.yml
# 備份 containerd 預設網路配置
cd /etc/cni/net.d/ && mv 10-containerd-net.conflist 10-containerd-net.conflist.bak
Flannel 配置說明
Flannel 部署後會自動產生網路配置,格式如下:
# /etc/cni/net.d/10-flannel.conflist
{
"name": "cbr0",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
如果需要自訂 Flannel 的後端模式,可以在 kube-flannel.yml 中修改 net-conf.json 區段:
net-conf.json: |
{
"Network": "10.15.0.0/16",
"Backend": {
"Type": "vxlan",
"Directrouting": true
}
}
這裡 Directrouting: true 的意思是:當叢集節點在同一個子網路時,跨節點 Pod 通訊會走直接路由(類似 host-gw 模式),省去 VXLAN 封裝的開銷;如果不在同一子網路則自動回退到 VXLAN 封裝。
安裝 Kubernetes Dashboard
Dashboard 提供了一個圖形化介面來管理叢集,對於日常監控和除錯蠻方便的。
wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml
下載後需要修改 Service 類型為 NodePort,這樣才能從外部存取:
spec:
type: NodePort
ports:
- port: 443
targetPort: 8443
nodePort: 30443
kubectl apply -f recommended.yaml
建立管理員帳號
Dashboard 預設的 token 權限有限,建議另外建立一個具備 cluster-admin 權限的 ServiceAccount:
cat > kubernetes-dashboard-admin.yml << EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kubernetes-dashboard
EOF
kubectl apply -f kubernetes-dashboard-admin.yml
# 取得管理員 token
kubectl -n kubernetes-dashboard create token admin-user
完成後,透過瀏覽器存取 https://<任意節點IP>:30443/ 即可進入 Dashboard。
驗證叢集狀態
所有元件部署完成後,來確認一下叢集是否正常運作。
節點狀態
| 節點 | 狀態 | 角色 | 版本 | 內部 IP | 作業系統 | 容器運行時 |
|---|---|---|---|---|---|---|
| c1 | Ready | control-plane | v1.28.3 | 192.168.31.31 | Anolis OS 8.8 | containerd://1.7.8 |
| c2 | Ready | worker | v1.28.3 | 192.168.31.32 | Anolis OS 8.8 | containerd://1.7.8 |
| c3 | Ready | worker | v1.28.3 | 192.168.31.33 | Anolis OS 8.8 | containerd://1.7.8 |
系統 Pod 運行狀態
| 命名空間 | Pod 名稱 | 狀態 | 所在節點 |
|---|---|---|---|
| kube-flannel | kube-flannel-ds-* | Running | c1, c2, c3 |
| kube-system | coredns-* | Running | c2 |
| kube-system | etcd-c1 | Running | c1 |
| kube-system | kube-apiserver-c1 | Running | c1 |
| kube-system | kube-controller-manager-c1 | Running | c1 |
| kube-system | kube-proxy-* | Running | c1, c2, c3 |
| kube-system | kube-scheduler-c1 | Running | c1 |
| kubernetes-dashboard | dashboard-metrics-scraper-* | Running | c3 |
| kubernetes-dashboard | kubernetes-dashboard-* | Running | c3 |
三個節點都是 Ready 狀態,所有系統 Pod 都正常運行,叢集搭建完成!
快速測試
簡單跑個容器驗證一下:
kubectl create deployment --image rockylinux:9.2 --replicas 2 rockylinux -- tail -f /dev/null
小結
用 kubeadm 搭建 K8s v1.28.3 叢集的完整流程大致就是這樣。幾個容易踩坑的地方提醒一下:
- containerd 的
SystemdCgroup一定要設為true,尤其是使用 cgroup v2 的系統 - Pod 網段要與 CNI 元件的配置保持一致
- 如果存取不了外部鏡像倉庫,記得把
sandbox_image換成國內鏡像源 - KubeProxy 建議用 IPVS 模式,效能比 iptables 模式好很多
有任何問題歡迎在下方留言討論,下次我再來分享更多 Kubernetes 實戰經驗。

發佈留言