本文旨在用纯软件的方法,来模拟真实的二层网络,我将采用Docker来模拟一台真实的主机,用OVS(Open vSwitch)来模拟一台真实的二层交换机,并构造一个稍有复杂性的网络拓扑。
注意,我所模拟出的网络,是可以和真实物理网络进行交互的,这其中的核心思想是将一台真实的主机看作一个网络,即在这台主机上利用纯软件的方法来构建一个网络。
本文不涉及任何编程实现,你只需要准备好VMware、Ubuntu22.04、Docker、OVS。
这比用网络模拟器好玩多了,因为无论是虚拟机还是Docker容器里,你都是真的可以跑你自己的东西的。
我建议你用和我一样的软件以及系统,避免一些不必要的问题。
首先用Vmware创建两台虚拟机,然后装上Ubuntu22.04,当然22.04是我实操时所使用的版本,更换其他版本的Ubuntu是否也可行我没有验证,但理论上新一些的版本是没有问题的。
建议你给apt进行换源,以下我们安装必备软件时会使用包管理器,换源可以保证你有一个较好的体验
注意,我们需要把两台虚拟机的网卡连接模式改为桥接模式。

更改为桥接模式是为了让虚拟机和宿主机像是物理上处于同一交换域。
Docker这个网络上有很多帖子给出安装过程了,在此就不赘述了。
另外,如果你想在非root用户下使用Docker,可以考虑使用命令:
sudo usermod -aG docker $USER
否则,你必须在root下才能使用Docker。
OVS不需要安装其他复杂的组件,我们只需要最简单的即可完成需求。
shellsudo apt update sudo apt install -y docker.io openvswitch-switch
OVS的安装也比较简单,使用包管理器即可,安装完成后使用以下命令查看版本:
shellsudo ovs-vsctl --version
我的版本是2.17.9
创建Docker容器之前得先准备好镜像,目前因为一些众所周知的监管需求,我们没有办法使用官方镜像仓库来下载镜像了。
在此我推荐一个Github上的开源项目,里面给出了一些还可以用的镜像仓库:
https://github.com/dongyubin/DockerHub?tab=readme-ov-file
注意一下时效性,我从中随便挑选了一个(2025.11.21实测有效):https://1ms.run/
我们需要安装的镜像是alpine,该镜像包含了必要的网络命令,足够了
sudo docker pull docker.1ms.run/library/alpine:latest
查看当前的镜像信息:
shellsudo docker image list -a
现在我们的准备工作已经完成了,可以开始构建物理网络的仿真模拟了。
先来展示一下当前的主机情况:
我们先确定各个主机都能互相Ping通,因为虚拟机的网络配置都是桥接到A,逻辑上,这四台主机就像是连在一台交换机上的,即位于同一子网、交换域,所以任意一台主机发出的ARP是可以直接得到响应的,肯定是可以Ping通的。
A和B是两台不相同,物理上真实存在的互联主机。
注意,上述我提到了A和B是通过路由器相连的,你可能不具备这样的条件,事实上,A和B可以同时接到一台交换机上,也可以直接用网线直连,我们要的效果是两者处于同一交换域,并不需要路由器的三层转发功能,路由器在当前场景也只是退化当成交换机在用。
这里我引入了B,是为了验证我最开始提到的,模拟网络可以和真实网络进行交互。

最开始,我提到了模拟网络的核心思想,即将一台真实的主机看作一个网络,那逻辑上我们的网络拓扑图就会像下面这样:

真实情况是,C和D只是运行在A上的虚拟机而已,物理上不存在,物理上也不存在如上图所示的将它们连在一块的交换机,上图是一个用于辅助理解当前网络连接关系的逻辑图。
此时回过头来看,四者相互Ping通应该是一个很自然的事情,不难理解。
我们接下来会利用OVS,将主机C变成一个交换机,并在主机C上创建两个Docker容器,用于模拟真实存在的主机,注意,C和D地位是相同的,都是运行在A上的一模一样的Ubuntu虚拟机,任何在C上的操作,你都可以在D上使用。
那么网络拓扑会改变为:

那么现在的情况就是,多出了两台新的主机,即Docker容器1和容器2,原先的主机C现在已经不算是一台主机了(不作为主机参与到该交换域当中),它变成了交换机,用于将容器1和2连接到交换域当中。
根据逻辑拓扑图理解,容器1和容器2只要在网段192.168.1.0/24中,就应该可以和主机ABD互相Ping通。
首先创建一个OVS的网桥:
shellsudo ovs-vsctl add-br ovs-br1
在历史上,网桥就是一个简化版的交换机(仅有两个口的交换机),OVS当中的网桥并不是传统意义上的网桥,只是一个沿用了相同功能的概念。
然后我们可以给这个新创建出来的OVS网桥分配ip并启用:
shellsudo ip addr add 192.168.1.20/24 dev ovs-br1 sudo ip link set up dev ovs-br1
OVS网桥本身也可以看作一个设备,为它分配IP也是可以的,如果把它用在三层网络当中,它就可以充当网关的角色了,但现在我们只当它是一个交换机,我们只考虑二层网络。
现在我们利用先前下载的镜像docker.1ms.run/library/alpine:latest,创建两个容器,用来模拟真实主机,分别取名为host1和host2。
shellsudo docker run -d --name host1 --net=none docker.1ms.run/library/alpine:latest sleep infinity sudo docker run -d --name host2 --net=none docker.1ms.run/library/alpine:latest sleep infinity
查看当前的容器信息:
sudo docker container list -a
现在交换机和容器都创建完毕了,接下来就是将容器接入交换机了。
shellsudo ovs-docker add-port ovs-br1 eth0 host1 --ipaddress=192.168.1.21/24 --gateway=192.168.1.20 sudo ovs-docker add-port ovs-br1 eth0 host2 --ipaddress=192.168.1.22/24 --gateway=192.168.1.20
现在你可以进入到任意一个容器当中(我的环境中是host5,如果按照我先前的流程,那么应该是host1或host2),查看该容器的ip配置,是否一致。
shellsudo docker exec -it host1 /bin/sh --现在已经进入容器内部了-- ip a Ctrl+D 退出容器 --现在已经从容器内退出了--

那么现在在虚拟机(主机C)侧网络设备是什么情况呢?如下所示:

此时,你要是想让其他主机和主机C交互,你需要使用IP192.168.1.20,也就是我们分配给OVS网桥的IP
意思就是,在宿主机(主机A),或是其他主机(B/D),你需要Ping 192.168.1.20才可以通,而不是先前的192.168.1.6。

现在我们尝试在主机A和主机D,Ping一下我们刚刚创建的容器:

可以看出,主机A和D都是可以和容器进行互通的,也可以和主机C(或者说是现在的连接了两个容器的交换机)互通。
你还可以在主机B试一试,应该也是可以Ping通容器和主机C的,这样虚拟网络和真实网络的互通性也就验证了。
至此,我们的模拟网络就搭建完毕了。
如果你感兴趣的话,还可以在主机C里面用tcpdump抓一下vmware虚拟出的网卡的数据,这样可以获取到主机ABD和容器的交互数据,在我这里是ens33
shellsudo tcpdump -i ens33 icmp

这里我只抓了Ping的帧(ICMP),可以看出其他主机和容器的通信都是正常的。
那如果说你抓的是OVS网桥ovs-br1,那是什么数据呢?答案是其他主机或容器和主机C的交互数据。

不过,容器和容器之间的交互数据应该怎么抓?
在主机C当中使用命令ip a
可以看到有一些随机字符组成的网卡名称,这就是容器的接口,抓这个接口上的数据即可

这种随机字符组成的网卡是什么时候出现的呢?答案是在我们执行了以下命令后出现的(也就是将容器连接到交换机的时候):
shellsudo ovs-docker add-port ovs-br1 eth0 host1 --ipaddress=192.168.1.21/24 --gateway=192.168.1.20
你可以留心一下,在执行这条命令前后用ip a对比一下,是不是多出来了一个新的网络接口。
这个网络拓扑不是特别复杂,但是足够了,已经把基本的二层网络原理都涵盖到了,更复杂的网络无非就是继续套娃。
对于主机C,也就是那个变成了交换机的虚拟机,我们也仅仅只是创建了一个OVS网桥,事实上,多创建几个也可以,这样的话一台主机就代表了一个更加复杂的网络,这也是我们的核心思想,把主机看作网络。
目前还没有考虑交换机级联和环网的情况,这是进阶的场景,OVS是支持生成树协议的,所以环网不必担心,当然我会在实测后再讨论相关细节。
本文作者:Test
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!