容器的网络是 Docker 里最像「魔法」的部分:容器里 ifconfig 看到的网卡哪来的、-p 映射怎么实现的、为什么同一网络里的容器能用名字互访。本章把这些魔法全部落地成 Linux 的普通机制(veth、bridge、iptables,和第 5 章 KVM 网络是同一套零件),再把三种网络模式、端口映射、自定义网络的名字解析讲透。
新建容器时,Docker 干了三件事,每件都能在宿主机上验证:
ip link show docker0 可见)相当于容器世界的交换机,网段默认 172.17.0.1/16。容器 A(eth0)═══ veth ═══┐
容器 B(eth0)═══ veth ═══┼── docker0 ── NAT(iptables)── 外网
│
172.17.0.1
这和第 5 章 KVM 的 virbr0 是同一套原理(veth + bridge + NAT),Docker 换了个名字重演。验证:veth 那端在宿主机显示为 vethxxxxx@if2(ip link 里一堆),docker exec 容器 ip a 和宿主机 ip link 对照,一根虚拟网线的两端。到这一步,「容器网络」就祛魅了:没有新机制,全是 Linux 网络的老零件组合。
| 模式 | 实现 | 用途 |
|---|---|---|
| bridge(默认) | veth + docker0(或自定义网桥)+ NAT | 绝大多数场景 |
| host | 容器直接用宿主网络栈(无隔离) | 性能敏感(负载均衡、抓包工具) |
| none | 只有 lo,没有 eth0 | 网络完全自定义(特殊实验) |
docker network ls # bridge/host/none 三个内置 + 你自建的
docker run -d --network host nginx # 直接监听宿主 80 端口,无需 -p
host 模式省掉 NAT 和 veth(性能最好,端口就是宿主端口),代价是零隔离(容器和宿主共享端口空间,冲突照样冲突)。安全敏感环境禁用;日常用 bridge。
docker run -d -p 8080:80 nginx # 宿主 8080 → 容器 80
docker run -d -p 127.0.0.1:8080:80 nginx # 只绑本机回环(更安全)
docker run -d -P nginx # -P 大写:自动分配宿主端口
第 9 章讲过方向别写反,这里补实现原理:-p 8080:80 就是宿主机 iptables 里加了一条 DNAT 规则(目的地址转换,计网课第 5 章的 NAT 类型),外部访问宿主:8080 的包被改写目的地后送进容器。验证:sudo iptables -t nat -nL DOCKER | grep 8080。所以端口映射的数量、冲突、失效,最终都是 iptables 规则的事,docker port web 可查某容器的全部映射。
默认 bridge 有个坑:容器之间只能按 IP 互访,不支持名字解析,而 IP 重启就变。自定义网络(Docker 自带内置 DNS 服务器)解决:
docker network create mynet
docker run -d --name web --network mynet nginx
docker run -d --name app --network mynet busybox sleep 3600
docker exec app ping web # 直接 ping 容器名!内置 DNS 解析
docker exec app wget -qO- http://web # 直接访问
这是微服务本地联调的标准姿势:web、db、redis 各自起容器挂同一网络,配置里写服务名,和写生产配置的逻辑一致(生产由 K8s 的 Service DNS 接管同一思想)。容器断开重连换 IP,名字解析自动跟着变。docker network inspect mynet 能看到网络里的全部成员及其 IP。
容器的常用操作小抄:docker network connect mynet web(把运行中的容器追加进网络)、disconnect、rm(先没容器在用)。
--add-host=host.docker.internal:host-gateway(Docker 20.10+,写法跨平台)。