docker容器如何相互通讯?容器又是如何访问外部网络的?
这是一个非常经典且核心的 Docker 网络面试题。搞懂这个,你就能明白为什么有时候容器能 ping 通百度,却 ping 不通隔壁的数据库。
Docker 的网络模型可以想象成一个 “国中之国”。宿主机(你的电脑)是“大国”,Docker 引擎在里面建了一个“小国”(虚拟子网)。
我们分两个场景来讲:内政(容器互通) 和 外交(容器与外界互通)。
1. 容器之间怎么通讯?(内政)
默认情况下,容器运行在 Bridge 模式(网桥模式)。
🧱 核心设施:虚拟交换机 (docker0)
当 Docker 启动时,它会在宿主机上安装一个虚拟的“网线交换机”,名字通常叫 docker0。
- 所有容器启动时,都会被插一根虚拟网线(veth pair)到这个
docker0交换机上。 docker0会给每个容器发一个内部 IP(比如172.17.0.2,172.17.0.3...)。
📡 通讯方式 A:通过 IP 直连
- 原理:因为大家都在同一个交换机(
docker0)下,容器 A 只要知道容器 B 的 IP,喊一声就能听到(ARP广播)。 - 缺点:容器重启后 IP 会变!你不可能在代码里写死
172.17.0.x。
📡 通讯方式 B:通过服务名(DNS 自动发现)—— 推荐
这就是 docker-compose 的魔法所在。
- 原理:Docker 引擎内置了一个微型 DNS 服务器。
- 流程:
- 你的 Scheduler 容器想找 Jenkins,发请求给
http://jenkins:8080。 - 请求先被 Docker 的内置 DNS 拦截。
- DNS 查表:“哦,
jenkins这个名字对应的容器现在的 IP 是172.18.0.5”。 - 自动把请求转发到
172.18.0.5:8080。
- 场景:这就是为什么在你的
docker-compose.yml里,Scheduler 只要写http://jenkins就能连上,完全不用管 IP。
2. 容器和外界怎么通讯?(外交)
这里分为 “出去” 和 “进来” 两个方向。
📤 方向一:容器访问互联网(Outbound)
比如你的 Runner 容器里执行 pip install requests,需要去外网下载包。
- 机制:NAT (网络地址转换) / IP 伪装
- 过程:
- 容器:我想访问
8.8.8.8(Google)。 - docker0:发现这是外网地址,把它丢给宿主机。
- 宿主机(路由器):开启了 IP 转发功能。它把数据包抓过来,把 源 IP 从
172.17.0.x(内部 IP)悄悄改成 宿主机 eth0 的 IP(比如192.168.1.100)。这叫 SNAT (Source NAT)。 - 路由器/公网:看到的是宿主机在发请求,于是把数据回传给宿主机。
- 宿主机:收到回包,查表发现是刚才那个容器发的,再把包转回给容器。
- 结论:容器默认就能“出去”上网,靠的是宿主机当它的代理人。
📥 方向二:外界访问容器(Inbound)
比如你在浏览器里访问 Jenkins 的网页 http://localhost:8080。
- 机制:端口映射 (Port Mapping)
- 过程:
- 容器里的 Jenkins 监听的是容器内的
:8080。外界根本访问不到172.x.x.x这个私有网段。 - 我们在启动时使用了
-p 8080:8080。 - Docker 进程(准确说是
docker-proxy或者iptables规则)会在 宿主机的 8080 端口 进行监听。 - 当你访问宿主机 8080 时,流量被 Docker 拦截,通过 DNAT (Destination NAT) 强行把 目标 IP/端口 修改为
容器IP:8080,然后扔进docker0网桥。
- 结论:外界想进来,必须在宿主机“开门”(映射端口)。如果没有
-p,容器就是一座孤岛,只能出去,进不来。
📝 总结图谱 (面试脑图)
| 通讯场景 | 核心技术 | 关键命令/配置 | 类比 |
|---|---|---|---|
| 容器 <-> 容器 | Bridge (网桥) + DNS | docker-compose 服务名 |
公司内线电话,直拨分机号(名字) |
| 容器 -> 外网 | SNAT (源地址转换) | 默认开启 (IP Forwarding) | 员工通过公司总出口访问百度 |
| 外网 -> 容器 | DNAT (端口映射) | -p 8080:80 |
公司前台转接,打总机转给某员工 |
这就是为什么在 Host 模式 下快的原因:因为没有了中间的 NAT 转换和网桥转发,容器直接用宿主机的网卡,少了两层“手续”。

浙公网安备 33010602011771号