宿主机系统为Macos或者Windows时,docker容器的网络是怎么走的?
1. 核心架构差异:俄罗斯套娃 (The Virtual Machine Layer)
-
Linux (原生):Docker 容器只是宿主机内核里的一个隔离进程。容器和宿主机共用一个内核。
-
Mac/Windows (Docker Desktop):
-
macOS 和 Windows 的内核(XNU/NT)根本看不懂 Linux 的 Namespaces 和 Cgroups。
-
解决方案:Docker Desktop 会在你的电脑里悄悄启动一个 轻量级 Linux 虚拟机 (VM)。
-
Mac: 使用 HyperKit 或 Apple Virtualization Framework。
-
Windows: 使用 WSL 2 (Windows Subsystem for Linux 2)。
-
真相:你的所有容器,其实都不是跑在 Mac/Windows 上,而是跑在这个隐藏的 Linux VM 里。
2. 流量漂流记 (Mac/Windows 版)
我们再把刚才的“访问 Google”大戏演一遍。这次场景更复杂,多了个“中间人”。
场景设定:
- 容器:
172.17.0.2(跑在 Linux VM 里) - 中间层:Linux VM (隐藏的虚拟机)
- 宿主机:你的 MacBook (IP:
192.168.50.5) - 目标:Google (
8.8.8.8)
🎬 第一幕:容器到 VM (和 Linux 一样)
- 容器发起请求。
- 包经过
veth到达 VM 内部的docker0网桥。 - VM 内核 查路由表,决定走 VM 的
eth0出去。 - 第一次 NAT:VM 内核把源 IP 从
172.17.0.2修改为 VM 的 IP。
🎬 第二幕:穿越次元壁 (VM 到 macOS) —— 关键差异
现在数据包到了 VM 的边界。怎么传给 macOS?
- 传统虚拟机 (Bridged):VM 有自己的虚拟网卡,像局域网里的另一台电脑。
- Docker Desktop (User-mode Networking / VPNKit):
- 为了省电和网络兼容性(比如连 VPN 时),Docker Desktop 不使用传统的桥接。
- 它使用了一个用户态代理程序 (User-space Proxy),通常叫
VPNKit或slirp。 - 动作:
- 代理程序拦截 VM 发出的 TCP/IP 数据流。
- 解包:它把数据包拆开,拿到里面的 payload (数据)。
- 伪装:它在 macOS 上发起一个新的 Socket 请求(就像普通的 Mac App 一样)。
- 发送:macOS 内核看到的是“Docker Desktop.app”在请求 Google,而不是什么容器在请求。
🎬 第三幕:macOS 到互联网
- macOS 内核处理请求,通过 WiFi/有线网卡发出。
- 这一步就和你在 Mac 上用 Chrome 上网完全一样了。
3. 反向访问的痛点 (Inbound & Host Mode)
这就解释了 Mac/Windows Docker 的两大怪象:
怪象 A:为什么我 Ping 不通容器 IP?
- 现象:在 Linux 上,你可以
ping 172.17.0.2。在 Mac 上,直接 timeout。 - 原因:
- 这个
172.17.x.x是隐藏 VM 里的私有网段。 - macOS 的路由表里根本没有去往
172.17.0.0的路由规则。 - 而且,由于中间隔着那个“代理程序”,并没有一条直接的虚拟网线连着 Mac 和 VM。
怪象 B:为什么 --network host 不生效?
- 现象:你在 Linux 上用 host 模式跑 Nginx,直接访问
localhost:80就行。在 Mac 上跑,访问localhost:80却打不开。 - 原因:
--network host的意思是:“让容器共用宿主机的网络栈”。- 但是在 Mac 上,“宿主机”其实是那个 Linux VM!
- 所以,Nginx 实际上是监听了 Linux VM 的 80 端口,而不是你 MacBook 的 80 端口。
- 你需要显式地用
-p 80:80,让 Docker Desktop 的代理程序把 Mac 的 80 转发进 VM 的 80。
4. Windows (WSL 2) 的特殊情况
WSL 2 稍微改进了一点:
- Hyper-V 虚拟交换机:WSL 2 会在 Windows 上创建一个虚拟网卡 (vEthernet)。
- 互通性:
- Windows 可以 Ping 通 WSL 2 里的 IP(因为有路由自动配置)。
- 但是,
localhost转发依然有时会有 bug(虽然微软做了 "Localhost Forwarding" 魔法,让你可以用 localhost 访问 WSL 里的服务,但本质还是端口转发)。
📝 “钓鱼”问题
Q: 既然 Mac 上的 Docker 是跑在虚拟机里的,为什么我在 docker run -v /Users/me/code:/app 时,容器能看到我很 Mac 上的文件?虚拟机不是磁盘隔离的吗?
A: (满分回答)
“这也是 Docker Desktop 的魔法之一。
它使用了 virtio-fs (Linux 5.x+) 或者旧版的 gRPC FUSE 文件系统共享技术。
当容器读取/app时,请求先发给 Linux VM 内核,内核通过 virtio 管道把文件请求转发给 macOS 宿主机进程。
macOS 进程读取实际的硬盘文件,再把数据通过管道传回 VM。
这就是为什么 Mac 上 Docker 的磁盘 I/O 性能通常比原生 Linux 慢很多的原因——因为每一次读写都要跨越 VM 边界进行 IPC 通信。”
🛠️ 今日总结
- Linux: 原生支持,性能最好,IP 直通,网络逻辑清晰。
- Mac/Win: 套娃架构 (VM)。
- 网络靠 代理 (Proxy/VPNKit) 转发,而不是直接路由。
- 磁盘靠 跨 OS 文件共享 (virtio-fs)。
- 代价:I/O 性能损耗,Host 模式失效,Ping 不通容器 IP。
理解了这个,你就明白了为什么生产环境一定要用 Linux,而 Mac/Win 只能做开发环境。

浙公网安备 33010602011771号