入與避坑指南)
簡(jiǎn)介本資源面向在 Kubernetes 集群中部署與運(yùn)維容器網(wǎng)絡(luò)的工程師提供 Calico v3.20.6 所需的容器鏡像與核心部署清單幫助解決離線環(huán)境或內(nèi)網(wǎng)集群無(wú)法直接拉取鏡像、網(wǎng)絡(luò)策略難以落地的問(wèn)題。壓縮包共 5 個(gè)文件包含 4 個(gè) tar 鏡像包與 1 個(gè) yaml 配置文件整體約 96.68MB其中鏡像分別對(duì)應(yīng)節(jié)點(diǎn)代理、CNI 插件、控制器與 FlexVol 插件yaml 則用于定義 DaemonSet、CNI 配置、策略控制器及 BGP 參數(shù)等部署對(duì)象。已有 1084 人學(xué)習(xí)下載適合需要快速搭建 Calico 網(wǎng)絡(luò)、驗(yàn)證跨節(jié)點(diǎn)通信與網(wǎng)絡(luò)隔離策略的中高級(jí)讀者。借助這套文件讀者可完成鏡像導(dǎo)入與清單應(yīng)用理解各組件在 Pod 網(wǎng)絡(luò)分配、策略執(zhí)行和路由通告中的職責(zé)并在此基礎(chǔ)上調(diào)試多租戶或安全要求較高場(chǎng)景下的服務(wù)通信。1. 從一次 Calico 拉取鏡像失敗說(shuō)起為什么我堅(jiān)持用 v3.20.6 這套離線包凌晨?jī)牲c(diǎn)某公司內(nèi)網(wǎng) Kubernetes 集群節(jié)點(diǎn) NotReadykubectl get pods -n kube-system 里 calico-node 清一色 ImagePullBackOff。節(jié)點(diǎn)上 docker images 空空如也registry 又在內(nèi)網(wǎng)隔離區(qū)外網(wǎng)拉取根本走不通。這種場(chǎng)景我一年能碰上三四回每次都是同一套解法提前備好 calico-v3.20.6 的容器鏡像包和 calico.yaml 清單離線導(dǎo)入、按序部署。Calico 是 Kubernetes 里最常見(jiàn)的 CNI 插件之一負(fù)責(zé) Pod 網(wǎng)絡(luò)互通和 NetworkPolicy 落地裝不好輕則節(jié)點(diǎn) NotReady重則整個(gè)集群網(wǎng)絡(luò)癱瘓。這套資源適合兩類(lèi)人一是內(nèi)網(wǎng)隔離環(huán)境里搭 K8s 的運(yùn)維二是想在生產(chǎn)集群升級(jí)前先在測(cè)試環(huán)境復(fù)現(xiàn)一遍 Calico 部署流程的開(kāi)發(fā)者。下面把我自己裝這套環(huán)境的完整過(guò)程拆開(kāi)寫(xiě)版本、參數(shù)、踩坑點(diǎn)都落到具體命令上。2. 裝前把環(huán)境列清楚版本、節(jié)點(diǎn)與鏡像清單2.1 版本選擇為什么鎖定 v3.20.6 而不是最新版Calico 的版本和 Kubernetes 版本之間有兼容矩陣不是越新越好。v3.20.6 屬于 v3.20 系列的一個(gè)補(bǔ)丁版本這個(gè)系列對(duì) Kubernetes 1.21 到 1.24 的支持比較穩(wěn)很多還在跑 1.22、1.23 的存量集群直接用它最省心。我一般不會(huì)在生產(chǎn)集群上追最新大版本原因有兩個(gè)一是新版本對(duì) iptables 模式、eBPF 數(shù)據(jù)面的默認(rèn)行為可能變存量 NetworkPolicy 規(guī)則容易出玄學(xué)問(wèn)題二是離線鏡像包和 yaml 清單必須版本對(duì)齊混用 v3.20 的 yaml 配 v3.21 的鏡像大概率在 calico-node 啟動(dòng)階段報(bào) CRD 字段不識(shí)別。選版本時(shí)先確認(rèn)三件事集群的 K8s 版本、節(jié)點(diǎn)操作系統(tǒng)內(nèi)核版本、以及是否用了 eBPF 模式。v3.20.6 默認(rèn)走 iptables 模式內(nèi)核要求 3.10 以上即可CentOS 7 的 3.10 內(nèi)核也能跑。如果你的集群是 1.25 以上那這套 v3.20.6 就不合適了得換更新的系列。這一步別偷懶版本對(duì)不上后面全是白工。2.2 節(jié)點(diǎn)環(huán)境對(duì)照表裝之前把每個(gè)節(jié)點(diǎn)的信息列成表比裝到一半才發(fā)現(xiàn)內(nèi)核不對(duì)要省事得多。下面是我一般會(huì)確認(rèn)的字段檢查項(xiàng)要求查看命令K8s 版本1.21 ~ 1.24kubectl version --short內(nèi)核版本 3.10uname -r容器運(yùn)行時(shí)Docker 或 containerddocker version/ctr version節(jié)點(diǎn)主機(jī)名唯一且已加入集群kubectl get nodes網(wǎng)卡名與 calico.yaml 中配置一致ip addr防火墻放行 IPIP 協(xié)議或已關(guān)閉iptables -LSELinux建議 permissivegetenforce這張表里最容易翻車(chē)的是網(wǎng)卡名和防火墻。calico.yaml 里有個(gè)IP_AUTODETECTION_METHOD環(huán)境變量默認(rèn)是first-found多網(wǎng)卡機(jī)器上它可能選到一張業(yè)務(wù)網(wǎng)卡導(dǎo)致 Pod 跨節(jié)點(diǎn)不通。我一般會(huì)顯式指定成interfaceeth0或者can-reach某網(wǎng)關(guān)IP把選擇權(quán)抓在自己手里。2.3 鏡像與清單文件清單這套資源里實(shí)際要用到的文件就兩類(lèi)容器鏡像包和 calico.yaml。鏡像包通常是一個(gè) tar 歸檔里面包含 calico/node、calico/cni、calico/kube-controllers 這幾個(gè)核心鏡像版本號(hào)都是 v3.20.6。calico.yaml 是部署清單里面定義了 DaemonSet、Deployment、ServiceAccount、CRD 等一堆資源。拿到資源后先核對(duì)鏡像 tag用下面這條命令看 tar 包里到底有哪些鏡像# 查看鏡像包內(nèi)容不實(shí)際導(dǎo)入 tar -tf calico-v3.20.6-images.tar | head -20 # 或者已經(jīng)導(dǎo)入后查看本地鏡像 docker images | grep calico邏輯說(shuō)明tar -tf只列出歸檔內(nèi)容不解壓適合導(dǎo)入前確認(rèn)包是否完整docker images | grep calico用于導(dǎo)入后核對(duì) tag 是否和 yaml 里寫(xiě)的一致。參數(shù)上沒(méi)什么花頭關(guān)鍵是 tag 必須精確匹配v3.20.6和v3.20.6-amd64在 yaml 里就是兩個(gè)不同的字符串對(duì)不上就是 ImagePullBackOff。3. 導(dǎo)入鏡像與部署 calico.yaml兩條命令背后的參數(shù)取舍3.1 鏡像導(dǎo)入docker load 與 ctr 的差別鏡像導(dǎo)入這一步用 Docker 和用 containerd 的命令完全不同先確認(rèn)你的運(yùn)行時(shí)是哪個(gè)。K8s 1.24 之后很多集群默認(rèn)用 containerddocker load就不好使了。Docker 運(yùn)行時(shí)的導(dǎo)入方式# 在每個(gè)節(jié)點(diǎn)上執(zhí)行導(dǎo)入 calico 鏡像 docker load -i calico-v3.20.6-images.tar # 導(dǎo)入后確認(rèn) docker images | grep calicocontainerd 運(yùn)行時(shí)的導(dǎo)入方式# containerd 用 ctr 導(dǎo)入注意 -n 指定 namespace ctr -n k8s.io images import calico-v3.20.6-images.tar # 確認(rèn)導(dǎo)入結(jié)果 ctr -n k8s.io images ls | grep calico邏輯說(shuō)明docker load -i的-i指定輸入文件導(dǎo)入后鏡像進(jìn)入 Docker 的本地存儲(chǔ)。containerd 這邊關(guān)鍵是-n k8s.ioK8s 用的 namespace 就是k8s.io不加這個(gè)參數(shù)鏡像導(dǎo)進(jìn)去 K8s 也看不見(jiàn)這是最常見(jiàn)的坑之一。導(dǎo)入操作要在每個(gè)可能跑 calico-node 的節(jié)點(diǎn)上執(zhí)行因?yàn)?DaemonSet 會(huì)在所有節(jié)點(diǎn)起 Pod鏡像沒(méi)提前鋪好就會(huì)拉取失敗。3.2 calico.yaml 關(guān)鍵參數(shù)逐項(xiàng)改calico.yaml 拿到手不能直接 apply得先改幾個(gè)地方。下面是我一般會(huì)動(dòng)的參數(shù)# calico.yaml 中需要關(guān)注的環(huán)境變量片段 - name: CALICO_IPV4POOL_CIDR value: 10.244.0.0/16 # 必須和 kubeadm init 時(shí)的 pod-network-cidr 一致 - name: IP_AUTODETECTION_METHOD value: interfaceeth0 # 多網(wǎng)卡環(huán)境必須顯式指定 - name: CALICO_IPV4POOL_IPIP value: Always # 跨網(wǎng)段通信建議開(kāi)啟 IPIP邏輯說(shuō)明CALICO_IPV4POOL_CIDR是最容易出錯(cuò)的一項(xiàng)它必須和集群初始化時(shí)kubeadm init --pod-network-cidr指定的網(wǎng)段完全一致不一致的話 Pod 拿不到 IP。IP_AUTODETECTION_METHOD默認(rèn)first-found在多網(wǎng)卡機(jī)器上會(huì)亂選改成interfaceeth0把網(wǎng)卡釘死。CALICO_IPV4POOL_IPIP設(shè)成Always表示所有流量走 IPIP 隧道封裝跨網(wǎng)段節(jié)點(diǎn)通信更穩(wěn)代價(jià)是有一點(diǎn)封裝開(kāi)銷(xiāo)同網(wǎng)段集群可以設(shè)Never省掉這層開(kāi)銷(xiāo)。改完 yaml 后用kubectl apply -f calico.yaml部署。如果集群里已經(jīng)有舊版本 Calico先kubectl delete -f 舊yaml再 apply 新的別直接覆蓋CRD 字段沖突會(huì)報(bào)錯(cuò)。3.3 部署順序與等待策略apply 之后不要立刻去查 PodCalico 的啟動(dòng)有依賴順序先是 CRD 和 ServiceAccount 創(chuàng)建然后 calico-node DaemonSet 起 Pod最后 kube-controllers 才就緒。我一般用下面這條命令盯著# 持續(xù)觀察 calico 相關(guān) Pod 狀態(tài)直到全部 Running kubectl get pods -n kube-system -l k8s-appcalico-node -w # 另開(kāi)一個(gè)窗口看 kube-controllers kubectl get pods -n kube-system -l k8s-appcalico-kube-controllers -w邏輯說(shuō)明-w是 watch 模式狀態(tài)變化會(huì)實(shí)時(shí)刷新比反復(fù)敲 get 省事。正常情況下 calico-node 會(huì)在 1 到 2 分鐘內(nèi)全部 Running如果超過(guò) 5 分鐘還有 Pod 卡在 Init 或 CrashLoopBackOff就得去看日志了。kubectl logs -n kube-system pod名 -c calico-node是第一個(gè)要看的報(bào)錯(cuò)信息基本都在里面。4. 避坑與排查五條我踩過(guò)的 Calico 部署坑4.1 坑一節(jié)點(diǎn) NotReadycalico-node 一直 Init現(xiàn)象kubectl get nodes顯示節(jié)點(diǎn) NotReadycalico-node Pod 卡在 Init:0/1 或者 PodInitializing。原因最常見(jiàn)的是鏡像沒(méi)導(dǎo)入到該節(jié)點(diǎn)或者導(dǎo)入到了錯(cuò)誤的 namespace。containerd 環(huán)境下用ctr images import忘了加-n k8s.io鏡像進(jìn)了 default namespaceK8s 在 k8s.io 里找不到。解決在該節(jié)點(diǎn)執(zhí)行ctr -n k8s.io images ls | grep calico確認(rèn)鏡像存在。不存在就重新導(dǎo)入命令帶上-n k8s.io。Docker 環(huán)境則用docker images確認(rèn)。4.2 坑二Pod 跨節(jié)點(diǎn)不通同節(jié)點(diǎn)正常現(xiàn)象同一節(jié)點(diǎn)上的 Pod 互相能 ping 通跨節(jié)點(diǎn)就不通NetworkPolicy 也沒(méi)生效。原因IP_AUTODETECTION_METHOD選錯(cuò)了網(wǎng)卡。多網(wǎng)卡機(jī)器上默認(rèn)first-found可能選到一張沒(méi)有跨節(jié)點(diǎn)路由的網(wǎng)卡IPIP 隧道建不起來(lái)。解決把IP_AUTODETECTION_METHOD改成interfaceeth0換成你實(shí)際的網(wǎng)卡名重新 apply。改完可以進(jìn) calico-node Pod 用ip route看路由表確認(rèn)隧道網(wǎng)卡正確。4.3 坑三CALICO_IPV4POOL_CIDR 與集群網(wǎng)段不一致現(xiàn)象calico-node 起來(lái)了但新創(chuàng)建的 Pod 一直 ContainerCreatingdescribe 顯示 failed to allocate IP。原因calico.yaml 里的CALICO_IPV4POOL_CIDR和kubeadm init --pod-network-cidr指定的網(wǎng)段不一樣IPAM 分配不到地址。解決kubectl get pod -n kube-system calico-node-pod -o yaml | grep CIDR看實(shí)際生效值和集群初始化參數(shù)對(duì)齊后重新 apply。已經(jīng)分配出去的舊 IP 池可以用calicoctl ipam show檢查。4.4 坑四CRD 字段沖突導(dǎo)致 apply 報(bào)錯(cuò)現(xiàn)象kubectl apply -f calico.yaml報(bào)field is immutable或 CRD 版本不匹配。原因集群里殘留了舊版本 Calico 的 CRD新 yaml 里的字段定義和舊 CRD 沖突。解決先kubectl delete -f 舊版本calico.yaml等 Pod 清理干凈再 apply 新版本。如果 CRD 刪不掉用kubectl patch crd 名稱 -p {metadata:{finalizers:[]}} --typemerge清掉 finalizer 再刪。4.5 坑五內(nèi)核模塊未加載IPIP 隧道起不來(lái)現(xiàn)象calico-node 日志報(bào)Failed to create tunnel或ipip: Operation not supported。原因節(jié)點(diǎn)內(nèi)核沒(méi)加載ipip模塊或者內(nèi)核版本太低不支持。解決modprobe ipip手動(dòng)加載然后lsmod | grep ipip確認(rèn)。要持久化就寫(xiě)進(jìn)/etc/modules-load.d/calico.conf。內(nèi)核低于 3.10 的節(jié)點(diǎn)建議升級(jí)內(nèi)核否則 IPIP 模式跑不起來(lái)。5. 裝完怎么驗(yàn)證從節(jié)點(diǎn)狀態(tài)到跨節(jié)點(diǎn)連通性部署完不是看到 Running 就完事了得實(shí)際驗(yàn)證網(wǎng)絡(luò)通不通。我一般按下面這個(gè)順序走一遍每一步都有明確的通過(guò)標(biāo)準(zhǔn)。第一步確認(rèn)所有節(jié)點(diǎn) Readykubectl get nodes -o wide輸出里 STATUS 列全是 Ready說(shuō)明 calico-node 已經(jīng)在每個(gè)節(jié)點(diǎn)正常工作。如果有 NotReady回到第 4 章排查。第二步確認(rèn) calico-node 和 kube-controllers 全部 Runningkubectl get pods -n kube-system | grep calico期望看到 calico-node 的 Pod 數(shù)量等于節(jié)點(diǎn)數(shù)calico-kube-controllers 一個(gè) Running。數(shù)量對(duì)不上說(shuō)明 DaemonSet 沒(méi)調(diào)度到某些節(jié)點(diǎn)檢查節(jié)點(diǎn)是否有污點(diǎn)。第三步驗(yàn)證 Pod 跨節(jié)點(diǎn)通信。這是最關(guān)鍵的一步起兩個(gè)測(cè)試 Pod 分別調(diào)度到不同節(jié)點(diǎn)# 創(chuàng)建測(cè)試 Pod用 nodeName 強(qiáng)制調(diào)度到指定節(jié)點(diǎn) kubectl run test-pod-1 --imagebusybox --overrides{spec:{nodeName:node-1}} -- sleep 3600 kubectl run test-pod-2 --imagebusybox --overrides{spec:{nodeName:node-2}} -- sleep 3600 # 拿到 test-pod-2 的 IP kubectl get pod test-pod-2 -o wide # 從 test-pod-1 里 ping test-pod-2 的 IP kubectl exec -it test-pod-1 -- ping -c 3 test-pod-2的IP邏輯說(shuō)明--overrides里的nodeName強(qiáng)制把 Pod 調(diào)度到指定節(jié)點(diǎn)避免調(diào)度器把兩個(gè) Pod 放到同一節(jié)點(diǎn)導(dǎo)致測(cè)不出跨節(jié)點(diǎn)通信。ping 通說(shuō)明 IPIP 隧道和路由都正常。如果 ping 不通先kubectl exec進(jìn) Pod 看ip route再回節(jié)點(diǎn)上看ip route和 iptables 規(guī)則。第四步驗(yàn)證 NetworkPolicy 生效。建一個(gè)默認(rèn)拒絕的 policy確認(rèn)未放行的流量被阻斷# 應(yīng)用一個(gè)默認(rèn)拒絕入站的 NetworkPolicy cat EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-ingress namespace: default spec: podSelector: {} policyTypes: - Ingress EOF # 再次從 test-pod-1 ping test-pod-2應(yīng)該超時(shí) kubectl exec -it test-pod-1 -- ping -c 3 -W 2 test-pod-2的IP邏輯說(shuō)明podSelector: {}表示選中 namespace 下所有 PodpolicyTypes: Ingress表示只控制入站。應(yīng)用后 ping 應(yīng)該超時(shí)說(shuō)明 policy 生效。驗(yàn)證完記得刪掉這個(gè) policy不然會(huì)影響后續(xù)測(cè)試kubectl delete networkpolicy default-deny-ingress。第五步清理測(cè)試資源kubectl delete pod test-pod-1 test-pod-2這套驗(yàn)證流程走完基本能確認(rèn) Calico v3.20.6 在集群里是真正跑通了而不是表面 Running。從那以后我每次裝完 Calico不管節(jié)點(diǎn)狀態(tài)多好看都強(qiáng)制把跨節(jié)點(diǎn) ping 和 NetworkPolicy 這兩步走一遍因?yàn)?Running 的 Pod 不代表網(wǎng)絡(luò)真的通這個(gè)教訓(xùn)是某次生產(chǎn)事故換來(lái)的。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取