状态:草稿(2026-10-17),部分经过实际测量。 基准版本 cilium/cilium v1.20.1(提交 7d68cfb394f2)。由于小型集群正在运行此版本(agent、 operator-generic、hubble-relay v1.20.1,cilium-envoy 1.37 系列),实际测量于 2026-10-17 在该集群中 仅通过读取 完成(对单个 agent Pod 执行
cilium-dbg·bpftool·hubble observe读取命令、读取 ConfigMap 及策略数量、读取 relay 日志)。 DNS 模式的连接结果引用了为贡献做准备而在 2026-10-16 至 17 之间记录的日志(PR 草稿)。 编写规则:以实测数据为准,未测量的标记为「未实测」,未能确认的标记为「未确认」。
目录
1. 一行总结
如果把 Cilium 浓缩为一句话,那就是: 「每个节点上的办公室(代理)修改规则表(BPF 映射),内核各处的检查站(BPF 程序)仅凭规则表来决定放行、丢弃或地址转换」 。检查站不会给办公室打电话。由于每个数据包都上送到用户空间会降低性能,因此判断所需的所有信息都已提前写入映射中。因此,在阅读 Cilium 代码时,首先要问的问题永远是: 这些信息是由谁写入哪个映射的, ,以及哪个钩子在读取它。」
| 组件 | 职责 | 运行位置 |
|---|---|---|
| cilium-agent | 接收 Kubernetes 状态以计算策略、服务和 identity,并写入 BPF 映射。编译并加载 BPF 程序。DNS 代理和 Hubble 观察器也在此进程中 | 每个节点(DaemonSet) |
BPF 程序(bpf/) |
挂载于 Pod veth、主机设备、隧道设备和 socket cgroup,处理数据包和 connect() ) |
内核内部 |
| cilium-operator | 注册 CRD、IPAM(根据模式)、清理遗留的 identity 和 CiliumEndpoint、LB-IPAM | 集群中一个(领导者选举) |
| cilium-envoy | 处理 HTTP 等 L7 策略以及 Ingress 和 Gateway 的代理。通过 xDS 从代理接收配置 | 每个节点(独立 DaemonSet) |
| Hubble | 代理内的观察器将 BPF 生成的事件解释为流并放入环形缓冲区,由 relay 汇聚各节点的流 | 观察器在代理内,relay 在集群中有一个 |
2. 架构图
- 上方两个方框在集群中各有一个。代理 直接 监视 API 服务器。由于它不通过 operator,因此即使 operator 暂时停止,已运行节点的的数据路径也会保持不变。
- 中间的大方框代表一个节点。代理方框内的六个芯片是本文将要跟踪的组件,下方的圆柱体是这些组件使用的 BPF 映射。
- 最下方的表格是「钩子位置 → 挂载程序」。文件名即为挂载位置。
bpf_lxc.c是 Pod 端,bpf_host.c是主机端,bpf_overlay.c是隧道设备,bpf_sock.c是 socket 系统调用。虚线框(XDP)在小型集群中处于关闭状态。 - 左侧的虚线箭头是 BPF 通过
cilium_eventsperf 映射导出的事件上送到代理内 Hubble 观察器的路径(4.4)。
3. 代码导读
仓库虽然庞大,但初次浏览时需要关注的地方是固定的。
| 路径 | 内容 | 首先阅读的文件 |
|---|---|---|
daemon/ |
代理入口点和 cell 集合 | daemon/cmd/cells.go |
bpf/ |
加载到内核中的 C 程序。入口文件 bpf_*.c 及公共头文件 bpf/lib/ |
bpf/bpf_lxc.c |
pkg/datapath/ |
BPF 编译与加载(loader)、设备、路由与 sysctl 调整、映射定义 |
pkg/datapath/loader/endpoint.go |
pkg/loadbalancer/ |
v1.18 之后重构的服务负载均衡控制平面(StateDB 表 + reconciler) | pkg/loadbalancer/README.md |
pkg/policy/ |
策略存储、selector cache、每个端点的策略计算 | pkg/policy/selectorcache.go |
pkg/fqdn/ |
DNS 代理、名称到 IP 缓存、 matchPattern 正则表达式转换 |
pkg/fqdn/matchpattern/matchpattern.go |
pkg/hubble/, hubble-relay/ |
流解析、环形缓冲区、gRPC 服务器以及 relay | pkg/hubble/observer/local_observer.go |
operator/ |
operator 入口点和 cell | operator/cmd/root.go |
代理和 operator 都通过名为 hive 的依赖注入框架进行组装。一个功能即是一个「cell」,cell 仅声明它提供什么以及需要什么。代理 main 在 daemon/main.go:11 中只有一行,而该
hive.New(cmd.Agent) 在 Agent 中分为 daemon/cmd/cells.go:103
三个部分。 Infrastructure·ControlPlane·datapath.Cell 注释直接说明了划分的原因。控制平面属于「纯业务逻辑」,旨在无需内核权限即可运行集成测试。而实际操作 BPF 的工作则集中到了 ControlPlane(cells.go:194的 pkg/datapath/cells.go:52 模块中。 datapath 实测的 cilium-dbg status --verbose 模块树也分成了 controlplane / datapath / infra 这三个部分(第 6 节)。
4.核心路径追踪
4.1 代理编译与加载 BPF 的路径
Cilium 不会将 BPF 对象预先打包到镜像中,而是 在节点上使用 clang 进行编译(代理镜像中包含 clang 19.1.7,经实测)。由于每个节点的内核功能和配置(是否启用隧道、伪装、IPv6 等)各不相同,因此必须通过 #ifdef 将未使用的功能彻底剔除,以通过验证器并避免触及指令数上限。
- 基于的数据路径。
pkg/datapath/orchestrator/orchestrator.go:167reconciler在节点配置每次发生变化时调用Reinitialize,pkg/datapath/loader/base.go:298Reinitialize为隧道设备编译bpf_overlay.c、XDP,如果启用了 socket LB,则编译bpf_sock.c并将其挂载到base.go:406cgroup 中(pkg/socketlb/socketlb.go:83Enable). - 针对每个端点(Pod)。
pkg/datapath/loader/endpoint.go:83ReloadDatapath首先调用cache.go:176fetchOrCompile。这正是设计的关键所在。它使用端点配置中 除每个 Pod 不同的值(端点 ID、IP、ifindex 等)之外的其余部分来生成哈希,如果相同哈希的对象已存在,则直接复制复用而不进行编译。实际编译由compile.go:160compile执行 clang 完成。 - 赋值与挂载。
endpoint.go:195reloadEndpoint将 Pod 特定的值作为 常量覆盖写入(Constants: endpointConfiguration(...)模板中并加载,先将策略程序放入 tail call 映射中,然后将cil_from_container挂载到 veth 的 ingress 上。顺序代码中有注释说明:如果先打开入口,数据包将进入半准备就绪的程序。Pod 端的 egress 程序(cil_to_container)仅在RequireEgressProg()为真时挂载。
实测数据说明了原因。单个节点上的 13 个端点共享了 一个模板哈希,并且只有 bpf_lxc.o(约 1.0MB)和
bpf_host.o(约 1.4MB)两个模板。编译仅进行一次,其余通过修改常量加载,从而将 clang 从 Pod 创建延迟中剥离。
4.2 替代 kube-proxy:Service 如何在 connect() 中转变为后端
kube-proxy 将 Service 转换为 iptables(或 IPVS)规则。Cilium 将相同的信息放入 BPF 哈希映射中,并 尽可能在数据包甚至尚未生成之前 就修改 socket 的目的地址。在 v1.18 中,该控制平面基于 StateDB 进行了重构,其结构正如
pkg/loadbalancer/README.md 中的图示。
- 反射(reflect)。
pkg/loadbalancer/reflectors/k8s.go:208runServiceEndpointsReflector将 Service 和 EndpointSlice 的变更短暂汇聚后作为单个事务写入(避免在大规模滚动更新时数千次单独修改映射)。 - 写入表。
pkg/loadbalancer/writer/writer.go:326UpsertServiceAndFrontends等模块对齐服务、前端和后端三张表的引用。即使有多个数据源,也只会通过此 Writer 写入。 - 与映射对齐。
pkg/loadbalancer/reconciler/bpf_reconciler.go:725Update监视前端表并在updateFrontend(第 842 行) 中写入映射。映射cilium_lb4_services_v2(bpf/lib/lb.h:284的键是(주소, 포트, 프로토콜, 슬롯),槽 0 是「头部」(后端数量、标志),槽 1..n 是后端 ID。重试由 reconciler 负责,因此数据源无需关心映射错误。 - 在 socket 中解析。
bpf/bpf_sock.c:467cil_sock4_connect在 cgroupconnect4钩子中调用__sock4_xlate_fwd(第 298 行) ,并在其中使用lb4_lookup_service查找头部,然后通过sock_select_slot() % count + 1选择槽位将 socket 地址更改为后端。应用程序相信自己连接到了 ClusterIP,但内核 socket 从一开始就绑定到了后端,因此每个数据包无需进行 NAT 操作。
然而,第 4 步中存在一个分支。
if (is_defined(ENABLE_SOCKET_LB_HOST_ONLY) && !in_hostns)
return -ENXIO;
if (!udp_only && !sock_proto_enabled(protocol))
return -ENOTSUP;
/* In case a direct match fails, we try to look-up surrogate
* service entries via wildcarded lookup for NodePort and
* HostPort services.
*/
svc = lb4_lookup_service(&key, true);
if (!svc)
svc = sock4_wildcard_lookup_full(&key, in_hostns);
if (!svc)
return -ENXIO;
来源:cilium v1.20.1 bpf/bpf_sock.c 第 322–335 行,GPL-2.0-only OR BSD-2-Clause
socketLB.hostNamespaceOnly 如果启用,Pod 命名空间中的 socket 将直接在此处返回。那么服务地址转换由谁来做呢?
bpf_lxc.c 在编译时自行判断。
#if !defined(ENABLE_SOCKET_LB_FULL) || \
defined(ENABLE_SOCKET_LB_HOST_ONLY) || \
defined(ENABLE_L7_LB) || \
defined(ENABLE_SCTP) || \
defined(ENABLE_CLUSTER_AWARE_ADDRESSING)
# define ENABLE_PER_PACKET_LB 1
#endif
来源:cilium v1.20.1 bpf/bpf_lxc.c 第 90–96 行,GPL-2.0-only OR BSD-2-Clause
如果存在任何无法在 socket 中完全解析的情况,Pod veth 的 tc 程序中就会引入基于数据包的 LB(__per_packet_lb_svc_xlate_4,第 152 行)。官方文档解释称,这种绕过是为了原本需要 ClusterIP 的情况(如 Istio Sidecar),以及 socket LB 无效的 Pod(如 KubeVirt、Kata、gVisor)(kubeproxy-free 文档「Socket LoadBalancer Bypass in Pod Namespace」)。实测的小型集群正是这种模式(Socket LB Coverage: Hostns-only)。由于 TT Lab 集群运行 KubeVirt VM,因此这一选择是有原因的(KubeVirt 实践 VM 系列)。结果,从 Pod 到服务的连接在 cil_from_container 内部解析,socket 反向转换映射(cilium_lb4_reverse_sk)中只有 13 个主机命名空间 socket。「kube-proxy 替代 = socket LB」是一个容易产生误解的误区。
4.3 toFQDNs 策略通过 DNS 代理实现「允许此 IP」的路径
BPF 仅识别 IP 和端口。然而,人类倾向于使用诸如 *.docker.com 之类的名称编写策略。Cilium 的解决方案是 即拦截 DNS 响应,学习 名称→IP,并为学到的 IP 附加来自名称的 identity。名称不会下沉到 BPF,BPF 仍然照常只询问「是否允许此 identity?」。
- 注册选择器。 如果策略中包含
toFQDNs,则pkg/fqdn/namemanager/manager.go:140RegisterFQDNSelector会接收选择器,如果缓存中的现有名称有匹配项,则会立即显示 IP。选择器的正则表达式由pkg/policy/api/fqdn.go:115ToRegex通过 matchpattern 创建。 - 拦截查询。 由于相同策略的
rules.dns(端口 53),bpf_lxc会将该 Pod 的 DNS 数据包重定向到节点的 DNS 代理。pkg/fqdn/dnsproxy/proxy.go:895ServeDNS通过CheckAllowed(第 763 行) 检查查询名称。 该正则表达式由GeneratePattern(第 1439 行) 将rules.dns的模式用|组合而成。如果不匹配,则返回拒绝响应(小型集群配置为refused). - 响应 → 名称缓存。 如果允许则发送到上游,在接收到响应并将其返回给客户端 之前
NotifyOnDNSMsg,会调用pkg/fqdn/messagehandler/message_handler.go:277UpdateOnDNSMsg。这会经过namemanager/manager.go:214UpdateGenerateDNS到达 - 。
manager.go:422deriveLabelsForName名称匹配。fqdn:<패턴>将响应名称与所有 FQDN 选择器的正则表达式进行比对,为每个匹配的选择器收集 - 标签。
manager.go:315updateMetadata为 IP 分配 identity。pkg/policy/selectorcache.go:768UpdateIdentities将响应中每个 IP 的这些标签上传到 ipcache,ipcache 为标签组合分配匹配的 identity。新 identity 通过 - 进入选择器缓存,并且使用该选择器的端点策略映射中将出现允许条目。
UpdateGenerateDNS等待并返回。WaitForRevision会返回一个通道,用于等待 ipcache 将该更改反映到映射中(tofqdns-proxy-response-max-delay)。处理程序仅等待一段时间(小型集群为 100ms),然后将响应写入 Pod。这样做的顺序是为了确保 Pod 一收到响应就进行连接时,策略映射已经打开。
这里重要的一点是 matchpattern 被使用了两次(图中的 ③ 和 ⑤)。转换规则很简短。
func escapeRegexpCharacters(pattern string) string {
// Convert '.' in the match pattern as literal '.' for regex pattern.
pattern = strings.ReplaceAll(pattern, ".", "[.]")
// '**.' in match pattern prefix is a subdomain wildcard specifier which matches one ore more
// entire labels.
pattern = subdomainWildcardSpecifierPrefix.ReplaceAllString(pattern, dnsWildcardREGroup)
// Base case: * becomes .*, but only for DNS valid characters
// `*` wildcard matches all DNS characters within the subdomain boundary(doesn't include '.' literal)
pattern = wildcardSpecifier.ReplaceAllString(pattern, allowedDNSCharsREGroup+"*")
return pattern
}
来源:cilium v1.20.1 pkg/fqdn/matchpattern/matchpattern.go 第 131–144 行,Apache-2.0
* 变为 [-a-zA-Z0-9_]*(第 17 行),
且不能跨越点号。 只有最前面的 **. 可以接收多个字段(第 37 行的 ^[*]{2,}\[\.\],第 23 行的 dnsWildcardREGroup),而中间的
** 在最后一行的表现就像 * 一样。在末尾添加点号的规范化由 Sanitize(第 87 行)
完成。
实测案例。 这是在准备贡献时在小型集群中测量的记录(2026-10-16~17,PR 草案)。镜像仓库的策略是
toFQDNs: matchPattern: "*.docker.com" ,DNS 是 * 在打开的 Pod 中对三个名称分别进行了两次连接测试。
| 名称 | DNS 解析 | 连接 | 原因 |
|---|---|---|---|
hub.docker.com |
成功 | 连接 (HTTP 200) | 单格 → *.docker.com 匹配 |
production.cloudflare.docker.com |
成功 | 超时 (curl 28) | 两格 → * 无法跨越点号 |
docker.com |
成功 | 超时 | *. 前面必须有一个空格 |
第二行是用于下载 Docker Hub 镜像层的 CDN 名称,策略看起来是匹配的,但表现出来的症状是仅镜像拉取失败。在同一条记录中,
将 main 的 matchpattern 用 Go 直接运行的(Validate(Sanitize(p))) 结果也和表格相同(**.docker.com 匹配第二行),在撰写本文时将转换规则
迁移到 Python 并重新运行的结果也是如此(图下方表格)。由于 DNS 都已解析成功,因此受阻的地方不是 ③ 而是 ⑤。相同的性质
在实战 Pod 的 DNS 出口策略中引发了什么问题,我写在了 堵住 DNS 泄露之路 中。
本文仅探讨该性质究竟源自代码的哪一行。
4.4 Hubble:从 perf 环形缓冲区到 relay
- BPF 生成事件。 当数据包被转发、阻止或进行策略判定时,
bpf/lib/trace.h:210_send_trace_notify等会写入简短的记录(cilium_events(bpf/lib/events.h:13,PERF_EVENT_ARRAY)。由于记录所有数据包代价高昂,因此会通过MONITOR_AGGREGATION进行缩减。实测端点的ep_config.h是MONITOR_AGGREGATION 3(简化活跃连接的追踪,ConfigMap 的monitor-aggregation: medium)。 - 代理读取。
pkg/monitor/agent/agent.go:324handleEvents打开 perf 读取器,并针对每条记录通过notifyPerfEventLocked(第 428 行) 分发给消费者。Hubble 就是其中一个消费者(pkg/hubble/monitor/consumer.go:73). - 解析并存入环形缓冲区。
pkg/hubble/observer/local_observer.go:116Start的循环将事件Decode转换为 Flow(流),经由指标钩子放入container/ring.go:168Ring.Write的固定大小环形缓冲区中。其大小为 2 的幂减 1(默认 4095),因此通过掩码代替除法来获取索引。GetFlows(第 260 行)只负责读取这个环形缓冲区。 - relay 收集。 relay 连接到各个代理的 Hubble 服务器(:4244, TLS)(
pkg/hubble/relay/pool/manager.go:51)。 当请求到来时,pkg/hubble/relay/observer/server.go:62GetFlows向所有对端发送相同的请求(observer.go:37), 将返回的流按时间顺序重新排序(sortFlows,第 67 行)后输出。
该设计的核心理念是“不进行持久化存储”。流仅存在于节点内存的环形缓冲区中,relay 也不做保存。实测节点上的环形缓冲区在 4095/4095 满载且以每秒 58.5 个的速度写入的情况下,单个节点能记住的历史大约是 约 70 秒。如果需要长期保留,请单独使用指标或 exporter。
5. 与用户贡献的关联
作者已准备好将 4.3 的相关性质修正到文档中的更改(正文第 5 节“仅做好了准备”)。目前尚 未发布。 Cilium 不接收很大程度上由 AI 编写的沟通内容(如 PR 正文等),因此正文必须由作者亲自撰写。所以这里也只写到“仅做好了准备”。
| 文档位置(v1.20.1) | 当前内容 | 代码依据 |
|---|---|---|
Documentation/security/policy/layer7.rst:159 |
L7 DNS 的 matchPattern 说明中 **. 缺失 |
GeneratePattern 也 ToUnAnchoredRegexp → 类似 escapeRegexpCharacters |
layer7.rst:166, :227 |
仅匹配一格的示例被称为「any subdomains」 | * → [-a-zA-Z0-9_]*(第 141 行) |
layer3.rst:548 |
**. 有说明,但缺少「仅限开头」 |
第 37 行正则表达式固定为 ^ ,测试的 test.**.cilium.io. 案例 |
依据是 4.3 的实测表和 Go 复现结果。在准备阶段已确认三份文档文件与 matchpattern.go 的 v1.20.1 和 main 分支相同,草案中如是写道。
6. 实测
2026-10-17,在一个小型集群(8 个节点,无 kube-proxy DaemonSet)中,仅向其中一个控制平面节点的代理 Pod 发送了读取命令。原始数据已另行保存,以下是去除名称和地址后的值。
| 命令 | 观察结果 |
|---|---|
cilium-dbg status |
KubeProxyReplacement: True, Routing: Network: Tunnel [vxlan] Host: BPF, Attach Mode: TCX, Device Mode: veth, Masquerading: BPF, Host firewall: Enabled,控制器 90/90,模块 125 OK,集群健康状况 8/8 |
cilium-dbg status --verbose |
Socket LB: Enabled, Socket LB Coverage: Hostns-only, Backend Selection: Random, XDP Acceleration: Disabled,IPAM 在每个节点 /24 中分配了 15 个 |
cilium-dbg service list |
211 个前端(ClusterIP 171,NodePort 20,LoadBalancer 20) |
cilium-dbg bpf lb list, map list |
cilium_lb4_services_v2 573 格 = 头部 358 格 + 后端槽位 215 格。头部中 127 格为端口 0/ANY 通配符格(具体用途未确认)。后端映射 163,反向映射 231,套接字反向映射 13 |
bpftool net show |
13 个 lxc 设备全部只有 tcx/ingress cil_from_container ,无 egress。物理 NIC cil_from/to_netdev, cilium_host·cilium_net cil_to_host·cil_from_host, cilium_vxlan cil_from/to_overlay,XDP 为空 |
bpftool prog show |
231 个 Cilium 程序(tc 218,cgroup sock_addr 8,cgroup sock 3,用于套接字关闭的 tracing 2)。在 tc 侧,每个端点大约有 3 个入口 + 10 个 tail call |
bpftool cgroup tree |
cgroup 根目录下有 cil_sock4_connect·sendmsg4·recvmsg4·getpeername4·post_bind4·sock_release 等 11 个 |
状态目录 ls |
2 个模板(bpf_lxc.o 1,086,040 字节, bpf_host.o 1,452,768 字节)。13 个端点的 template.txt 哈希值完全相同 |
hubble observe --last 500 |
500 个对应 10.5 秒的时长,FORWARDED 489 · DROPPED 11(全部为 UNSUPPORTED_L3_PROTOCOL)。环形缓冲区 4095/4095,58.5 流/秒 |
cilium-dbg fqdn cache list, fqdn names |
该节点为空(FQDN 选择器 0)——该节点上没有绑定 DNS 规则的 Pod |
| 策略数量(整个集群) | 22 个 CNP 中包含 rules.dns 的有 5 个,包含 toFQDNs 的有 2 个 |
| relay 日志 | 通过 :4244、TLS 连接到 8 个对端 |
有三项内容可读。
- Socket LB 仅开启了一半。 这是在 4.2 中看到的
ENABLE_SOCKET_LB_HOST_ONLY路径。从程序数量中也可以看出。每个端点都附加了类似于tail_nodeport_rev_dnat_ipv4·tail_no_service_ipv4的 tc 侧 LB tail call,且套接字反向映射映射只有 13 格。 - 没有 Pod 侧的 egress 钩子。 进入 Pod 的 数据包不是通过 veth 的 egress,而是由主机侧程序调用策略 tail call(
cil_lxc_policy)后直接转发的。这是 4.1 中的RequireEgressProg()为假的情况。这与「每个 Pod 两个程序」的常见架构图不同。 - 在此节点上没有发现需要查看的 FQDN。 由于只读取了一个代理,因此未实测包含 DNS 规则的 Pod 所在节点的缓存。
7. 从哪里开始阅读
daemon/cmd/cells.go:103— 浏览单元列表,绘制代理内部包含哪些内容的地图。注释很友好。pkg/loadbalancer/README.md和bpf_reconciler.go:842— 这是最短路径,用于了解 Cilium 的新型控制平面语法:“表 → reconciler → 映射”。bpf/bpf_sock.c:298— 读取映射的端。对照查看第 2 部分中使用的格子是如何被读取的。pkg/datapath/loader/endpoint.go:195— 模板、常量、tcx 附加顺序。pkg/fqdn/dnsproxy/proxy.go:895从namemanager/manager.go:214到bpf/bpf_lxc.c:1780cil_from_container— 用户空间代理与 BPF 策略交汇的地方。#ifdef— 最后阅读。由于存在大量的 tail call 和
,必须先掌握前面五个部分才不会迷失方向。
8. 适合首次贡献的地方
pkg/loadbalancer/README.md:33这些是在阅读源码时实际发现的问题(未公开,未确认在 main 分支中是否依然存在)。 Kubernetes 数据来源据称在reflector.go中,但 v1.20.1 中并没有该文件,实际实现是reflectors/k8s.go(同一 README 第 61 行的列表正确记录了reflectors目录)。这是一个只需修改一行的文档修复点。bpf/bpf_lxc.c:88注释中的拼写错误。 「enable per-packet LB is SCTP is enabled」 中的 is 应该是 「if」。这行刚好在 4.2 中引用的条件正上方,容易让读者驻足思考。pkg/fqdn/matchpattern/matchpattern.go:19·:135中的 「one ore more」。 处拼写错误。并且ToAnchoredRegexp(第 95–99 行)的文档注释只解释了*而没有提及**.。这与第 5 节的文档修改属于同一主题,适合结合起来处理。- 通配符头部格的说明。 实测发现头部格中有三分之一是端口 0/ANY 格,但除了
bpf-lb-enable-wildcard-entries选项注释中的一行之外,没有找到其他用途说明。这是用于解释bpf lb list的/ANY的候选文档(未确认是否存在现有文档)。 - 官方指南。 如果是首次参与,请先查看 贡献指南
指向的
good-first-issue标签。AI 政策:由于 Cilium 不接受很大程度上由 AI 编写的 PR 正文和评论,因此沟通内容需由人工亲自编写。
9. 局限性
- 未涵盖的内容:ClusterMesh、BGP 控制平面、WireGuard/IPsec 加密、Gateway API/Ingress 的 Envoy 配置路径(
pkg/ciliumenvoyconfig)、egress gateway、Maglev、XDP 加速、IPv6、identity 分配详情、operator 的 IPAM 分配器(通过构建标签ipam_provider_*引入——小型集群不写为ipam: kubernetes)。 - 未实测项:包含 DNS 规则的 Pod 所在节点的 FQDN 缓存和选择器、绕过
tofqdns-proxy-response-max-delay导致响应优先发出的情况、BPF 编译耗时(模板在大约六周前的代理启动时创建,本次无法查看)、节点重启时的端点恢复。 - 4.3 中的连接表并非本次(2026-10-17)重新测量的值,而是准备贡献时的记录。这是因为本次实测规定了不启动新 Pod。
- Envoy 仅记录了 xDS 关系。L7 策略路径未实测(
Proxy Status显示 「0 redirects active」)。
TT 的一句话总结。 Cilium 的检查站是不会接电话的,它只看提前拿到的规则表。所以如果觉得奇怪,与其去找检查站,不如去找 是谁写的这张表。还有,别忘了
*是个连一个点都过不去的胆小鬼。
参考
所有资料均于 2026-10-17 查阅。源码为 cilium/cilium v1.20.1 标签(提交 7d68cfb394f2)。
- Agent Cell: https://github.com/cilium/cilium/blob/v1.20.1/daemon/cmd/cells.go, https://github.com/cilium/cilium/blob/v1.20.1/pkg/datapath/cells.go
- BPF 加载: https://github.com/cilium/cilium/tree/v1.20.1/pkg/datapath/loader, https://github.com/cilium/cilium/blob/v1.20.1/pkg/datapath/orchestrator/orchestrator.go
- BPF 程序: https://github.com/cilium/cilium/tree/v1.20.1/bpf (
bpf_lxc.c,bpf_host.c,bpf_overlay.c,bpf_sock.c,bpf_xdp.c,lib/lb.h,lib/trace.h,lib/events.h) - 负载均衡控制器: https://github.com/cilium/cilium/tree/v1.20.1/pkg/loadbalancer
- FQDN: https://github.com/cilium/cilium/tree/v1.20.1/pkg/fqdn, https://github.com/cilium/cilium/blob/v1.20.1/pkg/policy/api/fqdn.go, https://github.com/cilium/cilium/blob/v1.20.1/pkg/policy/selectorcache.go
- Hubble: https://github.com/cilium/cilium/tree/v1.20.1/pkg/hubble, https://github.com/cilium/cilium/tree/v1.20.1/pkg/monitor/agent, https://github.com/cilium/cilium/tree/v1.20.1/hubble-relay
- operator: https://github.com/cilium/cilium/blob/v1.20.1/operator/cmd/root.go, https://github.com/cilium/cilium/tree/v1.20.1/operator/pkg/ipam
- kube-proxy 替换文档(绕过 Socket LB): https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/kubernetes/kubeproxy-free.rst
- DNS 策略文档: https://github.com/cilium/cilium/blob/v1.20.1/Documentation/security/policy/layer3.rst, https://github.com/cilium/cilium/blob/v1.20.1/Documentation/security/policy/layer7.rst
- 贡献指南: https://github.com/cilium/cilium/blob/v1.20.1/Documentation/contributing/development/contributing_guide.rst
- 同系列的其他文章: 阅读 MetalLB(谁在宣告 LoadBalancer 服务的地址)
引用的代码: bpf/bpf_sock.c·bpf/bpf_lxc.c 采用 GPL-2.0-only OR BSD-2-Clause, pkg/fqdn/matchpattern/matchpattern.go 采用 Apache-2.0 (Copyright Authors of Cilium)。