4、ovsdb-server 与数据库:OVSDB 的 Schema 设计、Open_vSwitch 表结构、ovsdb-client 工具使用

聊到 Open vSwitch,很多人第一反应是 ovs-vswitchd,觉得它才是核心。嗯,这话没错,但少了 ovsdb-server,ovs-vswitchd 就是个没脑子的蛮力工兵。我个人习惯把 ovsdb-server 比作 OVS 的「大脑皮层」——所有配置、状态、拓扑信息都存这里。你想想看,没有数据库,你怎么持久化流表?怎么在重启后恢复网桥配置?

这一章,我们就来扒一扒 OVSDB 的 Schema 设计、核心表结构,以及怎么用 ovsdb-client 跟数据库「对话」。

4.1 OVSDB 的 Schema 设计哲学

OVSDB 用的是 JSON Schema 来描述数据库结构。说白了,就是用 JSON 格式定义好「有哪些表、每张表有哪些列、每列是什么类型」。我第一次看到这个设计时觉得有点怪——数据库 Schema 用 JSON 写?后来在项目中遇到跨版本升级,才发现这设计太聪明了:JSON 天生自描述,解析器写起来简单,而且扩展性极强。

Schema 文件通常以 .ovsschema 结尾。你可以在 OVS 源码的 vswitchd/ 目录下找到 vswitch.ovsschema。这个文件定义了整个 OVS 的配置模型。

核心设计原则:

  • 弱类型 + 强校验:列类型用 JSON 字符串表示(如 "integer"、"string"),但实际写入时会做类型检查
  • 版本化演进:每个 Schema 都有版本号,升级时自动做迁移
  • 引用完整性:支持 UUID 引用,表之间可以建立关联

举个例子,一个简化的 Schema 片段长这样:

{
  "name": "Open_vSwitch",
  "version": "8.2.0",
  "tables": {
    "Bridge": {
      "columns": {
        "name": {"type": "string"},
        "ports": {"type": {"key": {"type": "uuid",
                                    "refTable": "Port"},
                            "min": 0, "max": "unlimited"}},
        "datapath_type": {"type": "string"}
      }
    }
  }
}

看到 refTable 了吗?这就是 OVSDB 的「外键」机制。Bridge 表的 ports 列引用 Port 表的 UUID,实现了表间关联。

我的经验: 如果你要自己写工具操作 OVSDB,千万别硬编码列名。一定要先读取 Schema 再动态生成操作语句。我曾经在升级 OVS 2.5 到 2.9 时踩过坑——某个列名变了,硬编码的脚本直接崩了。

4.2 Open_vSwitch 表结构深度解析

OVSDB 里最重要的表就是 Open_vSwitch 表。它是一张全局配置表,整个 OVS 实例只有一行记录。你可以把它理解成 OVS 的「身份证」加「控制面板」。

列名 类型 说明
bridges UUID 集合 引用所有 Bridge 表记录
ovs_version string 当前 OVS 版本号,只读
external_ids map 键值对,用于标识系统(如 system-id)
other_config map 各种杂项配置(如 flow_restore_wait)
ssl UUID 引用 SSL 表,配置 SSL 连接

除了 Open_vSwitch 表,还有几张核心表你需要熟悉:

  • Bridge:定义网桥,包含名称、端口列表、流表版本等
  • Port:定义端口,关联 Interface 表
  • Interface:定义接口,包含类型(如 internal、vxlan)、MTU、MAC 地址等
  • Flow_Table:流表配置,如流表优先级、查找算法
  • Controller:控制器连接配置,如 IP、端口、协议

注意: 不要直接修改 Open_vSwitch 表的 ovs_version 列。它是只读的,由 ovsdb-server 自动维护。我曾经见过有人手贱改了它,结果导致版本检测逻辑出问题,升级脚本直接罢工。

4.3 ovsdb-client 工具使用

ovsdb-client 是跟 OVSDB 打交道的瑞士军刀。它比 ovs-vsctl 更底层,直接操作 JSON-RPC 接口。我个人习惯在调试复杂问题时用它——因为能看到最原始的数据。

先看几个最常用的命令:

# 列出所有数据库
ovsdb-client list-dbs

# 列出某个数据库的所有表
ovsdb-client list-tables Open_vSwitch

# 列出某张表的所有列
ovsdb-client list-columns Open_vSwitch Bridge

# 查询数据(类似 SQL 的 SELECT)
ovsdb-client dump Open_vSwitch Bridge

举个例子,我想看看当前系统有哪些网桥:

$ ovsdb-client dump Open_vSwitch Bridge
Bridge table
_uuid                                name    ports
550e8400-e29b-41d4-a716-446655440000 br0     [b1c2d3e4-...]
550e8400-e29b-41d4-a716-446655440001 br1     [f5g6h7i8-...]

输出格式是表格,UUID 列是每行记录的唯一标识。注意 ports 列显示的是 UUID 列表,你需要再查 Port 表才能看到具体端口名。

调试技巧:--format=json 参数可以输出 JSON 格式,方便用 jq 做后续处理。比如:ovsdb-client dump --format=json Open_vSwitch Bridge | jq '.data[][1]' 就能提取所有网桥名称。

ovsdb-client 还支持事务操作:

# 启动一个事务
ovsdb-client transact Open_vSwitch '
[["Open_vSwitch",
  {"op": "insert",
   "table": "Bridge",
   "row": {"name": "br-test"}}]]'

这个命令在 Bridge 表插入了一行。注意事务操作是 JSON 数组格式,每条操作是一个 JSON 对象。你可以一次提交多个操作,要么全部成功,要么全部回滚。

避坑指南: 我曾经在生产环境用 ovsdb-client 直接修改数据库,结果忘了加条件,把整个 Bridge 表清空了。嗯,那次教训很深刻。所以我的建议是:永远先 dump 备份,再执行写操作。哪怕只是改一个列值,也要先确认 UUID 正确。

4.4 实战:用 ovsdb-client 排查问题

假设你发现某个网桥的端口不见了,怎么查?

  1. 先 dump Bridge 表,确认网桥还在不在
  2. 再 dump Port 表,看端口 UUID 是否被引用
  3. 最后 dump Interface 表,检查接口状态

具体命令:

ovsdb-client dump Open_vSwitch Bridge br0
ovsdb-client dump Open_vSwitch Port
ovsdb-client dump Open_vSwitch Interface

如果端口 UUID 在 Bridge 表的 ports 列里,但 Port 表里找不到对应记录,那就是数据不一致了。这时候可以用 ovsdb-client transact 手动修复——当然,前提是你知道正确的 UUID。

说实话,这种问题在正常运维中很少见。但如果你在做 OVS 二次开发,或者写自动化脚本,理解 ovsdb-client 的底层操作就非常关键。它让你能绕过 ovs-vsctl 的封装,直接跟数据库对话。

好了,这一章的内容就到这里。下一章我们会深入 ovs-vswitchd 的数据路径,看看流表到底是怎么匹配和转发的。