跳转至

单主机网络 — bridge、端口发布与反向代理

这一章回答三个问题: 容器之间怎么通信(bridge 网络 + 内置 DNS); 容器里的服务怎么被外面访问(端口发布,一道受控的门); 怎么按 URL 把流量分给不同服务(第 7 层路由,反向代理)。 主线例子:c3 容器 ping c4——通;ping 隔壁网络的 c2——100% 丢包。 一次成功和一次失败,就把 Docker 网络模型的设计讲完了。

1. 顶层全景:一张网图的三种读法

  宿主机 ── eth0(物理/虚拟网卡)
      │
  docker0(Linux bridge,路由器角色,占 172.17.0.1)
      │        子网 172.17.0.0/16
      ├─ veth ── c1(eth0, 172.17.0.2)
      ├─ veth ── c2(eth0, 172.17.0.3)      ← 同网:互通
      │
  sample-net(自建 bridge, 172.18.0.0/16)
      ├─ veth ── c3 ─┐
      ├─ veth ── c4  │ 同网互通;跨网(sample-net → bridge)默认防火墙隔离
      │
  对外:不开门(默认 ingress 全挡)→ 端口映射 -p 8080:80(受控的门)

  图说:读法一"同网互通",读法二"跨网隔离",读法三"对外只走发布的门"。

2. 网络即防火墙(SDN 的默认立场)

Docker 的网络模型安全优先:默认创建的网络是软件定义网络(SDN), 同网络的容器自由通信,不同网络的容器互相隔离,除非显式地把某个容器同时接进两个网1。

书里的三服务例子值得照抄,因为它就是最小权限网络的设计样板2:

  需求:webAPI 能调 productCatalog,但不能直接碰 database;
        productCatalog 两边都能调。
  做法:front 网(webAPI, productCatalog)+ back 网(productCatalog, database)
       productCatalog 一肩两网,webAPI 和 database 各只在自己的网里。
  收益:webAPI 被攻破,攻击者仍够不到 database——还得再破一层 productCatalog。

  图说:网络边界 = 防火墙边界;多接一个网 = 多开一扇门,要 deliberate(刻意)。

配套规矩(书里的最佳实践):SDN 创建又快又免费,让每个网络只装需要互相对话的服务; 别把所有容器都丢在默认 bridge 上——同一网络里的服务越少,一个被攻破时的爆炸半径越小3。 现代发行版上防火墙规则由 nftables 管理(旧的是 iptables),排查命令是 sudo nft list ruleset4。还有一步到位的安全清单:只发布需要的服务; 本地开发优先绑 127.0.0.1;生产在反向代理/ingress 终结 TLS;UDP 无连接更易伪造, 非必要不开5。

3. bridge 网络解剖(主线例子)

Docker 守护进程首次启动时自动建一个 Linux 网桥 docker0,并定义名为 bridge 的默认网络;不显式指定网络的容器都挂这里6。查看与解剖:

  $ docker network ls                → bridge  driver=bridge  scope=local
  $ docker network inspect bridge
    → IPAM: subnet 172.17.0.0/16, gateway 172.17.0.1
    → 容器可用地址 172.17.0.2 ~ 172.17.255.255;172.17.0.1 留给网桥自己[^7]

两个容易踩的坑先交代。其一,默认只放行出口(egress),入口(ingress)全挡—— 容器能上外网,但外面进不来,这正是"端口发布"存在的理由(见 §5)8。 其二,公司内网/VPN 常与 172.17.0.0/16 撞车,改 daemon.json 的 "bip": "192.168.100.1/24" 可换默认子网7。

自建网络与内置 DNS(主线例子的前半段):

  $ docker network create --driver bridge sample-net
  $ docker network inspect sample-net | grep Subnet → 172.18.0.0/16(自动分下一块)

  c3、c4 挂到 sample-net:
  $ docker container run --name c3 --rm -d --network sample-net alpine:3.22 ping 127.0.0.1
  $ docker container run --name c4 --rm -d --network sample-net alpine:3.22 ping 127.0.0.1

  进 c3:
  / # ping c4
  → PING c4 (172.20.0.3): 64 bytes from 172.20.0.3 ... ← 通了,而且是按名字通的!

按名字能 ping 通,是自建网络相对默认 bridge 的关键差异: 同一用户自定义网络上的容器,自动获得 Docker 内置 DNS 的名字解析—— 这就是第 09 章"服务发现"在单机的形态9。

跨网隔离(主线例子的后半段):

  从 c3(在 sample-net)ping 隔壁 bridge 网络的 c2:
  / # ping c2        → ping: bad address 'c2'      ← 名字都解析不了
  / # ping 172.17.0.3
  → 11 packets transmitted, 0 packets received, 100% packet loss
  → 网络提供了额外一层隔离,也因此多一层安全[^10]

结论要背:不同网络上的容器默认互相防火墙隔离;DNS 名字解析不跨网络。

其余 knobs:--subnet "10.1.0.0/16" 自定义子网;Docker 20.10 起 --ipv6 双栈(IPv4 + fd00:dead:beef::/48 之类 IPv6 前缀);VPN/overlay 环境下用 --opt com.docker.network.driver.mtu=1400 调 MTU 防分片;子网别重叠11。 一个容器还能 docker network connect sample-net c6 加入多个网—— 这正是 §2 三服务例子的实现手段;有容器挂着的网络删不掉(network rm 会报 active endpoints 错误)12。

4. host 与 none:两个特殊网络

host 网络去掉容器与宿主机的网络命名空间隔离:容器没有自己的私有 IP, 直接用宿主机的网络栈绑接口13。适用:对延迟敏感的高性能网络服务、 需要广播/多播发现、要直接检查宿主网络的调试工具。三条警示同样硬: 仅 Linux 完整支持(Mac/Windows 上 --network host 挂的是 Desktop VM 的网络, 宿主机访问仍要发布端口);安全上容器被攻破等于宿主网络被看穿; 与宿主机应用撞端口会直接冲突。生产跑业务容器没有理由用 host 网络, 用用户自定义网络14。

none(null)网络:给容器建命名空间但不接任何网络——只剩 loopback(lo), 够不到任何别的容器和外网15。适用:安全敏感负载、数据全靠卷挂载的离线处理、 取证/分析容器。纪律:不需要网络的服务,就该显式跑在 none 网络上—— 默认放行出口恰恰是大多数容器根本不需要的权限15。

5. 端口发布:对外只有一道受控的门

容器和宿主机各有独立的端口空间,默认毫无关系。要对外服务,就把容器端口接到 一个空闲的宿主端口上——这个动作叫 publish(发布)。书里把它比作在防火墙 上开一道门:不开大门,只开一个受控的闸16。

方式 命令 结果
随机发布 -P(大写) Docker 从临时端口段(如 32768–60999)挑一个;docker container port web → 80/tcp -> 0.0.0.0:5500017
指定映射 -p 8080:80 宿主 8080 → 容器 80;-p 3000:4321/udp 显式开 UDP(TCP 和 UDP 要分别映射)18
只声明 Dockerfile EXPOSE 80 只是元数据,什么都不开;-P 自动映射时才轮到它起作用19

EXPOSE ≠ publish 这一组区别是本章考试重点:EXPOSE 是镜像里的"意向书", publish 是运行时在宿主包过滤器(iptables/nftables,或 Desktop VM 的等效规则)上 凿门19。

还有个从单机直通 Kubernetes 的概念:让一个新容器加入另一个容器的网络命名空间 (--network container:myapp),两个容器就共享 IP、端口和 loopback—— 这是 Kubernetes Pod 的地基:一个 Pod 里的多个容器(主应用 + 日志/监控/网格边车) 像同进程一样通过 localhost 交互。调试时可以用 nicolaka/netshoot 工具镜像 这样贴到应用容器上,在完全一致的网络现场里跑 curl/dig/tcpdump20。 (共享命名空间 = 共享全部网络权限,一个被攻破能看所有流量,同样要谨慎20。)

6. 反向代理:按 URL 分流(第 7 层路由落地)

第 09 章说过第 7 层路由按 URL 分发请求;本章把它演完。场景是绞杀者模式的 第一刀:单体 city-tours(Python/Flask,监听 5000)容器化后,把目录功能 catalog 拆成 Node.js 微服务(监听 3000)——但客户端的 URL 不能变: acme.com/catalog?... 还得原样工作21。

Traefik 是开源的云原生边缘路由器,与 Docker 的集成方式优雅: 它挂载 docker.sock,读每个容器上的标签(labels)自动发现路由规则22:

  catalog 容器(优先级 10,精确匹配 /catalog 前缀):
    --label traefik.enable=true            ← 默认 false,要显式启用
    --label traefik.port=3000              ← 转发到容器内 3000
    --label traefik.priority=10
    --label traefik.http.routers.catalog.rule="Host(\"acme.com\") && PathPrefix(\"/catalog\")"

  city-tours 容器(优先级 1,兜底):
    --label traefik.priority=1
    --label traefik.http.routers.city-tours.rule="Host(\"acme.com\")"

  Traefik 本体:
    docker container run -d --name traefik \
        -p 8080:8080 -p 80:80 \
        -v /var/run/docker.sock:/var/run/docker.sock \
        traefik:v2.0 --providers.docker=true \
        --providers.docker.exposedbydefault=false --api.insecure=true

  效果:acme.com/catalog* → catalog:3000;acme.com 其余 → city-tours:5000
  优先级 10 的规则先吃 /catalog,优先级 1 的兜底其余;8080 是 Traefik 的管理 UI[^22]

绞杀者第一刀落成了:URL 没变、客户端无感、单体内部却已少了一块。 这就是第 09 章 Fowler 警告与第 7 层路由两个知识点的合流。

7. 作者的判断与证据

  • "网络即防火墙、按服务切网"是本章的核心立场,依据是 SDN 的默认隔离语义 和爆炸半径论证13。
  • "业务容器没有理由用 host 网络":安全 + 端口冲突 + 丢隔离,三条理由都硬14。
  • Traefik 方案是"众多选择之一":书里明说 Nginx/HAProxy 同样能做; 选 Traefik 是因为标签自动发现与 Docker 契合22。
  • 本章机制均有可复现实验(ping 通/丢包、port 映射、curl --resolve),证据强度高。

判断(我们的,不是书里的): 本章真正的"隐藏主线"是默认拒绝(default deny): 入口默认全挡、跨网默认隔离、EXPOSE 默认不开、Traefik 默认不暴露 (exposedbydefault=false)。Docker 网络的设计哲学是"每个动作都要显式授权"—— 记住这一条,比记住所有命令都值钱;它也正是 Kubernetes NetworkPolicy 思想的源头。 如果错,会错在: 默认 bridge 网络上容器间其实是互通的(隔离性弱于用户自定义网), 严格 default-deny 需要靠用户自定义网络 + 外部策略引擎补齐; 但"对外默认关闭、发布需显式"这个语义在所有网络类型上都成立。

8. 边界与局限

  • 单主机视野:overlay/多主机网络(Weave、Calico、Contiv)只给了一张类型表, 用法留给编排章节(第 14 章)23。
  • 防火墙规则由实现层管理(nftables/VM 等效规则),书里只教了查看,没教手写规则; 深度定制要另学 nftables。
  • Traefik 的示例是 v2.0 的命令行风格,新版本配置格式有变化;概念(标签路由、 优先级)稳定,照抄命令前先查版本。
  • IPv6/双栈与 MTU 微调只给了命令,没有场景化的排障故事。

9. 可带走的

  1. 网络即防火墙:同网互通、跨网隔离、DNS 不跨网;一个容器可挂多网做"中转"910。
  2. 最小权限组网:每个网只装需要互访的服务;别用默认 bridge 装一切3。
  3. bridge 解剖:docker0、172.17.0.0/16、.1 是网关、容器从 .2 开始排号7。
  4. 默认只出不让进;对外只能走 publish 这道门816。
  5. -p 宿主:容器(可带 /udp),-P 随机,EXPOSE 只是声明171819。
  6. host 网络:省 NAT、丢隔离;业务容器别用;none 网络:断网服务的正确姿势1315。
  7. --network container:X = 共享网络命名空间 = Kubernetes Pod 的地基 + 现场调试利器(netshoot)20。
  8. 第 7 层路由用反向代理(Traefik 读标签自动发现),URL 不变就能把单体拆成微服务2122。

10. 原文地图

主题 原书章 原文位置
SDN 防火墙语义 Network firewalling text/109-fm-network-firewalling.txt:37(搜「security first」) · text/109-fm-network-firewalling.txt:41(搜「freely communicate」) · text/109-fm-network-firewalling.txt:48(搜「no communication」)
三服务组网例子 Network firewalling text/109-fm-network-firewalling.txt:51(搜「webAPI」) · text/109-fm-network-firewalling.txt:59(搜「separate networks」) · text/109-fm-network-firewalling.txt:64(搜「compromised」)
网络类型表(overlay 等) Network firewalling text/109-fm-network-firewalling.txt:6(搜「Overlay」) · text/109-fm-network-firewalling.txt:14(搜「Weave」)
nftables Working with the bridge network text/110-fm-working-with-the-bridge-network.txt:2(搜「nftables」)
最小权限组网建议 Working with the bridge network text/110-fm-working-with-the-bridge-network.txt:14(搜「Least-privilege」) · text/110-fm-working-with-the-bridge-network.txt:18(搜「avoid the temptation」)
docker0 与默认 bridge Working with the bridge network text/110-fm-working-with-the-bridge-network.txt:27(搜「docker0」) · text/110-fm-working-with-the-bridge-network.txt:60(搜「172.17.0.0/16」)
默认只放行出口 Working with the bridge network text/110-fm-working-with-the-bridge-network.txt:97(搜「egress」)
bip 换子网 Working with the bridge network text/110-fm-working-with-the-bridge-network.txt:65(搜「clash」) · text/110-fm-working-with-the-bridge-network.txt:70(搜「bip」)
自建网与内置 DNS Working with the bridge network text/110-fm-working-with-the-bridge-network.txt:111(搜「user-defined bridge」) · text/110-fm-working-with-the-bridge-network.txt:116(搜「network create」) · text/110-fm-working-with-the-bridge-network.txt:126(搜「172.18.0.0/16」)
ping c4 通 Working with the bridge network text/110-fm-working-with-the-bridge-network.txt:296(搜「ping c4」) · text/110-fm-working-with-the-bridge-network.txt:319(搜「name resolution」)
跨网 100% 丢包 Working with the bridge network text/110-fm-working-with-the-bridge-network.txt:332(搜「172.17.0.3」) · text/110-fm-working-with-the-bridge-network.txt:339(搜「100% packet loss」) · text/110-fm-working-with-the-bridge-network.txt:343(搜「isolation」)
多网连接与删网限制 Working with the bridge network text/110-fm-working-with-the-bridge-network.txt:367(搜「network connect」) · text/110-fm-working-with-the-bridge-network.txt:384(搜「active」)
双栈与 MTU Working with the bridge network text/110-fm-working-with-the-bridge-network.txt:147(搜「--ipv6」) · text/110-fm-working-with-the-bridge-network.txt:165(搜「mtu=1400」)
host 网络 The host and null network types text/111-fm-the-host-and-null-network-types.txt:31(搜「removes the network namespace」) · text/111-fm-the-host-and-null-network-types.txt:38(搜「high-performance」) · text/111-fm-the-host-and-null-network-types.txt:44(搜「Linux only」) · text/111-fm-the-host-and-null-network-types.txt:48(搜「sniff」)
none 网络 The host and null network types text/111-fm-the-host-and-null-network-types.txt:100(搜「null」) · text/111-fm-the-host-and-null-network-types.txt:103(搜「loopback」) · text/112-fm-running-in-an-existing-network-namespace.txt:1(搜「completely isolated」)
发布端口的动机 Managing container ports text/113-fm-managing-container-ports.txt:38(搜「expose an application」) · text/113-fm-managing-container-ports.txt:43(搜「mapping a container port」)
-P 随机映射 Managing container ports text/113-fm-managing-container-ports.txt:56(搜「-P」) · text/113-fm-managing-container-ports.txt:70(搜「55000」)
-p 指定与 UDP Managing container ports text/113-fm-managing-container-ports.txt:104(搜「-p 8080:80」) · text/113-fm-managing-container-ports.txt:112(搜「4321/udp」)
EXPOSE vs publish Managing container ports text/113-fm-managing-container-ports.txt:115(搜「only documents intent」) · text/113-fm-managing-container-ports.txt:116(搜「wires a container port」)
Pod 地基与 netshoot Managing container ports text/113-fm-managing-container-ports.txt:2(搜「foundation of Kubernetes Pods」) · text/113-fm-managing-container-ports.txt:16(搜「netshoot」) · text/113-fm-managing-container-ports.txt:28(搜「privileges」)
绞杀者场景 HTTP-level routing using a reverse proxy text/114-fm-http-level-routing-using-a-reverse-proxy.txt:13(搜「monolithic」) · text/114-fm-http-level-routing-using-a-reverse-proxy.txt:22(搜「completely starved」) · text/114-fm-http-level-routing-using-a-reverse-proxy.txt:27(搜「catalog」)
Traefik 标签路由 HTTP-level routing using a reverse proxy text/114-fm-http-level-routing-using-a-reverse-proxy.txt:210(搜「edge router」) · text/114-fm-http-level-routing-using-a-reverse-proxy.txt:219(搜「traefik.enable」) · text/114-fm-http-level-routing-using-a-reverse-proxy.txt:239(搜「PathPrefix」) · text/114-fm-http-level-routing-using-a-reverse-proxy.txt:261(搜「priority」) · text/114-fm-http-level-routing-using-a-reverse-proxy.txt:274(搜「traefik:v2.0」)
安全清单 HTTP-level routing using a reverse proxy text/114-fm-http-level-routing-using-a-reverse-proxy.txt:1(搜「Security checklist」) · text/114-fm-http-level-routing-using-a-reverse-proxy.txt:7(搜「terminate TLS」)

  1. 出处:「Network firewalling」第 36–49 段(text/109-fm-network-firewalling.txt:37,搜「security first」;:40,搜「SDNs」;:48,搜「no communication」)。 ↩↩

  2. 出处:「Network firewalling」第 50–65 段(text/109-fm-network-firewalling.txt:57,搜「webAPI to communicate」;:59,搜「separate networks」;:64,搜「compromised」)。 ↩

  3. 出处:「Working with the bridge network」第 14–21 段(text/110-fm-working-with-the-bridge-network.txt:14,搜「Least-privilege」;:18,搜「avoid the temptation」;:19,搜「blast radius」)。 ↩↩↩

  4. 出处:「Working with the bridge network」第 1–13 段(text/110-fm-working-with-the-bridge-network.txt:2,搜「nftables」;:8,搜「nft list ruleset」)。 ↩

  5. 出处:「HTTP-level routing using a reverse proxy」第 1–10 段(text/114-fm-http-level-routing-using-a-reverse-proxy.txt:4,搜「Publish only」;:7,搜「terminate TLS」;:8,搜「UDP」)。 ↩

  6. 出处:「Working with the bridge network」第 23–42 段(text/110-fm-working-with-the-bridge-network.txt:27,搜「docker0」;:29,搜「default network」;:40,搜「single host」)。 ↩

  7. 出处:「Working with the bridge network」第 43–75 段(text/110-fm-working-with-the-bridge-network.txt:56,搜「IPAM」;:60,搜「172.17.0.0/16」;:62,搜「172.17.0.1」;:70,搜「bip」)。 ↩↩

  8. 出处:「Working with the bridge network」第 97–105 段(text/110-fm-working-with-the-bridge-network.txt:97,搜「egress」;:99,搜「veth」)。 ↩↩

  9. 出处:「Working with the bridge network」第 110–137 段(text/110-fm-working-with-the-bridge-network.txt:112,搜「embedded DNS」;:116,搜「network create」;:126,搜「172.18.0.0/16」)。 ↩↩

  10. 出处:「Working with the bridge network」第 287–343 段(text/110-fm-working-with-the-bridge-network.txt:296,搜「ping c4」;:328,搜「bad address」;:339,搜「100% packet loss」;:343,搜「extra layer of isolation」)。 ↩

  11. 出处:「Working with the bridge network」第 139–172 段(text/110-fm-working-with-the-bridge-network.txt:140,搜「IPv6」;:149,搜「fd00:dead:beef」;:163,搜「mtu=1400」;:170,搜「overlapping」)。 ↩

  12. 出处:「Working with the bridge network」第 344–386 段(text/110-fm-working-with-the-bridge-network.txt:367,搜「network connect」;:380,搜「network rm」;:384,搜「active」)。 ↩

  13. 出处:「The host and null network types」第 26–40 段(text/111-fm-the-host-and-null-network-types.txt:31,搜「removes the network namespace isolation」;:36,搜「use cases」)。 ↩↩

  14. 出处:「The host and null network types」第 41–98 段(text/111-fm-the-host-and-null-network-types.txt:44,搜「Linux only」;:48,搜「sniff」;:51,搜「strongly recommended」;:91,搜「Port conflicts」;:97,搜「user-defined network」)。 ↩↩

  15. 出处:「The host and null network types」第 100–112 段(text/111-fm-the-host-and-null-network-types.txt:101,搜「doesn't connect」;:103,搜「loopback」;:106,搜「security-sensitive」)。 ↩↩↩

  16. 出处:「Managing container ports」第 33–50 段(text/113-fm-managing-container-ports.txt:40,搜「open a gate」;:43,搜「publish a port」;:45,搜「independently」)。 ↩↩

  17. 出处:「Managing container ports」第 51–98 段(text/113-fm-managing-container-ports.txt:56,搜「-P」;:65,搜「ephemeral」;:70,搜「55000」;:81,搜「HostPort」)。 ↩↩

  18. 出处:「Managing container ports」第 100–113 段(text/113-fm-managing-container-ports.txt:104,搜「-p 8080:80」;:107,搜「host port」;:112,搜「4321/udp」)。 ↩↩

  19. 出处:「Managing container ports」第 114–119 段(text/113-fm-managing-container-ports.txt:115,搜「only documents intent」;:116,搜「wires a container port」)。 ↩↩↩

  20. 出处:「Managing container ports」第 1–31 段(text/113-fm-managing-container-ports.txt:2,搜「foundation of Kubernetes Pods」;:6,搜「same network namespace」;:16,搜「netshoot」;:28,搜「privileges」)。 ↩↩↩

  21. 出处:「HTTP-level routing using a reverse proxy」第 12–31 段(text/114-fm-http-level-routing-using-a-reverse-proxy.txt:13,搜「monolithic」;:22,搜「completely starved」;:27,搜「catalog」;:30,搜「checkout」)。 ↩↩

  22. 出处:「HTTP-level routing using a reverse proxy」第 204–282 段(text/114-fm-http-level-routing-using-a-reverse-proxy.txt:210,搜「edge router」;:213,搜「metadata」;:219,搜「traefik.enable=true」;:239,搜「PathPrefix」;:261,搜「priority」;:269,搜「traefik:v2.0」;:279,搜「exposedbydefault=false」)。 ↩↩↩

  23. 出处:「Network firewalling」第 1–34 段(text/109-fm-network-firewalling.txt:6,搜「Overlay」;:14,搜「Weave」;:18,搜「Contiv」;:27,搜「Calico」)。 ↩