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