资讯详情

Docker网络配置全解析:bridge、overlay与排错

📅 2026/10/28 20:13:35 | 华诺云谱 👁 阅读
Docker网络配置全解析:bridge、overlay与排错
1. 先把Docker网络这件事想明白前几天群里有人贴了一条报错截图容器里ping baidu.com提示Temporary failure in name resolution但宿主机上网正常。往下问用的是docker run默认网络宿主机跑的还是旧内核。这类问题我这些年见过太多次绝大多数不是 Docker 的 bug而是对Docker 网络配置的底层模型没摸清。这篇就围绕网络模式、自定义网络、端口映射、DNS、跨主机通信和排查手法把我自己的理解和踩坑记录摊开讲一遍。先说清楚这篇是写给谁的。如果你平时只是docker run -p一把梭容器能起来能访问那你从这里能拿到的是“为什么它能访问”以及“哪天它不能访问了怎么三分钟定位”如果你在搭多容器的服务编排、在跨主机环境里布网、或者正准备把一批服务从物理机搬进容器那第三、四、五章的内容大概能帮你省掉几个通宵。文中所有命令都在 Linux 环境验证过宿主机内核建议不低于 4.xDocker 版本用近几年任意一个稳定版都能对上。Docker 网络这件事很多人觉得玄是因为它把 Linux 内核里原本散落的几样东西打包成了一个--network参数。实际上它没有发明什么新协议用的全是现成的network namespace 做隔离、veth pair 做连线、网桥做交换、iptables 做 NAT 和过滤。把这四样东西对应到 Docker 的命令行里整个模型就通了。下面我就按“底层机制 → 默认模式 → 自定义网络 → 端口映射 → 跨主机 → 排查”这个顺序展开每一块都给出可直接复现的步骤和参数含义。1.1 容器网络到底在解决什么容器本质上是宿主机上的一个受隔离的进程它看到的网络接口、路由表、/etc/resolv.conf全都是独立的副本。所谓“配置网络”就是给这个副本造出一块或几块虚拟网卡、分配地址、写好路由、指定 DNS再决定它和外部的流量怎么走。这四件事任何一件没配对症状都很具体网卡没造出来容器里ip addr只有个lo直接连不上任何东西地址配了但路由不对出现“能 ping 通网关、ping 不通外网”地址路由都对但 NAT 规则没生效现象是能访问内网服务、访问不了公网地址前三项都好DNS 指向了不通的地址表现为域名解析失败但 IP 直连正常。我做排查时习惯按这个顺序倒推命中率非常高。因为这四个环节是依次依赖的后一个出问题一定不会掩盖前一个的症状。你先看容器里有没有eth0再看路由ip route再ping 8.8.8.8判断出网最后nslookup判断域名。四步走完问题域基本就锁死了比对着日志瞎猜快得多。需要提前说一句的是容器网络和虚拟机网络有本质区别。虚拟机通常有独立的虚拟网卡和完整的协议栈实现容器只是共享宿主机内核的命名空间所以它没有“虚拟交换机启动”这个过程也没有 DHCP 客户端的完整流程默认 bridge 模式下地址是 Docker 分配的静态值。理解这一点你就不会问出“为什么容器没有 DHCP 租约”这类问题也不会在 IP 冲突时去翻 DHCP 服务器。1.2 底层三板斧命名空间、veth pair、网桥拆开看Docker 在宿主机上建立一个名为docker0的虚拟网桥它本身是一块虚拟网卡带 IP默认172.17.0.1、带子网、带路由。每启动一个使用 bridge 网络的容器Docker 做三件事用ip netns创建一个新的网络命名空间创建一对 veth 虚拟网线一头插进容器命名空间并重命名为eth0另一头留在宿主机上命名形如vetha1b2c3把它接到docker0网桥上给容器内的eth0分配地址配一条指向docker0的默认路由。这三步在宿主机上可以直接观察。起一个容器挂着然后敲docker run -d --name net-demo nginx ip link show type veth bridge link show你会看到一对 veth其中一端master docker0。再进容器看docker exec net-demo ip addr show eth0 docker exec net-demo ip route容器内eth0的地址一般是172.17.0.2这种默认路由指向172.17.0.1。到这里容器到宿主机、容器到同网桥内其他容器的二层连通就已经建立了。剩下出网的部分靠的是宿主机内核的转发和 iptables 的地址伪装这一块放到第四章细说。注意docker0是一块真实存在的网卡它会占一个 IP、参与路由决策。如果你的宿主机的内网恰好用了172.17.0.0/16那必然冲突——这是新手最容易撞上的坑之一现象是容器能起但访问某些内网地址时通时不通。1.3 五种默认网络模式怎么选docker network ls默认会列出三个网络bridge、host、none。除此之外还有container:共享模式以及由驱动决定的overlay、macvlan、ipvlan。选型的核心就一条你要的是隔离、性能、还是可寻址性。三者很难同时满足得取舍。模式隔离性性能独立 IP典型用途bridge默认中命名空间隔离有 NAT 开销容器内网 IP绝大多数单机场景自定义 bridge中网络级隔离同上容器内网 IP多容器互联推荐默认选择host无共享宿主机网络栈最高无 NAT用宿主机 IP高性能、端口固定的服务none最强只有 lo—无纯计算任务、离线批处理container:xxx与目标容器共享无额外开销与目标容器共用sidecar、调试容器overlay跨主机逻辑隔离有封装开销跨主机可路由 IP多机集群macvlan二层隔离接近物理机局域网可路由 IP需要被外部直接寻址这张表我建议存下来。实际选型的时候还有个简单的判断法如果外部设备需要直接通过 IP 访问容器优先考虑 macvlan 或 host如果只是容器之间互相调用用自定义 bridge如果要跨机器组网才轮到 overlay。很多人一上来就想上 overlay结果单机环境里多了一层封装开销和 MTU 麻烦得不偿失。2. 默认网络模式逐个拆解与参数细节默认模式之所以容易被轻视是因为它们“能跑”跑着的时候没人管出问题时才发现默认值里埋了不少前提条件。这一章我把 bridge、host、none、container 四种模式的实际行为和边界条件讲清楚重点放在那些文档里一句话带过、但实际会咬人的地方。2.1 默认bridge能通但不推荐docker run不带--network参数时容器会挂到默认的bridge网络上。这个网络有几个硬性特征必须记住它的子网是172.17.0.0/16网关172.17.0.1容器之间默认不能通过容器名互相解析只能靠 IP 互访而且所有容器都挤在同一个网段里没有任何网络级隔离。容器名解析这件事值得单独说。默认 bridge 网络不带内嵌 DNS容器里的/etc/resolv.conf直接继承宿主机的配置所以你ping 另一个容器名是不通的。想让容器名生效只有两条路用--link已废弃别碰或者自己建一个自定义网络。这也正是下一章要展开的内容。另一个容易被忽略的点是--iccfalse。默认 bridge 下容器互通是允许的如果你在daemon.json里关掉了 ICC同网桥内的容器就互相不可达了。这个参数在老版本安全加固里常见现在基本被“用自定义网络做隔离”取代。还有地址分配的问题。默认 bridge 的动态分配是从172.17.0.2开始按序给的容器删了地址会回收但顺序不保证。如果你的脚本里硬编码了172.17.0.2某天重启后拿到别的地址服务就挂了。这种事故我见过不止一次所以凡是涉及容器间调用的一律不要用 IP 硬编码要么用自定义网络加容器名要么用服务发现。2.2 host与none两个极端--network host的表现最直白容器不再创建独立的网络命名空间直接复用宿主机的网络栈。容器里监听0.0.0.0:8080宿主机上就能用localhost:8080访问中间没有任何 NAT。性能最好延迟最低一些对网络敏感的场景高频短连接、需要拿到真实客户端 IP 的服务确实适合它。但代价同样明确。第一端口冲突变成硬冲突——两个 host 模式的容器都想监听 80第二个直接起不来。第二-p参数在这个模式下完全无效写了也不生效很多人排半天才发现自己-p白写了。第三隔离性基本没了容器里netstat能看到宿主机上所有监听端口容器进程也能改宿主机路由表安全边界等于零。--network none是另一个极端容器里只有一块lo。听起来没用实际上在两类场景很合适一是纯计算任务比如跑个数据转换、跑个编译压根不需要网络用 none 可以彻底杜绝意外外联二是网络调试你想手工在容器里配网卡做实验从零开始最干净。我做过一次 veth 手工配置的演示就是用 none 起容器然后宿主机上ip link set vethX netns 容器PID把网线手动插进去配合docker inspect -f {{.State.Pid}}拿到 PID整个过程非常直观比看十页文档管用。提示用--network none的容器/etc/resolv.conf依然会被写入但没有任何出口可用。如果你的程序启动时会阻塞在 DNS 预解析上加个--dns或干脆挂个空文件进去能省掉几秒启动时间。2.3 container模式共享网络栈的巧用--network container:目标容器名或ID让新容器直接共享目标容器的网络命名空间两者共用同一块eth0、同一个 IP、同一张路由表。它和 host 模式的思路类似但共享对象从宿主机换成了另一个容器。这个模式最典型的用途是 sidecar。比如你有一个业务容器想给它加个网络抓包或流量代理的辅助容器辅助容器用container:挂上去就能直接看到业务容器的所有流量不用改业务容器的任何配置。调试场景也常用业务容器镜像是精简版连curl都没有那就起一个带全套工具的容器共享它的网络栈localhost直接打通。坑在于生命周期绑定。目标容器一停共享它的容器网络就废了反过来共享容器先起目标容器后起也不行——必须目标先存在。另外和 host 模式一样-p在这个模式下无效端口由目标容器的配置决定。还有个细节共享网络栈意味着共享端口空间两个容器不能监听同一个端口这一点和 host 模式一致。3. 自定义网络才是正经用法只要你的场景超过“一个容器跑一个服务”就应该用自定义网络没有例外。自定义 bridge 网络解决了默认 bridge 的三个致命问题自带 DNS 解析、支持网络级隔离、可以自定义子网和网关。这一章讲怎么建、参数怎么算、怎么验证。3.1 创建、接入与内嵌DNS验证建一个最基本的自定义网络docker network create --driver bridge \ --subnet 172.20.0.0/24 \ --gateway 172.20.0.1 \ app-net然后把两个容器接进去docker run -d --name svc-a --network app-net nginx docker run -d --name svc-b --network app-net alpine sleep 3600 docker exec svc-b ping -c 3 svc-aping svc-a能通说明 DNS 生效了。这个 DNS 不是来自宿主机而是 Docker 在127.0.0.11上起的一个内嵌解析器容器里的/etc/resolv.conf会写成nameserver 127.0.0.11。它只解析同一自定义网络内的容器名和网络别名跨网络是查不到的——这正是隔离性的来源。有个细节值得记127.0.0.11是每个容器网络命名空间内的回环地址由 Docker 注入的 iptables 规则重定向到实际解析器。所以你在容器里cat /etc/resolv.conf看到127.0.0.11但宿主机上压根没有这个监听别去宿主机上找找不到的。再加一个能力网络别名。用--network-alias可以让同一个容器在网络上拥有多个名字做灰度或者服务别名时很方便。docker run -d --name svc-c --network app-net --network-alias api nginx docker exec svc-b ping -c 2 api另外运行中的容器可以动态接入和摘除网络不用重启docker network connect app-net svc-b docker network disconnect app-net svc-bconnect的时候还能指定 IP 和别名比如--ip 172.20.0.50 --alias db。这个能力在故障切换场景里挺有用——把备用容器接进网络、指定同一个别名前端代码一行不用改。3.2 子网、网关、IP段的计算方法自定义网络最容易出错的地方是地址规划。--subnet给的是整个网段--ip-range限定 Docker 从这个范围里分配容器地址--gateway指定网关。三者有硬约束--gateway必须落在--subnet内且不能和--ip-range完全重叠--ip-range必须是--subnet的子集网段不能和宿主机上已有的路由冲突否则容器路由表会出现歧义。具体怎么算举个例子。假设你要建一个能放 200 个容器的网络留足扩展空间参数取值说明subnet172.20.0.0/16总容量 65534 个地址ip-range172.20.10.0/24容器只用这一段254 个可用gateway172.20.0.1落在 subnet 内不在 ip-range 内这样设计的好处是把容器地址压缩在一个 /24 里网段其余部分留给未来可能的路由或静态地址宿主机上的路由表也更干净。如果直接--subnet 172.20.0.0/16而不限ip-rangeDocker 会从整个 /16 里分配你的路由表里会出现一大片 /16 的条目排查时干扰很大。地址容量算法很简单/24可用 254 个去掉网络地址和广播地址网关通常也占一个实际容器可用 253/26可用 62 个/28只有 14 个。给容器网络留地址宁可多留几个不要卡着刚好够用——容器滚动更新时新旧实例会短暂共存容量算得太紧会出现“地址池耗尽”的启动失败。如果你的环境里自定义网络特别多比如每个项目一套默认地址池会从172.18、172.19一路往上排很容易撞到内网。稳妥做法是在daemon.json里显式指定地址池{ default-address-pools: [ { base: 10.200.0.0/16, size: 24 } ] }改完systemctl reload docker生效。注意这个配置只影响之后新建的网络已有的网络不受影响需要重建。另外10.200.0.0/16按 /24 切能切出 256 个网络够用很久了。注意--subnet一旦创建就不能改。写错了只能删网络重建重建意味着上面所有容器要先断开、重新接入。所以建网络之前先ip route看一眼宿主机路由表确认网段不冲突这一步花十秒能省半小时。3.3 隔离性是怎么生效的同一个自定义网络内的容器互通不同网络之间默认不通。这个隔离不是靠容器配置实现的而是靠宿主机上的 iptables 链DOCKER-ISOLATION-STAGE-1和DOCKER-ISOLATION-STAGE-2实现的前者在FORWARD链里拦下跨网桥的流量后者做二次过滤。想验证隔离效果起两个网络各放一个容器互 ping 一下docker network create net-a docker network create net-b docker run -d --name a1 --network net-a alpine sleep 3600 docker run -d --name b1 --network net-b alpine sleep 3600 docker exec a1 ping -c 1 -W 1 b1 # 应该失败注意这里会先卡在 DNS 解析上因为跨网络连名字都查不到这本身就是隔离的一部分。想看 IP 层是否真的隔离得先把 IP 查出来再 pingdocker inspect -f {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}} b1然后docker exec a1 ping -c 1 -W 1 那个IP一样不通。需要打通两个网络的时候标准做法是让某个容器同时接入两个网络充当路由器或网关的角色。比如一个 nginx 同时挂frontend和backend对外只暴露 80后端服务全在backend网络里外部根本访问不到。这种“双网卡网关”模式比配 iptables 转发规则干净得多也是我做多层架构时的默认选择。4. 端口映射背后的iptables链端口映射是-p一行参数但它背后的链路最长、出问题最多。这一章把-p的各种写法、报文在宿主机上的实际路径、以及“映射了但连不上”的几种典型原因讲透。4.1 -p参数的写法与语义差异-p的格式是[宿主机IP:]宿主机端口:容器端口[/协议]方括号部分可省略。不同写法行为差别很大写法含义安全性-p 8080:80绑定 0.0.0.0:8080所有网卡可访问-p 127.0.0.1:8080:80只绑本地回环仅本机可访问-p 8080:80/udp只映射 UDP注意协议-p 8080:80 -p 8443:443多个端口可重复--expose 80仅声明不映射无实际效果这里有两个高频误区。第一-p 8080:80不指定宿主机 IP 时绑的是0.0.0.0也就是所有网卡都能访问在公网机器上这等于把端口暴露出去如果你的防火墙没拦就是事故。稳妥写法是显式指定127.0.0.1:8080:80前面再挂一层反向代理。第二--expose只是文档性的声明它不会创建任何 iptables 规则也不会让外部访问到很多人以为写了--expose就能访问结果白折腾。还有一个隐藏参数--publish-all-P它会把这个镜像 Dockerfile 里所有EXPOSE的端口随机映射到宿主机的高位端口用docker port查具体映射。这个在本地调试时挺方便生产环境不建议用——端口随机配置和防火墙都没法提前准备。4.2 报文来到宿主机后走了哪条路理解这条链路排查时能省下大量时间。假设外部客户端访问宿主机的8080容器内服务监听80报文路径大致是报文到达宿主机网卡进入nat表的PREROUTING链DOCKER链里有一条 DNAT 规则把目的地址宿主机IP:8080改写为容器IP:80报文经路由决策转发到docker0最终到达容器容器回包时源地址是容器 IP到宿主机POSTROUTING时被 MASQUERADE 改写回宿主机地址客户端才不会因为源地址不可路由而丢弃。查看这些规则iptables -t nat -L DOCKER -n -v iptables -t nat -L POSTROUTING -n -v你会看到类似DNAT tcp dpt:8080 to:172.20.0.2:80的条目和一条MASQUERADE all -- 172.20.0.0/24 !172.20.0.0/24的伪装规则。这两条缺一不可只有 DNAT 没有 MASQUERADE报文能进容器但回不来只有 MASQUERADE 没有 DNAT外部压根不知道往哪个容器送。这里要提一下docker-proxy进程。老版本 Docker 会在宿主机上起一个用户态代理监听映射端口把流量转发给容器新版本默认走纯 iptables 转发userland-proxyfalse但某些场景下代理仍然存在。如果你在ss -lntp里看到docker-proxy占着端口那是正常的别急着 kill。注意如果你在宿主机上手工清过 iptables 规则比如跑过iptables -FDocker 的规则链会被清掉已运行的容器端口映射立刻失效而且不会自动恢复。恢复办法是重启 Docker 服务或者systemctl reload docker让它重建规则。这也是我建议“别在装了 Docker 的机器上手动玩 iptables -F”的原因。4.3 映射不通的四类原因按我处理过的工单统计端口映射失败基本落在四类第一类服务没监听对地址。容器内服务绑了127.0.0.1:80而不是0.0.0.0:80那么即使 DNAT 正确报文进容器后目的地址是容器 IP服务不认。这个错误极其常见因为很多开发环境的默认配置就是绑回环。验证方法docker exec 容器 ss -lntp看监听地址是0.0.0.0还是127.0.0.1。第二类宿主机的防火墙拦在前面。firewalld、ufw或云厂商的安全组在PREROUTING之前就把包丢了Docker 的规则压根没机会执行。现象是iptables -t nat -L DOCKER -n -v里对应规则的包计数一直是 0。这时候查防火墙别查 Docker。第三类网段冲突。宿主机内网是172.17.0.0/16和docker0撞了路由表里两条同网段条目打架表现为“有时通有时不通”。解法是改daemon.json里的bip参数把docker0挪到别的网段{ bip: 10.201.0.1/24, fixed-cidr: 10.201.0.0/24 }第四类内核转发没开。net.ipv4.ip_forward为 0 时宿主机不做转发NAT 规则形同虚设。Docker 启动时会尝试打开它但如果你的sysctl配置在别处又给关回去了就会出问题。sysctl net.ipv4.ip_forward应该是 1。还有一个和 bridge 相关的内核参数值得单独提net.bridge.bridge-nf-call-iptables。它决定二层桥接流量是否经过 iptables 过滤。某些发行版或加载顺序问题会让它是 0症状是端口映射时通时不通、容器间通信异常。这个参数在较新内核里归br_netfilter模块管排查时先lsmod | grep br_netfilter确认模块加载了再看参数值。5. 跨主机与特殊网络场景单机上的问题靠前面几章基本能覆盖。一旦涉及多台机器网络模型就换了一套规则。这一章讲 overlay 和 macvlan 这两种跨主机方案的前提条件和常见坑以及它们各自不适合什么场景。5.1 overlay网络的前提条件与MTU陷阱overlay 的思路是在宿主机网络之上再叠一层虚拟网络容器之间用 VXLAN 封装通信对外看起来像在同一个二层网络里。它在单机上也能跑但真正的价值在跨主机。前提条件有三个缺一个都起不来集群里所有节点要能互相通信这个通常靠编排工具管理、需要有一个共享的键值存储来同步网络状态用编排工具时会自动处理、节点间要有开放的 VXLAN 端口默认 4789/UDP。docker network create --driver overlay \ --subnet 10.0.9.0/24 \ --opt encrypted \ cross-net--opt encrypted会启用 IPsec 加密代价是性能下降测试环境可以不加。overlay 最容易咬人的地方是MTU。VXLAN 封装要在原始报文外面加 50 字节左右的头所以 overlay 网络的 MTU 默认是 1450而不是 1500。如果宿主机物理网卡 MTU 是 1500封装后总长超过 1500 就会被分片或者丢弃症状是大包能过、小包能过、中间某个尺寸的包全丢——比如ping -s 1400通ping -s 1470不通或者 HTTP 请求体超过一定大小就卡死。排查方法很简单容器之间互 ping 指定大小docker exec c1 ping -c 3 -M do -s 1422 c2-M do禁止分片-s 1422是载荷大小。1422 8ICMP 头 20IP 头 1450正好卡在 overlay 的 MTU 上。如果这个尺寸通往上加到 1450 就不通了说明 MTU 配置正确问题不在这一层。如果宿主机本身的网络 MTU 就不是 1500比如某些隧道环境是 1400那 overlay 的 MTU 要跟着往下调创建网络时加--opt com.docker.network.driver.mtu1350。这个参数一定要在创建时指定事后改不了。5.2 macvlan与ipvlan让容器拿到局域网IPmacvlan 的思路完全不同它给容器分配一个和宿主机同网段的 IP容器的 MAC 地址直接出现在物理交换机上从外部看容器就是一台独立的机器。这个能力在“需要被外部设备直接访问”的场景里无可替代比如一些老系统只能靠 IP 白名单认证容器化之后必须保留可路由 IP。docker network create -d macvlan \ --subnet 192.168.1.0/24 \ --gateway 192.168.1.1 \ -o parenteth0 \ mv-net-o parenteth0指定走哪块物理网卡。macvlan 的坑集中在两点。第一宿主机和 macvlan 容器默认不能直接通信因为物理网卡在 macvlan 模式下会阻止这种“同一网卡进出”的流量。解法是再建一个 macvlan 网络专门给宿主机用或者用 ipvlan 的 L3 模式绕开。第二某些交换机会做 MAC 地址限制比如每个物理口只允许一个 MAC这时候 macvlan 容器的流量会被直接丢弃而且现象非常诡异——容器自己有 IP、能 ping 通自己但外界完全看不到。遇到这种情况先找网络管理员确认交换机配置别在容器侧找原因。ipvlan 和 macvlan 定位接近区别是多个容器共用宿主机的 MAC 地址靠 IP 区分。它在交换机有 MAC 数量限制的环境里更友好但同样有宿主机不能直接访问的限制。选型上如果环境对 MAC 数量敏感就用 ipvlan否则 macvlan 的兼容性更好一些。6. 常见问题速查与排查手法这一章我把高频问题和对应命令整理成速查表后面再补几个诊断工具的具体用法。排查网络问题的核心原则是不要猜让命令告诉你答案。6.1 问题速查表现象优先检查常用命令容器内 DNS 解析失败/etc/resolv.conf内容docker exec x cat /etc/resolv.conf容器名互 ping 不通是否在同一自定义网络docker network inspect net端口映射不通服务监听地址、防火墙ss -lntp、iptables -t nat -L DOCKER -n -v能访问内网不能访问公网ip_forward、MASQUERADEsysctl net.ipv4.ip_forward大包丢失、连接卡死MTUping -M do -s size容器启动即网络异常网段冲突ip route、docker network inspect跨网络访问失败隔离规则、是否双网卡iptables -L DOCKER-ISOLATION-STAGE-1 -n -v宿主机重启后映射失效Docker 规则未重建systemctl restart docker这张表建议贴在工位上。实际排查时按“容器内 → 宿主机 → 外部”三层往外走每一层都有明确命令可以验证不要跳层。6.2 DNS问题的三种成因DNS 报错是最高频的问题成因基本是三类。第一类是默认 bridge 网络没有内嵌 DNS容器名互访不通。这个前面讲过解法是换自定义网络没有别的办法。第二类是/etc/resolv.conf被错误覆盖。比如挂载了宿主机的resolv.conf进去-v /etc/resolv.conf:/etc/resolv.conf或者容器内程序启动时自己重写了这个文件。注意容器里那个文件是挂载点宿主机上的服务比如systemd-resolved改了它容器内会立刻同步如果新内容是nameserver 127.0.0.53在容器命名空间里这个地址没有服务解析必然失败。解法是指定 DNS--dns 223.5.5.5 --dns 119.29.29.29。第三类是自定义网络的 DNS 被覆盖。Docker 在自定义网络里注入127.0.0.11但如果你同时在daemon.json里配了dns全局值或者docker run时加了--dns注入可能被覆盖容器名就解析不了了。这时候cat /etc/resolv.conf一看便知——正常应该是nameserver 127.0.0.11在最前面。提示--network host模式下不要用--dns因为容器共享宿主机网络栈/etc/resolv.conf是宿主机的加了也没用还可能让配置看起来混乱。6.3 抓包与命名空间排查命令查不出来的时候上抓包。容器内的网卡在宿主机上对应一个 veth直接在那个 veth 上抓能看到容器视角的原始流量# 先找到容器对应的 veth docker exec 容器 cat /sys/class/net/eth0/iflink # 输出比如 12然后在宿主机上找 ifindex 为 12 的接口 ip -o link | awk -F: $1 ~ /12$/ {print $2} # 抓到容器边界上的包 tcpdump -i vethXXXX -nn -vv如果想更彻底地进入容器的网络命名空间用 PID 直连PID$(docker inspect -f {{.State.Pid}} 容器) nsenter -t $PID -n ip addr nsenter -t $PID -n tcpdump -i eth0 -nnnsenter这个手法我很推荐它让你站在容器内部看网络比docker exec更底层——docker exec依赖容器里有对应工具精简镜像可能连ip命令都没有而nsenter用的是宿主机的工具不受镜像限制。还有一个容易被忽略的工具conntrack。NAT 场景下的连接跟踪表如果满了或者有残留会出现“新连接建立不了但老连接正常”的怪现象conntrack -L | grep 容器IP sysctl net.netfilter.nf_conntrack_count sysctl net.netfilter.nf_conntrack_max如果count接近max就该调大上限了。这个在流量大的机器上不是罕见问题。7. 我踩过的坑和几条硬经验写到这里把几个印象最深的事故记录一下都是文档里不会写、但实际会让人熬通宵的东西。第一件网段冲突。某次在一台内网机器上部署容器起得很正常但访问某个内部服务时好时坏。查了两小时最后ip route一看宿主机内网就是172.17.0.0/16和docker0完全重叠。报文在两个同网段路由之间反复横跳。改bip重启 Docker 后一切正常。结论部署前先看一眼宿主机网段172.17、172.18、10.0.0.0/8这些常用段都可能撞。第二件iptables -F的教训。早期有次为了排查一个防火墙问题在宿主机上执行了iptables -F当时所有容器的端口映射立刻失效。更麻烦的是Docker 不会自动重建规则必须重启服务。从那以后我给自己定了条规矩装了 Docker 的机器动 iptables 一律先iptables-save备份且只用-I插规则不碰-F。第三件MTU 引发的“玄学卡顿”。一个服务在容器里跑小请求正常上传稍大的文件就超时。查了应用日志、查了磁盘、查了内存最后ping -M do -s 1472发现大包不通才定位到宿主机网卡 MTU 是 1400而容器默认 1500。调整 Docker 网络的 MTU 后恢复。这类问题的特征是“小流量正常、大流量异常”看到这个模式先怀疑 MTU。第四件自定义网络的 DNS 被全局配置覆盖。在daemon.json里配了dns之后自定义网络里的容器名解析全部失效因为127.0.0.11被挤掉了。这个问题的隐蔽性在于镜像构建时一切正常只有用了自定义网络加容器名互访的场景才暴露。我的做法是要么不用全局dns要么明确知道它的副作用在需要容器名解析的场景里手动确认resolv.conf的第一行。最后分享一个我现在的习惯新建任何自定义网络时顺手把subnet、gateway、ip-range三个值写进项目的 README并附一句“此网段不可与其他项目冲突”。这事看着多余但团队里三五个人各自建网络、几个月后要合并部署的时候这句备注能省掉一整轮排查。网络配置这种基础设施出问题时排查成本远高于设计成本前期多花十分钟规划后面少掉一堆头发。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑