Cilium 数据面 NAT 表清理实战:cilium-dbg bpf nat flush命令深度解析
【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium
导读
cilium-dbg bpf nat flush是 Cilium 提供的用于清空本地节点全部 NAT(网络地址转换)映射条目的调试命令。在排查 SNAT 端口耗尽、连接追踪表异常或需要让节点上的地址转换状态“归零”重来时,这个命令是数据面(eBPF datapath)排障工具箱中的关键一环。读完本文,你将掌握该命令的完整用法、权限前提、底层执行流程(从 CLI 到 eBPF map 的调用链),并了解它与bpf nat list、bpf nat retries等姊妹命令的协作方式。
一、命令概览与适用场景
该命令位于cilium-dbg调试客户端之下,其完整命令层级为:
cilium-dbg bpf nat flush [flags]命令的功能描述为"Flush all NAT mapping entries",即清空所有 NAT 映射条目。它操作的对象是 Cilium 数据面中负责SNAT(源地址转换)的全局 NAT 表——当 Pod 流量经过节点转发到外部网络时,eBPF 程序会依据这些表将 Pod 的源 IP/端口改写为节点地址。
典型适用场景包括:
- SNAT 端口分配异常:当节点上全局 NAT 表积累了大量陈旧条目(例如长连接中断后遗留的映射),导致端口分配紧张时,可以清空后让数据面重新建立映射;
- 连接追踪(conntrack)与 NAT 表状态不一致:手动清理 NAT 表,配合
cilium-dbg bpf ct flush等操作,恢复数据面一致性; - 测试与验证:验证 NAT 表重建能力、评估流量恢复路径。
⚠️操作风险提示:该命令会删除节点上所有 NAT 映射条目,正在进行的连接可能因此中断(后续流量需要重新建立映射)。请在维护窗口或充分评估影响后执行。
二、完整命令参考
2.1 命令语法与专属选项
cilium-dbg bpf nat flush [flags]该命令本身不接收位置参数,仅有一个专属选项:
| 选项 | 说明 |
|---|---|
-h, --help | 显示 flush 子命令的帮助信息 |
2.2 继承自父命令的全局选项
作为cilium-dbg命令树的子命令,flush会继承来自父命令(cilium-dbg bpf nat→cilium-dbg bpf→cilium-dbg)的全局选项:
| 选项 | 说明 |
|---|---|
--config string | 指定配置文件(默认$HOME/.cilium.yaml) |
-D, --debug | 开启调试消息输出 |
-H, --host string | 指定服务端 API 的 URI(用于连接 cilium-agent 的 API 端点) |
--log-driver strings | 日志输出端点(示例:syslog) |
--log-opt map | 日志驱动选项(示例:format=json) |
2.3 实际执行示例
# 直接清空当前节点全部 NAT 映射条目 cilium-dbg bpf nat flush # 查看帮助 cilium-dbg bpf nat flush --help # 以调试模式执行(输出更多诊断信息) cilium-dbg bpf nat flush --debug执行成功时,命令会逐张表打印被清除的条目数量,例如:
Flushed 42 entries from /sys/fs/bpf/tc/globals/cilium_snat_v4_external三、权限前提:必须以 root 运行
NAT 表属于内核 BPF 全局状态,修改权限受严格限制。命令在入口处调用了common.RequireRootPrivilege进行校验,相关实现见 pkg/common/utils.go:
// RequireRootPrivilege checks if the user running cmd is root. If not, it exits the program func RequireRootPrivilege(cmd string) { if os.Getuid() != 0 { fmt.Fprintf(os.Stderr, "Please run %q command(s) with root privileges.\n", cmd) os.Exit(1) } }即:若当前用户不是 root(UID 不为 0),程序会直接报错退出。因此请使用sudo或 root 用户执行:
sudo cilium-dbg bpf nat flush四、底层实现:从 CLI 到 eBPF map 的完整调用链
flush子命令的核心实现位于 cilium-dbg/cmd/bpf_nat_flush.go,其执行流程可分为四步:
4.1 命令注册
命令通过 Cobra 框架注册为bpf nat的子命令(父命令定义见 cilium-dbg/cmd/bpf_nat.go):
var bpfNatFlushCmd = &cobra.Command{ Use: "flush", Short: "Flush all NAT mapping entries", Run: func(cmd *cobra.Command, args []string) { common.RequireRootPrivilege("cilium bpf nat flush") flushNat() }, } func init() { BPFNatCmd.AddCommand(bpfNatFlushCmd) }4.2 探测节点启用的 IP 协议族
flushNat()首先调用getIpEnableStatuses()获取节点上 IPv4/IPv6 的启用状态(实现见 cilium-dbg/cmd/helpers.go):
- 优先调用 cilium-agent 的healthz API(带 5 秒超时)确认 agent 存活;
- 若 agent 存活,再通过ConfigGet API读取
Status.Addressing.IPv4.Enabled/IPv6.Enabled字段; - 若 agent 不可达,则回退读取运行时系统配置。
只有启用的协议族对应的 NAT 表才会被清空,避免对未启用协议做无意义操作。
4.3 定位全局 NAT map
根据协议族状态,调用nat.GlobalMaps(见 pkg/maps/nat/nat.go)获取全局 NAT map:
func GlobalMaps(registry *metrics.Registry, ipv4, ipv6 bool) (ipv4Map, ipv6Map *Map) { if ipv4 { ipv4Map = NewMap(registry, MapNameSnat4Global, IPv4, maxEntries()) } if ipv6 { ipv6Map = NewMap(registry, MapNameSnat6Global, IPv6, maxEntries()) } return }对应两张 BPF 全局表(常量定义同上文件 pkg/maps/nat/nat.go):
| 常量 | BPF map 名称 | 含义 |
|---|---|---|
MapNameSnat4Global | cilium_snat_v4_external | 全局 IPv4 SNAT 表 |
MapNameSnat6Global | cilium_snat_v6_external | 全局 IPv6 SNAT 表 |
map 容量由maxEntries()决定:优先取配置项NATMapEntriesGlobal,未配置时回退到LimitTableMax(见 pkg/maps/nat/nat.go)。
4.4 打开 map 并逐条删除
随后代码遍历这两张表(存在且非 nil 才处理),依次完成打开 → 清空 → 关闭:
for _, m := range []*nat.Map{ipv4Map, ipv6Map} { if m == nil { continue } path, err := m.Path() if err == nil { err = m.Open() } if err != nil { if os.IsNotExist(err) { fmt.Fprintf(os.Stderr, "Unable to open %s: %s. Skipping.\n", path, err) continue } Fatalf("Unable to open %s: %s", path, err) } defer m.Close() entries := m.Flush() fmt.Printf("Flushed %d entries from %s\n", entries, path) }值得注意的容错设计:
- 若某张 map不存在(如节点只启用了 IPv4,IPv6 表未被创建),会打印
Unable to open ... Skipping.并跳过,不会中断整个命令; - 其他打开错误(如权限不足)则直接终止并报错。
清空动作最终落到(*Map).Flush()(见 pkg/maps/nat/nat.go),它按协议族分派到doFlush4/doFlush6:
func (m *Map) Flush() int { if m.family == IPv4 { return int(doFlush4(m).deleted) } return int(doFlush6(m).deleted) }而doFlush4/doFlush6的实现方式是:可靠遍历(DumpReliablyWithCallback)整个 map,对每个 key 调用DeleteLocked逐一删除(见 pkg/maps/nat/nat.go),并统计成功删除的条目数返回给上层打印。
从实现可以看出,
flush是"遍历 + 逐条删除"而非一次性ClearAll式的整表重置,删除过程中若个别条目删除失败,会记录错误日志但不会让整个操作失败。
五、与其他 NAT 调试命令的配合
bpf nat是 cilium-dbg 下管理 NAT 映射表的命令组(参见 cilium-dbg_bpf_nat.md 命令参考),完整子命令包括:
| 子命令 | 功能 |
|---|---|
cilium-dbg bpf nat list | 列出所有 NAT 映射条目,支持-o json\|yaml\|jsonpath输出格式(参考 cilium-dbg_bpf_nat_list.md) |
cilium-dbg bpf nat flush | 清空所有 NAT 映射条目(本文主题) |
cilium-dbg bpf nat retries | 查看 NAT 端口分配重试的直方图统计 |
推荐的排障顺序通常是:
- 先看:
cilium-dbg bpf nat list观察当前 NAT 表规模和条目内容,确认是否存在异常堆积; - 再查:
cilium-dbg bpf nat retries查看端口分配重试直方图,判断是否出现 SNAT 端口分配冲突(源码中SnatCollisionRetries = 32表示端口分配最多重试 32 次,见 pkg/maps/nat/nat.go); - 最后清:确认需要重置后,执行
cilium-dbg bpf nat flush并观察打印出的清除条目数,评估数据面恢复情况。
六、命令参考文档说明
本命令的权威参考文档由cilium-dbg cmdref工具自动生成,存放于 Documentation/cmdref/cilium-dbg_bpf_nat_flush.md。当命令实现(cilium-dbg/cmd/bpf_nat_flush.go)或 Cobra 定义变更时,需通过update-cmdref.sh重新生成,以保证 CLI 实际行为与文档严格一致。因此本文展示的命令用法、选项与继承的全局参数,均与当前仓库中命令实现保持同步。
总结
cilium-dbg bpf nat flush用于清空节点上cilium_snat_v4_external与cilium_snat_v6_external两张全局 SNAT map;- 必须以 root 权限执行,否则命令直接退出;
- 底层通过 agent API 探测 IPv4/IPv6 启用状态,再对启用协议对应的 map 执行"遍历 + 逐条删除",并汇报清除条目数;
- 对不存在的 map 具备跳过容错,属于低风险、可反复执行的清理类调试操作;
- 配合
bpf nat list、bpf nat retries使用,可以构成完整的 NAT 数据面问题定位闭环。
【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考