主题
搭建实验环境
环境是什么样的
两台机器分工:本地写和读文档,远程跑实验。
text
本地(macOS / Linux) 实验主机(Debian 13)
┌──────────────────────────┐ ┌──────────────────────────────────┐
│ │ │ │
│ learn-ovs/ │ │ ~/labs/learn-ovs/ │
│ ├── docs/ 文档源码 │ │ ├── common/ 镜像 + 脚手架 │
│ │ └─ pnpm dev │ │ ├── 01-first-bridge/ │
│ │ localhost:5173 │ │ └── ... │
│ └── labs/ 实验源码 │ rsync │ │
│ └─ ./sync.sh ──────┼───────────▶│ netlab up │
│ │ ssh │ └─ containerlab │
│ │ │ └─ docker 容器 │
│ │ │ └─ ovs-vswitchd │
└──────────────────────────┘ └──────────────────────────────────┘为什么不在本地跑实验?OVS 的内核 datapath 需要 Linux 的 openvswitch 内核模块,macOS 上没有。而且容器里的 OVS 要用宿主机内核,套一层虚拟机会让"看内核 datapath"这件事变得别扭。
只有一台 Linux 机器也行
如果你就在一台 Linux 机器上干活,把仓库放在那台机器上,跳过 sync.sh,直接进实验目录跑脚本就行。文档站用 pnpm dev --host 起,从别的机器浏览器访问。
实验主机需要什么
| 依赖 | 用途 | 检查命令 |
|---|---|---|
Linux + openvswitch 内核模块 | OVS 的内核 datapath | modprobe openvswitch && grep -w openvswitch /proc/modules |
| Docker | 跑容器 | docker version |
| containerlab ≥ 0.75 | 创建容器和 veth 链路 | containerlab version |
| netlab ≥ 26.08 | 把拓扑描述翻译成 containerlab 配置 | netlab version |
sudo(免密最省事) | 加载内核模块、containerlab 建链路 | sudo -n true |
rsync | 接收 sync.sh 推来的文件 | rsync --version |
当前用户在 clab_admins 和 docker 组 | 不用 root 就能操作 | groups |
安装 netlab 和 containerlab 参考各自官方文档(netlab.tools、containerlab.dev)。
netlab 常常不在 PATH 里
netlab 一般装在一个 Python 虚拟环境里,登录 shell 默认看不到它:
bash
$ netlab version
bash: netlab: command not found
$ ls ~/.venvs/netlab/bin/netlab
/home/you/.venvs/netlab/bin/netlab ← 在这儿实验脚本会自动把 ~/.venvs/netlab/bin 加进 PATH,所以跑实验不受影响。但你手动敲 netlab 命令时需要先:
bash
export PATH=$HOME/.venvs/netlab/bin:$PATH本地只需要 Node.js 20+ 和 pnpm,用来跑文档站。
三步跑起来
1. 起文档站(本地)
bash
git clone <本仓库> learn-ovs
cd learn-ovs
pnpm install
pnpm dev浏览器打开 http://localhost:5173。
2. 把实验推到实验主机
先告诉 sync.sh 目标机器。默认值写在脚本里,改成你自己的:
bash
export OVSLAB_REMOTE=你的用户名@实验主机地址
./labs/sync.sh输出会逐条列出传了哪些文件:
text
同步 labs/ → you@192.0.2.10:labs/learn-ovs/
<f+++++++ sync.sh
cd+++++++ 01-first-bridge/
<f+++++++ 01-first-bridge/start.sh
...
完成。到实验主机上跑:
ssh you@192.0.2.10
cd labs/learn-ovs/01-first-bridge && ./start.sh想先看会改什么,加 --dry-run。
sync.sh 为什么要排除一堆文件
netlab up 会在实验目录里生成运行时文件:clab.yml、ansible.cfg、hosts.yml、group_vars/、host_vars/、node_files/、netlab.snapshot.pickle。
这些属于实验主机,不能被本地版本覆盖或删掉 —— 否则正在跑的实验会被打断,stop.sh 也会因为找不到快照而拆不干净。sync.sh 把它们排除在 --delete 之外。
3. 跑第一个实验
bash
ssh 你的用户名@实验主机地址
cd labs/learn-ovs/01-first-bridge
./start.sh第一次会花两三分钟构建两个容器镜像,之后就是几秒钟的事。
看到这个就成了:
text
== 配置两台主机的地址 ==
h1 eth1 = 192.168.10.1/24
h2 eth1 = 192.168.10.2/24
环境就绪。现在 h1 和 h2 物理上都连到了 ovs1,但 ovs1 还没有网桥,
两条线是断的。然后就可以开始实验 1 了。
实验目录长什么样
text
labs/
├── sync.sh 把 labs/ 推到实验主机
├── common/
│ ├── ovslab.sh 所有实验共用的一层薄脚手架
│ ├── netlab-defaults.yml 让本教程的实验和别的实验互不干扰
│ └── images/
│ ├── Dockerfile 两个镜像的构建描述
│ └── ovs-entrypoint.sh 容器里怎么拉起 OVS 守护进程
└── 01-first-bridge/
├── topology.yml netlab 拓扑描述
├── start.sh 起环境
├── solve.sh 参考答案
├── verify.sh 检查 + 打印关键状态
└── stop.sh 拆环境四个脚本的分工在本教程怎么用讲过。common/ovslab.sh 只有一百来行,建议扫一遍 —— 你会经常用到里面的 nx 函数。
两个容器镜像
start.sh 会在镜像不存在时自动构建:
| 镜像 | 扮演 | 装了什么 |
|---|---|---|
learn-ovs/host | 实验里的主机(h1、h2) | iproute2、ping、arping、tcpdump、iperf3、jq |
learn-ovs/ovs | 实验里的交换机(ovs1、ovs2) | 上面这些 加上 openvswitch-switch、bridge-utils |
刻意分成两个而不是一个通用镜像:这样主机上就是没有 ovs-vsctl 命令,和真实环境的边界一致。你不会不小心在主机上敲交换机的命令还以为生效了。
容器里的 OVS 是怎么起来的
物理机上这一步由 systemd 做(systemctl start openvswitch-switch)。容器里没有 systemd,ovs-entrypoint.sh 直接调 OVS 自带的 ovs-ctl 脚本 —— 和 systemd unit 调的是同一个脚本:
bash
/usr/share/openvswitch/scripts/ovs-ctl start --system-id=random --no-monitor它负责建库、拉起 ovsdb-server、初始化数据库、拉起 ovs-vswitchd。
内核模块由宿主机加载(ovslab.sh 里的 modprobe openvswitch 兜底),容器只是使用宿主机内核里的 datapath。容器由 containerlab 以 privileged 方式运行,所以有权限这么做。
和你机器上其它实验的隔离
netlab 默认把每个实验登记成名叫 default 的实例,同一台机器上只能有一个。如果你机器上已经跑着别的 netlab 实验,直接起本教程的实验会报:
text
It looks like the lab instance 'default' is already running in directory ...本教程用 netlab 的 multilab 插件绕开这件事,配置在 common/netlab-defaults.yml:给实验分配 id 47,管理网段改成 172.31.47.0/24,docker 网络名改成 nl_mgmt_47。于是:
bash
$ netlab status --all
┏━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┓
┃ id ┃ directory ┃ status ┃
┡━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━┩
│ default │ /home/you/other-lab │ started │ ← 你原有的实验
│ 47 │ /home/you/labs/learn-ovs/01-... │ started │ ← 本教程的实验
└─────────┴──────────────────────────────────┴─────────┘两套实验可以同时跑,互不影响。
为什么还要覆盖 name
multilab 插件默认会把实验改名成 ml-47,容器就变成 clab-ml-47-h1 这种认不出来的名字。netlab-defaults.yml 里把 name 改回 '{name}'(求值成拓扑自己的名字),容器才是可读的 clab-first-bridge-h1。
教程里所有 docker exec 命令都依赖这个命名,别改。
常见问题
netlab: command not found
netlab 在虚拟环境里。见上面的提示框,或者直接跑实验脚本(它们会自己处理 PATH)。
rsync: command not found
实验主机上没装:
bash
sudo apt-get install -y rsyncflock: cannot open lock file .../~/.netlab/lab.lock
netlab 拿到锁文件路径后直接 Path().resolve(),不做 ~ 展开。common/netlab-defaults.yml 里必须写绝对路径(当前用的是 /tmp/netlab-learn-ovs.lock)。如果你改过这个值,改回绝对路径。
You are not a member of the clab_admins group
bash
sudo usermod -aG clab_admins,docker $USER然后重新登录让组生效。
无法加载 openvswitch 内核模块
bash
sudo modprobe openvswitch失败的话检查内核是否带了这个模块:
bash
ls /lib/modules/$(uname -r)/kernel/net/openvswitch/应该能看到 openvswitch.ko.xz 以及 vport-vxlan.ko.xz、vport-gre.ko.xz、vport-geneve.ko.xz(第 4 部分的隧道实验需要后面这几个)。
实验起一半失败了,环境是脏的
bash
./stop.sh # 正常拆stop.sh 也失败的话,手动清:
bash
export PATH=$HOME/.venvs/netlab/bin:$PATH
netlab status --all # 看还登记着什么
netlab status -i 47 --cleanup # 强行注销本教程的实例
docker ps -a --format '{{.Names}}' | grep '^clab-' | xargs -r docker rm -f想彻底清掉,包括镜像
bash
./stop.sh
docker rmi learn-ovs/ovs learn-ovs/host下次 start.sh 会重新构建。
下一步
环境好了,去 实验 1:第一个网桥。