Docker 网络配置的使用
Docker 网络配置的使用
容器不是孤岛。绝大多数线上事故里,容器「跑起来了但连不上」,最后都落在网络上:容器之间互相解析不到名字、端口映射没生效、跨主机通不了、macvlan 抢了宿主机流量。这篇把 Docker 网络从模型到实操梳理一遍,重点放在「怎么配」和「配错了怎么查」。
一、先理解模型:容器网络是怎么搭起来的
Docker 的网络能力由 libnetwork 提供,遵循 CNM(Container Network Model)规范,三个核心概念:
- Sandbox:一个网络命名空间(netns),装着容器的网卡、路由表、iptables 规则
- Endpoint:虚拟网卡(veth),把 sandbox 接到某个网络上
- Network:一组可以互相通信的 endpoint 集合,由驱动实现(bridge、overlay、macvlan……)
落到 Linux 内核层面,创建一个默认 bridge 容器时发生了这些事:
- 创建 netns,里面只有
lo - 创建一对 veth:一端在宿主机
docker0上(如veth3f2a1b),一端在容器内改名eth0 - 从
docker0的网段给容器分配 IP,网关指向docker0 - 宿主机上写 iptables NAT 规则,实现出网 SNAT 和端口映射 DNAT
所以容器出网、容器互联、外部访问,本质都是宿主机上的一张 iptables/nftables 规则表在起作用。理解这点,排障时就不会只盯着容器内部看。
二、六种网络驱动,什么时候用哪个
| 驱动 | 场景 | 特点 |
|---|---|---|
bridge |
单机容器互联(默认) | 独立网段,NAT 出网,需端口映射 |
host |
追求极致网络性能 | 共享宿主网络栈,无隔离,端口直接占用 |
none |
完全隔离 | 只有 lo,适合离线计算、自定义网络注入 |
overlay |
跨主机通信(Swarm) | VXLAN 封装,多主机二层打通 |
macvlan |
容器要局域网真实 IP | 容器像物理机一样出现在交换机上 |
ipvlan |
类似 macvlan 但省 MAC | L2/L3 模式,适合 MAC 表容量受限环境 |
单机场景 90% 用 bridge,而且不要用默认的 bridge,要自建——原因见第四节。
三、日常命令速查
# 查看现有网络
docker network ls
# 看细节:网段、网关、接了哪些容器
docker network inspect mynet
# 创建自定义 bridge 网络
docker network create \
--driver bridge \
--subnet 172.20.0.0/16 \
--gateway 172.20.0.1 \
mynet
# 运行时接入网络
docker run -d --name web --network mynet nginx:alpine
# 给已运行的容器追加网络(热插拔,不用重启)
docker network connect mynet api
# 断开
docker network disconnect mynet api
# 清理没有被使用的网络
docker network prune
docker network inspect 的输出里,Containers 字段是排查「谁在这个网段里」最快的手段,IPAM.Config 则告诉你网段有没有和公司内网冲突。
四、默认 bridge 的三个坑
默认 docker0 网络(172.17.0.0/16)看起来开箱即用,但生产上问题不少:
1. 只能靠 IP 互访,没有名字解析
默认 bridge 上,容器 A 想访问容器 B,只能写 172.17.0.2 这种地址。IP 会变,配置就废了。老办法 --link 早已废弃,不要再用。
2. 所有容器挤在一个网段
无法按业务划分网段,也无法做网络层面的隔离。
3. 网段冲突
172.17.0.0/16 和公司 VPN、云上 VPC 撞车是常见事故。默认网段可以改(/etc/docker/daemon.json 里的 bip),但改完要重启 daemon,影响所有容器。
{
"bip": "10.20.0.1/24",
"default-address-pools": [
{ "base": "10.201.0.0/16", "size": 24 }
]
}
五、自定义 bridge:单机场景的标准答案
docker network create --driver bridge --subnet 172.20.0.0/16 mynet
docker run -d --name web --network mynet nginx:alpine
docker run -d --name api --network mynet --network-alias backend myapp:1.0
# 验证:直接用容器名访问
docker run --rm --network mynet curlimages/curl \
curl -s -o /dev/null -w "%{http_code}\n" http://web
自定义网络带来的关键能力:
- 内嵌 DNS:Docker 在
127.0.0.11跑了一个 DNS 服务,自动把容器名、--network-alias、--name解析成容器 IP。容器里的/etc/resolv.conf会指向它。 - 网络隔离:不同网络默认不通,前端网络和后端网络可以物理隔开。
- 独立网段:可以精确规划,避开内网冲突。
注意一个细节:只有自定义网络才有名字解析,默认 bridge 没有。这是新手最容易踩的坑。
用 Compose 编排时
services:
web:
image: nginx:alpine
networks: [frontend]
ports: ["8080:80"]
api:
image: myapp:1.0
networks: [frontend, backend]
db:
image: postgres:16-alpine
networks: [backend]
networks:
frontend:
backend:
internal: true # 无外网出口,只能内部访问
internal: true 是个被低估的配置:数据库所在网络彻底断掉出网能力,即便被拿下也传不出数据。
六、端口映射还是 host 网络
# 映射到所有网卡
docker run -d -p 8080:80 nginx
# 只监听 127.0.0.1,不对公网暴露
docker run -d -p 127.0.0.1:8080:80 nginx
# 随机端口
docker run -d -P nginx
端口映射的实现是 iptables 的 DNAT 规则,加上 docker-proxy 进程兜底(用户态转发,用于某些边界场景)。这意味着:
- 映射端口会绕过 UFW/firewalld 的常规规则,因为 Docker 直接往 nat 表的
DOCKER链里插规则。想限制来源,得在DOCKER-USER链里写。 -p 8080:80不写绑定地址时,默认监听0.0.0.0,公网可直接访问。永远显式写127.0.0.1:或内网地址。
--network host 则完全跳过 NAT:容器直接用宿主机的网卡和端口,性能最好,但端口冲突、隔离性差,同一端口只能跑一个实例。
docker run -d --network host nginx
# 直接监听宿主机 80
七、overlay:跨主机组网
单机 bridge 出了宿主机就断了。跨主机需要 overlay,底层是 VXLAN(UDP 4789)。
# 初始化 Swarm(overlay 依赖 Swarm 的 KV 存储)
docker swarm init --advertise-addr 192.168.1.10
# 在另一台机器加入
docker swarm join --token SWMTKN-1-xxxx 192.168.1.10:2377
# 创建可被普通容器使用的 overlay 网络
docker network create -d overlay --attachable --subnet 10.0.9.0/24 crossnet
# 两台机器上的容器现在可以直接用名字互访
docker run -d --name svc-a --network crossnet nginx:alpine
关键点:
--attachable必须加,否则只有 Swarm service 能用,docker run接不上- 需要放通 4789/udp(VXLAN)和 7946/tcp+udp(控制面)
- overlay 有封装开销,跨主机带宽敏感型业务要实测
不用 Swarm 也可以用第三方驱动(Calico、Flannel、Cilium),思路一样:把跨主机流量封装或路由过去。
八、macvlan:让容器成为局域网里的「一台机器」
有些场景容器必须拿局域网真实 IP——比如老系统靠 IP 白名单鉴权、设备发现依赖二层广播、需要被 DHCP 分配地址。
# 宿主机 eth0 所在网段 192.168.1.0/24
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 \
lan
docker run -d --name nas-agent --network lan --ip 192.168.1.50 nginx:alpine
容器直接出现在交换机上,局域网其他机器能 ping 通 192.168.1.50,没有 NAT。
两个必须知道的限制:
- 宿主机默认 ping 不通 macvlan 容器。因为流量从宿主机 eth0 出去后,交换机认为目标 MAC 在别处,不会回环。解决办法是给宿主机再加一个 macvlan 子接口:
ip link add macv0 link eth0 type macvlan mode bridge
ip addr add 192.168.1.60/24 dev macv0
ip link set macv0 up
ip route add 192.168.1.50/32 dev macv0
- 很多云厂商和交换机限制单端口 MAC 数量(反 MAC 欺骗),超过就丢包。云上环境用之前先确认,否则换 ipvlan 的 L3 模式更稳。
九、出问题了怎么查
网络排障有个固定顺序:容器内 → 宿主机 → 链路上。
# 1. 看容器 IP、网关、接了哪些网络
docker inspect -f '{{json .NetworkSettings.Networks}}' web | jq
# 2. 进容器的网络命名空间看网卡和路由(容器里通常没 ip 命令)
PID=$(docker inspect -f '{{.State.Pid}}' web)
nsenter -t $PID -n ip addr
nsenter -t $PID -n ip route
# 3. 看 DNS 解析
nsenter -t $PID -n cat /etc/resolv.conf
docker exec web nslookup api
# 4. 抓包,确认包到没到、回没回
nsenter -t $PID -n tcpdump -i eth0 -nn port 5432
# 5. 看宿主机 NAT 规则和计数
iptables -t nat -L DOCKER -n -v
iptables -t filter -L DOCKER-USER -n -v
# 6. 看 veth 两端是不是都 UP
ip -br link | grep veth
常见症状对照表:
| 症状 | 大概率原因 |
|---|---|
| 容器名解析失败 | 用了默认 bridge;或两容器不在同一自定义网络 |
| 端口映射不生效 | 绑定到了 127.0.0.1;或宿主机防火墙拦了 DOCKER-USER |
| 容器能出网但外部连不上 | 没做 -p;或安全组/防火墙未放通 |
| 跨主机不通 | 4789/udp、7946 未放通;overlay 没加 --attachable |
| 时通时不通 | 网段与内网/VPN 冲突,或 conntrack 表满 |
| 宿主机 ping 不通容器 | macvlan 回环限制 |
conntrack 表满值得单独提一句:高并发短连接场景下 nf_conntrack: table full, dropping packet 会让网络随机丢包,调大 net.netfilter.nf_conntrack_max 并缩短 nf_conntrack_tcp_timeout_time_wait 能缓解。
十、一份可以直接照抄的配置清单
- 不用默认 bridge,按业务自建网络,网段避开 172.17.0.0/16 和内网保留段
- 数据库等敏感服务单独放一个
internal: true网络,不给外网出口 - 端口映射永远显式写绑定地址,公网服务前面挂反向代理,只暴露 80/443
- 需要限流的入口在
DOCKER-USER链写规则,别指望 UFW 管住 Docker 的端口 - 生产跨主机优先考虑 Kubernetes + CNI,纯 Docker overlay 适合小规模或过渡期
- macvlan 上线前先确认交换机 MAC 限制,并在宿主机建好 macvlan 子接口
- 给关键容器配
--network-alias,避免服务名和容器名耦合 - 排障先
nsenter进 netns,比在容器里装一堆工具快得多
小结
Docker 网络不复杂,复杂的是它把 Linux 网络栈的能力原样暴露了出来。记住三句话就够了:自定义 bridge 解决单机互联和名字解析,端口映射本质是 iptables DNAT,跨主机要靠 overlay 或 CNI。剩下的都是在这三条主线上做取舍。
- 点赞
- 收藏
- 关注作者
评论(0)