乐于分享
好东西不私藏

经典网络插件 Calico 的网络机制、IPPool、Flow Log

经典网络插件 Calico 的网络机制、IPPool、Flow Log

经典网络插件 Calico 的网络机制、IPPool、Flow Log

前面写 CoreDNS 时,我们讲的是“容器如何通过域名找到服务”。但域名解析成功以后,还有一个更底层的问题:数据包到底怎么从一个 Pod 走到另一个 Pod

这个问题在 VM 迁移到 Kubernetes 时很容易出现。比如原来 portal-web 和 order-api 跑在两台 VM 上,网络团队给 VM 分配固定网段,路由器知道这些地址该往哪里转。迁到 Kubernetes 后,portal-web 和 order-api 变成 Pod,Pod IP 可能来自 192.168.0.0/16 这样的集群内地址;如果两个 Pod 被调度到不同节点、不同机架、甚至不同三层子网,底层网络可能根本不知道这个 Pod 网段。

这时问题就变成了:如果物理网络只认识节点 IP,不认识 Pod IP,Kubernetes 跨节点通信该怎么走?

本篇介绍主流 Kubernetes 网络项目 Calico。它不是 CNCF 孵化或毕业项目,但在生产集群里非常常见,本篇主要介绍 Calico 网络机制,以及 Calico 一些高阶功能:

  • • IPIP 模式: Calico 如何把 Pod 包封装进节点包,让流量跨过不认识 Pod 网段的底层网络。
  • • 路由转发逻辑: Felix、BIRD、Linux 路由表和 tunl0 各自负责什么。
  • • Flow 可观测: Calico 3.30+ 里的 Goldmane 和 Whisker 如何把流量、策略命中、字节数看出来。
  • • IPPool 与浮动 IP: 多 IPPool 如何服务机架/区域/出口策略,Floating IP 又适合哪些特殊协议场景。

为什么需要 Calico

Kubernetes 对网络有一个基本要求:Pod 之间应该可以直接通信,不需要每个应用自己做 NAT、端口映射或连接代理。

但这个要求落到真实基础设施上,并不轻松。

一个新读者可以先想象下面这个场景:

Node-A: 10.0.1.11  Pod portal-web: 192.168.10.21Node-B: 10.0.2.17  Pod order-api: 192.168.23.44

portal-web 发包给 order-api 时,源地址是 192.168.10.21,目标地址是 192.168.23.44。但底层交换机、路由器、公有云 VPC 可能只认识 10.0.1.11 和 10.0.2.17,并不知道 192.168.23.44 在 Node-B 后面。

解决这件事通常有两条路:

  • • 让底层网络学习 Pod 路由。 比如 Calico 通过 BGP 把 Pod CIDR 发布给网络设备或其他节点。
  • • 在节点之间做 overlay 封装。 比如 IP-in-IP 或 VXLAN,让底层网络只看到节点 IP。

Calico 的强项,是它既能做纯三层路由,也能在需要时做 overlay;既能给 Pod 分配 IP,又能做 NetworkPolicy;到 3.30 以后,还能通过 flow logs 把网络流量和策略命中情况更直观地展示出来。

Calico 是什么

Calico 从组件上看,可以先记住这几个名字:

组件
可以怎样理解
主要职责
Calico CNI
Pod 接网师傅
Pod 创建时设置网络接口和 IP
Calico IPAM
地址分配器
从 IPPool 中给 Pod 分配地址
Felix
每个节点上的网络管家
写 Linux 路由、iptables/nftables/eBPF 规则
BIRD
BGP 路由传播者
把节点上的 Pod 路由分发给 BGP peer
Typha
大规模集群的中转层
减少每个 Felix 对 API/数据存储的压力
kube-controllers
Kubernetes 状态同步器
监听 K8s 对象并维护 Calico 资源

核心功能一:IPIP 模式与跨子网路由转发

功能设计解读

IPIP,全称 IP-in-IP,可以简单理解为:把一个原始 IP 包,再包进另一个 IP 包里

原始包长这样:

inner src = portal-web Pod IPinner dst = order-api Pod IP

IPIP 封装以后,外层再套一层节点 IP:

outer src = Node-A IPouter dst = Node-B IPinner src = portal-web Pod IPinner dst = order-api Pod IP

这样底层网络只需要知道 Node-A 到 Node-B 怎么走,不需要知道 Pod CIDR。到了 Node-B 后,内核把外层 IP 头拆掉,再把里面的原始 Pod 包交给目标 Pod。

Calico 通过 IPPool 的 ipipMode 控制什么时候做 IPIP:

ipipMode
含义
适合场景
Never
不做 IPIP 封装
底层网络已经能路由 Pod CIDR
Always
所有跨主机 Calico 工作负载流量都走 IPIP
底层网络完全不认识 Pod 网段
CrossSubnet
只在跨子网时做 IPIP
同子网直连,跨三层边界才封装

为什么这篇先讲 IPIP?因为在很多企业环境里,Calico 当然可以用 BGP 和核心交换机、TOR 交换机、Route Reflector 对接,让底层网络直接知道 Pod CIDR。问题是这件事往往不只属于平台部:它需要网络团队配 ASN、peer、路由过滤、变更窗口和故障回退。很多平台团队搭 Kubernetes 私有集群时,更希望先让底层网络继续只认识节点 IP,把 Pod 网段藏在节点之间。

IPIP/VxLAN 的价值,就是用更少的网络侧协作,先把跨节点 Pod 通信跑稳

从默认行为看,官方 calico/node 创建默认 IPv4 IPPool 时,就是 IPIP 模式,这也是很多人初学 Calico 最先遇到 IPIP 的原因。

IPIP 一条跨节点 Pod 流量可以用下面的图理解:

outer: 10.0.1.11 → 10.0.2.17

inner: Pod-A → Pod-B

Pod-A192.168.10.21

Node-A查路由

tunl0IPIP 封装

Underlay只看 Node IP

Node-B解封装

Pod-B192.168.23.44

更细一点看,路由判断逻辑大概是下面这张图:

Always

CrossSubnet跨子网

CrossSubnet同子网

目标 Pod IP

同节点?

veth 直达

命中 IPIP Pool?

普通三层路由

ipipMode

tunl0 封装

按 Node IP 转发

解封装到 Pod

如果从 Linux 节点上看,tunl0 前后的动作大概是这样:

  1. 1. Pod-A 的包先从自己的 veth 进入 Node-A 的主机网络命名空间。
  2. 2. Node-A 查路由表,发现目标网段在 Node-B 后面,下一跳指向远端 Node IP,并经 tunl0 出去。
  3. 3. tunl0 把原始 Pod 包封装成 IPIP 包,外层地址变成 Node-A IP 和 Node-B IP。
  4. 4. 物理网卡只转发外层节点 IP;底层交换机不需要知道 Pod-B 的地址。
  5. 5. Node-B 收到协议号 4 的 IPIP 包后解封装,再按内层目标 Pod IP 经 cali/veth 送进 Pod-B。

所以排查 IPIP 时,不能只看 Pod IP。关键是看三表:源节点到目标 Pod CIDR 的路由、节点之间的 underlay 路由、目标节点到本地 Pod veth 的路由

应用场景介绍

IPIP 模式适合三类场景。

第一类是底层网络无法承载 Pod 路由的私有云或传统机房。比如网络团队只给 Kubernetes 节点分配了服务器网段,交换机不愿意学习每个 Pod CIDR。这时 IPIP 能把 Pod 通信“藏”在节点 IP 之间,减少对底层网络改造的要求。

第二类是跨子网、跨机架、跨可用区的集群。同一机架里的节点可能直连没问题,但跨机架就需要走三层路由。CrossSubnet 可以避免同子网也被封装,减少不必要的开销。

第三类是云平台不方便发布自定义 Pod 路由的环境。有些 VPC 或安全组模型更容易接受节点 IP 通信,不容易接受把 Pod 网段发布到网络层。IPIP 可以降低落地门槛。

使用方法说明

一个典型 IPPool 可以这样写:

apiVersion: projectcalico.org/v3kind: IPPoolmetadata:  name: pod-ipip-cross-subnetspec:  cidr: 192.168.0.0/16  ipipMode: CrossSubnet  natOutgoing: true  nodeSelector: all()  allowedUses:    - Workload    - Tunnel

如果确认所有跨节点流量都必须封装,也可以使用:

spec:  ipipMode: Always

核心功能二:Calico 3.30+ Flow 可观测能力

功能设计解读

过去排查 Kubernetes 网络,常常要在几层之间来回跳:

  • • 应用日志说连接超时;
  • • kubectl exec 进去 curl
  • • 查 NetworkPolicy;
  • • 查 Pod IP;
  • • 查节点路由;

Calico 3.30 以后引入了一组更直观的 flow 可观测能力,核心是两个组件:

  • • Goldmane: flow logs API,也就是一个 gRPC API,用来聚合网络流量数据。
  • • Whisker: Web 控制台,用来实时查看和过滤 flow logs。

官方文档把 Goldmane 定义为 flow logs API,它可以返回聚合后的网络流量数据,包括策略执行细节、包数、字节数等。Whisker 则是浏览器界面,展示来自 Goldmane 的流量流。

数据从节点流量进入观测面的过程,可以画成一张更准确的图:

Pod / Service流量

Calico 数据面allow / deny

Flow 事件端点 / 策略 / 字节

GoldmanegRPC API

Whisker过滤查看

API 集成

这类能力最重要的改变,是把“网络是否真的被策略拦住”从猜测变成可观察的数据。Flow log 里可以看到 allow 或 deny,也能看到输入/输出字节数、源和目的端信息。对新读者来说,这比直接读 iptables 规则更容易建立判断。

需要注意:官方文档中 flow logs API 仍标注为 tech preview,生产使用时要接受接口和行为可能变化。对于 Calico 3.30 及以后版本,新安装集群通常随 Calico 提供 Goldmane/Whisker;如果是从 3.29 或更早版本升级,往往需要手动启用相关 CR。

应用场景介绍

Flow 可观测最适合三类使用场景。

第一类是应用连不通的排障。比如 order-api 调 payment-api 偶发超时,过去大家会在应用、Service、CoreDNS、CNI 之间互相怀疑。现在可以先看 flow:源 Pod 到目标 Pod 的连接有没有出现、动作是 allow 还是 deny、是否有字节返回。

第二类是NetworkPolicy 上线前验证。很多团队写策略时最怕“一上生产就把业务切断”。通过 flow logs,可以观察某个 namespace 的真实访问关系,再配合 staged policy 或灰度策略验证,减少盲写策略的风险。

第三类是多团队平台治理。平台团队可以看跨 namespace 调用是否符合预期,安全团队可以看异常 deny 或大流量访问,业务团队可以在排障时给出更明确的源/目的信息。

它不适合替代全链路追踪。Flow logs 更偏网络层和策略层,能告诉你“哪个端点与哪个端点通信、策略如何判定、流量量级如何”,但不能告诉你某个 HTTP 接口内部为什么慢。

使用方法说明

如果集群是从旧版本升级上来的,可以按官方方式启用 Goldmane 和 Whisker:

apiVersion: operator.tigera.io/v1kind: Goldmanemetadata:  name: default---apiVersion: operator.tigera.io/v1kind: Whiskermetadata:  name: default

查看 Whisker 控制台,官方推荐先用 port-forward:

kubectl port-forward -n calico-system service/whisker 8081:8081

然后访问:

http://localhost:8081

默认不建议把 Whisker 直接暴露到集群外。官方文档也提醒,Whisker 默认并不会自动暴露入口,并且 Calico 会创建网络策略限制访问。生产上更推荐先通过 port-forward、安全网关或受控内网入口访问。

真正用的时候,不要只把 Whisker 当成“有流量列表”。可以拿一个具体调用来读。

假设 mall namespace 里的 order-api 调用 finance namespace 里的 payment-api

kubectl -n mall exec deploy/order-api -- \  curl -sS -m 2 http://payment-api.finance.svc.cluster.local:8080/health

然后在 Whisker 里按 source_namespace=mallsource_name=order-apidest_namespace=financedest_port=8080 过滤。此时重点不是看“有没有一行日志”,而是看这几个字段:

观察项
怎么判断
action=deny
网络策略拦截,继续看 policies 命中哪条策略
没有 flow
流量可能没发出,先查 DNS、Service、Endpoint、Pod 是否真的发起请求
allow
 但只有 bytes_out
源端发出去了,但没看到有效返回,继续查目标 Pod、回程策略、节点路由
allow
 且 bytes_in/out 都增长
网络层基本通,问题更可能在应用、HTTP 状态码、数据库或下游依赖
packets
 很高但 bytes 很低
可能是短连接、重试、探针过密,适合继续看客户端重试和连接池

把它画成排障图,大概是这样:

deny

allow

order-apicurl payment-api

Whisker 过滤源/目的/端口

看到 flow?

查 DNS / Service / Endpoint

action

看 policies修策略

bytes_in是否增长?

查回程策略目标 Pod / 路由

网络已通查应用性能

FLow WebUI:

这里要注意一个边界:Flow logs 不是 APM,不会直接告诉你某个 HTTP 请求耗时 300ms 还是 3s。它更适合先判断网络层三件事:有没有流量、有没有被策略拒绝、有没有明显的单向流量或异常流量量级。如果 flow 显示 allow 且双向字节都正常增长,性能问题通常要继续交给应用日志、指标、链路追踪和后端依赖去定位。

核心功能三:多 IPPool 与 Floating IP 场景

功能设计解读

IPPool 是 Calico IPAM 的核心资源。它定义了 Calico 可以从哪些 CIDR 给 Pod 分配 IP,也定义了这个地址池的封装模式、NAT 行为、节点选择和用途。

一个 IPPool 不只是一个网段,它还包含很多控制字段:

字段
作用
cidr
这个池子的地址范围
blockSize
每个节点按块分配地址时的块大小,IPv4 默认通常是 /26
ipipMode
 / vxlanMode
是否使用 IPIP 或 VXLAN,二者不能同时设置
natOutgoing
Pod 访问非 Calico IPPool 地址时是否做源 NAT
nodeSelector
哪些节点可以从这个池子分配地址
allowedUses
这个池子用于 Workload、Tunnel 或 LoadBalancer
assignmentMode
自动分配,还是只在手动指定时分配

多 IPPool 的价值,是把“所有 Pod 都从一个大池子拿地址”改成“不同拓扑、团队、出口策略使用不同地址池”。

例如按机架分池:

rack=1

IPPool192.168.1.0/24

node-2

node-3

rack=0

IPPool192.168.0.0/24

node-0

node-1

官方文档也强调,节点只会从选中自己的 IPPool 分配 workload 地址。因此如果采用 nodeSelector 分池,要确保所有节点至少被一个 IPPool 选中,否则 Pod 可能拿不到 IP,启动失败。

Floating IP 是另一个容易被忽略的能力。它不是 Kubernetes Service 的替代品,而是给某个 workload endpoint 额外挂一个可以“漂移”的 IP。进入 floating IP 的流量,会在宿主机上通过 NAT 转换到 Pod 的真实 IP。

可以这样理解:

NAT

重新绑定

Client

Floating IP10.0.0.1

Pod-A真实 IP

Pod-B新 IP

Kubernetes Service 更适合大多数场景,因为它是原生抽象,并且能做负载均衡。Floating IP 的特殊价值在于:它可以用于 Kubernetes Service 不支持的协议场景。官方文档明确提到,Service 主要面向 TCP、UDP、SCTP,而 Floating IP 可以用于更多协议;但它一次只 front 一个 Pod,不做负载均衡。

应用场景介绍

多 IPPool 适合下面几类场景。

第一类是机架、可用区、区域感知的地址规划。如果网络团队希望 rack-0 的 Pod 用 192.168.0.0/24,rack-1 的 Pod 用 192.168.1.0/24,就可以用节点 label 和 nodeSelector 分池。这样外部防火墙、路由聚合、审计规则会更清晰。

第二类是遗留防火墙按源网段放行的系统迁移。比如某些数据库、防火墙或第三方系统只允许 10.20.30.0/24 访问。迁移到 Kubernetes 后,不一定要让所有 Pod 都进入这个网段,可以只让指定 namespace 或 Pod 通过 cni.projectcalico.org/ipv4pools 使用特定 IPPool。

第三类是出口策略分组。平台团队可以把对外访问类 Pod、普通业务 Pod、系统组件 Pod 放进不同地址池,让安全设备看到更稳定的源地址范围。

Floating IP 则更适合特殊协议或单实例入口场景。例如:

  • • 某个网络探测服务需要被外部系统用非 TCP/UDP 协议访问。
  • • 某个有主备切换模型的服务,一次只希望一个 Pod 承接入口 IP。
  • • 迁移旧系统时,外部依赖暂时只能识别一个固定 IP。

但它不适合普通 Web/API 负载均衡。只要是 HTTP/gRPC/TCP 服务,优先用 Kubernetes Service、Ingress、Gateway 或负载均衡器;Floating IP 应该是特殊网络场景的补充工具。

使用方法说明

多 IPPool 可以这样配置:

apiVersion: projectcalico.org/v3kind: IPPoolmetadata:  name: rack-0-ippoolspec:  cidr: 192.168.0.0/24  ipipMode: CrossSubnet  natOutgoing: true  nodeSelector: rack == "0"---apiVersion: projectcalico.org/v3kind: IPPoolmetadata:  name: rack-1-ippoolspec:  cidr: 192.168.1.0/24  ipipMode: CrossSubnet  natOutgoing: true  nodeSelector: rack == "1"

配合节点标签:

kubectl label node node-a rack=0kubectl label node node-b rack=1calicoctl get ippool -o wide

如果希望某个 Pod 或 namespace 使用指定池,可以使用注解:

metadata:  annotations:    cni.projectcalico.org/ipv4pools: '["legacy-fw-pool"]'

如果希望指定 Pod IP,则使用:

metadata:  annotations:    cni.projectcalico.org/ipAddrs: '["192.168.0.10"]'

官方文档提醒,这类 IP 注解必须在 Pod 创建时就存在,后面再加不会改变已有 Pod 的地址。

Floating IP 的注解类似:

metadata:  annotations:    cni.projectcalico.org/floatingIPs: '["10.0.0.1"]'

还要注意两个边界:

  • • Floating IP 默认是关闭的,需要在 CNI 配置里打开 feature_control.floating_ips
  • • 官方文档说明,Kubernetes Pod Floating IP 当前不支持 operator-managed Calico clusters;因此生产使用前要先确认自己的安装方式。

适合什么场景

Calico 的网络能力很强,但不是每个集群都要把所有模式都用上。

场景
推荐能力
采用重点
传统机房底层网络不认识 Pod CIDR
IPIP CrossSubnet 或 Always
先确认 MTU、路由、BGP 状态
底层网络可学习 Pod 路由
非 overlay / BGP 路由
减少封装开销
多机架/多区域地址规划
多 IPPool + nodeSelector
所有节点必须命中至少一个池
防火墙按源网段放行
指定 namespace/Pod 使用特定 IPPool
Pod 创建前加注解
网络策略排障
Goldmane + Whisker flow logs
先看 allow/deny,再看字节和方向
特殊协议固定入口
Floating IP
单 Pod 前置,不做负载均衡

对平台团队来说,Calico 最值得投入的不是“会装”,而是把网络模式选清楚:

  1. 1. 底层网络能不能路由 Pod CIDR?
  2. 2. 是否需要跨子网 overlay?
  3. 3. IPPool 是否要按机架、可用区、团队或出口策略分开?
  4. 4. NetworkPolicy 上线前有没有 flow 可观测支撑?
  5. 5. 遇到连不通时,团队能否从 DNS、Service、路由、隧道、策略、flow 一层层定位?

如果这些问题没有先想清楚,Calico 就会变成“装上以后能跑,但出问题没人敢碰”的基础组件。

总结

Calico 是 Kubernetes 网络里很值得深入理解的项目。它不只是一个 CNI 插件,也不只是 NetworkPolicy 的执行器,而是把 IPAM、路由、overlay、策略、可观测 串在一起的一套网络能力。

IPIP 模式解决的是底层网络不认识 Pod CIDR 时的跨节点通信问题。它通过把 Pod 包封装进节点 IP 包,让 underlay 只负责节点之间的转发。Always 更直接,CrossSubnet 更节制,选择哪一个取决于底层网络能不能承载 Pod 路由。

Calico 3.30+ 的 Flow 可观测能力,则让网络排障从“猜策略、翻路由、抓包”多了一个更友好的入口。Goldmane 负责聚合 flow logs,Whisker 负责展示和过滤,特别适合连通性排查、策略验证和微隔离落地。

多 IPPool 和 Floating IP 更偏生产精细化。前者服务地址规划、拓扑感知和遗留防火墙迁移;后者服务特殊协议和单实例固定入口。

希望看完这篇文章后,我们再遇到 Kubernetes 网络问题时,能先问三个问题:这个包要去哪里?底层网络认不认识目标地址?如果它被允许或拒绝,我能不能看见证据?

参考资料

  • • Calico Open Source About:https://docs.tigera.io/calico/latest/about/
  • • Calico Component architecture:https://docs.tigera.io/calico/latest/reference/architecture/overview
  • • Calico Configuring calico/node:https://docs.tigera.io/calico/latest/reference/configure-calico-node
  • • Calico Self-managed manifest configuration:https://docs.tigera.io/calico/latest/getting-started/kubernetes/self-managed-onprem/config-options
  • • Calico Overlay networking IPIP/VXLAN:https://docs.tigera.io/calico/latest/networking/configuring/vxlan-ipip
  • • Calico Determine best networking option:https://docs.tigera.io/calico/latest/networking/determine-best-networking
  • • Calico Enable flow logs API and Whisker:https://docs.tigera.io/calico/latest/observability/enable-whisker
  • • Calico View flow logs:https://docs.tigera.io/calico/latest/observability/view-flow-logs
  • • Calico Flow logs API:https://docs.tigera.io/calico/latest/observability/flow-logs-api
  • • Calico IPPool resource:https://docs.tigera.io/calico/latest/reference/resources/ippool
  • • Calico Assign IP addresses based on topology:https://docs.tigera.io/calico/latest/networking/ipam/assign-ip-addresses-topology
  • • Calico Restrict a pod to use an IP address range:https://docs.tigera.io/calico/latest/networking/ipam/legacy-firewalls
  • • Calico Use a specific IP address with a pod:https://docs.tigera.io/calico/latest/networking/ipam/use-specific-ip
  • • Calico Add a floating IP to a pod:https://docs.tigera.io/calico/latest/networking/ipam/add-floating-ip