首页 / 资讯中心 / 文章详情

Docker容器隔离与网络通信原理:从namespace到iptables一网打尽

Docker容器隔离与网络通信原理:从namespace到iptables一网打尽 ★ FEATURED ARTICLE
先说个现象很多人本机用Docker跑一两个容器感觉一切都很美好可一旦项目多起来要同时起十几个服务最让人头疼的一定是网络。容器之间互相不通、端口映射诡异失效、跨主机通信直接懵圈这些问题几乎每个人都会撞上。这是Docker系列第五篇前面几篇把镜像、容器命令、数据卷这些基础聊得差不多了这次换个视角把Docker容器的隔离原理和网络通信从底层掰开揉碎讲一遍。文章不绕弯子。我会从内核的命名空间namespace和cgroups讲起再沿着一条真实的数据包路径把veth pair、docker0网桥、iptables NAT这些关键点逐个拆透最后附上我平时排查网络问题用的一套命令组合。适合想把Docker用明白、遇到网络问题不抓瞎的同学也适合对“容器到底怎么隔离的、怎么通信的”有好奇心的读者。看完你可以自己上手复现一遍绝对比只看文档收货大得多。1. 隔离的根命名空间与cgroups到底做了什么1.1 容器不是虚拟机共享内核带来的隔离边界先说清楚一个基础认知虚拟机的隔离是彻底的每个虚拟机都有自己完整的客户操作系统哪怕底层共享物理硬件上层也是两套互不感知的内核。容器不一样容器没有自己的内核它和宿主机共享同一个Linux内核。既然共享内核那隔离从哪来全靠内核自身提供的命名空间机制给进程创造出一整套“看起来独立”的系统视图。这个边界一定要记住容器的隔离是内核层面的“视图隔离”不是物理层面的“资源隔离”。容器里的进程看到的进程号、网络接口、挂载点、主机名都是被重新虚拟过的但发生系统调用时用的还是宿主机内核。这也就解释了为什么容器在某些场景下并不适合承载高安全要求的任务——共享内核意味着攻击面更大隔离并不绝对。打个比方你就理解了集装箱酒店里面每个房间的家具摆设是独立布置的房卡只能开自己的门门牌号也是独立的。但水管、电路、承重墙是整栋楼共用的。某个房间如果乱拉电线影响的不只是它自己。这就是容器和虚拟机最生动的区别。Docker容器默认启用了内核提供的几大命名空间常见的六种如下命名空间隔离内容类比Mountmnt文件系统挂载点视图房间里的“装修”各家不同PID进程编号容器内PID从1开始每个房间都有自己的一套门牌号Networknet网络接口、IP地址、路由表、iptables规则每个房间独立网口和网线IPCSystem V IPC对象、消息队列房间之间的对讲频道互相看不见UTS主机名和域名每个房间可以起自己的名字Useruser用户和组ID前台登记表与门禁卡系统注意这里的措辞Docker“默认启用”了绝大多数命名空间但User命名空间比较特殊Docker默认并不开启用户映射需要daemon层配置userns-remap。后面讲到权限相关的坑时再说。1.2 cgroups能看得到还不够还得限得住命名空间解决的是“能看到什么”的问题cgroups解决的才是“能用多少”的问题。容器光有隔离视图还不够如果不限制资源一个写死循环的容器完全可以把宿主机CPU吃满把同机其他容器全部拖垮。这是生产环境里最常见的事故形态之一比什么网络故障都致命。cgroups在内核里做的事情就是把进程分成组然后按组限制CPU、内存、IO等资源。Docker每启动一个容器就会为它建一组对应的cgroup目录并且把容器内所有进程都放进去。这样内核调度器在分配资源时就有了“这一组最多能用多少”的硬性约束。cgroup v1和v2的实现细节差异比较大。v1时代内存、CPU、IO分开在不同的子系统目录里管理v2把统一层级合并了控制更加清晰。现在的发行版基本都切到了cgroup v2Docker也做了完整适配。日常使用不需要手动去操作cgroup但出问题排查时很有用比如容器内存持续高直接去读容器对应的cgroup目录下的memory.peak文件比docker stats给的信息更细。cgroup能限制的不止CPU和内存还有IO带宽、进程数PID cgroup、CPU绑定核数cpuset等等。这些限制是内核强制执行的进程没法靠“自觉”绕过。这恰恰是容器能安全多租户运行的根本隔离视图保证看不到别人配额保证抢不了别人的资源。两者缺一不可。2. 六大命名空间逐个拆解隔离的每一面2.1 PID命名空间容器内也有PID 1PID命名空间的作用是让一组进程看到自己的进程号从1开始同时对其他命名空间里的进程视而不见。所以你在容器里执行ps aux看到的PID列表和宿主机上看到的完全是两回事。具体来说容器里的主进程在宿主机上有一个真实PID比如35781但它在容器内的视图里就是PID 1。这个设计有两个影响需要特别注意。第一容器内的PID 1承担着类似init的角色。Linux里的孤儿进程会被最近的祖先进程收养而在容器里PID 1就是所有进程的祖先。如果容器主进程不做“收破烂”的工作——也就是不调用wait()回收僵尸子进程——那么容器里会积攒一大堆状态为defunct的僵尸进程杀也杀不掉只能重启容器。很多用Node.js、Python直接当容器主进程的朋友跑一段时间就发现容器里僵尸进程一堆原因就在这。标准解法是用tini这类轻量init进程作为PID 1然后让业务进程当它的子进程把信号转发和僵尸回收都交给它。第二PID命名空间内的进程看不到其他容器的进程这既是安全隔离的基础也带来一个隐藏的不便容器内无法通过PID直接调试宿主进程。反过来宿主机倒是可以通过nsenter进入容器的命名空间操作这也是后面实操验证环节的核心工具。ps命令看到的进程列表其实是内核按当前进程的PID命名空间过滤后的结果。/proc文件系统也是同样的道理容器内的/proc只能看到本命名空间内的进程信息。这正是“视图隔离”最直观的体现。2.2 Network命名空间容器网络虚拟化的地基Network命名空间是整个容器网络通信的基石。每一个网络命名空间都有自己独立的网络设备、IP地址、路由表、ARP表、iptables规则和netfilter钩子。这意味着容器A的路由表在容器B里完全不可见容器A配错路由也不会影响容器B的网络栈。Docker启动容器时会先创建一个全新的网络命名空间然后插上一根“虚拟网线”也就是veth pair一头连进新命名空间另一头挂在宿主机的一个网桥上。这样一来容器既有了自己独立的网络视图又通过那根“虚拟网线”和外界连上了路。你在宿主机上用ip addr看到的vethxxxx接口就是那根虚拟网线留在宿主机端的一头。正因为网络命名空间是隔离的每个容器都可以配置完全不同的网络参数甚至可以自己起一套路由协议、自己加载防火墙规则。这也是所有容器网络插件overlay、VXLAN、macvlan能实现的前提每个容器都像一台独立主机的网络接口才有办法通过底层隧道把它们“串”起来。遇到网络问题第一反应应该是确认容器落在哪个网络命名空间、挂到了哪个网桥、走的哪条veth。这是我排查容器网络故障时永远的第一步。2.3 剩下的四个命名空间也不可小觑Mount命名空间隔离的是文件系统的挂载点视图。容器里看到的根目录其实是镜像各层叠加外加容器可写层拼出来的视图宿主机上并没有一个真实的“/”直接对应它。容器里挂载一个临时挂载点宿主机和其他容器都看不到。数据卷之所以能通本质上是把宿主机目录通过共享挂载的方式“送”进了容器的挂载命名空间。理解了这个你就明白为什么卷里面的文件改动能互相看见而普通容器内生成的临时文件就不行。UTS命名空间隔离的是主机名和域名。默认情况下每个容器会拿一个随机ID当主机名比如f4a0b2c1e9d3。你也可以用docker run -h myhost指定一个容器内部的hostname命令就会返回它。这个隔离对业务没啥影响但有些按主机名做配置分发的中间件在容器里会踩坑。IPC命名空间隔离的是System V IPC对象和POSIX消息队列。两个不同IPC命名空间里的进程不能通过消息队列通信。这个相对冷门但如果你的应用重度依赖共享内存消息队列迁移到容器时要特别留意。User命名空间隔离的是用户和组ID。它可以让容器内一个uid为0的进程在宿主机上其实映射到了一个非特权uid比如100000。概念上很安全但Docker默认不启用因为一旦启用容器内权限和挂载卷的文件属主映射会很绕生产环境轻易不开在严格安全要求下才考虑。3. 容器网络通信的三板斧veth、docker0、iptables3.1 veth pair和docker0一根虚拟网线加一台虚拟交换机容器默认网络模式是bridgeDocker会在宿主机上创建一个名为docker0的Linux网桥默认网段在172.17.0.0/16附近实际以环境为准。Linux网桥在概念上就是一个虚拟的二层交换机负责在同一网段内转发数据帧。每个容器启动时Docker会创建一对veth虚拟设备。veth是成对出现的一个包从一端进去另一端原封不动出来完全就是一根虚拟网线的行为。这一对veth一端挂在docker0网桥上另一端作为eth0塞进容器的网络命名空间。于是容器里的eth0拿到一个172.17.0.X的IP宿主机上看则是vethXXXX这样的接口。用文字描述这个拓扑就是容器A eth0 —veth— docker0网桥 —veth— 容器B eth0docker0不只是个二层交换机它还兼任容器的默认网关。容器里的默认路由通常是172.17.0.1也就是docker0自己的IP。容器要访问外网时数据包先到docker0内核根据路由表判断包该往哪走。所以docker0必须配置IP地址不像普通交换机那样只工作在二层。这一点是理解容器出网链路的关键。很多初学者抓包时疑惑“为什么在docker0上能看到包在物理网卡上也看到包但内容不一样”理解了网关和NAT之后就顺理成章了。3.2 容器访问外网的链路与NAT一个容器访问外部IP时数据包的完整路径大致是这样容器内eth0发出数据包 → 穿过veth到达docker0 → docker0按路由表判断目标不在本网段 → 走宿主机默认路由 → 在出物理网卡前经过iptables POSTROUTING链 → 源IP被替换成宿主机物理网卡的IP → 数据包发出去。关键就藏在源IP替换这一步。容器IP是172.17.x.x这种私有网段地址外面的路由器收到源地址为私有网段的包根本没有回包路径。Docker在iptables的POSTROUTING链写了一条MASQUERADE规则把所有源地址在docker网段的出站流量改成宿主机出口IP。回包时内核的连接跟踪conntrack机制会记住映射关系再把目的IP改回那个172.17.x.x完成双向通信。这条MASQUERADE规则是Docker创建网络时自动配上的。一旦有人把iptables清空或者禁用了docker0网段的地址伪装最直接的症状就是“容器之间能互通但访问外网全失败”。这种故障在阿里云、腾讯云这类安全加固过的环境里尤其常见因为有些运维脚本会顺手执行iptables -F。还有ip_forward这个内核参数。Linux默认未必开启IP转发而docker0网桥上的流量从veth转发到物理网卡必须依赖内核的net.ipv4.ip_forward1。Docker在启动时会自动打开它但某些安全赛题脚本会把它改回去。一旦改回去容器访问外网也会失败。排查时记得顺手看一眼sysctl net.ipv4.ip_forward。3.3 为什么外网默认ping不进容器有朋友会想既然容器能上网外网应该也能访问容器的IP吧答案是访问不了其一是因为容器172.17.x.x是私有地址外面根本没有到达它的路由其二即使有路由宿主机iptables默认INPUT和FORWARD策略也不会放行外部主动发起的、没有既有连接状态的入站流量。你想让外部访问容器唯一的正规途径就是端口映射。端口映射说白了就是在宿主机上开一扇“定向门”数据包先到达宿主机IP的某个端口然后内核netfilter在PREROUTING阶段把目标地址改写成容器的IP和端口也就是下一章要讲的-p参数背后的DNAT规则。这里也可以顺带解释一个安全常识不要把容器的私有IP直接暴露到公网也不要依赖“内网IP能被外网路由”的侥幸。所有对外服务都应该通过宿主机的端口映射对外把暴露面控制在一层可控的入口上。这是容器网络设计里最基本的安全姿态。4. 端口映射与容器间通信的实现细节4.1 -p参数背后是两个机制在配合docker run -d -p 8080:80 nginx表面上是“把容器的80端口映射到宿主机的8080端口”底层其实是两个机制同时在工作用户态的docker-proxy进程和内核态的iptables DNAT规则。docker-proxy会监听宿主机的8080端口然后把到达的连接转发给容器的IP:80。这个进程存在的意义主要是兼容某些特殊情况比如DNAT规则无法覆盖的场景或者宿主机开启了非常严格的防火墙策略。与此同时iptables的DNAT规则会在数据包进入PREROUTING链时直接把目标IP和端口改写成容器的IP和端口让流量走内核转发路径不经过用户态代理性能更高。查docker-proxy很简单ps aux | grep docker-proxy看iptables规则更直观iptables -t nat -L -n在DOCKER链里你会看到类似下面的规则DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.3:80这就是端口映射在内核层面的真实形态。进阶用法上-p 127.0.0.1:8080:80会把监听地址限制在宿主机回环接口上外部根本访问不到适合做本机调试。-P则随机从宿主机端口池里挑一个大端口映射到容器暴露端口适合快速测试但不好用于固定服务。这里有个被很多新手忽略的坑容器主进程监听的地址。如果你的服务在容器内只监听了127.0.0.1就算端口映射配得再对外部也访问不了。因为DNAT把包送到了容器IP结果容器里服务只认回环地址连接自然被拒绝。碰到端口映射不生效先进容器执行netstat -tlnp看清楚进程监听在哪个地址上。4.2 同一桥接网络的容器怎么互相访问同一个bridge网络里的容器IP位于同一子网互相通信不经过任何NAT直接靠内核二层转发完成。数据包从容器A的eth0发出穿过veth到docker0docker0查MAC表直接转发到容器B的veth全程不查询iptables规则。Docker默认允许同一bridge网络上的容器互相通信这个开关在自定义网络里对应参数com.docker.network.bridge.enable_inter_container_communication默认是true。如果你建网络时把它设成了false同网络内的容器也会互相隔离那通常是多租户场景刻意为之。需要注意默认docker0网络和自定义bridge网络之间是相互隔离的。两个容器一个落在默认网络一个落在自定义网络即使IP段接近直接通信也走不通。解决办法有两种要么把容器放到同一个自定义网络里要么用docker network connect给容器“加装”第二块网卡让它同时接入两个网络。业务上常见的做法是前台服务在自定义网络A后台数据库在自定义网络B然后通过docker network connect B 前台容器让前台容器同时能看到两个网络而数据库容器保持相对封闭。这样既控制了通信范围又维持了必要的连通。4.3 不同网络模式的选择逻辑Docker的网络模式除了默认bridge还有host、none、container三种选不对会直接导致网络链路错乱。host模式最直观容器不创建新的网络命名空间直接用宿主机的网络栈。容器里执行的ip addr看到的网卡和宿主机一模一样端口也直接占用宿主机端口不存在NAT性能最好。但隔离性几乎为零容器内一个进程监听端口和宿主机进程监听端口没有区别。适合需要高吞吐、低延迟的容器比如压测工具、UDP转发网关前提是你接受它没有网络隔离的事实。none模式最简单容器只有lo回环接口没有任何外界网络能力。适合纯本地计算的批处理任务或者你打算手动用ip配置复杂网络的特殊场景。container模式则是共享另一个容器的网络命名空间两个容器共享同一个IP、同一套端口空间。这种模式在“主程序辅助程序”的sidecar场景里很好用比如nginx容器和一个日志采集容器共享网络栈采集器访问localhost就能拿到nginx的端口状态不需要额外暴露端口也不用担心端口冲突。我在生产环境见过一次失控的网络故障起因就是把一个bridge模式的容器硬生生配置了host模式的静态路由两边路由表互相干扰数据包乱转。所以排查一切网络问题之前先确认容器的网络模式。这一点花十秒钟确认能省下半小时挠头。5. 亲手验证把隔离和网络链路剥开展示5.1 准备工作创建测试容器再多的理论不如自己抓一次包来得深刻。为了验证你可以跑两个轻量的ubuntu容器保持它们一直运行docker run -d --name box1 ubuntu sleep 300 docker run -d --name box2 ubuntu sleep 300进入容器后先把基础工具装齐docker exec -it box1 bash apt update apt install -y iproute2 curl procps net-tools dnsutilsbox2同样处理。iproute2提供ip命令tcpdump用来抓包ubuntu镜像默认不带需要单独装也可以直接用host上的tcpdump抓docker0口dnsutils提供nslookup。如果你觉得装工具麻烦用busybox镜像更轻但精简版工具不齐全做网络实验还是ubuntu方便。5.2 用nsenter和命名空间证据验证隔离在宿主机执行docker inspect -f {{.State.Pid}} box1拿到容器主进程在宿主机的真实PID。然后ls -l /proc/pid/ns你会看到一堆指向命名空间对象的软链接类似net - net:[4026532345] pid - pid:[4026531836] mnt - mnt:[4026532456]再随便找一台宿主机自己的进程对比你会发现net:后面的编号完全不同。只要两个进程的net软链接指向不同的编号就说明它们处在不同的网络命名空间里。这是“隔离确实存在”的最硬核证据。接下来用nsenter切进容器的网络命名空间直接执行命令nsenter -t pid -n ip addr你会看到容器内eth0的IP和宿主机docker0的网段一致。不用-t指定PID的话也可以先nsenter -t pid -n sh进去再慢慢敲命令。这里有个小细节ip netns list默认看不到Docker创建的命名空间因为Docker没有给容器命名空间创建挂载文件而nsenter走的是/proc/pid/ns路径所以一定好用。5.3 在docker0上抓包看通信链路先在宿主机上抓docker0口tcpdump -i docker0 -n icmp or arp然后进box1pingbox2的IPdocker exec -it box1 bash ping 172.17.0.3回到宿主机上看tcpdump输出你会看到docker0上出现了来自box1MAC地址发往box2MAC地址的ARP请求和ICMP回显。这个过程完全发生在docker0这一层没有经过宿主物理网卡证明同一桥接网络容器通信是二层转发。再做一个实验在宿主机上同时抓物理网卡流量tcpdump -i eth0 -n icmp然后在box1里ping 8.8.8.8——注意这里举的是公共目的地址换成任意可达的外网IP也可以。你会看到docker0上的ICMP请求源IP是172.17.0.2而物理网卡上出现的数据包源IP已经变成了宿主机的实际IP。同一个ping两个抓包位置看到两个源IP这中间只隔着一条MASQUERADE规则。网络虚拟化的那层纱一下就透了。5.4 用iptables规则解读端口映射启动一个带端口映射的容器docker run -d --name web -p 8080:80 nginx查看NAT表iptables -t nat -L -n输出里会有一段Chain DOCKER (2 references) target prot opt source destination DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.2:80这条规则说的就是所有到达宿主机8080端口的TCP包目标地址都会被改写成172.17.0.2:80也就是那个nginx容器的IP和端口。顺便再看一眼docker-proxy进程ps aux | grep docker-proxy你会看到它绑定在0.0.0.0:8080上。两条链路同时存在一条走用户态代理一条走内核DNAT。docker-proxy在Linux上其实主要是兼容性补丁生产环境里DNAT才是主力路径。5.5 自定义bridge网络实验创建一个自定义bridge网络docker network create -d bridge --subnet192.168.50.0/24 mynet把box1连进去docker network connect mynet box1此时box1有了两个网卡一个是原默认bridge的eth0一个是mynet的eth1。它同时能访问两个网络的设备而box2如果不连mynet就只能待在默认网络里两边互相不通。这个实验直接解释了为什么微服务编排时服务都必须显式声明加入同一个自定义网络。默认的docker0只适合跑单个或少量容器正经项目从第一天起就应该建自定义网络把服务按业务域隔离。6. 踩坑实录容器网络问题的常见排查路径6.1 端口映射突然失效现象很典型昨天晚上还能访问的8080端口今天完全打不开。按顺序排查基本能定位。第一确认容器还在运行。用docker ps看状态如果容器挂了先看docker logs找应用崩溃原因。第二在宿主机上本地测试curl http://127.0.0.1:8080。如果宿主机能通外部不通那问题基本出在宿主机网络栈之外——云安全组、宿主机防火墙、上层负载均衡的规则都可能挡住流量。第三如果宿主机也通不了进容器自测docker exec -it 容器 bash然后curl http://127.0.0.1:80。服务本身都没起来的话问题在前端服务不在网络层。第四检查NAT规则iptables -t nat -L DOCKER -n最常见的原因是有人手动执行了iptables -F清空规则Docker写入的DNAT和MASQUERADE规则全部消失。这时服务进程还在但端口映射已经失效。重启Docker通常会重新生成规则但注意它会重建网络可能牵连其他容器。我在生产环境更倾向于手动重建影响到的DNAT规则然后把“禁止清空Docker自有链”写在运维规范里。如果你发现主机用的是firewalld或nftablesDocker的规则和它们的交互也有历史包袱。有些发行版需要显式放行docker0流量否则规则匹配顺序会把Docker链的包提前丢掉表现也是端口映射时好时坏。6.2 容器之间ping不通或业务不通这种问题分三层看。第一层两个容器是否在同一个网络里。如果分别在不同自定义网络里默认就是隔离的ping不通很正常。想让他们通信用docker network connect将一个容器连入另一个网络而不是强行改路由。第二层同一网络里也ping不通。这时建议立刻查FORWARD链iptables -L FORWARD -n如果默认策略是DROP而Docker的转发规则又没生效同网段容器间也不会通。这类问题常见于安全加固过的系统或者装了额外防火墙管理工具的机器。解决办法是确保Docker链的规则仍在或者调整FORWARD默认策略。第三层ping通但业务端口不通。这最常见的原因是容器内服务只监听了127.0.0.1。前面已经强调过这里再重复一次DNAT把数据包送到容器IP但容器里的进程只监听回环口连接必然失败。进容器执行ss -tlnp一眼就能看出监听地址是不是0.0.0.0或::。还有一个容易被忽略的坑容器内如果启动了独立防火墙例如容器里跑了iptables服务并改了策略也可能把到业务端口的包拦掉。容器网络调试要把容器内防火墙也纳入检查范围。6.3 容器内DNS解析失败的常见场景容器里的/etc/resolv.conf默认指向127.0.0.11这是Docker的内嵌DNS服务器地址。它的作用是给自定义网络里的容器提供服务名解析比如一个网络里叫db的容器其他容器可以直接用db作为主机名访问它。但有一个历史性的坑早期的Docker默认bridge网络并不提供这种内嵌DNS服务。所以容器挂在默认docker0下时用服务名互访经常解析失败只能靠IP或者手动指定--link这个参数老版本才有。现在的新版本对默认bridge网络也好了一些但最稳妥的做法仍然是用自定义网络服务名解析才可靠。排查外部域名解析失败时优先做两件事。第一看宿主机能不能正常解析如果宿主机都不能解析先修宿主机DNS。第二给容器显式指定DNS服务器docker run --dns 114.114.114.114 --name test ubuntu sleep 300或者全局配置Docker daemon的DNS参数。很多企业内网环境、云主机环境的默认DNS配置和容器内嵌DNS之间会有冲突排查时建议逐层对比从宿主机/etc/resolv.conf到容器内/etc/resolv.conf看差异在哪。另外注意容器内嵌DNS只负责服务名解析对外部域名的解析会按需转发到上游DNS。如果Docker daemon本身配置的DNS地址不可达容器内解析外部域名自然也会失败。6.4 跨主机通信的选型与坑点默认的bridge网络只能在单台宿主机内通信跨主机会有明显瓶颈。常见的跨主机方案有几种思路宿主机路由、overlay/VXLAN、macvlan/ipvlan。宿主机路由方案让容器的私有网段通过物理路由可达核心是规划好不同宿主机的容器网段不能重叠然后在路由器上添加这些网段的路由。它实现简单、性能好适合网络环境可控、机器数量不多的场景。overlay/VXLAN方案把二层流量封装进UDP隧道跨三层网络也能跑。Docker Swarm和Kubernetes常走这条路线。优点是部署灵活缺点是封装开销大MTU必须仔细处理。跨主机最经典的坑就是MTU。云主机默认MTU一般是1500VXLAN封装会额外占用一部分字节如果隧道网络没有相应减小有效载荷就会出现“小包通、大包丢”HTTP请求时好时坏甚至大规模超时的诡异现象。排查方法ping -M do -s 1472 目标IP如果大包不通小包通基本就是MTU问题。调整方向是给容器网络设置合理MTU或者同步修改物理网络MTU保证两端一致。macvlan/ipvlan方案让容器IP直接挂在物理网络上容器像一台台独立主机性能极高。但它对底层网络规划要求严格IP地址必须预先规划好一旦上了生产环境再想改网段代价很大。这里也多提醒一句遇到跨主机网络故障不要一上来就怀疑应用代码先检查网络插件、路由表、MTU、VXLAN端口是否通。我见过一个半夜折腾两小时查应用问题的案例最后发现是VXLAN的UDP端口被安全组挡了业务完全正常。跨主机网络排查时先问安全组再看路由然后敲MTU最后才轮到应用层。我个人在实际操作里的体会是容器网络越到后面越“低层”问题往往不在Docker本身而在你对Linux网络栈的熟悉程度。命名空间、veth、网桥、NAT、路由这一套东西搞清楚了Docker网络故障大多能靠推理定位。而那些急着找现成命令的人往往今天抄一个明天换一个最后还是回归到最基本的抓包和路由分析上来。这个系列不讲虚的多动手抓一次包比什么文档都有用。
阅读完成 · 觉得有帮助?
咨询建站