Docker

Docker 网络排障实战:Namespace、Bridge、DNS、iptables、Conntrack 与抓包

按应用、DNS、路由、veth/bridge、防火墙、conntrack、MTU 和分点抓包逐层定位 Docker 网络故障,并把修复写回声明式配置。

TY
Tycho
技术博主
• 2026-09-28 • 26 分钟阅读 • 5 次浏览
Docker 网络排障实战:Namespace、Bridge、DNS、iptables、Conntrack 与抓包

一、建立 Docker 网络心智模型

排障前先理解包路径。用户定义 bridge 网络中,每个容器有独立 network namespace,经 veth pair 接到 Linux bridge;Docker 提供内嵌 DNS,宿主机通过 iptables/nftables 完成转发和 NAT。

process -> container eth0 -> veth -> docker bridge
        -> host FORWARD / DOCKER-USER / DOCKER chains
        -> host NIC -> upstream

service name -> 127.0.0.11 embedded DNS -> container IP
published port -> DNAT -> container port

二、保存现场,禁止先重启

重启会丢失 namespace、conntrack 和瞬时错误。先记录容器、网络、路由、DNS、端口、规则和时间。

date -Is
docker ps --no-trunc
docker network ls
docker inspect app-api > evidence/app-api.inspect.json
docker network inspect app-net > evidence/app-net.inspect.json
ip -br addr
ip route
ss -lntup

三、从应用层确认错误类型

先区分 DNS、TCP 与 HTTP

在容器内分别测试名称解析、TCP 连接和 HTTP,避免把所有问题叫“网络不通”。

docker exec app-api getent hosts db
docker exec app-api sh -lc 'cat /etc/resolv.conf; ip route'
docker exec app-api nc -vz -w 3 db 5432
docker exec app-api curl -sv --connect-timeout 3 http://catalog:8080/healthz

Name or service not known 看 DNS;Connection refused 表示地址可达但无监听;timeout 可能是路由、策略、防火墙或丢包;HTTP 5xx 已经到应用层。

四、用户定义 Bridge 与服务发现

生产 Compose 应使用显式网络和服务名,不依赖容器 IP。用户定义 bridge 提供内嵌 DNS;默认 bridge 的行为和隔离更有限。

services:
  api:
    image: registry.example/api:1.8.0
    networks: [frontend, backend]
  db:
    image: postgres:18
    networks:
      backend:
        aliases: [orders-db]
networks:
  frontend: {driver: bridge}
  backend:
    driver: bridge
    internal: true
docker compose config
docker network inspect project_backend
docker exec project-api-1 getent ahostsv4 orders-db

internal: true 阻止该网络直接访问外部,但多网卡容器仍可能通过另一个网络出站,必须按完整拓扑评估。

五、检查 Namespace、veth 与 Bridge

获取容器 PID 进入其 network namespace,比较容器 eth0 与宿主 veth 对端。

pid=$(docker inspect -f '{{.State.Pid}}' app-api)
sudo nsenter -t "$pid" -n ip -br link
sudo nsenter -t "$pid" -n ip addr
sudo nsenter -t "$pid" -n ip route
ip -d link show type veth
bridge link
bridge fdb show

没有默认路由、接口 DOWN、地址不在网络子网或 veth 未接 bridge 都会导致连接失败。

六、内嵌 DNS 127.0.0.11

自定义网络容器通常把查询发到 127.0.0.11。确认 /etc/resolv.conf、服务别名、网络附件和查询类型。

docker exec app-api cat /etc/resolv.conf
docker exec app-api getent ahosts catalog
docker inspect -f '{{json .NetworkSettings.Networks}}' app-api | jq
docker inspect -f '{{json .NetworkSettings.Networks}}' catalog | jq

两个容器必须共享至少一个网络。Compose 项目名会影响网络名,但服务 DNS 通常用 service key。不要把 localhost 当成另一个容器;容器内 localhost 只指自己。

七、监听地址与端口发布

服务监听 127.0.0.1 时只接受容器内部回环连接,应监听 0.0.0.0 或容器接口地址。EXPOSE 只是元数据,不等于发布宿主端口。

docker exec app-api ss -lntp
docker port app-api
curl -v http://127.0.0.1:8080/healthz
services:
  api:
    ports:
      - "127.0.0.1:8080:8080"

绑定宿主 127.0.0.1 可避免服务意外暴露公网;需要外部访问时通过受控反向代理。

八、路由、多网络与默认网关

多网络容器可能选错默认网关。检查每个网络的 gw-priority 或 Compose 路由设计,避免靠连接顺序碰运气。

docker exec app-api ip route get 1.1.1.1
docker inspect app-api --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{$v.IPAddress}} {{$v.Gateway}}{{println}}{{end}}'
docker network connect --gw-priority 1 egress-net app-api

变更在线容器只用于验证;最终修复写回 Compose/部署清单并重建。

九、Docker 防火墙链与 DOCKER-USER

自定义策略不要破坏 Docker 链

Docker 会创建转发和 NAT 规则。自定义策略应优先放在 DOCKER-USER,不要直接清空 Docker 链。关闭 Docker iptables 管理通常会破坏容器网络且增加风险。

sudo iptables -S DOCKER-USER
sudo iptables -S FORWARD
sudo iptables -t nat -S DOCKER
sudo nft list ruleset > evidence/nft-ruleset.txt
sudo iptables -I DOCKER-USER 1 -s 172.20.0.0/16 -d 10.0.0.0/8 -j REJECT
sudo iptables -A DOCKER-USER -j RETURN

规则先在维护窗口验证,并保存可恢复版本。iptables 与 nft 后端混用时,要确认实际生效层。

十、Conntrack 与端口耗尽

高并发 timeout 可能来自 conntrack 表满或临时端口耗尽。比较当前/上限和 TCP 状态。

sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
conntrack -S
ss -s
ss -tan state time-wait | wc -l
sysctl net.ipv4.ip_local_port_range

不要先扩大上限;先检查连接复用、客户端超时、keepalive、重试风暴和泄漏。扩大 conntrack 会增加内存消耗。

十一、分点抓包定位丢失位置

用同一五元组关联三个观察点

同一请求分别在容器接口、bridge 和宿主出口抓包,用五元组与时间关联。抓包可能包含敏感数据,要限制范围并安全销毁。

pid=$(docker inspect -f '{{.State.Pid}}' app-api)
sudo nsenter -t "$pid" -n tcpdump -ni any host 10.20.0.15 and port 5432 -w /tmp/container.pcap
sudo tcpdump -ni br-REPLACE host 10.20.0.15 and port 5432 -w /tmp/bridge.pcap
sudo tcpdump -ni eth0 host 10.20.0.15 and port 5432 -w /tmp/host.pcap

SYN 只在容器出现:veth/bridge/host policy;SYN 出宿主无回应:上游路由/ACL/服务;握手成功后 RST:监听或应用;重复重传:丢包、MTU 或拥塞。

十二、MTU 与 Overlay/VPN

VPN、云隧道和 overlay 会降低可用 MTU。小请求成功、大响应卡住时检查 Path MTU。

ip link show
docker network inspect app-net -f '{{json .Options}}'
ping -M do -s 1472 TARGET_IP
tracepath TARGET_IP

调整 Docker daemon 或网络 MTU 后通常需要重建网络;先根据底层链路计算,不要任意设小值。

{
  "mtu": 1450
}

十三、标准排障决策树

  1. 容器是否运行、应用是否监听正确地址。
  2. DNS 是否返回预期地址,容器是否共享网络。
  3. ip route get 是否走预期接口和网关。
  4. TCP 是 refused、timeout 还是 reset。
  5. DOCKER-USER/FORWARD/NAT 是否命中。
  6. conntrack、端口、MTU 是否异常。
  7. 分点抓包确认包在哪一层消失。
docker events --since 30m
journalctl -u docker --since '-30 min'
dmesg -T | rg 'conntrack|drop|oom|veth'

十四、修复、复测与官方资料

修复必须写回声明式配置,重建单个服务后重复原失败请求,并验证同网络其他服务没有回归。保留修复前后路由、规则和抓包摘要。

docker compose up -d --no-deps --force-recreate api
docker compose ps
docker exec app-api getent hosts db
docker exec app-api nc -vz db 5432
curl --fail http://127.0.0.1:8080/healthz
  • Docker Networking overview、Bridge driver、Packet filtering and firewalls 官方文档。
  • Linux network_namespaces(7)、veth(4)、iproute2、netfilter/conntrack 文档。

验收不仅是“curl 通了”,还包括 DNS、路由、策略、端口暴露和重建后状态都符合预期。

TY

Tycho

热爱分享技术知识,帮助开发者成长。

评论 (0)

评论功能当前已关闭
暂无评论,快来抢沙发吧!