ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

本地虚拟机+Vagrant快速搭建Tungsten Fabric实验环境

本地虚拟机+Vagrant快速搭建Tungsten Fabric实验环境

1. 为什么要在本地用虚拟机跑 Tungsten Fabric

如果你正在接触 SDN 控制器、云网络虚拟化或者多云互联方向,Tungsten Fabric 这个名字大概率绕不开。它最早由某家网络设备厂商开源,后来进入 Linux 基金会体系,核心定位是提供一个可编排、可观测、支持多编排器接入的虚拟网络控制平面。说白了,它能把一套物理网络抽象成软件定义的 Overlay,让虚拟机、容器、裸金属之间的东西向流量按策略走。

但问题来了:这东西的官方部署文档大多面向生产集群,动辄要求多台物理服务器、独立管理网、BGP 对等体、vRouter 内核模块。对于只是想先跑通、看看 Dashboard 长什么样、验证一下 VXLAN 隧道能不能起来的同学,直接上生产环境成本太高。我见过太多人卡在“装到一半发现网卡名不对”“控制节点和计算节点时间不同步”这类问题上,最后连登录界面都没见到就放弃了。

所以本地用虚拟机搭一套最小可用的 Tungsten Fabric 实验环境,是性价比最高的入门路径。而 Vagrant 在这里的价值就很明显了:它用一份文本配置描述虚拟机规格、网络、同步目录和初始化脚本,一条命令拉起整套拓扑,换台机器也能复现。相比手动开虚拟机再一台台装系统,Vagrant 把“环境准备”这件事从半天压缩到十几分钟。

这篇文章适合三类人:一是刚接触 SDN 控制器、想先跑通再深入原理的运维或开发;二是需要频繁重建实验环境、受够了手动配网卡的测试人员;三是准备把 Tungsten Fabric 集成进自己平台、需要先摸清组件交互的架构同学。我会从整体设计思路讲到具体参数,再到踩过的坑,尽量让你照着做就能出一套能登录、能看拓扑、能下发策略的环境。

2. 整体设计与方案选型拆解

2.1 为什么选 Vagrant 而不是手动开虚拟机

手动开虚拟机不是不行,但有几个硬伤。第一是网络配置重复劳动:Tungsten Fabric 至少需要管理网、控制网、数据网三个平面,每台机器都要配多张网卡,手动点界面容易配错顺序。第二是状态不可复现:今天装好了,明天想重来一遍,之前改过的内核参数、hosts 文件、防火墙规则全得回忆。第三是团队协作困难:你本地跑通了,同事照着文档做却因为 VirtualBox 版本差异失败。

Vagrant 的核心优势在于“基础设施即代码”。Vagrantfile 里写清楚 box 镜像、内存 CPU、网络类型、provision 脚本,任何人拿到这个文件都能拉起结构一致的集群。而且 Vagrant 对 VirtualBox 和 libvirt 都有成熟支持,本地开发机不用改太多东西。

注意:Vagrant 本身不提供虚拟化能力,它只是编排层,底层还是靠 VirtualBox、libvirt 或 Hyper-V。所以选型时要先确认本机虚拟化后端是否可用。

2.2 拓扑规划:三节点还是 All-in-One

Tungsten Fabric 的组件大致分两类:控制平面(控制节点、配置节点、数据库节点、分析节点、Web UI)和转发平面(vRouter,通常跑在计算节点)。生产环境这些角色分开部署,实验环境可以合并。

我建议的本地拓扑是“一控两算”三节点:

节点角色主机名示例内存建议CPU网卡
控制+配置+数据库+分析+UItf-control8GB2管理网、控制网
计算节点1tf-compute16GB2管理网、控制网、数据网
计算节点2tf-compute26GB2管理网、控制网、数据网

为什么不推荐 All-in-One?因为 Tungsten Fabric 的 vRouter 需要加载内核模块并接管数据网卡,如果和控制平面挤在同一台机器,网卡角色容易冲突,排查问题时分不清是控制面还是转发面的锅。三节点虽然吃内存,但边界清晰,出问题好定位。

如果本机内存实在紧张,可以退而求其次用两节点:一台控制+计算,一台纯计算。但我不建议再压缩,否则连容器运行时都跑不起来。

2.3 网络平面划分与地址规划

这是整个实验环境最容易翻车的地方。Tungsten Fabric 对网络平面有明确预期:

  • 管理网:用于 SSH、Vagrant 通信、节点间基础连通。建议用 NAT 或 host-only,地址段比如 192.168.56.0/24。
  • 控制网:承载 XMPP、BGP、配置下发等控制流量。可以用 internal network,地址段 192.168.100.0/24。
  • 数据网:承载 VXLAN Overlay 的实际业务流量。同样用 internal network,地址段 192.168.200.0/24。

地址规划要提前定好,因为后面写 hosts 文件和配置文件都要用。我习惯用末位区分角色:控制节点 .10,计算节点 .11 和 .12。这样看到 IP 就知道是谁。

提示:VirtualBox 的 internal network 默认不提供 DHCP,所以每台机器的这些网卡都要在 provision 脚本里配静态 IP。别指望 Vagrant 的private_network自动搞定,它更适合 host-only 场景。

2.4 镜像选择与版本匹配

Tungsten Fabric 对操作系统内核有要求,因为 vRouter 是内核模块。太新的内核可能没有预编译模块,太旧的又缺依赖。我实测下来,CentOS 7 系列或者 Ubuntu 18.04/20.04 比较稳。如果你用 Ubuntu 22.04,内核 5.15 以上,vRouter 模块编译容易报错,需要额外打补丁。

Vagrant box 建议选官方或社区维护的通用镜像,比如generic/centos7或generic/ubuntu2004。不要用带桌面环境的 box,白白吃内存。另外 box 的虚拟化后端要和你的 Vagrant provider 一致,VirtualBox 就用 virtualbox 版本,libvirt 就用 libvirt 版本。

版本匹配上,Tungsten Fabric 的容器镜像 tag 要和部署工具版本对应。比如用某个稳定分支的 deployer,就拉对应分支的容器镜像。混用版本最常见的症状是容器起来了但服务注册不上,日志里一堆版本协商失败。

3. 核心细节解析与实操要点

3.1 Vagrantfile 的关键配置项

Vagrantfile 是整个环境的入口,几个配置项必须写对。

第一是 box 和 provider。以 VirtualBox 为例:

config.vm.box = "generic/centos7" config.vm.provider "virtualbox" do |vb| vb.memory = 8192 vb.cpus = 2 vb.customize ["modifyvm", :id, "--nicpromisc2", "allow-all"] end

nicpromisc2这行很关键。Tungsten Fabric 的数据网需要网卡处于混杂模式,否则 VXLAN 封装解封装会丢包。VirtualBox 默认不允许混杂模式,必须显式打开。这个坑我踩过,现象是控制面一切正常,但两台计算节点上的虚拟机互相 ping 不通。

第二是网络定义。管理网用private_network配 host-only,控制网和数据网用private_network配 internal network:

config.vm.network "private_network", ip: "192.168.56.10" config.vm.network "private_network", ip: "192.168.100.10", virtualbox__intnet: "tf-control" config.vm.network "private_network", ip: "192.168.200.10", virtualbox__intnet: "tf-data"

注意virtualbox__intnet的值要一致,同一张 internal network 的机器才能互通。名字随便起,但三台机器要对应上。

第三是 provision 脚本。我习惯把初始化逻辑拆成 shell 脚本,Vagrantfile 里只引用:

config.vm.provision "shell", path: "scripts/common.sh" config.vm.provision "shell", path: "scripts/control.sh" if "control"

这样脚本可以单独调试,不用每次改 Vagrantfile 都重新 vagrant up。

3.2 主机名与 hosts 解析

Tungsten Fabric 的组件之间大量通过主机名通信,所以每台机器的 hostname 和 /etc/hosts 必须一致且正确。

在 common.sh 里做这几件事:

hostnamectl set-hostname tf-control cat >> /etc/hosts <<EOF 192.168.56.10 tf-control 192.168.56.11 tf-compute1 192.168.56.12 tf-compute2 192.168.100.10 tf-control 192.168.100.11 tf-compute1 192.168.100.12 tf-compute2 EOF

为什么要同时写管理网和控制网的 IP?因为不同组件用的地址源不一样。Web UI 和 SSH 走管理网,但控制平面内部通信可能解析到控制网地址。如果只写一条,某些服务会连错网卡。

注意:hostname 不要带下划线或大写,Tungsten Fabric 某些组件对主机名格式敏感,带特殊字符会导致注册失败。

3.3 内核参数与防火墙调整

vRouter 需要开启 IP 转发,并且要调整一些内核参数:

cat >> /etc/sysctl.conf <<EOF net.ipv4.ip_forward = 1 net.ipv4.conf.all.forwarding = 1 net.ipv4.conf.default.rp_filter = 0 net.ipv4.conf.all.rp_filter = 0 EOF sysctl -p

rp_filter设为 0 是必须的。反向路径过滤会丢弃源地址和入接口不匹配的包,而 VXLAN 场景下这是常态。不关掉的话,Overlay 流量会被内核静默丢弃,日志里还看不到明显报错,非常难查。

防火墙方面,实验环境建议直接关掉 firewalld 和 SELinux,减少变量:

systemctl stop firewalld systemctl disable firewalld setenforce 0 sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config

生产环境当然要精细配置规则,但实验阶段先把连通性跑通,再逐步收紧。

3.4 容器运行时与依赖安装

Tungsten Fabric 的组件以容器形式运行,所以需要容器运行时。早期版本用 Docker,新版本逐步转向 containerd。实验环境用 Docker 最省事:

yum install -y docker systemctl enable docker systemctl start docker

另外需要安装一些基础工具:net-tools、bind-utils、tcpdump、git、python3。tcpdump 尤其重要,后面排查 VXLAN 问题全靠它。

如果部署工具依赖 Python 库,建议用虚拟环境隔离,避免污染系统 Python。我一般用 venv:

python3 -m venv /opt/tf-venv source /opt/tf-venv/bin/activate pip install -r requirements.txt

4. 实操过程与核心环节实现

4.1 编写 Vagrantfile 与目录结构

先规划目录:

tf-lab/ ├── Vagrantfile ├── scripts/ │ ├── common.sh │ ├── control.sh │ └── compute.sh └── config/ └── instances.yaml

Vagrantfile 里定义三台机器,用循环减少重复:

nodes = [ { name: "tf-control", ip: "192.168.56.10", ctrl: "192.168.100.10", data: nil, mem: 8192 }, { name: "tf-compute1", ip: "192.168.56.11", ctrl: "192.168.100.11", data: "192.168.200.11", mem: 6144 }, { name: "tf-compute2", ip: "192.168.56.12", ctrl: "192.168.100.12", data: "192.168.200.12", mem: 6144 } ] Vagrant.configure("2") do |config| nodes.each do |node| config.vm.define node[:name] do |n| n.vm.box = "generic/centos7" n.vm.hostname = node[:name] n.vm.network "private_network", ip: node[:ip] n.vm.network "private_network", ip: node[:ctrl], virtualbox__intnet: "tf-control" n.vm.network "private_network", ip: node[:data], virtualbox__intnet: "tf-data" if node[:data] n.vm.provider "virtualbox" do |vb| vb.memory = node[:mem] vb.cpus = 2 vb.customize ["modifyvm", :id, "--nicpromisc2", "allow-all"] vb.customize ["modifyvm", :id, "--nicpromisc3", "allow-all"] end n.vm.provision "shell", path: "scripts/common.sh" n.vm.provision "shell", path: "scripts/control.sh" if node[:name] == "tf-control" n.vm.provision "shell", path: "scripts/compute.sh" if node[:name].start_with?("tf-compute") end end end

这里nicpromisc2和nicpromisc3分别对应第二、第三张网卡。VirtualBox 的网卡编号从 1 开始,第一张是 NAT,所以控制网是 2,数据网是 3。

4.2 控制节点初始化脚本

control.sh 主要做三件事:装部署工具、生成配置、拉起控制平面。

#!/bin/bash set -e # 安装依赖 yum install -y git python3 python3-pip pip3 install --upgrade pip # 拉取部署工具 cd /opt git clone https://github.com/example/tf-deployer.git cd tf-deployer pip3 install -r requirements.txt # 生成 instances.yaml cat > /opt/tf-deployer/instances.yaml <<EOF provider_config: bms: ssh_user: root ssh_pwd: vagrant ntpserver: 192.168.56.10 instances: tf-control: provider: bms ip: 192.168.100.10 roles: - config_database - config - control - analytics - webui tf-compute1: provider: bms ip: 192.168.100.11 roles: - vrouter tf-compute2: provider: bms ip: 192.168.100.12 roles: - vrouter global_configuration: CONTAINER_REGISTRY: registry.example.com REGISTRY_PRIVATE_INSECURE: True EOF # 执行部署 python3 /opt/tf-deployer/launch.py --config /opt/tf-deployer/instances.yaml

instances.yaml 是核心配置文件,roles 决定了每台机器跑哪些容器。控制节点承担 config_database、config、control、analytics、webui 五个角色,计算节点只跑 vrouter。

提示:CONTAINER_REGISTRY要换成你实际能访问的镜像仓库地址。如果仓库是自签证书,REGISTRY_PRIVATE_INSECURE设为 True 跳过校验,实验环境够用。

4.3 计算节点初始化脚本

compute.sh 相对简单,主要是准备 vRouter 运行环境:

#!/bin/bash set -e yum install -y docker tcpdump systemctl enable docker systemctl start docker # 加载 vrouter 内核模块(如果部署工具不自动加载) modprobe vrouter || true # 确保数据网卡 up ip link set eth2 up

vRouter 容器启动时会尝试加载内核模块并接管数据网卡。如果模块加载失败,容器会反复重启。常见原因是内核版本和模块不匹配,这时候要么换 box 镜像,要么手动编译模块。

4.4 启动与验证

在 tf-lab 目录下执行:

vagrant up

第一次会下载 box、创建三台虚拟机、跑 provision 脚本,视网络情况大概 15 到 30 分钟。全部起来后,SSH 进控制节点:

vagrant ssh tf-control

检查容器状态:

docker ps --format "table {{.Names}}\t{{.Status}}"

正常应该看到 config、control、analytics、webui、database 等容器都是 Up 状态。如果有容器反复重启,用docker logs <容器名>看日志。

然后验证控制平面:

source /opt/tf-deployer/venv/bin/activate tfctl status

如果能看到各组件健康状态,说明控制面起来了。接着在浏览器访问http://192.168.56.10:8143,用默认账号登录 Web UI,应该能看到拓扑视图和节点列表。

计算节点上验证 vRouter:

docker exec -it vrouter agent --dump

能看到 vRouter 的接口、路由、隧道信息就说明转发面也正常了。

5. 常见问题与排查技巧实录

5.1 容器起不来或反复重启

这是最常见的问题,原因通常分三类。

第一类是镜像拉取失败。检查docker pull能不能手动拉下来,如果报证书错误,确认REGISTRY_PRIVATE_INSECURE配置生效。如果报网络超时,检查控制节点到镜像仓库的连通性。

第二类是依赖服务没就绪。Tungsten Fabric 组件之间有启动顺序,config 依赖 database,control 依赖 config。如果 database 没起来,config 会一直重试。用docker logs看具体卡在哪一步。

第三类是资源不足。8GB 内存跑控制节点其实偏紧,如果同时跑 analytics 和 webui,可能触发 OOM。用free -h看内存,必要时把控制节点内存加到 12GB。

5.2 计算节点注册不上控制平面

现象是控制节点 Web UI 里看不到计算节点,或者计算节点上 vrouter 容器日志报连接超时。

排查顺序:

  1. 从计算节点 ping 控制节点的控制网 IP,确认三层通。
  2. 检查控制节点上 control 容器的端口是否监听,通常是 5269(XMPP)和 179(BGP)。
  3. 检查计算节点 hosts 文件里控制节点主机名解析是否正确。
  4. 检查时间同步,XMPP 对时间偏差敏感,超过几分钟会拒绝连接。

时间同步用 chronyd:

yum install -y chrony systemctl enable chronyd systemctl start chronyd chronyc sources

5.3 VXLAN 隧道不通

两台计算节点上的虚拟机互相 ping 不通,但各自能 ping 通网关。这种问题八成出在数据网。

先在计算节点上确认 VXLAN 接口存在:

ip -d link show vxlan_tunnel

然后用 tcpdump 抓数据网卡:

tcpdump -i eth2 -n udp port 4789

如果看到封装包发出去了但对面没回,检查对面节点的混杂模式是否打开。VirtualBox 的nicpromisc设置只对当前网卡生效,如果数据网是第三张网卡,要设nicpromisc3。

还有一个隐蔽的坑:internal network 的名字如果拼写不一致,两台机器其实不在同一张虚拟网络里。检查 Vagrantfile 里virtualbox__intnet的值是否完全相同。

5.4 Web UI 登录后空白或报错

Web UI 依赖 analytics 和 config 服务。如果登录后页面空白,先看浏览器控制台有没有 404 或 500。然后检查 analytics 容器日志,常见原因是 Cassandra 或 Kafka 没起来。

analytics 对磁盘 IO 比较敏感,如果虚拟机磁盘是机械盘或者动态分配空间,可能启动很慢。建议 Vagrant box 用固定大小磁盘,或者至少给足 40GB。

5.5 常见问题速查表

现象可能原因排查命令解决方向
容器反复重启依赖未就绪/镜像拉取失败docker logs <name>检查依赖顺序、镜像仓库连通性
计算节点不注册控制网不通/时间偏差ping、chronyc sources修网络、同步时间
VXLAN 不通混杂模式未开/网段不一致tcpdump -i eth2 udp port 4789开混杂模式、核对 intnet 名
Web UI 空白analytics 未就绪docker logs analytics等启动、加磁盘 IO
vRouter 模块加载失败内核版本不匹配modprobe vrouter换 box 镜像或编译模块

5.6 几个我踩过的坑

第一个坑是 Vagrant 的private_network在 VirtualBox 下默认用 host-only,如果你写virtualbox__intnet但没指定ip,Vagrant 可能不创建网卡。所以 internal network 也要给 IP,哪怕你后面会改。

第二个坑是 provision 脚本的幂等性。第一次vagrant up跑通了,第二次vagrant provision可能因为重复写 hosts 文件导致内容翻倍。脚本里加判断,比如grep -q检查后再追加。

第三个坑是磁盘空间。CentOS 7 默认 box 可能只有 10GB,跑几个容器就满了。Vagrantfile 里可以指定磁盘大小,但 VirtualBox provider 需要装vagrant-disksize插件。或者干脆选一个磁盘大点的 box。

第四个坑是 SSH 端口冲突。如果你同时跑多个 Vagrant 项目,默认 22 转发端口会撞。Vagrant 一般会自动处理,但偶尔抽风,vagrant reload一下就好。

6. 环境重建与日常维护建议

实验环境最大的价值是可重建。我习惯把整个 tf-lab 目录用 git 管理,Vagrantfile 和 scripts 都提交。每次想重来,直接vagrant destroy -f && vagrant up,十几分钟又是一套干净环境。

日常维护上,几个命令用得最多:

vagrant status # 看三台机器状态 vagrant ssh tf-control # 进控制节点 vagrant halt # 优雅关机 vagrant suspend # 挂起,省内存 vagrant resume # 恢复

如果只是想临时释放内存,vagrant suspend比halt快,恢复后容器状态还在。但 suspend 时间长了 VirtualBox 可能出问题,超过一天建议 halt。

快照功能也值得用。在环境完全跑通、验证无误后,打个快照:

vagrant snapshot save clean-state

后面实验做坏了,vagrant snapshot restore clean-state秒回干净状态,比重新 up 快得多。

最后再分享一个小技巧:如果你经常需要调整 instances.yaml 重新部署,可以把部署命令封装成脚本,放在控制节点的 /usr/local/bin 下,比如tf-redeploy。这样不用每次记那一长串 python 命令,也不怕忘了激活虚拟环境。环境这东西,越顺手越愿意折腾,越折腾越能摸清它的脾气。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进