Skip to content

Linux 网络基础和容器网络

容器网络,本质是“Linux 网络命名空间 + 虚拟网卡 + 桥接 + NAT + 路由 + iptables”的组合拳。

Docker 桥接网络的「裸奔版」,用最纯净的 Linux 内核机制,理解 namespace + veth + bridge + NAT 的全链路,揭开 Docker 背后的神秘面纱

等处理的概念

  • MetalLB 就像是你本地/裸机 Kubernetes 的“LoadBalancer 发号器”,让你在没有云的情况下,依然拥有云的访问体验
  • ip addr
  • ip link
  • ip route 路由表
  • 网卡 多 IP
  • 虚拟网卡
  • 网桥 虚拟交换机
  • 网关
  • 防火墙
  • 默认路由/缺省路由
    • 通常情况下,默认路由 = 网关 IP
    • default via X dev Y
  • 第一跳 = 数据包离开你主机后,抵达的第一台网关设备 IP
    • 第一跳的设备
  • traceroute 命令
    • 它的输出就是:从你电脑出发到目标地址,中间经过的每一跳网关(路由器)
  • telnet 命令
  • nc 命令
  • ping 命令
    • ping -c 3 www.baidu.com
  • ip route 是导航系统,决定“去哪” NAT
  • iptables 是关卡系统,决定“能不能去、要不要伪装”。
  • tcpdump -i docker0 icmp
  • 网络命名空间(即 namespace,Linux 内核的概念)
  • 理解 192.168.1.0/24192.168.1.0/22 的区别

网络基础核心概念

概念解释
IP 地址网络设备的唯一地址(如 192.168.1.1)
子网掩码决定同一个网段的设备(如 /24 即 255.255.255.0)
网关你出网的门(通常是路由器)
DNS把网址翻译成 IP 地址
MAC 地址网卡烧录的物理地址
端口一个 IP 下多个服务的编号(如 80 是 HTTP)
TCP vs UDPTCP 是可靠连接(有握手),UDP 是快递(不保证收)
三次握手TCP 建立连接的过程(SYN-SYN/ACK-ACK)
四次挥手TCP 断开连接的过程
NAT多个内网 IP 公用一个公网 IP 出网(家用 Wi-Fi 就是)
路由多跳设备决定你怎么到达目标 IP(trace route)

理解 docker0

docker0 是 Docker 默认创建的一个虚拟网桥(Bridge),即默认 bridge 网络,它用于容器与宿主机、其他容器之间的网络通信。


默认桥接网络 bridge 的通信原理

  1. 启动容器,默认挂到 docker0 网桥上(172.17.0.1/16)
  2. 会自动创建一对 veth pair:
    • 一端在容器里(命名为 eth0)
    • 一端在宿主机里(随机名,比如 vethf58a1c@)
  3. docker0 作为桥,把多个 veth 接口连起来,像个二层交换机
  4. 容器间通信靠 docker0 转发
  5. 容器访问外网,靠宿主机做 NAT 转换(iptables MASQUERADE)

docker0


服务发现

  • 默认 bridge(docker0),不支持 DNS 服务发现
  • 自定义 bridge 支持 DNS 服务发现
  • Compose 默认网络,支持 DNS 服务发现,本质上是自定义 bridge 网络

容器访问外网的路径(默认 bridge 网络)

sh
# 视角1
容器 eth0 -> veth pair -> docker0 网桥 -> NAT 转换(iptables MASQUERADE) -> 宿主机 eth0 -> 外网

# 视角2
容器 eth0(172.17.0.x)

veth pair(虚拟网卡)

docker0(桥接网络,172.17.0.1)

宿主机 iptables NAT(源地址转换为宿主机 IP)

宿主机物理网卡 eth0(如 10.0.4.2)

路由到外网

自定义 Docker 网桥网络

docker0 网络虽然开箱即用,但不支持容器名互通,只能用 IP,自定义 bridge 是支持 DNS 的

自定义 bridge 网络访问外网

sh
容器 eth0 -> veth pair -> 自定义网桥 -> NAT -> 宿主机 eth0 -> 外网

创建一个自定义桥接网络

sh
docker network create \
  --driver bridge \
  --subnet 192.168.100.0/24 \
  --gateway 192.168.100.1 \
  my-custom-net

这时执行 ip addr 能在宿主机上能看到对应的网桥网卡

br-xxxxxxxxxxxx

这个 br- 开头的网卡就对应你刚创建的 my-custom-net 网络,可执行下面的命令来验证

sh
docker network inspect my-custom-net

你会看到类似:

json
"Options": {
    "com.docker.network.bridge.name": "br-3b3a3bfa5c3f"
}

这个 bridge.name 就是你宿主机上的真实网卡名

验证自定义 Docker 网桥网络的服务发现

创建网络

sh
docker network create \
  --driver bridge \
  --subnet 192.168.100.0/24 \
  --gateway 192.168.100.1 \
  my-custom-net

启动两个 alpine 容器

sh
docker run -it --rm --name test1 --network my-custom-net alpine sh
docker run -it --rm --name test2 --network my-custom-net alpine sh

在 test1 容器内执行:

sh
ping test2

它能解析成 IP 并正常 ping 通,说明服务发现 OK

疑问:Docker Compose 中,容器之间能通过服务名通信,是因为用了自定义网桥吗?

答案:没错!你跑 docker-compose up 的时候,它会自动为该项目创建一个自定义网络 network(名字类似于 项目名\_default)。所以 Compose 里的容器可以直接用服务名(如 db, web)相互访问,就是因为这些容器都被绑定到了一个 同一个自定义 bridge 网络上,并启用了 DNS。

验证 Docker Compose 的服务发现

yaml
version: "3"
services:
  app1:
    image: alpine
    command: ["sh", "-c", "apk add --no-cache iputils && sleep infinity"]

  app2:
    image: alpine
    command: ["sh", "-c", "apk add --no-cache iputils && sleep infinity"]

启动服务

sh
docker-compose up -d

验证自定义网络是否创建

sh
docker network ls | grep _default

进入容器 ping 服务名验证 DNS

sh
docker exec -it <app1容器ID或名> sh

# 容器内执行
ping app2

它能解析成 IP 并正常 ping 通,说明服务发现 OK

docker network 操作命令清单

bash
# 查看所有网络
docker network ls

# 查看网络详情(如 bridge):
docker network inspect bridge

# 创建自定义网络:
docker network create my-net

# 启动容器加入网络:
docker run -d --network=my-net nginx

Docker 网络类型全览(docker network ls

类型说明特点
bridge默认网络基于 docker0,支持端口映射/NAT
host与宿主机共用网络命名空间无隔离,性能好,适合高性能通信
none没有网络连接容器完全裸奔(需手动配置)
overlay跨主机网络(Swarm)多宿主机通信,依赖 KV 存储
macvlan容器作为局域网一员容器像物理机一样拥有独立 IP
ipvlan类似 macvlan,更灵活新手不常用

常见的网络配置方式

网络模式描述适用场景
桥接(Bridge)容器与宿主机在同一网络段,容器可以与宿主机和其他容器通信。容器间需要直接通信,且与宿主机同一局域网内。
NAT容器通过宿主机的公网 IP 访问外网,容器 IP 被隐藏。容器访问外网但不需要暴露容器 IP,外部访问不直接访问容器。
主机(Host)容器直接使用宿主机的网络接口,容器和宿主机共享 IP 地址。高效的容器与宿主机通信,减少网络开销。
Overlay在物理网络之上构建虚拟网络,支持跨主机通信。容器集群中跨主机的容器通信,跨网络的通信。
Macvlan容器有独立的 MAC 地址,直接接入物理网络。容器需要独立的 IP 和 MAC 地址,直接接入物理网络。
None容器不连接任何网络,不具备网络访问。需要完全隔离的容器,无需访问外部网络。

容器间通信的 3 个常见场景

场景条件是否能通信原因
同一个 bridge 网络默认通过 docker0 桥直接转发
不同 bridge 网络默认隔离除非手动连接
host 网络共用网络栈和宿主机一样

容器访问宿主机内网 IP(如 10.0.4.2)的流程

sh
容器 (172.10.0.2)

查路由表 (目标 10.0.4.2 不在本地网段)

转发到网关 (172.10.0.1 - docker0)

veth

docker0

宿主机内核 (10.0.4.2)

处理数据包

直接交给宿主机处理

宿主机处理后响应

容器
  1. 容器访问宿主机的内网 IP:
    • 容器的 eth0 网卡上有一个 IP 地址,比如 172.10.0.2,这个 IP 地址属于容器所连接的自定义网络(比如 172.10.0.0/24)。
    • 容器发送数据包,目标是宿主机内网 IP(比如 10.0.4.2)。
  2. 容器的路由查找:
    • 容器会查看自己的路由表,检查目标地址 10.0.4.2 是否在容器的 IP 子网(即 172.10.0.0/24)内。
    • 因为 10.0.4.2 不在容器的子网内,所以容器会把这个数据包交给默认网关,也就是 172.10.0.1(这是 Docker 的 docker0 网桥的 IP)。
  3. 数据包从容器发送到 Docker 网桥:
    • 数据包会通过容器的 eth0 网卡发送到 docker0 网桥(172.10.0.1),即通过 veth(虚拟以太网接口)对进行转发。
  4. Docker 网桥处理数据包:
    • docker0 网桥接收到数据包后,会根据其网桥规则,将数据包转发到宿主机的对应网络接口。
  5. 宿主机内核处理:
    • 宿主机收到数据包后,内核会检查目标 IP 10.0.4.2。由于 10.0.4.2 是宿主机的一个 IP(假设宿主机 eth0 的 IP 地址是 10.0.4.2),宿主机内核会认为该数据包是自己发出的,直接将数据包交给宿主机进行处理。

总结:容器通过 docker0 网桥向宿主机发起请求时,会先走容器的 eth0 -> docker0 -> 宿主机内核,然后由宿主机处理返回数据。这一过程的关键是,宿主机内核通过路由表和 IP 配置,知道 10.0.4.2 是自己内网的 IP,所以直接处理数据包。

host.docker.internal

在 macOS 和 Windows 上,使用 host.docker.internal 可以轻松地访问宿主机

以下是容器通过 host.docker.internal 访问宿主机的流程图:

容器 -> 查找 DNS -> host.docker.internal -> 宿主机 IP
容器 eth0 -> 查找路由表 -> 找到目标是宿主机(host.docker.internal)
容器 eth0 -> 发送数据包 -> docker0 (veth pair) -> 宿主机
宿主机处理请求 -> 响应数据包返回 -> 容器

解释:

  1. 容器查找 DNS:容器通过 DNS 查询 host.docker.internal,该域名在 Docker 的 DNS 配置中会解析为宿主机的 IP 地址。
  2. 路由查找:容器查找自己的路由表,发现目标 IP 是宿主机地址,然后决定通过网关(通常是 Docker 网桥 docker0)转发数据包。
  3. 数据包转发:数据包从容器的 eth0 网卡发送,经过容器的 veth pair,最终通过 Docker 网桥(docker0)发送到宿主机。
  4. 宿主机处理:宿主机收到数据包后,处理请求并将响应返回给容器。
  5. 数据包返回:宿主机将响应数据包通过 docker0 网桥,再经过 veth pair 返回给容器的 eth0,完成通信。