一、建立 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
}
十三、标准排障决策树
- 容器是否运行、应用是否监听正确地址。
- DNS 是否返回预期地址,容器是否共享网络。
ip route get是否走预期接口和网关。- TCP 是 refused、timeout 还是 reset。
- DOCKER-USER/FORWARD/NAT 是否命中。
- conntrack、端口、MTU 是否异常。
- 分点抓包确认包在哪一层消失。
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、路由、策略、端口暴露和重建后状态都符合预期。