记一次“套娃”式家庭网络折腾:从飞牛NAS到虚拟机,Istore再到Docker
一、故事的开始——我那位同学的“逆天”玩法
前几天和朋友聊起家庭网络改造的事,他突然跟我说了一个让我愣了几秒的操作:
“我有一台飞牛NAS,底层是Linux。我在上面用KVM跑了一个虚拟机,里面装了个软路由系统。然后,我还在这个软路由系统里用Docker跑了一些服务……”
我当时第一反应是:“你可真能折腾。”
但转念一想,这个“套娃”其实挺有意思的。它把虚拟化、网络、容器三层技术硬是叠在了一起:
-
宿主机层:飞牛NAS,本质是一个Debian系的Linux系统,自带KVM虚拟化能力。
-
虚拟机层:在这台NAS上用KVM跑了一个完整的软路由系统(比如iStoreOS )。
-
容器层:在软路由系统内部,又用Docker部署了一些插件和服务。
这套玩法其实在发烧友圈子里并不罕见。NAS性能过剩了,总想物尽其用,把软路由也集成进去,顺便还能用Docker扩展一些功能,比如去广告、DNS加速、内网穿透等等。
但问题也随之而来——这么一层套一层,网络到底是怎么连通的?会不会每层都做一次NAT,最后慢得没法用?
我决定帮他梳理一下这背后的虚拟化网络原理。
二、把拆开看:三层“套娃”的网络走向
我们先画出他的实际网络路径(假设主路由地址为 192.168.1.1):
关键问题在于:NAS里的KVM创建虚拟机时,选了哪种网络模式?
情况一:如果选的是“NAT模式”
虚拟机被分配一个内网IP,比如 192.168.122.100,和NAS不在同一网段。此时:
-
NAS通过NAT引擎把虚拟机的流量转成NAS自己的IP发出去。
-
数据包出来时,源IP已经变成
192.168.1.100。 -
如果这个软路由还要作为旁路由给家里其他设备用,其他设备根本访问不到这个
192.168.122.x的网络——它藏在NAT后面。 -
即便强行在NAS上加端口转发让设备能指过去,数据包每过一层就要做一次SNAT,路径变成:设备→主路由→NAS(NAT)→虚拟机(NAT或者再MASQUERADE)→Docker容器。NAT确实会套两层甚至三层。
这种模式下,虽然上网本身通常还能跑通(因为NAT本来就是干这个的),但旁路由的“透明代理”功能就麻烦了——源IP被层层改掉,软路由没法正确区分来自不同设备的请求,策略路由、流量统计全部乱套。
结论:NAT模式不适合旁路由场景。
情况二:如果选的是“桥接模式”(唯一正确的选择)
在KVM的配置中,把虚拟机的网卡直接桥接到NAS的物理网卡(Linux下通常用 br0 这个桥接设备)。此时:
-
虚拟机直接出现在物理局域网里,拿到一个
192.168.1.x的地址(比如192.168.1.2)。 -
从二层的角度看,它和NAS、主路由、以及其他设备都在同一个广播域,相当于一根网线直接插在主路由的交换机上。
-
家里其他设备只需要把自己的网关和DNS指向
192.168.1.2,流量就会自然而然地经过虚拟机里的软路由处理,NAS自身完全不参与NAT翻译。
这个时候,网络上的“套娃”感就消失了。NAS只是提供了一个物理载体和CPU、内存资源,网络层面和“搞一台独立小主机跑软路由”几乎没有区别。
三、那“多出来的NAT”到底在哪儿?
即便虚拟机用了桥接模式,很多教程在配置旁路由时,都会让加一条防火墙规则:
bash
iptables -t nat -I POSTROUTING -o br-lan -j MASQUERADE
这条规则会让软路由对所有从它出去的流量做一次源地址伪装。这确实是一层NAT。
为什么需要它?
原因是数据包的“来回路径不对称”。举个例子:
-
你的手机(IP: 192.168.1.50)把网关指向软路由(192.168.1.2),对外发出一个请求。
-
软路由对数据包做了一些“特殊处理”(比如转发到某个本地服务端口处理后再发出),此时数据包的源IP还是 192.168.1.50。
-
回程的数据包到达主路由时,主路由直接把包发给 192.168.1.50,而不会经过软路由。
-
192.168.1.50 收到这个回包时傻眼了——“我明明请求的是软路由,怎么回包直接来自主路由?”于是把它丢弃。
这个经典的“包过去了回不来”问题,解决办法就是在软路由上做一次MASQUERADE,把源IP改成192.168.1.2。回程的数据包到了主路由,主路由发现目的IP是192.168.1.2,自然会把它发回给软路由,软路由再根据conntrack表把包还给手机。
所以,这层NAT是旁路由工作模式决定的,跟NAS、虚拟机、Docker都无关。
而这一层NAT的性能开销非常小,Linux内核做一次SNAT通常只有微秒级甚至纳秒级的延迟,远小于加解密、磁盘IO等操作。
四、Docker在软路由里跑,又多了一层网络?
软路由系统(比如iStoreOS、OpenWrt)本身就是一个完整的Linux,它支持Docker。在Docker里启动一个容器时,默认用的是 docker0 网桥,走的是NAT模式(所以你从容器内部访问外部没问题,但外部访问容器需要端口映射)。
如果是在这个软路由的Docker里跑Web服务、去广告、DNS解析等,这些服务暴露的端口直接在软路由的局域网IP(192.168.1.2)上,局域网内设备直接访问,没有任何问题。
但如果Docker容器里也要启用透明代理(TPROXY),那就稍微复杂一些——容器默认网络模式下没有访问宿主机网络栈的完整能力,大概率需要配置 macvlan 或者 host 网络模式,给容器一个独立的局域网IP。这个属于容器网络的另一个话题,不是本文的重点,但至少说明:Docker层的网络模式同样可以选择,不是非得NAT不可。
五、总结:这个“套娃”到底套了几层NAT?
对于我那位同学的“逆天”玩法,最终答案是:
如果虚拟机配置正确(桥接模式),那么整个链路上,相对普通设备上网,仅仅多了旁路由自身的那一层MASQUERADE。NAS虚拟化层和Docker容器层都不额外引入NAT。
具体的堆栈情况如下:
| 层级 | 网络模式 | 是否引入NAT |
|---|---|---|
| 飞牛NAS宿主机 | 桥接(br0)绑定物理网卡 | 否 |
| KVM虚拟机 | 使用桥接网络,获得局域网IP | 否 |
| 软路由系统 | 旁路由模式,启用MASQUERADE | 是(正常需求的1层) |
| Docker容器 | 默认bridge/NAT模式 | 是(但仅容器出口,不影响旁路由透明代理) |
他听完之后说:“原来我要做的就是把KVM的网络改成桥接,其他都是正常操作。”
我说:“对,就这么简单。”
六、写在最后
家庭网络折腾,很多时候都是在“能用”和“最优”之间摸索。当我们在一个硬件上叠加多种技术时,最重要的是理解每一层的数据流向,而不是被“套娃”这个词吓住。
这个同学的做法看似复杂,其实踩的就是“虚拟网络模式”这个经典知识点。希望这篇文章能给有类似需求的朋友一些启发:
-
搞清网络路径,比纠结性能更重要。
-
桥接模式,是旁路由场景下虚拟机的唯一正确答案。
-
NAT本身不可怕,可怕的是不明白它为什么存在。
折腾不息,理解不止。
更多推荐
所有评论(0)