Kubernetes (簡稱k8s)是一套用於自動部署,擴展以及管理容器化應用的開源系統。
k8s集群由一個 控制平面組件 和一個或多個節點組件 構成(每個組件代表一台或多台運行容器運行時和 kubelet.service
服務的主機機器)。安裝 Kubernetes 有兩種選擇:一種是更為正統的Kubernetes安裝方式(此處所描述的方式),另一種則是使用 k3s,kind 或者 minikube包 進行本地安裝。
安裝
配置一個 Kubernetes 集群的方法有很多,本文將主要介紹如何使用 kubeadm包 進行配置。
部署工具
kubeadm
如果要使用 kubeadm 來引導 Kubernetes 集群,請在每一個節點上 安裝 kubeadm包 和 kubelet包。
手動配置
如你想要手動創建一個 Kubernetes 集群,請 安裝 etcdAUR,並安裝 軟體包組 kubernetes-control-plane包組 (在控制平面節點) 或 kubernetes-node包組 (在 worker 節點)。
集群管理
要管理一個 Kubernetes 集群,請在控制平面節點和要與集群交互的主機上安裝 kubectl包。
容器運行時
控制平面節點和常規 worker 節點都需要一個容器運行時以運行 kubelet
實例。
安裝 containerd包 或 cri-o包 來提供一個運行時。
containerd 運行時
有兩種方式可以安裝 containerd:
- 安裝 containerd包 軟體包。
- 安裝非特權模式(rootless)containerd,你需要安裝 nerdctl-full-binAUR,其是一個完整的 nerdctl 包(containerd,CNI 插件,和 RootlessKit)。隨後你需要運行
containerd-rootless-setuptool.sh install
以安裝非特權模式 containerd。
請注意,由於 Arch Linux 使用 systemd 作為 init 系統,你需要在部署控制平面之前 選擇 systemd cgroup 驅動。
(可選) 包管理器
helm包 是一個用於管理預配置的 Kubernetes 資源的工具,這可能會有所幫助。
配置
集群中的所有節點(控制平面和工作節點)都需要運行一個 kubelet.service
實例。
kubelet.service
或使用 kubeadm
之前,請仔細閱讀以下小節。所有提供的 systemd 服務都允許通過環境文件進行命令行參數覆蓋:
-
kubelet.service
:/etc/kubernetes/kubelet.env
-
kube-apiserver.service
:/etc/kubernetes/kube-apiserver.env
-
kube-controller-manager.service
:/etc/kubernetes/kube-controller-manager.env
-
kube-proxy.service
:/etc/kubernetes/kube-proxy.env
-
kube-scheduler.service
:/etc/kubernetes/kube-scheduler.env
禁用交換分區
Kubernetes 目前不支持在系統上啟用交換分區。詳情請參閱 KEP-2400: 節點系統交換支持。
有關如何禁用交換分區的說明,請參閱 Swap#關閉交換分區。
為 containerd 選擇 cgroup 驅動
如要在使用 runc
時,同時也在 /etc/containerd/config.toml
中使用 systemd
cgroup 驅動,請設置:
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] ... [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true
如果 /etc/containerd/config.toml
不存在,可以使用以下命令生成默認配置:
# mkdir -p /etc/containerd/ # containerd config default > /etc/containerd/config.toml
請記住 重啟 containerd.service
以使更改生效。
有關是否保留 cgroupfs
驅動或使用 systemd
cgroup 驅動的深入討論,請參閱 官方文檔。
選擇容器運行時接口 (CRI)
在使用 kubelet.service
之前,必須配置並啟動 容器運行時。
你需要在創建或加入集群時,將帶有容器運行時接口端點的標誌 --cri-socket
傳遞給 kubeadm init
或 kubeadm join
。
例如,如果你選擇 containerd包 作為 CRI 運行時,則在創建時附帶上--cri-socket
標誌:
kubeadm init --cri-socket /run/containerd/containerd.sock
Containerd
在 Kubernetes 1.27.4 版本之前,當使用 containerd包 作為容器運行時,需要向 kubeadm init
或 kubeadm join
提供其 CRI 端點。為此,請將其 --cri-socket
標誌指定為 /run/containerd/containerd.sock
[1]。
kubeadm join --cri-socket=/run/containerd/containerd.sock
在 Kubernetes 1.27.4 版本之後,kubeadm 將自動檢測此 CRI,只有在安裝了多個 CRI 時才需要 --cri-socket
標誌。
CRI-O
當使用 CRI-O 作為容器運行時,需要向 kubeadm init
或 kubeadm join
提供其 CRI 端點:--cri-socket='unix:///run/crio/crio.sock'
選擇集群網絡參數
選擇 Pod CIDR 範圍
必須為相應的容器運行時配置集群的網絡設置。這可以使用 cni-plugins包 來完成。
Pod CIDR 地址 指的是分配給 Kubernetes 集群中 Pod 的 IP 地址範圍。當 Pod 被調度到集群中的節點上運行時,它們會從此 CIDR 範圍中分配 IP 地址。
Pod CIDR 範圍 在部署 Kubernetes 集群時指定,並且僅限於集群網絡內。它不應與集群中使用的其他 IP 範圍(例如如service CIDR 範圍)重疊。
你將向 kubeadm init
或 kubeadm join
傳遞 --pod-network-cidr
標誌,並指定虛擬網絡的 CIDR 值,以創建或加入集群。
例如:
kubeadm init --pod-network-cidr='10.85.0.0/16'
會將你的 Kubernetes Pod CIDR 範圍設置為 10.85.0.0/16
。
(可選)選擇 API 伺服器廣播地址
如果你的控制平面節點位於多個子網中(例如,你可能安裝了 Tailscale tailnet),在使用 kubeadm init 初始化 Kubernetes 主節點時,你可以使用 --apiserver-advertise-address
標誌指定 API 伺服器將廣播的 IP 地址。注意,此 IP 地址應可被集群中的所有節點訪問。
(可選)選擇其他的節點網絡代理提供者
節點代理提供者(如 kube-proxy
)是在集群中每個節點上運行的網絡代理,用於維護節點上的網絡規則,以允許從集群內部或外部的網絡會話與你的 Pod 進行網絡通信。
默認情況下,kubeadm包 選擇 kube-proxy
作為在集群中每個節點上運行的節點代理。
容器網絡接口 (CNI) 插件(如 Cilium)提供了 kube-proxy 的完整替代方案。
如果你想使用 Cilium 的節點網絡代理實現以充分利用 Cilium 的網絡策略功能,你應向 kubeadm init
傳遞 --skip-phases=addon/kube-proxy
標誌,以跳過 kube-proxy 的安裝。
Cilium 將在其安裝過程中安裝完整的替代方案。詳情請參閱 [2]。
創建集群
在使用 kubeadm
創建新的 Kubernetes 集群之前,請先 啟動 並 啟用 kubelet.service
。
kubelet.service
會失敗(但會重啟),直到為其提供配置。不使用配置文件的 kubeadm
在使用 kubeadm
創建新的 Kubernetes 集群時,必須先創建一個控制平面,然後其他工作節點才能加入。
- 如果集群以後要轉換為 高可用性 集群(堆疊式 etcd 拓撲),則需要在
kubeadm init
時提供--control-plane-endpoint=<IP 或域名>
(無法事後進行此操作!)。 - 可以使用 配置文件 來代替一組參數進行
kubeadm init
。
初始化控制平面
要初始化控制平面,你需要向 kubeadm init
傳遞以下必要的標誌。
如果成功運行,kubeadm init
將在 /etc/kubernetes/
和 /var/lib/kubelet/
下生成 kubelet
和各種控制平面組件的配置。
最後,它將輸出可以複製並粘貼的命令,用於設置 kubectl包 並使工作節點加入集群(基於有效期為 24 小時的令牌)。
要將 kubectl
與剛創建的控制平面節點一起使用,請設置配置(以 root 或普通用戶身份):
$ mkdir -p $HOME/.kube # cp -i /etc/kubernetes/admin.conf $HOME/.kube/config # chown $(id -u):$(id -g) $HOME/.kube/config
安裝 CNI 插件(Pod 網絡插件)
Pod 網絡插件(CNI 插件)以不同的方式實現 Kubernetes 網絡模型,從簡單的解決方案如 flannel 到更複雜的解決方案如 calico。有關更多選項,請參閱 此列表。
一個日益被採用的高級 CNI 插件是 cilium,它通過 eBPF 實現了 令人印象深刻的性能。要安裝 cilium
作為 CNI 插件,請使用 cilium-cli包:
# cilium-cli install
這將創建 /opt/cni/bin/cilium-cni
插件、配置文件 /etc/cni/net.d/05-cilium.conflist
,並在 Kubernetes 集群上部署兩個 Pod。
/opt/cni/bin/
插件目錄。請參閱 CRI-O#Plugin directories。使用配置文件的 kubeadm
你很可能會發現,創建控制平面需要多次嘗試才能找到適合你特定設置的最佳配置。為了使此過程更容易(並且使 kubeadm包 的過程更具可重複性),你可以使用配置文件運行初始化步驟。
創建初始化配置文件
你可以在任何地方創建此文件,但在此示例中我們將使用 /etc/kubeadm
。
# mkdir -pv /etc/kubeadm # cd /etc/kubeadm # kubeadm config print init-defaults > init.yaml
這將生成以下文件。
/etc/kubeadm/init.yaml
apiVersion: kubeadm.k8s.io/v1beta3 bootstrapTokens: - groups: - system:bootstrappers:kubeadm:default-node-token token: abcdef.0123456789abcdef ttl: 24h0m0s usages: - signing - authentication kind: InitConfiguration localAPIEndpoint: advertiseAddress: 1.2.3.4 bindPort: 6443 nodeRegistration: criSocket: unix:///var/run/containerd/containerd.sock imagePullPolicy: IfNotPresent name: node taints: null --- apiServer: timeoutForControlPlane: 4m0s apiVersion: kubeadm.k8s.io/v1beta3 certificatesDir: /etc/kubernetes/pki clusterName: kubernetes controllerManager: {} dns: {} etcd: local: dataDir: /var/lib/etcd imageRepository: registry.k8s.io kind: ClusterConfiguration kubernetesVersion: 1.29.0 networking: dnsDomain: cluster.local serviceSubnet: 10.96.0.0/12 scheduler: {}
大多數默認設置應該可以工作,但你需要更新其中的一些設置。
引導令牌
使用 kubeadm token generate
創建一個令牌,並在配置中使用它代替 token: abcdef.0123456789abcdef
。
公告地址
advertiseAddress: 1.2.3.4
應該是正在初始化的控制平面的網絡接口的 IPv4 地址,可能是 192.168.0.0/16
子網中的某個地址。
節點名稱
默認節點名稱可以保留為 node
並添加到本地 DNS 伺服器或 hosts 文件中,或者你可以將其更改為本地網絡上可路由的地址。它應該是一個 DNS 兼容的主機名,例如 kcp01.example.com
。這將允許你的控制平面在加入其他節點時在本地網絡上被發現。
初始化集群
完成所有這些更改後,我們可以初始化我們的集群。
# kubeadm init --config /etc/kubeadm/init.yaml
這將產生大量輸出,輸出提供了有關如何將節點加入集群、更新 kubeconfig 以與新集群交互以及其他任務的說明。
使用 calico 進行 CNI 配置
在開始添加節點和運行工作負載之前,你最後需要的東西是 正確配置的 CNI。此示例將使用 calico 來實現這一點。
# cd /etc/cni/net.d # curl https://raw.githubusercontent.com/projectcalico/calico/v3.27.2/manifests/calico.yaml -O # kubectl create -f calico.yaml
如果此操作成功完成,你就可以開始添加節點並在集群上運行工作負載啦。
創建重置配置文件
為防止 kubeadm包 第一次初始化不成功,你還可以創建一個用於重置命令的配置文件:
# kubeadm config print reset-defaults > /etc/kubeadm/reset.yaml
這將創建以下文件:
/etc/kubeadm/reset.yaml
apiVersion: kubeadm.k8s.io/v1beta4 certificatesDir: /etc/kubernetes/pki criSocket: unix:///var/run/containerd/containerd.sock kind: ResetConfiguration
重置集群
要將集群重置為零,請運行以下命令:
# kubeadm reset --config /etc/kubeadm/reset.yaml
可以根據需要多次執行此操作,以確定集群的理想配置。
創建加入配置文件
很可能在初始化集群後,你可以使用 init 命令輸出中列出的命令加入任何節點,但如果你遇到問題,在要加入的節點上準備一個加入配置文件將會很有幫助。你可以在控制平面上創建此文件,或者在要加入集群的節點上運行命令,我們假設你執行了後者。
# kubeadm config print join-defaults > /etc/kubeadm/join.yaml
這將創建以下文件。
/etc/kubeadm/join.yaml
apiVersion: kubeadm.k8s.io/v1beta3 caCertPath: /etc/kubernetes/pki/ca.crt discovery: bootstrapToken: apiServerEndpoint: kcp01.example.com:6443 token: abcdef.0123456789abcdef unsafeSkipCAVerification: true timeout: 5m0s tlsBootstrapToken: abcdef.0123456789abcdef kind: JoinConfiguration nodeRegistration: criSocket: unix:///var/run/containerd/containerd.sock imagePullPolicy: IfNotPresent name: node01.example.com taints: null
discovery.bootstrapToken.token
,第二個用於 discovery.tlsBootstrapToken
屬性。加入集群
使用在#創建集群中生成的令牌信息,可以通過命令 kubeadm join
使另一台機器作為工作節點加入集群。
請記住,你還需要通過將標誌 <SOCKET>
傳遞給命令 kubeadm join
來為工作節點選擇容器運行時接口。
例如:
# kubeadm join <api-server-ip>:<port> --token <token> --discovery-token-ca-cert-hash sha256:<hash> --node-name=<name_of_the_node> --cri-socket=<SOCKET>
要生成新的引導令牌,可以使用以下命令:
kubeadm token create --print-join-command
如果你使用 Cilium 並且發現工作節點仍然處於 NotReady
狀態,請使用以下命令檢查工作節點的狀態:
kubectl describe node <node-id> --namespace=kube-system
如果你發現以下條件狀態:
Type Status Reason ---- ------ ------ NetworkUnavailable False CiliumIsUp Ready False KubeletNotReady container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized
請在工作節點上重啟 containerd.service
和 kubelet.service
。
提示與技巧
拆除集群
當需要從頭開始重建集群時,可使用 kubectl包 按官方文檔 拆除集群 的步驟操作:
kubectl drain <节点名称> --delete-local-data --force --ignore-daemonsets
此處 <節點名稱>
是需要清空並重置的節點名稱,可通過 kubectl get node -A
列出所有節點。
隨後重置該節點:
# kubeadm reset
在代理後方操作
kubeadm
會讀取 https_proxy
、http_proxy
和 no_proxy
環境變量。需確保 Kubernetes 內部網絡包含在 no_proxy
中,例如:
export no_proxy="192.168.122.0/24,10.96.0.0/12,192.168.123.0/24"
其中第二個 CIDR(10.96.0.0/12)是 Kubernetes 默認的服務網絡地址段。
故障排除
無法獲取容器統計信息
如果 kubelet.service
輸出以下錯誤:
Failed to get system container stats for "/system.slice/kubelet.service": failed to get cgroup stats for "/system.slice/kubelet.service": failed to get container info for "/system.slice/kubelet.service": unknown container "/system.slice/kubelet.service"
則需要為 kubelet 添加配置(參見 相關的上游問題)。
/var/lib/kubelet/config.yaml
systemCgroups: '/systemd/system.slice' kubeletCgroups: '/systemd/system.slice'
使用 Flannel CNI 和 systemd-networkd 時 Pod 無法通信
參見 上游問題報告。
systemd-networkd 會為每個連結分配一個持久的 MAC 地址。此策略在其默認配置文件 /usr/lib/systemd/network/99-default.link
中定義。然而,Flannel 依賴於能夠選擇自己的 MAC 地址。要覆蓋 systemd-networkd 對 flannel*
接口的行為,請創建以下配置文件:
/etc/systemd/network/50-flannel.link
[Match] OriginalName=flannel* [Link] MACAddressPolicy=none
然後 重啟 systemd-networkd.service
。
如果集群已經在運行,你可能需要手動刪除每個節點上的 flannel.1
接口和 kube-flannel-ds-*
Pod,包括主節點。Pod 會立即重新創建,並且它們自己會重新創建 flannel.1
接口。
刪除 flannel.1
接口:
# ip link delete flannel.1
刪除 kube-flannel-ds-*
Pod。使用以下命令刪除所有節點上的所有 kube-flannel-ds-*
Pod:
$ kubectl -n kube-system delete pod -l="app=flannel"
CoreDNS Pod 一直處於 Pending 狀態,控制平面節點保持 "NotReady"
當在單台機器上使用 kubeadm init
引導 Kubernetes 時,如果沒有其他機器 kubeadm join
集群,控制平面節點默認會被添加上污點(Taint)。因此,工作負載將不會調度到該機器上。
可以通過以下命令確認控制平面節點是否被添加了污點:
kubectl get nodes -o json | jq '.items[].spec.taints
要臨時允許在控制平面節點上調度,執行:
kubectl taint nodes <your-node-name> node-role.kubernetes.io/control-plane:NoSchedule-
然後 重啟 containerd.service
和 kubelet.service
以應用更新。
[kubelet-finalize] 畸形頭:缺少 HTTP content-type
你可能忘記了 選擇 systemd cgroup 驅動。參見 kubeadm 問題 2767 報告此問題。
CoreDNS Pod 由於循環無法啟動
當主機節點運行本地 DNS 緩存(如 systemd-resolved)時,CoreDNS 可能會由於檢測到轉發循環而無法啟動。可以通過以下方式檢查:
# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE cilium-jc98m 1/1 Running 0 21m cilium-operator-64664858c8-zjzcq 1/1 Running 0 21m coredns-7db6d8ff4d-29zfg 0/1 CrashLoopBackOff 6 (41s ago) 21m coredns-7db6d8ff4d-zlvsm 0/1 CrashLoopBackOff 6 (50s ago) 21m etcd-k8s 1/1 Running 19 21m kube-apiserver-k8s 1/1 Running 17 21m kube-controller-manager-k8s 1/1 Running 16 21m kube-proxy-cvntt 1/1 Running 0 21m kube-scheduler-k8s 1/1 Running 23 21m
# kubectl logs -n kube-system coredns-7db6d8ff4d-29zfg
... [FATAL] plugin/loop: Loop ([::1]:46171 -> :53) detected for zone ".", see https://coredns.io/plugins/loop#troubleshooting. Query: "HINFO 64811921068182325.3042126689798234092."
這是由於 kubelet 將主機的 /etc/resolv.conf
文件傳遞給所有使用默認 dnsPolicy
的 Pod。CoreDNS 使用此 /etc/resolv.conf
作為上游轉發請求的列表。由於它包含一個環回地址(如 127.0.0.53),CoreDNS 最終將請求轉發給自己。
參見 https://coredns.io/plugins/loop/#troubleshooting 以解決此問題。
參見
- Kubernetes Documentation - The upstream documentation
- Kubernetes Cluster with Kubeadm - Upstream documentation on how to setup a Kubernetes cluster using kubeadm
- Kubernetes Glossary - The official glossary explaining all Kubernetes specific terminology
- Kubernetes Addons - A list of third-party addons
- Kubelet Config File - Documentation on the Kubelet configuration file
- Taints and Tolerations - Documentation on node affinities and taints