banner
约 1,800 字
6 分钟

Docker 网络模式选型记录

-
-
无标签

摘要

本文基于排查老项目网络问题的实操经验,梳理了 Docker 的六种网络模式。作者指出默认 Bridge 存在子网冲突风险,推荐使用 User-defined Bridge 以获得自动 DNS 和灵活性。Host 模式性能高但易冲突,Overlay 专用于集群。最终建议开发环境统一使用 User-defined Bridge,生产环境根据场景选择 Overlay 或 Macvlan,监控服务使用 Host 模式。

最近在帮同事看一个老项目的网络问题,顺便把 docker 的几种网络模式重新理了一遍。每次用都要查文档,这次记下来,希望下次能直接抄自己的笔记。

提示:本文不是文档翻译,是我自己的实操记录和踩坑史。命令在 docker 24+ / compose v2 上验证过。

背景

服务一开始是单机 docker-compose,三四个容器。后来上 swarm,节点扩到 6 个,又踩了 overlay 的几个坑。回看整个过程,我觉得网络模式的选择本质上是在隔离和互通之间找平衡

bridge(默认)

最常用的模式,docker 0 网桥 + NAT。容器之间通过 IP 互通,但默认情况下容器之间只能用 IP 互相访问,不能用容器名,从外部访问需要端口映射。

bash
docker run -d -p 8080:80 nginx:alpine
# 外部访问 host:8080 → 容器:80

# 看 docker0 网桥
ip addr show docker0
# 通常是 172.17.0.1/16

# 容器之间默认只能用 IP 互通
docker inspect <container> | grep IPAddress

坑:docker0 的子网有时候会和宿主机局域网冲突。比如宿主机是 172.17.0.0/16,docker bridge 默认也是 172.17.0.0/16,容器内 ping 局域网就会出问题。改 daemon.json:

JSON
// /etc/docker/daemon.json
{
  "bip": "192.168.200.1/24",
  "default-address-pools": [
    { "base": "192.168.201.0/24", "size": 24 }
  ]
}

适用场景:单主机开发、web 服务、不需要被局域网其他机器直连的服务。

user-defined bridge(推荐替代 default bridge)

其实 docker network create bridge my-net 创建一个自定义 bridge 比 default bridge 强很多,强烈建议不要用 default bridge:

  • 支持自动 DNS 解析,容器名可以直接 ping(default bridge 要 --link,已废弃)

  • 可以随时 docker network connect/disconnect,不用重启容器

  • 不同网络上的容器默认隔离(更安全)

  • 配置更灵活(子网、网关、静态 IP 都可以指定)

bash
docker network create --driver bridge my-net

docker run -d --network my-net --name web nginx:alpine
docker run -d --network my-net --name api my-api:latest

# 在 web 容器里可以直接:
docker exec -it web ping api
# PING api (192.168.200.3): 56 data bytes
# 64 bytes from 192.168.200.3: seq=0 ttl=64 time=0.123 ms

我现在所有本地开发都用这个,包括 compose。

host

容器和宿主机共享网络栈,没有网络隔离。性能最好(少了一层 NAT),但端口冲突是麻烦事。

bash
docker run -d --network host nginx:alpine
# 容器直接监听 host:80,不再做端口映射

# 验证:容器内和宿主机看到的是同一个 netstat
docker run --rm --network host alpine netstat -tlnp

坑:多实例部署会直接端口冲突,所以 --network host 几乎只能单实例用。另外,容器进程在宿主机上 ss -tlnp 看到的是容器进程而不是 docker-proxy,调试时要注意。

适用场景:对性能敏感的负载(监控 agent、网络工具、Prometheus exporter)、单实例部署、明确知道只用一份的服务。

none

容器有自己的网络栈但完全没接口。纯隔离,要靠自定义配置(slirp4netns、sysctl、或者自己写 netns 配置)联网。

bash
docker run -d --network none --name sandbox alpine sleep 3600

# 验证:容器里只有 lo
docker exec sandbox ip addr
# 1: lo: <LOOPBACK,UP> mtu 65536 qdisc noqueue state UNKNOWN
#     link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
#     inet 127.0.0.1/8 scope host lo

我只在两种场景用过:

  • 跑一次性计算任务,不想有网络

  • 某些安全沙箱场景,依赖手动配置网络策略

macvlan

让容器直接拿一个真实网段上的 IP,对外看起来像物理机器。在容器和宿主机之间不能互通(除非显式开 -o macvlan_mode=bridge)。

bash
docker network create -d macvlan \
  --subnet=192.168.1.0/24 \
  --gateway=192.168.1.1 \
  -o parent=eth0 \
  -o macvlan_mode=bridge \
  my-macvlan

docker run -d --network my-macvlan --ip 192.168.1.50 alpine

# 在局域网其他机器上可以直接 ping 到 192.168.1.50
# 不需要任何端口映射

坑(也是我栽过最痛的一个):

  1. 大多数云厂商(AWS / GCP / 阿里云 / 腾讯云)的虚拟网卡默认不开混杂模式,macvlan 直接不工作。报错是 failed to create network: cannot find parent ethernet device,或者容器能起但没 IP。要先开 ip link set eth0 promisc on,但云厂商一般不允许。

  2. 宿主机 ping 不通自己的 macvlan 容器(这是 macvlan 设计的限制,不是 bug)。要 host 也能通,需要桥接一个 dummy interface,麻烦。

适用:自建机房或家庭网络,需要被局域网其他机器像访问真机一样访问容器,不需要端口映射;老应用迁移,不想改架构。

ipvlan L2 / L3

macvlan 的近亲,但不复用父接口的 MAC(每个容器有独立 MAC)。对老交换机更友好(不违反 MAC 表限制)。

bash
docker network create -d ipvlan \
  --subnet=192.168.1.0/24 \
  --gateway=192.168.1.1 \
  -o parent=eth0 \
  -o ipvlan_mode=l2 \
  my-ipvlan

适用:和 macvlan 类似,但 MAC 表有限的老网络环境下更稳。我只在实验室用过,没大规模生产。

overlay

跨主机的容器网络,docker swarm / k8s 用。底层是 vxlan 封装,包会比普通包大 50 字节左右,要算 MTU。

bash
# 初始化 swarm
docker swarm init --advertise-addr <MANAGER_IP>

# 创建 overlay 网络
docker network create -d overlay --attachable my-overlay

# 部署服务
docker service create --network my-overlay --replicas 3 --name web nginx

坑:

  1. overlay 加密(--opt encrypted)会有显著性能损失,我测下来吞吐掉了 30%+。除非对安全有硬要求,否则不开。

  2. MTU:如果跨节点的物理网络是 1500,不调 MTU 会有大包传输问题(fragment 或丢包)。在 daemon.json 里设 "mtu": 1450

  3. DNS:在 swarm 1.13 之前有 DNS round-robin bug,现在好多了,但偶尔还是能踩到。

适用:swarm / k8s 集群多节点通信。单节点用 overlay 纯属浪费。

选型速查

模式

隔离性

性能

适用

default bridge

本地玩具

user-defined bridge

单机开发、compose

host

单实例、性能敏感

none

完全

沙箱、计算任务

macvlan / ipvlan

局域网穿透、老应用

overlay

中(加密后低)

swarm / k8s 集群

我现在的默认

本地开发一律 user-defined bridge,compose 项目里也是。生产如果是 swarm/k8s 就 overlay,物理机房特殊需求才 macvlan。host 我基本只在跑 node_exporter 这种监控 agent 时用。

另外有个不成文的规矩:同一台机器上不同项目的容器,强制用不同的 user-defined bridge,避免互相串。这条救过我至少两次。

参考资料

END

相关文章

暂无相关文章