ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Calico IPIP隧道模式实战:原理、部署与排障全解

Calico IPIP隧道模式实战:原理、部署与排障全解 在真实处理Kubernetes集群的日常中网络问题几乎是每个运维都会碰到的“硬骨头”。尤其是节点一多、跨网段通信需求一上来Pod 之间明明在一个集群里却死活 ping 不通最后发现是路由转发链路没打通。我在这条路上踩过不少坑最终用得最顺手、也最稳定的一套组合就是 Calico 配合 IPIP 隧道模式。这篇文章不打算复述官方文档而是从实操角度把 Calico IPIP 的原理、版本下载、部署验证、故障排查和选型思考一次讲透希望能让刚接触 Calico 的人快速上手也能让已经在用的人少走几个弯路。1. IPIP 模式的前因后果Calico 为什么需要隧道封装1.1 Calico 的两种通信模式直接路由与 Overlay 封装Calico 的核心设计理念是把它当作一组分布式路由器来看待。每个运行了 calico-node 的节点本质上就是一台带 BGP 能力的路由器节点之间通过 BGP 协议交换 Pod 网段的路由信息。理解了这一点就会明白 Calico 有两种截然不同的数据转发路径。第一种是“直接路由”模式也就是不开任何隧道。节点 A 上的 Pod 要访问节点 B 上的 Pod数据包直接以宿主机 IP 作为下一跳由底层的三层网络原样转发。这种模式的性能损耗最小几乎做到了纯内核转发吞吐量高、时延低。但它有一个前提条件承载 Kubernetes 集群的物理网络或云网络必须允许任意节点之间的数据包以 Pod IP 为源和目的直接路由。换句话说中间路由器得知道 Pod 网段的路由条目或者至少能对 Pod 网段做通配转发。第二种就是 Overlay 封装模式典型代表是 IPIP 和 VXLAN。数据包不是直接扔进底层网络而是套上一层新的 IP 头或 UDP 头外层地址是宿主机节点 IP内层才是真正的 Pod IP。这样一来底层网络唯一需要认识的就是各节点的宿主机 IP完全不用感知 Pod 网段的存在。这种模式牺牲了一点性能换来的是对底层网络的强兼容性。很多刚开始接触 Calico 的人会问既然直接路由性能好为什么还要用封装答案是现实网络往往不那么“配合”。比如说Kubernetes 集群跨了几个子网子网之间的路由器由其他团队或云厂商控制你没法往这些路由器上塞一堆 Pod 网段路由。再比如在公有云环境里VPC 默认不会帮你转发不属于网络自身的非标准网段这时候 Overlay 就成了最省事的逃生通道。1.2 IPIP 封装的工作过程从一个数据包的视角来看IPIP 全称是 IP-in-IP标准的协议类型编号是 4。它的原理非常粗暴在原始 IP 数据包外面再套一个完整的 IP 头外层源地址是发送节点 A 的宿主机 IP外层目的地址是接收节点 B 的宿主机 IP。中间网络设备看到的就是一份普通的 IP 通信从一个宿主机到另一个宿主机完全不知道里面还藏了一个 Pod 之间的数据包。从数据流转来看一次完整的 IPIP 通信大概分为四步Pod A 向 Pod B 发出请求数据包从 Pod A 的 veth 对进入节点 A 的内核协议栈。节点 A 的路由规则命中 Calico 下发的到 Pod B 网段的路由条目下一跳指向节点 B出接口为 tunl0。数据包经过 tunl0 接口时被封装外部增加一个 20 字节左右的 IP 头外层源地址为节点 A IP外层目的地址为节点 B IP然后发往物理网络。底层网络按普通 IP 包将其送达节点 B节点 B 内核收到后识别到这是 IPIP 封装包解封装还原内层数据包再根据原始 Pod 目的地址转发给 Pod B。整个过程对 Kubernetes 层面完全透明Pod 内的应用感知不到封装的存在。如果你用 tcpdump 在宿主机物理网卡上抓包会看到很多 IP 协议类型为 4 的报文那基本就是 IPIP 封装的流量。这里有一个值得注意的操作细节在启用 IPIP 后节点上会出现一个名为 tunl0 的隧道接口。它不依赖具体的物理网卡而是以内核模块方式工作。如果节点的内核没有加载 ipip 相关模块tunl0 就不会正常工作这往往是很多部署问题的根源之一。1.3 IPIP 与 VXLAN 模式怎么选一张表看清差别IPIP 和 VXLAN 都是 Calico 支持的 Overlay 封装方式但在实际选型上两者的权衡点还挺明显的。我根据自己的使用经验把它们的关键差异整理在下面。对比维度IPIPVXLAN封装方式IP-in-IP外层直接套 IP 头UDP 封装外层套 UDP/IP 头额外开销约 20 字节约 50 字节IPv6 支持不支持仅支持 IPv4支持 IPv6网络要求依赖宿主机 IP 路由可达依赖宿主机 IP 路由可达也支持单播组播等内核支持需 ipip 模块绝大多数系统默认支持需 VXLAN 支持主流内核都有典型场景跨网段、跨子网、底层路由可控大型集群、混合网络、IPv6 环境选择建议其实没什么玄学。如果集群规模不大节点数在几十台以内底层网络就是标准的三层网络那 IPIP 足够了封装开销小排错也更直观。如果集群规模上了几百台或者网络环境比较复杂尤其是有多租户、多网络平面、IPv6 需求的时候VXLAN 的灵活性和可管理性会更好。同一个集群里实际上也可以让不同 IP 池分别使用不同封装模式这个后面配置部分会讲到。2. 动手前先搞定版本Calico 指定版本下载全解2.1 版本这东西为什么值得较真很多人觉得下载 Calico 无非就是拿个最新 yaml 往下应用省事。但到实际生产环境里版本这事讲究得很。一方面Kubernetes 的大版本在快速推进Calico 的每个版本都有对应的 Kubernetes 兼容范围盲目用新版不一定适配你现有的集群用太老的版本又可能缺少新的 API 支持。另一方面很多企业环境是离线的内网没有外网权限所有镜像和二进制都得提前准备好一旦在某个固定版本上稳定运行了后续扩容也不能随随便便拉一个 latest 回来。因此我强烈建议安装之前先明确两个变量一个是 Kubernetes 集群的确切版本另一个是你打算安装的 Calico 主版本号。然后去官方文档的兼容性表格里核对一下确认 Kubernetes API 版本、kubelet 参数等都没有冲突再把 yaml 和镜像下载下来。2.2 下载 calicoctl / calico 命令行版本Calico 的命令行工具有点“历史包袱”。在较早的版本里它叫 calicoctl当时是直接归档在 projectcalico/calico 仓库下的。后来官方拆分出了独立的 projectcalico/calicoctl 仓库v3.19 到 v3.26 左右这段时间要下载对应版本的独立 calicoctl。再往后新版本又回归为 projectcalico/calico 仓库直接提供二进制工具名简化为 calico。所以如果你在搜索引擎里看到命令一会是 calicoctl 一会是 calico不用懵两者基本一脉相承只是发布形式不同。以 v3.25.0 为例下载独立 calicoctl 的命令如下curl -LO https://github.com/projectcalico/calicoctl/releases/download/v3.25.0/calicoctl-linux-amd64 mv calicoctl-linux-amd64 /usr/local/bin/calicoctl chmod x /usr/local/bin/calicoctl calicoctl version如果你用的是较新的 v3.27.0 及以上版本下载命令类似只是仓库路径和二进制名变了curl -LO https://github.com/projectcalico/calico/releases/download/v3.27.0/calico-linux-amd64 mv calico-linux-amd64 /usr/local/bin/calico chmod x /usr/local/bin/calico calico version下载完之后建议用官方发布的 SHA256SUMS 文件做一次校验。我见过不止一次因为下载中断或者镜像源污染导致二进制无法运行的情况。校验命令也很简单curl -LO https://github.com/projectcalico/calico/releases/download/v3.27.0/sha256sum.txt sha256sum -c sha256sum.txt 2/dev/null | grep calico-linux-amd642.3 下载指定版本的 calico.yaml 部署清单部署 Calico 时最常用的方式是直接把官方准备好的清单文件应用到集群里。默认文档给出的命令往往是kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/master/manifests/calico.yaml这种写法最大的问题就是“master”分支随时在变你无法保证和生产环境的一致性。正确做法是把清单固定到具体版本对应的 tag 上。GitHub 上每个 Calico 发布版本都会保留对应的 manifest 目录所以只要把 URL 里的版本号换掉即可curl -LO https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/calico.yaml如果你所在网络访问 GitHub 不稳官方文档站点也提供了归档版本路径同样可以下载curl -LO https://docs.tigera.io/archive/v3.25/manifests/calico.yaml下载下来之后不要急着直接 apply先打开 calico.yaml 检查几个关键内容。首先是镜像版本用 grep 看下 cni、node、kube-controllers 等镜像的 tag确认与你想要安装的版本一致。其次看安装模式这个清单会把项目相关的 CRD、RBAC、ServiceAccount、DaemonSet、Deployment 全部打包在一起结构很长但核心是 calico-node 这个 DaemonSet 和后面的 IPPool 配置。提前把内容过一遍后面出问题时会省很多事。2.4 离线环境不能缺的一环镜像拉取与导入生产环境里离线部署实在太常见了这里把镜像准备方法一并说了。根据 calico.yaml 里的镜像列表通常会涉及下面几个镜像calico/cnicalico/nodecalico/kube-controllerscalico/pod2daemon-flexvol在能访问外网的机器上按版本号把镜像拉下来打成 tar 包再传到目标节点或私有镜像仓库docker pull calico/node:v3.25.0 docker pull calico/cni:v3.25.0 docker pull calico/kube-controllers:v3.25.0 docker pull calico/pod2daemon-flexvol:v3.25.0 docker save calico/node:v3.25.0 calico/cni:v3.25.0 calico/kube-controllers:v3.25.0 calico/pod2daemon-flexvol:v3.25.0 -o calico-images.tar到了内网环境导入镜像docker load -i calico-images.tar如果内网有自建的镜像仓库更推荐的做法是改掉 calico.yaml 里的 image 前缀指向内网仓库地址这样所有节点都会从内网统一拉取不再依赖每个节点单独导入。改的时候注意yaml 里同一镜像可能出现在多处用 sed 批量替换比手改更可靠但替换完一定要抽查几个位置防止替换出错。3. 完整实操从零部署一套 IPIP 模式的 Calico3.1 最方便的上手方式直接应用默认清单如果你是第一次搭建环境或者集群属于测试环境最快速的方式就是直接应用上一节下载好的 calico.yaml。kubectl apply -f calico.yaml应用之后等上一分钟左右检查 Pod 状态kubectl get pods -n calico-system版本不同命名空间可能不一样。早期的 Calico 会把组件放在 kube-systemv3.16 之后的版本则默认使用独立的 calico-system 命名空间。看到 calico-node 在每个节点上都是 Runningcalico-kube-controllers 也稳定运行说明基本组件已经起来了。但注意“能跑”不代表“按你预期的模式跑”。默认清单里的 IPPool 设置可能不是 IPIP Always也可能是 CrossSubnet甚至在某些安装方式下根本不开 IPIP。所以真正要紧的是确认当前实际生效的 IPPool 配置。3.2 修改 IPPool 配置把 IPIP 模式调整到符合预期IPPool 是 Calico 用来管理 Pod IP 地址段的核心资源。你可以通过命令行工具查看当前 IP 池calicoctl get ippool -o yaml输出里能看到一个或几个 IPPool 对象。默认创建的 IPPool 通常长这样apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: default-ipv4-ippool spec: blockSize: 26 cidr: 192.168.0.0/16 ipipMode: Always natOutgoing: true disabled: false nodeSelector: all()重点就是 spec.ipipMode 这一项可取值有三个Always、CrossSubnet、Never。Always 表示所有跨节点的 Pod 流量都走 IPIP 隧道简单粗暴底层网络完全不用感知 Pod 网段。CrossSubnet 表示只有跨子网的流量才封装同一子网内的节点之间走直接路由性能更好。Never 表示完全禁用 IPIP也就是使用纯直接路由。我自己的经验是如果节点都集中在同一子网CrossSubnet 是最优选择。如果节点分布在不同子网且云平台对网络管控比较严Always 更省心。修改方式也很简单直接 patch 默认 IPPool 即可kubectl patch ippool default-ipv4-ippool --type merge -p {spec:{ipipMode:CrossSubnet}}修改之后再过几秒钟Calico 会自动重建对应的路由和隧道策略。你不需要重启节点但我遇到过个别情况需要重启一下 calico-node Pod 才能让 tunl0 接口上的策略完全刷新。如果改了模式后路由没有如期变化就重启对应节点的 calico-node Pod。3.3 验证 IPIP 是否真正生效三条命令看穿真相部署完成后不能只看 Pod 是 Running 就收工必须验证 IPIP 确实在按预期工作。我通常会在每个节点上依次做三件事。第一件事检查隧道接口 tunl0 是否存在且状态正常ip addr show tunl0 ip link show tunl0如果 tunl0 存在且链路状态是 UP说明 IPIP 隧道通道已经建立。有些系统默认 tunl0 存在但状态是 DOWN得确认它变成了 UP。第二件事查看路由表确认 Pod 网段的路由条目指向了 tunl0ip route | grep tunl0如果你看到类似下面的输出说明跨节点 Pod 网段已经通过 IPIP 转发192.168.1.0/26 via 192.168.20.101 dev tunl0 proto bird onlink 192.168.2.0/26 via 192.168.20.102 dev tunl0 proto bird onlink注意这里的 onlink 标志它意味着即使目标地址不在当前网卡直连网段内也强制走这个接口这是 Calico 驱动隧道路由的一种手段是正常现象。第三件事选两个不同节点上的 Pod做一次真实通信测试。先在节点 A 上的 Pod 里 ping 节点 B 上 Pod 的 IP同时我习惯在节点 A 的物理网卡上抓一下包看是否出现了协议类型为 4 的 IPIP 报文tcpdump -i eth0 -nn ip proto 4 -c 10能抓到 IPIP 报文说明封装确实发生了从 Pod 里能 ping 通说明解封装和回程路由也没问题。整个链路到这里才算真正闭环。4. IPIP 实战排坑MTU、rp_filter 与路由黑洞4.1 MTU 调整很多“时通时不通”的元凶MTU 问题是 IPIP 隧道模式里最经典、也最容易被忽视的坑。默认情况下物理网卡的 MTU 是 1500而 IPIP 封装会额外占用约 20 字节如果隧道接口或者 Pod 的 MTU 没有相应调小就会出现一个现象小包能通大包不通。典型的症状是某些应用访问时正常但一旦涉及大文件传输或大包请求连接直接卡死或超时。解决思路很清晰把链路里每一跳的 MTU 都调整到与封装开销匹配。如果物理网络 MTU 是 1500那么 IPIP 隧道的有效载荷 MTU 应该设为 1480对应的 Pod 网卡 MTU 也应设为 1480。判断 MTU 问题最直接的办法是带 DF 标志去 ping 对端宿主机 IP逐步减小包大小找到通与不通的临界值。比如先发 1472 字节的包ping -M do -s 1472 对端节点IP如果这个包能通说明物理网络 MTU 是 1500。然后再从 Pod 内部测试跨节点大包如果发现包在 1480 到 1500 之间丢包基本就能确认是封装后的 MTU 超限。此时需要修改 calico.yaml 中的全局 MTU 配置或者用 IPPool / Felix 配置把 MTU 固定到合理值。修改后重启 calico-node Pod让新配置生效。这里有个容易忽略的细节光改 Calico 的 MTU 不够节点物理网卡如果启用了巨型帧MTU 是 9000那隧道 MTU 可以调到 8980 附近。总之要先摸清物理网络允许的最大包再减去封装开销。4.2 内核模块与 rp_filter路由对但包就是不通另一种很常见的情况是你查看路由表路由条目完全正确tunl0 也起来了但跨节点 Pod 通信就是不通。这时候十有八九是内核的反向路径过滤也就是 rp_filter 在搞鬼。rp_filter 是 Linux 内核提供的一种防伪造 IP 的机制它会检查数据包的源 IP 是否符合当前接口的路由规则不符合就直接丢弃。在 Calico 的多网卡、多隧道环境下来自 tunl0 的解封装包很容易被系统判断为“源地址与入接口不匹配”然后被静默丢弃。官方清单里的 calico-node 在其初始化容器或安全上下文中已经把 rp_filter 设置过一轮但在某些内核参数被覆盖、或你修改了默认 sysctl 的环境里问题会反复出现。如果怀疑是这个问题可以在每个节点上执行sysctl -w net.ipv4.conf.all.rp_filter0 sysctl -w net.ipv4.conf.default.rp_filter0改为 0 是彻底关闭改成 2 是设为宽松模式。我个人建议生产环境不要直接改成 0而是评估一下集群的安全性再决定是开 2 还是关掉。改完后理论上即时生效如果还不通再结合 tcpdump 抓包看看是不是包在入接口就被丢了。4.3 BGP 对等与路由黑洞黑洞路由为什么会出现在路由表里使用 Calico 的纯 BGP 直接路由或 CrossSubnet 模式时节点之间需要通过 BGP 交换路由。如果你配置了 IPIP 但路由表里出现了黑洞路由比如blackhole 192.168.1.0/26 proto bird这就说明 BGP 会话虽然建立了但下一跳信息没有正确填充。出现这种情况至少要从两个方向排查。一是看 calico-node 的 BGP 状态。用命令行工具查看节点状态calicoctl node status正常输出会显示每个 BGP 对等端的连接状态如果是 Established说明 BGP 层面正常。如果是 Idle 或 Active就要检查节点间的 179 端口连通性以及集群的 AS 号配置是否一致。二是看 Felix 的日志。calico-node Pod 的日志里一般会留下 BGP 对等失败、路由计算异常等线索。日志级别可以通过环境变量调整排查时可以临时把日志调成 debug定位问题后再改回来。还有一种路由黑洞的原因比较隐蔽IPPool 的 nodeSelector 或者 disable 配置导致某些节点的 Pod 网段没有被正常宣告。遇到过有人在 IPPool 上设置了一堆节点选择器结果新加的节点不在选择器范围内Pod 能创建但路由一直缺失。这种问题从路由表看就是黑洞条目检查 IPPool 的节点选择器往往能一击命中。4.4 常见问题速查表症状与排查方向把实战中遇到的高频问题整理成一张表方便大家快速对照定位。现象可能原因排查方向跨节点 Pod 完全不通隧道接口未生效、BGP 未建立检查 tunl0 状态、calicoctl node status小包通大包不通MTU 超限ping -M do 测试调整隧道 MTU路由有 blackhole 条目BGP 下一跳没收敛、节点选择器不匹配查看 BGP 对等状态、IPPool 节点选择器通但不稳定、时断时续rp_filter 丢弃、底层网络丢包检查 rp_filter、抓包分析链路质量宿主机能通 PodPod 间不通NetworkPolicy 或者 iptables 规则拦截检查策略、calico-node 日志修改 ipipMode 后无变化calico-node 未刷新重启 calico-node Pod 或重建隧道接口5. 长期实战下来的选型心得与版本管理建议5.1 什么时候无脑上 IPIP什么时候换 VXLAN经过几次大规模环境折腾我形成了一个比较务实的选型原则。如果你的集群规模不大节点数量在百台以内底层是标准三层网络没有 IPv6 的硬性诉求那 IPIP 基本是最优解。它的封装开销小、排错直观、依赖简单内核支持也好。尤其是当你需要做跨子网通信且子网间路由器不方便添加大量明细路由时IPIP 几乎是无脑选择。但是一旦涉及 IPv6或者集群规模膨胀到几百上千节点网络路径复杂中间设备对协议类型的管控比较多我会优先考虑 VXLAN。VXLAN 使用 UDP 封装很多网络设备对 UDP 流量的兼容性天然比 IP-in-IP 协议好而且在 overlay 网络中做隔离、多租户也更灵活。代价就是每包多 30 字节左右的开销但在现代数据中心普遍开启巨型帧的背景下这点开销基本可以忽略。还有一点值得提Calico 的 IPPool 是支持同一个集群内不同 IP 池用不同模式的。我曾经在一个混合集群里一部分节点在同一子网我用 CrossSubnet 让它们走直接路由另一部分节点跨了子网我对它们所在的 IPPool 改成 Always。这样既把性能留在了同网段又保证了跨网段的可达性。5.2 版本管理的心得固定版本记录变更很多线上事故其实是“手滑升级”造成的。我个人强烈建议把 Calico 的版本作为一个独立参数纳入集群资产清单。部署时写清楚 Kubernetes 版本、Calico 版本、使用的 IPPool 配置、下载来源。升级前先在测试环境完整验证生产环境尽量使用和测试环境一致的版本。Calico 的版本迭代速度不慢但没必要追求最新稳定压倒一切。下载指定版本这个操作看起来只是多带个版本号的问题但在真实环境里能救命。我曾经因为图省事用了默认的 master 分支清单结果一个月后再去排查另一个问题时发现 Calico 版本已经悄悄变了两个环境的行为不一致排查半天才回过神。从那以后我所有环境的 Calico 安装和升级都有了固定的版本锁定流程calico.yaml 也会和代码一起纳入版本管理变更记录清清楚楚。5.3 最后分享一个小习惯每次动 IPIP 配置前先留快照在调整 IPPool 或切换隧道模式之前我习惯先把当前的路由表、tunl0 状态、calicoctl node status 全部记录一份。这看上去多花了一两分钟但回滚时能免掉很多猜测。毕竟 Calico 的配置变更大多是即时生效的一旦出问题第一时间回退或者重新配置都比翻日志猜原因要快得多。特别是刚上手 IPIP 的读者建议每次改动都做一次前后对比配置项很快就熟了。
返回列表