
简介面向在 Kubernetes 集群中实施网络互联与安全策略的运维、开发人员这份 Calico v3.20.6 部署包提供离线安装所需的容器镜像与核心清单适用于版本固定、内网部署及实验环境复现。压缩包共 5 个文件包含 4 个 tar 镜像和 1 个 yaml 清单整体约 96.68MB镜像覆盖节点代理、CNI 插件、策略控制器与 FlexVol 挂载插件清单则组织 Calico 的 DaemonSet、CNI 配置、策略控制器和 BGP 参数等部署对象便于一次性拉齐组件版本。已有 1086 人学习下载说明其在集群网络搭建场景中具有一定参考价值。读者可据此完成 Calico 的离线部署厘清各组件在 Pod 地址分配、跨节点路由和网络策略执行中的职责并借助清单结构排查通信异常、策略不生效等问题适合具备 Kubernetes 基础的中高级用户对照实践。1. 先把话说透calico v3.20.6 的镜像包和 calico.yaml 到底解决哪类部署问题内网机房里最尴尬的一幕是集群节点已经 Readycalico-node 却卡在 ImagePullBackOffkubectl describe 里只剩一条拉取超时。calico v3.20.6 的容器镜像包加 calico.yaml 就是给这种场景准备的镜像提前落盘清单锁定 CNI 版本节点不再依赖外网仓库。它解决的不是“装一个网络插件”而是把 calico kubernetes 部署里最容易漂移的镜像地址、版本号和 CNI 清单固定成一套可复现输入。适合自建集群交付、离线机房、版本回滚三类人尤其适合手里已经有 kubelet 和容器运行时、只差 CNI 这一步的集群。2. 拆开资源看组成calico v3.20.6 镜像清单与 calico.yaml 的对应位2.1 先认镜像三件套再看 yaml 里谁引用谁拿到一个 calico v3.20.6 的容器镜像 tar先别急着导入。calico.yaml 里真正被引用的镜像通常集中在三处initContainer install-cni 用 calico/cniDaemonSet 主容器用 calico/nodeDeployment 用 calico/kube-controllers。少数清单会多出 flexvol 一类镜像判断标准只有一个以 calico.yaml 里的 image 行和 tar 内实际 tag 为准别按记忆补。镜像名和 tag 对不上后面所有 kubectl apply 都会变成猜谜。镜像工作负载作用离线校验点calico/cni:v3.20.6calico-node 的 initContainer安装 CNI 配置和二进制节点 /etc/cni/net.d 是否生成calico/node:v3.20.6calico-node 主容器路由、IPPool、节点网络crictl images 是否能看到calico/kube-controllers:v3.20.6calico-kube-controllers同步策略、IPAM、控制器Deployment 是否 Available# 查看镜像 tar 的顶层结构先确认是 docker save 还是 containerd 导出格式 tar -tf calico-v3.20.6-images.tar | sed -n 1,30p # 列出 calico.yaml 中所有镜像引用和 tar 内 tag 一一对齐 grep -nE ^[[:space:]]*image: calico.yamltar -tf只列内容不解包如果是.tar.gz换成tar -tzf。sed -n 1,30p只看前 30 行避免输出刷屏。grep带-n是为了拿到行号后面改 yaml 时能快速定位。实际核对时把grep出来的每个image:值和crictl images或ctr images ls的结果逐个对照缺哪个补哪个不要用“latest”或近似 tag 顶替。2.2 为什么锁 v3.20.6版本匹配比“能跑”更重要calico 的版本不是孤立存在的。calico-node、calico-kube-controllers、calicoctl、CRD 定义、Kubernetes API 版本之间有一条隐性的匹配链。v3.20.6 是 v3.20 系列里的补丁版本适合把它当成一个固定基线节点镜像、控制器镜像、calicoctl 都锁在同一小版本排查问题时不会出现“客户端比服务端新一个大版本”的尴尬。常见做法是先用calicoctl version看客户端再和集群里的组件镜像 tag 对一遍差得太多就先别动生产集群。选型时还要看集群的 Kubernetes 版本边界。v3.20 系列通常对应较早一批 Kubernetes 版本集群如果已经在较新的 API 上跑直接套旧清单可能遇到 CRD 字段不识别、准入策略变化一类问题。我的习惯是先在测试集群把 calico.yaml 干跑一遍确认 CRD 能创建、DaemonSet 能滚动再决定是否推到交付环境。不要只看“apply 返回 0”那只能说明 API 收下了请求不代表 CNI 真的工作。# 记录当前集群里的 calico 组件与 CRD升级/回滚前留底 kubectl -n kube-system get ds calico-node -o yaml calico-node-before.yaml kubectl -n kube-system get deploy calico-kube-controllers -o yaml calico-kube-controllers-before.yaml kubectl get crd | grep projectcalico.org | awk {print $1} calico-crd-list.txt这三条命令的作用是把“现在是什么样”固定下来。-o yaml保存完整对象后面回滚时至少知道原来的镜像 tag、环境变量和挂载点。awk {print $1}只取 CRD 名称方便和 calico.yaml 里的 CRD 段做 diff。注意不要把备份文件随手放在/tmp节点重启就没了放到有持久化保障的目录或者至少同步到交付包里。# 只读检查现有 IPPool别覆盖业务正在用的地址段 kubectl get ippools.crd.projectcalico.org -o wide如果集群里已经有 IPPool先看清楚它的 CIDR、封装模式和 NAT 出口。离线重装 calico 时最怕的一件事是旧 IPPool 还在、新清单又创建一个默认段Pod 地址开始漂。-o wide能多看到几列关键信息比只看名字有用。没有 CRD 的新集群会提示资源不存在这属于正常现象说明是首次安装。2.3 离线落地前的三项核对架构、运行时、CNI 目录镜像包能不能直接用第一关是 CPU 架构。交付包里如果是 amd64节点却是 arm64导入时可能不报错跑起来才在 exec format error 上翻车。第二关是容器运行时kubelet 走 containerd 还是 docker shim决定你用ctr还是docker load这两条路导入的镜像库不是同一个。第三关是 CNI 目录/etc/cni/net.d和/opt/cni/bin必须存在且可写install-cni 才有地方落配置。# 节点架构决定镜像包能不能直接用 uname -m # 看清 kubelet 使用的容器运行时后面决定用 ctr 还是 docker load kubectl get nodes -o wide # CNI 配置目录和二进制目录必须存在否则 install-cni 会失败 ls -ld /etc/cni/net.d /opt/cni/bin # 在节点上确认 CRI 运行时端点containerd 节点通常能看到 k8s.io 命名空间 crictl info | grep -i runtimeuname -m输出x86_64或aarch64和镜像包标注的架构对齐。kubectl get nodes -o wide最右侧会显示容器运行时先确认是 containerd 还是 docker不要凭经验猜。ls -ld看目录权限和属主目录不存在就先创建别等 initContainer 报错再回头补。crictl info用来确认 CRI 端点可达连不上时先查运行时服务状态不是改 calico.yaml。注意离线环境里“镜像导入了”和“kubelet 能拉取”是两件事。导入动作只证明文件进了某个镜像库kubelet 看到的是 CRI 命名空间下的镜像视图。3. 把镜像送进节点containerd 与 docker 两条导入路径的差异3.1 containerd 节点ctr import 后必须做 tag 与可见性校验containerd 节点上最常见的错误是ctr images import不带-n k8s.io。默认命名空间里确实能看到镜像kubelet 却完全不知道Pod 照样 ImagePullBackOff。正确顺序是先导入到 k8s.io 命名空间再用crictl images从 CRI 视角确认一次。ctr是 containerd 原生客户端crictl更贴近 kubelet 的真实视角两边都看到才算落地。# 只在 containerd 节点执行-n k8s.io 不能省kubelet 看的是这个命名空间 ctr -n k8s.io images import /root/calico-v3.20.6-images.tar # 用 crictl 再看一次它贴近 kubelet 的真实视角 crictl images | grep -E calico/(node|cni|kube-controllers):v3.20.6 # 如果清单里写的是私有仓库前缀先看来源名再给本地镜像补一个同名 tag ctr -n k8s.io images ls | grep calico ctr -n k8s.io images tag docker.io/calico/node:v3.20.6 registry.local/calico/node:v3.20.6-n k8s.io是 containerd 命名空间参数不是 Kubernetes 命名空间别混淆。crictl images输出里的 REPOSITORY 和 TAG 要和 calico.yaml 的 image 行一致差一个前缀都可能触发重新拉取。ctr images tag的源镜像名以ctr images ls实际输出为准有的导入包会显示成calico/node:v3.20.6有的会补上docker.io/不要照抄示例。私有仓库前缀只在 calico.yaml 也写了同样前缀时才有意义否则 tag 了也不会被引用。如果节点上没有私有仓库也不想改 calico.yaml 的镜像地址最省事的做法是保持导入后的原 tag让清单里的calico/node:v3.20.6直接命中本地镜像。此时不要把imagePullPolicy改成 Always否则 kubelet 会绕过本地缓存去拉远端离线环境立刻失败。镜像导入完成后可以用crictl inspecti看镜像详细信息确认架构和标签没有错位。3.2 docker 运行时docker load 只对 docker shim 有效有些老集群的 kubelet 还挂在 docker shim 上这时候docker load才是有效路径。判断方法不是看节点上有没有 docker 命令而是看kubectl get nodes -o wide里的容器运行时列。节点明明走 containerd却在上面docker load镜像只会进 docker 的存储kubelet 依然拉不到。这个坑在混合运行时环境里特别常见交付前一定要逐节点确认。# 仅当节点确实走 docker shim 时docker load 才对 kubelet 有意义 docker load -i /root/calico-v3.20.6-images.tar # 确认三个 calico 镜像的仓库名和 tag docker images --format {{.Repository}}:{{.Tag}} | grep -E calico/(node|cni|kube-controllers):v3.20.6 # 私有仓库前缀和 calico.yaml 的 image 行保持一致 docker tag calico/node:v3.20.6 registry.local/calico/node:v3.20.6docker load -i的-i指定输入文件不要写成-f。--format只输出仓库和标签方便 grep不会把 IMAGE ID 和大小混进来。docker tag只是给本地镜像加别名不产生新层如果后面要推私有仓库再执行docker push。注意 docker 和 containerd 的镜像存储互不相通混合运行时集群要按节点分别处理不能在一个节点导完就认为全集群都有。3.3 镜像地址与 imagePullPolicy别让本地镜像被 Always 绕过calico.yaml 里默认的imagePullPolicy多数是 IfNotPresent但有些二次修改过的清单会把它改成 Always。离线环境里 Always 的含义很直接不管本地有没有kubelet 都先去仓库问一圈问不到就失败。导入镜像后先 grep 一遍imagePullPolicy确认没有残留的 Always再决定要不要统一改成 IfNotPresent。改之前备份改之后 dry-run别在生产清单上直接 sed 到底。# 先看清单里有没有被改成 Always离线环境里它会把本地镜像绕过去 grep -n imagePullPolicy calico.yaml # 确认没有业务定制后再统一改成 IfNotPresent cp calico.yaml calico.yaml.bak sed -i s/imagePullPolicy: Always/imagePullPolicy: IfNotPresent/g calico.yamlgrep -n先定位行号避免盲改。cp备份是必须的sed 改错还能回退。sed -i会原地修改文件执行前确认当前目录就是 calico.yaml 所在目录。改完后用grep -n imagePullPolicy calico.yaml再看一遍确认没有漏网。镜像地址和拉取策略是一对地址决定 kubelet 找谁策略决定它是否信任本地缓存两边都对上离线部署才稳。4. 改 calico.yaml 再 applyCIDR、网卡探测与镜像前缀的落地点4.1 先对齐 Pod CIDRCALICO_IPV4POOL_CIDR 不是随便填calico.yaml 里的CALICO_IPV4POOL_CIDR决定 Pod 地址池必须和 kube-controller-manager 的--cluster-cidr对齐。很多交付环境直接套默认192.168.0.0/16结果和机房物理网段撞车Pod 能起但跨节点不通排查半天才发现地址重叠。正确做法是先读控制面参数再改 calico.yaml最后才 apply。不要反过来先 apply 再改IPPool 已经分配出去的地址不会自动收回。# 从控制面参数确认集群 Pod CIDR不要凭 calico 默认值猜 kubectl -n kube-system get pods -l componentkube-controller-manager -o yaml | grep -E cluster-cidr|service-cluster-ip-range # 找 calico.yaml 里 IPPool 的 CIDR 配置位置 grep -n -A2 CALICO_IPV4POOL_CIDR calico.yaml-l componentkube-controller-manager用标签选控制面 Pod比手写 Pod 名稳。grep -E同时看 Pod CIDR 和 Service CIDR两个段不能和节点网段重叠。grep -n -A2会带出配置行和后面两行方便确认当前值是注释状态还是已启用。如果CALICO_IPV4POOL_CIDR被注释需要先取消注释再改值如果集群已有 IPPool改 yaml 不会自动迁移旧地址先规划再动手。# 改之前先备份这里只演示替换执行前用 grep 确认只命中目标值 cp calico.yaml calico.yaml.bak sed -i s#value: 192.168.0.0/16#value: 10.244.0.0/16# calico.yaml示例里的10.244.0.0/16只是占位实际值以控制面参数为准。sed用的是#作为分隔符避免 CIDR 里的斜杠转义。执行前先grep确认文件里只有目标行匹配这个值否则可能误改其他环境变量。改完后用grep -n -A2 CALICO_IPV4POOL_CIDR calico.yaml复核确认注释符已去掉、值已替换。4.2 多网卡节点IP_AUTODETECTION_METHOD 决定 calico 拿哪张网卡多网卡节点上calico 默认的 IP 自动探测可能选到管理网卡、存储网卡甚至一张没有默认路由的网卡。表现是 calico-node 起来了节点间 BGP 或隧道却建不起来。处理方式是在 calico-node 的环境变量里显式指定网卡常见值是interfaceeth0或interfaceens192。前提是先看清楚节点上网卡的命名和地址别把示例里的 eth0 直接抄到 ens 系列节点上。# 多网卡节点先看默认路由和网卡地址判断 calico 应该拿哪张网卡 ip -4 addr show ip route get 10.244.0.1 2/dev/null || trueip -4 addr show列出所有 IPv4 地址重点看哪张网卡承载节点间通信。ip route get查看到某个地址时实际走哪条路由没有这个地址时加|| true不让命令中断。确认网卡名后再改 calico.yaml 的 env 段# calico.yaml 的 calico-node env 段按实际网卡名改 interfaceeth0 - name: IP_AUTODETECTION_METHOD value: interfaceeth0这段 YAML 要放在 calico-node DaemonSet 的 env 列表里缩进和相邻变量保持一致。interface后面写节点上真实存在的网卡名不要写 IP。改完后 apply再通过kubectl -n kube-system logs ds/calico-node -c calico-node看启动日志里选中的地址是不是预期网卡。如果日志里仍然出现自动探测结果说明变量没生效先检查 env 名称拼写和缩进。4.3 镜像前缀、干跑与分段 apply镜像前缀改不改取决于你有没有私有仓库。没有仓库就保持 calico.yaml 原样让导入后的本地 tag 直接命中有仓库才统一替换image:前缀。替换后先做客户端干跑抓 YAML 语法和字段错误再正式 apply。apply 返回 0 不代表 DaemonSet 滚动成功必须盯 rollout status 和 Pod 状态尤其是离线环境里镜像拉取失败会拖很久。# 只有使用私有仓库时才替换前缀没有仓库就跳过这一行 sed -i s#image: calico/#image: registry.local/calico/#g calico.yaml # 客户端干跑先抓语法和字段错误不碰集群 kubectl apply --dry-runclient -f calico.yaml # 正式提交后盯 DaemonSet 滚动状态不要只看 apply 返回 0 kubectl apply -f calico.yaml kubectl -n kube-system rollout status ds/calico-node --timeout180s kubectl -n kube-system get pods -l k8s-appcalico-node -o widesed里registry.local换成你的仓库地址末尾斜杠别丢。--dry-runclient只做本地校验不会创建对象适合快速抓缩进和字段名错误。rollout status的--timeout180s给足滚动时间超时会返回非零方便脚本判断。get pods -o wide看 Pod 落在哪些节点、是否 Ready比只看 DaemonSet 的 DESIRED 列更直观。如果 apply 时 CRD 报 already exists先确认是不是旧版本 CRD 残留不要直接 delete。提示calico.yaml 里往往包含 CRD、ServiceAccount、DaemonSet、Deployment 多类对象。第一次 apply 建议保存完整输出后面排查“哪个对象没起来”时有据可查。5. 避坑排查calico v3.20.6 离线部署最常见的五处翻车点5.1 镜像拉取失败ContainerCreating 不一定是网络问题**本文还有配套的精品资源点击获取