1. 为什么要在虚拟化环境里跑 Tungsten Fabric
如果你正在接触 SDN 控制器、云网融合或者多租户网络虚拟化,Tungsten Fabric 这个名字大概率绕不开。它最早由 Juniper 开源,后来捐给 Linux 基金会,核心定位是一套面向云原生和多云场景的网络虚拟化与控制平面方案,能同时管理虚拟机和容器的工作负载,提供租户隔离、安全策略、路由分发、负载均衡等一整套能力。说白了,它想解决的就是"在共享的物理网络上,给每个租户切出一张独立、可编程、可策略化的虚拟网络"这件事。
但真到动手环节,很多人第一步就卡住了。Tungsten Fabric 的完整部署通常需要一组节点:控制节点、计算节点、网关节点,还要配 SDN 网关、vRouter 内核模块、消息队列、数据库等一堆组件。如果你只是想先把它跑起来看看架构长什么样、控制台长什么样、vRouter 怎么和控制器握手,直接上物理机或者云主机集群,成本高、周期长、清理麻烦。这时候用 Vagrant 在本地拉起一套虚拟机集群,就是性价比最高的选择。
Vagrant 的价值在于它把"虚拟机编排"这件事变成了一个可版本化、可复现的配置文件。你写一个 Vagrantfile,声明要几台机器、什么系统、多少内存、网络怎么连,然后一条vagrant up就能把整套环境拉起来。对 Tungsten Fabric 这种多节点、组件多、依赖复杂的系统来说,Vagrant 让你可以在几分钟内得到一个干净、隔离、随时能推倒重来的实验环境。这篇文章就是围绕"用 Vagrant 安装 Tungsten Fabric"这个具体场景,把选型理由、环境准备、配置细节、安装流程、踩坑排查和验证方法完整讲一遍,适合刚接触 SDN 控制器的运维和开发同学,也适合想快速搭个实验环境做验证的老手。
需要先说明一点:Tungsten Fabric 的版本迭代比较快,不同版本对内核、Docker、Python 版本的要求差异不小。本文基于社区里比较常见的单控制节点加多计算节点的实验拓扑来展开,具体版本号我会在关键位置标注,你实际操作时以自己选定的发行版文档为准。另外,整套环境对宿主机资源有要求,建议至少 16GB 内存、4 核以上 CPU、50GB 以上空闲磁盘,否则跑起来会非常吃力。
2. 宿主机环境与 Vagrant 基础准备
2.1 虚拟化后端的选择:VirtualBox 还是 libvirt
Vagrant 本身只是个编排层,真正干活的是它背后的虚拟化 provider。最常见的两个选择是 VirtualBox 和 libvirt(配合 KVM/QEMU)。这两者在 Tungsten Fabric 实验环境里的表现差别挺明显,我先把结论摆出来:Linux 宿主机优先用 libvirt,Windows 或 macOS 宿主机基本只能用 VirtualBox。
VirtualBox 的优点是跨平台、安装简单、图形界面友好,缺点是网络性能和嵌套虚拟化支持一般。Tungsten Fabric 的 vRouter 需要在内核里做转发,对网络性能敏感,VirtualBox 的 NAT 和 host-only 网络在高负载下容易成为瓶颈。libvirt 基于 KVM,性能接近原生,网络模型也更灵活,支持桥接、NAT、隔离网络等多种模式,适合做多节点实验。
如果你在 Linux 上,安装 libvirt 相关组件的命令大致如下(以常见的 Debian 系为例):
sudo apt update sudo apt install -y qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils sudo apt install -y vagrant vagrant-libvirt装完之后用vagrant plugin list确认vagrant-libvirt插件已经就位。如果没装上,可以手动执行vagrant plugin install vagrant-libvirt。这里有个坑:vagrant-libvirt插件对 Ruby 版本和 libvirt 开发库有依赖,如果报编译错误,先装libvirt-dev和ruby-dev再重试。
Windows 用户就老老实实用 VirtualBox,记得同时装好 VirtualBox Extension Pack,否则 USB 和部分网络功能会受限。macOS 用户如果用的是 Apple Silicon,要注意 VirtualBox 对 ARM 架构的支持一直不太理想,这种情况建议考虑用 QEMU 或者干脆找一台 x86 的 Linux 机器来做实验。
2.2 Vagrant box 的挑选与镜像来源
Vagrant 的虚拟机镜像叫 box。Tungsten Fabric 对操作系统有明确要求,社区文档里常见的是 Ubuntu 18.04/20.04 和 CentOS 7。选 box 的时候有几个硬性指标要看:
- 内核版本要匹配 vRouter 内核模块的编译要求,太新或太旧都可能编译失败。
- 要能方便地安装 Docker,因为 Tungsten Fabric 的很多组件是容器化部署的。
- 网络配置要支持多网卡,实验拓扑里通常需要管理网、控制网、数据网分离。
我一般会选官方的ubuntu/bionic64或者generic/ubuntu2004这类维护活跃的 box。选之前先看一眼 box 的更新时间和下载量,太冷门的 box 往往缺驱动或者网络配置有坑。可以用下面的命令查看和添加 box:
vagrant box list vagrant box add generic/ubuntu2004提示:box 下载速度受网络影响较大,如果卡住可以配置国内镜像源,或者提前用下载工具把 box 文件拉下来,再用
vagrant box add指定本地文件路径添加。
2.3 资源规划:别让内存成为瓶颈
多节点实验最容易被低估的就是内存。Tungsten Fabric 的控制节点要跑 Cassandra、Zookeeper、RabbitMQ、Redis、Kafka、控制器本身等一堆服务,光控制节点就建议给 8GB 内存。每个计算节点跑 vRouter 和若干测试容器,建议 4GB 起步。如果你规划一控两算,那就是 8+4+4=16GB,再加上宿主机系统本身的开销,宿主机至少要有 24GB 内存才比较从容。
CPU 方面,每个节点给 2 核基本够用,但编译 vRouter 内核模块的时候会吃满 CPU,建议宿主机留出余量。磁盘用动态分配的虚拟磁盘就行,但要注意 Vagrant 默认的磁盘扩容比较麻烦,最好在 Vagrantfile 里就把磁盘大小设够,比如每节点 20GB。
下面这张表是我常用的一个资源分配参考,你可以根据自己的机器情况调整:
| 节点角色 | 内存 | CPU | 磁盘 | 说明 |
|---|---|---|---|---|
| 控制节点 | 8GB | 2核 | 20GB | 跑数据库、消息队列、控制器 |
| 计算节点 | 4GB | 2核 | 20GB | 跑 vRouter 和测试负载 |
| 网关节点 | 4GB | 2核 | 20GB | 可选,做跨子网转发 |
| 宿主机 | 24GB+ | 4核+ | 50GB+ | 留足余量给虚拟化开销 |
3. 编写 Vagrantfile:把多节点拓扑声明出来
3.1 网络模型设计:三张网卡怎么分
Tungsten Fabric 的实验环境里,网络划分是核心。通常需要三类网络:管理网用于 SSH 和 API 访问,控制网用于控制器和 vRouter 之间的 XMPP 通信,数据网用于租户流量的 overlay 转发。在 Vagrant 里,我们可以用 private network 来模拟这些隔离网络。
一个常见的做法是给每个节点配三块网卡:第一块是 Vagrant 默认的 NAT 网卡,用于访问外网下载依赖;第二块是管理网,网段比如 192.168.56.0/24;第三块是控制/数据网,网段比如 192.168.100.0/24。这样控制器和 vRouter 之间的通信走内网,不会和外网流量混在一起。
在 Vagrantfile 里,private network 的配置大概长这样:
config.vm.network "private_network", ip: "192.168.56.10", virtualbox__intnet: "mgmt" config.vm.network "private_network", ip: "192.168.100.10", virtualbox__intnet: "ctrl"用 libvirt 的话,写法略有不同,通常用libvirt__network_name指定网络。这里要注意,VirtualBox 的intnet是内部网络,只有同一 intnet 的机器能互通,正好用来做隔离。libvirt 则更推荐预先定义好网络再引用。
3.2 用循环批量定义节点
手写每个节点的配置既啰嗦又容易出错,Vagrantfile 支持 Ruby 语法,可以用循环来批量生成。下面是一个简化后的结构示例:
nodes = [ { name: "tf-control", ip: "192.168.56.10", ctrl_ip: "192.168.100.10", mem: 8192, cpu: 2 }, { name: "tf-compute1", ip: "192.168.56.11", ctrl_ip: "192.168.100.11", mem: 4096, cpu: 2 }, { name: "tf-compute2", ip: "192.168.56.12", ctrl_ip: "192.168.100.12", mem: 4096, cpu: 2 } ] Vagrant.configure("2") do |config| config.vm.box = "generic/ubuntu2004" nodes.each do |node| config.vm.define node[:name] do |n| n.vm.hostname = node[:name] n.vm.network "private_network", ip: node[:ip] n.vm.network "private_network", ip: node[:ctrl_ip] n.vm.provider "virtualbox" do |vb| vb.memory = node[:mem] vb.cpus = node[:cpu] end end end end这段配置里,每个节点拿到两个私有 IP,一个走管理网段,一个走控制网段。hostname 设成节点名,方便后面用 Ansible 或者脚本批量操作。实际用的时候,你还可以在循环里加上 provision 脚本,让节点起来后自动装依赖、配 hosts、关防火墙。
3.3 主机名解析与免密登录的预处理
Tungsten Fabric 的组件之间大量依赖主机名通信,所以每个节点的/etc/hosts必须能正确解析所有节点。最省事的办法是在 provision 阶段统一写入。可以在 Vagrantfile 里加一段 shell provision:
cat >> /etc/hosts <<EOF 192.168.56.10 tf-control 192.168.56.11 tf-compute1 192.168.56.12 tf-compute2 EOF免密登录也是同理,控制节点要能 SSH 到计算节点做部署,提前把公钥分发好能省很多事。Vagrant 默认用vagrant用户和 insecure_private_key,你可以直接用这个密钥做节点间互信,也可以生成新密钥再分发。我一般会在 provision 里生成一对新密钥,把公钥追加到所有节点的 authorized_keys,私钥放在控制节点上。
注意:生产环境绝对不要用默认的 insecure key,实验环境图省事可以,但心里要清楚这只是临时方案。
4. 安装 Tungsten Fabric:从依赖到控制器就绪
4.1 系统层依赖:内核模块编译的前置条件
Tungsten Fabric 的 vRouter 是一个内核模块,编译它需要内核头文件和编译工具链。在 Ubuntu 上,这一步经常出问题,因为默认安装的内核头文件版本可能和当前运行内核不一致。稳妥的做法是先确认内核版本,再装对应版本的头文件:
uname -r sudo apt install -y linux-headers-$(uname -r) build-essential dkms如果linux-headers-$(uname -r)找不到,说明软件源里没有对应版本,这时候要么升级内核,要么换一个头文件齐全的 box。这是新手最容易卡住的地方之一,报错信息通常是"找不到内核头文件"或者"模块编译失败",看到这类错误先查内核版本匹配问题。
除了内核头文件,还需要 Docker。Tungsten Fabric 的很多组件以容器形式运行,Docker 版本建议用 19.03 或 20.10 这类经过验证的稳定版。安装完 Docker 后,记得把当前用户加入 docker 组,否则每次都要 sudo:
sudo usermod -aG docker $USER newgrp docker4.2 获取部署脚本与配置文件
Tungsten Fabric 社区提供了一套基于 Ansible 的部署工具,通常放在一个叫tf-dev-env或者类似名字的仓库里。部署的核心是一个 inventory 文件加一组 playbook。inventory 文件里要声明控制节点、计算节点、网关节点的主机名和 IP,以及各种角色参数。
一个简化的 inventory 结构大概是这样:
[control] tf-control ansible_host=192.168.56.10 [compute] tf-compute1 ansible_host=192.168.56.11 tf-compute2 ansible_host=192.168.56.12 [all:vars] ansible_user=vagrant ansible_ssh_private_key_file=~/.ssh/id_rsa这里的关键是把节点角色分清楚。控制节点承担数据库、消息队列、控制器等角色,计算节点只跑 vRouter。如果资源紧张,也可以把控制和计算合在一台机器上做 all-in-one,但那样就失去了多节点实验的意义,vRouter 和控制器的交互过程看不清楚。
4.3 执行部署:分阶段推进而不是一把梭
Ansible playbook 通常支持按标签分阶段执行。我的建议是不要一上来就跑全量,而是分三步走:先做基础环境准备,再装控制平面,最后装计算节点。这样出问题的时候容易定位。
第一步,基础准备,包括装依赖、配 hosts、调内核参数、加载必要模块。第二步,控制平面部署,会拉起 Cassandra、Zookeeper、RabbitMQ、Redis、Kafka 和控制器容器。这一步耗时最长,也最容易出问题,因为容器镜像拉取、数据库初始化都可能失败。第三步,计算节点部署,编译并加载 vRouter 内核模块,启动 vRouter agent,让它去连控制器。
执行命令大致是:
ansible-playbook -i inventory site.yml --tags prepare ansible-playbook -i inventory site.yml --tags control ansible-playbook -i inventory site.yml --tags compute每跑完一步,都去对应节点上用docker ps看看容器是不是都起来了,用docker logs看关键容器有没有报错。控制节点上如果 Cassandra 没起来,后面的控制器肯定起不来,所以顺序不能乱。
4.4 容器镜像拉取慢的应对办法
部署过程中最耗时的环节往往是拉容器镜像。Tungsten Fabric 的镜像数量多、体积大,网络不好的话可能卡几十分钟甚至超时。几个应对思路:一是提前在能访问镜像仓库的机器上把镜像拉下来,导出成 tar 包再导入到各节点;二是配置镜像加速;三是把镜像仓库地址换成离你更近的源。
导入镜像的命令是:
docker load -i tf-images.tar如果部署脚本支持指定本地镜像仓库,那就更省事,把所有镜像推到本地 registry,然后让 inventory 指向这个 registry。这一步在离线环境里几乎是必须的。
5. 部署后的验证与常见故障排查
5.1 控制平面健康检查
部署完成后,第一件事是确认控制平面是否健康。控制节点上应该能看到一组容器在运行,包括控制器、数据库、消息队列等。用docker ps看容器状态,重点看有没有反复重启的容器。反复重启通常意味着配置错误或者依赖没就绪。
控制器起来后,可以通过它的 API 或者命令行工具查看节点注册情况。正常情况下,计算节点上的 vRouter agent 会主动连上控制器,控制器会把它标记为活跃。如果计算节点一直不出现,八成是 XMPP 连接没建立起来,要去计算节点上看 vRouter agent 的日志。
5.2 vRouter 内核模块加载失败的排查链路
vRouter 内核模块加载失败是最高频的问题,我把完整的排查链路列一下,你按顺序走:
- 先看
dmesg有没有模块加载相关的报错,比如版本不匹配、符号找不到。 - 确认内核头文件版本和运行内核一致,
uname -r和/lib/modules下的目录要对得上。 - 检查模块是否已经编译出来,通常在
/lib/modules/$(uname -r)/extra/或者源码目录下。 - 用
modprobe vrouter手动加载,看具体报什么错。 - 如果报"模块签名"问题,检查 Secure Boot 是否开启,实验环境可以在 BIOS 里关掉。
- 如果报依赖模块缺失,用
modinfo vrouter看 depends 字段,把依赖模块先加载上。
这个链路走下来,基本能覆盖九成以上的模块加载问题。我遇到过最隐蔽的一次是内核小版本升级后头文件没跟着更新,表面看版本号一样,实际编译出来的模块和运行内核不匹配,最后重装头文件才解决。
5.3 节点间通信与防火墙
多节点环境里,节点之间的端口必须通。Tungsten Fabric 用到的端口不少,XMPP、HTTP、数据库、消息队列各有各的端口。Ubuntu 默认可能开着 ufw,CentOS 默认开着 firewalld,实验环境里最省事的做法是直接关掉防火墙,或者把相关网段全部放行。
sudo systemctl stop ufw sudo systemctl disable ufw关防火墙只是实验环境的权宜之计,真实环境一定要按最小权限原则精确放行。另外要确认net.ipv4.ip_forward是开启的,否则转发不通:
sudo sysctl -w net.ipv4.ip_forward=1这个参数最好写进/etc/sysctl.conf持久化,不然重启就丢了。
5.4 用测试负载验证 overlay 网络
控制平面健康、vRouter 加载成功,只能说明环境搭起来了,真正要验证的是 overlay 网络能不能通。做法是在两个计算节点上各起一个测试容器,接到同一个虚拟网络里,然后互相 ping。如果通,说明 vRouter 的隧道转发、控制器的路由下发都正常。
如果 ping 不通,排查顺序是:先看两个 vRouter 有没有建立隧道,再看控制器有没有把对方的路由下发过来,最后看安全策略有没有把流量挡掉。Tungsten Fabric 默认策略可能比较严格,实验阶段可以先放宽策略确认连通性,再逐步收紧。
6. 几个让我印象深刻的坑和应对经验
6.1 时间不同步导致证书校验失败
这个坑很隐蔽。虚拟机挂起再恢复之后,系统时间可能和宿主机偏差很大,而 Tungsten Fabric 组件之间用证书做认证,证书校验对时间敏感。时间偏差超过阈值,组件之间就会互相拒绝连接,日志里报的是证书错误,很容易误导人去查证书配置。解决办法是每个节点都装并启用时间同步服务,确保集群内时间一致。
sudo apt install -y chrony sudo systemctl enable --now chrony6.2 磁盘空间被容器日志悄悄吃满
跑一段时间后,控制节点可能突然各种服务异常,查半天发现是磁盘满了。Docker 的容器日志默认不限制大小,Tungsten Fabric 的组件日志量又不小,几天就能把 20GB 磁盘吃满。两个办法:一是给 Docker 配置日志轮转,在/etc/docker/daemon.json里加 log-driver 和 log-opts;二是定期清理无用镜像和停止的容器。
{ "log-driver": "json-file", "log-opts": { "max-size": "50m", "max-file": "3" } }改完重启 Docker 生效。这个配置建议在环境搭好之后就加上,别等磁盘满了再补救。
6.3 快照与重建:把实验环境当代码管理
Vagrant 有个很好用的能力是快照。在关键节点做完基础配置、装好依赖之后,打个快照,后面实验搞砸了直接回滚,比重新vagrant up快得多。
vagrant snapshot save tf-control base-ready vagrant snapshot restore tf-control base-ready更进一步,把 Vagrantfile、inventory、provision 脚本全部纳入版本控制,整个实验环境就变成了可复现的代码。换一台机器,clone 下来vagrant up就能得到一模一样的环境。这是我在做多轮实验时最依赖的工作方式,强烈建议你也这么干。
6.4 资源超配导致宿主机卡死
最后一个坑是资源规划。我见过有人在一台 16GB 的笔记本上开一控三算,结果宿主机自己先卡死了。虚拟化是有开销的,给虚拟机的内存总和不能接近宿主机物理内存。留出至少 4GB 给宿主机系统,再考虑虚拟机的分配。如果实在资源紧张,就减少计算节点数量,或者把控制节点和计算节点合并,先跑通流程再说。
这套用 Vagrant 搭 Tungsten Fabric 实验环境的方法,我自己反复用过很多次,每次换机器或者换版本都会重新走一遍。它最大的价值不是省了买服务器的钱,而是让整个环境变得可丢弃、可重建、可版本化。你可以在上面大胆试各种配置,搞坏了回滚快照或者重新 up 就行,这种心理上的轻松感,才是做实验最需要的状态。