Skip to content

Docker 网络模式

是什么

Docker 通过 Linux 的 network namespace 给每个容器一套独立的网络栈(网卡、路由表、iptables 规则等),再通过不同的网络驱动把这套栈接到宿主机/外部世界。详见 namespace.md

容器看到的网络实际的网络数据流是两回事,网络模式决定的是后者——容器的 veth 怎么连出去、用不用 NAT、IP 怎么分配。

容器内的视角:                      实际数据流(取决于网络模式):
┌──────────────┐                   ┌──────────────┐
│ eth0@ifN     │                   │ eth0 (veth)  │ ← 容器内看到的
│ 172.17.0.2   │                   │ 172.17.0.2   │
└──────┬───────┘                   └──────┬───────┘
       │                                   │
       │                                   ↓ 落到 docker0 桥(bridge 模式)
       │                                ┌──────┐
       │                                │docker0│ 172.17.0.1
       │                                └───┬──┘
       │                                    │ 走 iptables NAT
       │                                ┌───▼──┐
       │                                │ eth0 │ 宿主网卡
       │                                └──────┘
       └─ 在 host 模式下,这层 veth 不存在,容器直接用宿主 eth0

五种模式一览

模式 命令写法 容器有独立 IP 端口映射需要 性能 典型场景
bridge --network bridge (默认) ✅ docker0 网段 需要 -p 单机开发、测试
host --network host ❌ 用宿主机 IP 不需要 最优 高性能网络、监控 agent
none --network none ❌ 只有 lo 不需要 完全隔离的批处理任务
container --network container:<id> ❌ 共享别的容器 看被共享容器 sidecar 模式
自定义网络 --network <用户创建的网络> ✅ 自定义网段 看类型 多容器互通、DNS、服务发现

后两种其实是用户创建的网络(默认 driver 是 bridge),通过 docker network create 创建后再 attach。

bridge 模式(默认)

工作原理

                                  ┌──────────────────┐
                                  │    容器 A        │
                                  │  vethA  172.17.0.2│
                                  └────────┬─────────┘
                                           │ peer 端
                                  ┌────────▼─────────┐
                                  │      docker0     │ ← Linux bridge (brctl)
                                  │      172.17.0.1  │    内核级交换机
                                  └────────┬─────────┘
                                           │
                                  ┌────────▼─────────┐
                                  │    容器 B        │
                                  │  vethB  172.17.0.3│
                                  └──────────────────┘
                                           │
                                           │ (出网时)
                                  ┌────────▼─────────┐
                                  │   iptables NAT   │ ← 容器访问外网用
                                  │   172.17.0.0/16  │
                                  └────────┬─────────┘
                                           │
                                  ┌────────▼─────────┐
                                  │   宿主机 eth0     │
                                  └──────────────────┘
  • 每个容器有一对 veth pair,一端在容器内(叫 eth0),另一端挂在 docker0 这个 Linux bridge 上
  • docker0 是 Docker 创建的虚拟交换机,IP 通常是 172.17.0.1/16
  • 容器访问外网靠 iptables 做 MASQUERADE(SNAT)
  • 外部访问容器靠 iptables 做 DNAT(这就是 -p 8080:80 的本质)

PS: - docker0 是 Docker 早期设计的默认桥。生产环境不要用默认 bridge——容器之间无法用容器名互相解析,且没有自定义子网。 - 容器看到的网卡名不一定是 eth0,如果是自定义网络可能是 eth1 等。

端口映射原理

docker run -p 8080:80 nginx
# iptables 实际生成的规则(简化)
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 8080 \
  -j DNAT --to-destination 172.17.0.2:80
# 任何访问宿主 8080 的包,目的地址改成容器 IP:80

这条 DNAT 链就是 容器外网访问慢、性能差、连接数受限 的根源——每包都过 iptables。生产高并发要避开它(用 host 网络或直接 hostPort)。

host 模式

容器没有独立网络栈,直接用宿主机的 eth0,共享 IP 和端口空间。

docker run --network host nginx
# 容器里直接 netstat -tlnp 看到的就是宿主机的所有端口
# 不需要 -p 映射,nginx 直接监听宿主 80
普通 bridge 模式:                       host 模式:
┌──────────────┐                         ┌──────────────┐
│  容器内      │                         │  容器内      │
│  nginx:80    │                         │  nginx:80    │ ← 同一网络栈
└──────┬───────┘                         └──────┬───────┘
       │ veth                                  │
┌──────▼───────┐                         ┌──────▼───────┐
│  宿主机      │                         │  宿主机      │
│  eth0:8080   │ ← iptables DNAT 过来     │  eth0:80     │ ← 直接监听
└──────────────┘                         └──────────────┘
优势 代价
性能最优(少一跳 veth) 端口冲突——不能用宿主机已占用的端口
无 NAT,双向连通性完整 容器之间无网络隔离
localhost 直通 容器看到的 localhost = 宿主机的
适合监控、网络、抓包工具 macvlan/ipvlan 也能解决部分场景

PS: - Kubernetes 的 hostNetwork: true 字段对应的就是这种模式 - 在 Mac/Windows 桌面上,Docker Desktop 跑的是 VM,host 模式其实指 VM 的网络栈,不是宿主机——这点容易踩坑

none 模式

容器只有 lo 回环网卡,没有其他网络接口。

docker run --network none alpine ip addr
# 1: lo: <LOOPBACK> mtu 65536
#     inet 127.0.0.1/8
# 没有 eth0,没有任何对外通道

适用: - 离线批处理(解压文件、跑数据转换) - 安全沙箱(不连网,无法外泄) - 配合 docker network connect 按需接入

container 模式

新容器共享指定容器的 network namespace,IP 和端口完全一样。

# 启动 sidecar 模式:日志收集器跟业务容器共享网络
docker run -d --name app nginx
docker run -d --name logger \
  --network container:app \
  fluent/fluent-bit

# logger 看到的网络跟 app 一模一样,能用 localhost 访问 app 的 80
docker exec logger curl http://localhost
场景 例子
紧耦合 sidecar 日志/metrics agent 跟主容器共享端口
网络调优/调试 临时启动 netshoot 共享问题容器的网络栈
性能敏感 替代 pid:container 网络版本

PS: - 跟 K8s 的 shareProcessNamespace 思路类似,但这里是网络维度 - 不能被多个容器共享:第二个 --network container:X 启动的会失败

自定义网络(推荐生产用)

docker network create 创建的网络,比默认 bridge 多了三个关键能力:

docker network create \
  --driver bridge \
  --subnet 10.10.0.0/24 \
  --gateway 10.10.0.1 \
  my-net
# 启动容器并加入
docker run -d --name web --network my-net nginx
docker run -d --name db  --network my-net redis

# 关键能力 1: 容器名 DNS 解析(默认 bridge 没有!)
docker exec web ping db
# PING db (10.10.0.3): 56 data bytes  ← 真的能 ping 通

# 关键能力 2: 容器间自动服务发现
docker exec web nslookup db

自定义网络 vs 默认 bridge

维度 默认 bridge 自定义 bridge
容器名 DNS ❌(只 IP) ✅ 内置 DNS
容器隔离 弱(都能互通) 强(同网络才互通)
子网可配 ❌ 写死 172.17 ✅ 任意
链接容器(link) 需要 --link 旧机制 自动可解析
多主机 ❌(用 overlay)

PS: 所有新项目都应该用自定义网络,不要用默认 bridge。最大的区别就是 DNS——容器名能解析,极大简化 Compose / 多容器互访。

容器间通信

1. 同一自定义网络内:
   web → db                ✅ 容器名直接通
   web → db:6379           ✅ 端口直通,不需要 -p 暴露给宿主

2. 跨网络:
   web (net-a) → db (net-b)  ❌ 不通,需要把 db attach 到 net-a
   docker network connect net-a db

3. 跨主机:
   Docker Swarm 模式 → overlay 网络(vxlan 封装)
   K8s → CNI 插件(calico/cilium/flannel)

DNS 解析细节

容器内 /etc/resolv.conf 默认指向 Docker 内置 DNS(127.0.0.11):

docker exec web cat /etc/resolv.conf
# nameserver 127.0.0.11
# options ndots:2
容器内 DNS 查询流程:
   app → 127.0.0.11 (docker 内置 DNS)
            │
            ├── 容器名/db.service?  → 查本网络 → 返回 10.10.0.3
            ├── 外部域名?           → 转发给宿主 /etc/resolv.conf
            └── 不通?              → 失败

自定义网络里只有显式 docker run --name X 的容器才会被注册到 DNS。docker-compose 起的服务天然都注册了。

跨主机网络(overlay)

单 Docker 引擎下没有"跨主机网络"概念;要在多机间打通,要用 Swarm 模式 + overlay 网络

docker swarm init
docker network create --driver overlay --attachable my-overlay
# 任何加入 swarm 的节点上的容器,都能加入 my-overlay
多主机 overlay (vxlan):
   host1 容器 A (10.0.0.2)         host2 容器 B (10.0.0.3)
        │                                │
        ▼                                ▼
   ┌─────────┐   vxlan udp 4789   ┌─────────┐
   │ vxlan0  │ ──────────────────► │ vxlan0  │
   └────┬────┘                    └────┬────┘
        │                              │
       eth0  ◄─────── 物理网络  ──────► eth0

简单集群用 docker-compose --scale + overlay 也行,但生产一般直接上 K8s,CNI(Calico/Cilium)提供更丰富的网络策略和性能。

调试命令

# 看容器实际网络配置
docker inspect <container> | jq '.[0].NetworkSettings'

# 看所有网络
docker network ls
docker network inspect <net_name>

# 看 veth pair
ip link show | grep veth
# 一端 vethXXX 在容器内(容器里叫 eth0),另一端在 docker0

# 抓包(用 netshoot 镜像)
docker run -it --rm --network container:<target> nicolaka/netshoot tcpdump -i eth0

# 看 iptables 规则(bridge 模式的关键)
sudo iptables -t nat -L -n | grep -A2 'Chain DOCKER'

# 看路由
docker exec <container> ip route

常见坑

Mac/Win 上 --network host 不"host"

Docker Desktop 用 LinuxKit VM,host 网络指的是 VM 内部,不是你笔记本。要从笔记本访问 VM 里的容器,照样 -p 映射。

默认 bridge 容器名不通
现象:
   docker run --name web -d nginx
   docker run --name db  -d redis
   docker exec web ping db
   → ping: bad address 'db'

解决:自己创建网络,docker run --network mynet
端口映射被宿占用
docker run -p 80:80 nginx
# Error: bind: address already in use

# 解决:换端口,或用 host 网络
docker run --network host nginx
# 但要确认宿主 80 没被别的进程占
iptables 规则被冲掉

有些 systemd 服务(firewalld、ufw)会清空自定义链,docker 的 NAT 规则被冲掉后容器上不了网:

# 临时恢复
sudo systemctl restart docker

# 根治:
# 1. ufw 改 /etc/ufw/ufw.conf 里 manage_iptables=false
# 2. firewalld 加 docker0 到 trusted zone

参考