
1. 先理解Docker网络容器之间是怎么“对话”的很多朋友用Docker跑起来几个容器之后都会遇到同一个问题容器之间怎么互相访问我用-p 3306:3306把MySQL暴露出来了但容器A要连容器B的Redis是不是还得走宿主机的IP其实压根不用这么绕。Docker网络这套东西就是专门解决容器之间、容器与外部世界之间“怎么通信”的问题而搞清楚它你才能真正把容器玩明白而不是只会docker run一条命令走天下。我见过不少做微服务部署的人把每个容器都映射端口到宿主机然后服务之间走宿主机IP通信结果端口冲突一大堆、防火墙规则越改越乱最后排查问题的时候人都是麻的。Docker网络的核心价值就在于它给了你一套可规划的虚拟网络体系让容器之间有独立、清晰、可控的通信路径而不是全靠“暴露端口”这唯一一条路。1.1 Docker网络解决的三个核心痛点先摆结论Docker网络本质上解决的是三件事。第一容器间的隔离与互通。默认情况下Docker容器和外部网络是隔离的需要端口映射才能被外部访问而容器与容器之间如果放在同一个自定义网络里则可以直接用容器名互相通信不需要知道对方的IP地址。这种“虚拟局域网”的设计让服务的部署结构变得非常清晰。第二动态IP的管理问题。容器天生是“临时工”——今天启动明天删掉IP地址频繁变化。如果你在配置文件里写死了某个容器的IP容器一重启IP换了服务就断了。Docker网络内置的DNS解析机制能让你通过容器名来访问服务IP变了也无所谓这在实际部署里太关键了。第三网络安全边界的控制。把数据库放在一个不对外暴露的网络里把Web服务放在另一个对外开放的网络里中间用容器间的通信规则来控制谁可以访问谁。这种网络拓扑的规划比你用一堆iptables规则去限制IP和端口要直观得多。1.2 Docker网络的核心模型与现实类比Docker网络底层有一套叫CNMContainer Network Model的设计模型它把网络拆成了三层沙盒Sandbox每个容器独享的网络命名空间包含端口、路由、防火墙规则等。你可以把它理解为每台容器自带一个“独立的网卡配置环境”。端点Endpoint沙盒与网络的连接点负责把容器“插”到网络上类似你往路由器上插了一根网线。网络Network一组端点的集合端点之间按各自网络的规则实现互通。这套模型用生活化的方式理解就是你租了一栋公寓宿主机每个房间里住着容器沙盒房间之间本来是不通的但你可以给它们装上一个共享的客厅网络客厅里大家各自占一个网口端点就有了交流的空间。客厅是独享的还是公用一个大客厅还是干脆不要客厅这就是接下来要说的网络驱动解决的问题。2. Docker的五种网络驱动怎么选才是最合适的Docker原生提供了五种网络驱动分别是bridge、host、none、overlay和macvlan。很多人一看到五种就头晕其实你只需要记住平时90%的场景都用bridge多主机部署才考虑overlay极端场景才会用到host、none和macvlan。2.1 bridge网络最常用的默认网络bridge是Docker默认的网络模式你可以把它理解成一个虚拟交换机。容器启动时如果不指定网络就会自动连接到一个名叫docker0的虚拟网桥上。这个网桥在宿主机上表现为一个虚拟网卡IP地址通常是172.17.0.1/16容器则从这个网段里分配IP。在bridge模式下同一宿主机上的容器默认可以通过IP互通但从外部访问容器必须做端口映射。这里有几个容易被忽略的点我实际踩过坑默认bridge网络不支持容器名DNS解析只能靠IP访问。你自己创建的自定义bridge网络才支持。docker0这个默认网桥上的端口映射规则是自动写入iptables的如果你在宿主机上自定义了防火墙规则很容易跟Docker的规则产生冲突导致端口映射失效。宿主机上的iptables FORWARD链如果被设置成了DROP那么所有容器的网络都会异常这属于“症状很明显、原因很难找”的典型问题。如果你只是在本地开发、跑几个临时的容器用docker0就够了但你要是正经部署一套多服务应用我建议二话不说自定义bridge网络。2.2 host与none激进与极简的两个极端host模式的意思是容器直接使用宿主机的网络命名空间不额外做隔离。容器里看到的IP就是宿主机的IP端口也直接占用宿主机的端口。优势是性能好、没有NAT转发损耗劣势是没有隔离容易产生端口冲突而且容器里看到的环境跟宿主机混在一起排查问题的时候不太清爽。这种模式适合对网络延迟极其敏感的场景比如高性能的缓存或中间件。none模式更简单这个容器没有网络连接只有一个回环接口。它适合做纯计算任务、数据迁移任务这种“不联网也活得很好”的场景。用none还有一个好处——彻底断网天然杜绝了容器被外部访问的可能安全性拉满。2.3 overlay与macvlan多主机与物理网络的入场券overlay是用于Swarm集群的跨主机网络。它的原理是在多个宿主机之间创建一个虚拟子网让不同宿主机上的容器可以像在同一个局域网里一样通信底层数据通过VXLAN隧道封装转发。这个模式适合微服务集群部署场景但要注意VXLAN对UDP端口通常是4789依赖很大如果宿主机的防火墙挡了UDP流量你的跨主机通信就会变得非常诡异——时通时不通。macvlan则允许你给容器分配一个物理网络的MAC地址和IP地址让容器看起来像是直接接在物理局域网里的设备。这种模式适合需要容器直接使用物理网络IP、便于被局域网内其他设备直接访问的场景。但macvlan有个比较麻烦的限制宿主机网卡和macvlan容器之间不能直接通信因为MAC地址被“吞”了。我当年第一次用的时候容器能ping通外网宿主机却ping不通容器排查了很久才发现就是这个原因。来一张表帮你快速选型网络驱动隔离性通信范围端口映射典型场景bridge中宿主机内支持单机多容器应用host无宿主机不需要延迟敏感的中间件none完全隔离无不支持离线计算任务overlay中跨宿主机支持Swarm集群macvlan中物理局域网不需要容器直连物理网络3. 实操从0搭建一套可用的Docker网络说完了理论下面直接进入动手环节。这一节里我会带你完整走一遍“创建网络→接入容器→验证互通→端口映射→DNS解析”的全流程所有命令都是在Linux宿主机上实测过的。3.1 创建自定义bridge网络并接入容器先创建一个自定义的bridge网络名字就叫demo-net子网规划为172.25.0.0/16网关设为172.25.0.1docker network create --driver bridge \ --subnet172.25.0.0/16 \ --gateway172.25.0.1 \ demo-net创建好之后你可以用docker network ls确认然后用docker network inspect demo-net查看详细信息。我习惯在创建网络的时候显式指定--subnet原因是如果放任Docker自动分配它可能会跟你现有的网络段撞车尤其是在公司内网环境里172段、10段都很常见一旦撞了路由就乱了。自定义网段也是个“先说断后不乱”的好习惯。接着启动两个容器并接入这张网络。比如运行一个Redis和一个Nginxdocker run -d --name redis-1 --network demo-net redis:7 docker run -d --name nginx-1 --network demo-net -p 8080:80 nginx:1.27容器启动后从nginx-1里用redis-1这个容器名去访问Redis的6379端口docker exec -it nginx-1 ping redis-1正常情况下你看到的是PING返回的IP地址172.25.0.x这就是自定义bridge网络自带的DNS解析在起作用。这个体验比用IP地址舒服太多了——容器启动顺序无所谓IP变了也无所谓只要容器名不变通信就稳定。这在构建微服务应用的时候价值是直接拉满的。3.2 端口映射的底层逻辑与参数说明端口映射是接触Docker网络频率最高的功能但很多人只知其然不知其所以然。-p 8080:80这个参数左边是宿主机的端口右边是容器的端口。它的底层实现是Docker会在宿主机上启动一个docker-proxy进程监听8080端口然后将流量转发给容器的80端口。同时Docker还会在iptables的DNAT链中写入一条规则把访问宿主机8080端口的数据包目标地址改写成容器的IP和端口。这里有几个容易踩坑的细节我用实际体会跟你讲启动时加参数还是启动后加端口映射需要在docker run时指定才能创建DNAT规则容器启动后再想加只能重新创建容器这是很多新手痛苦的地方。所以启动前一定要想清楚端口规划。只监听特定网卡。如果你不想让某个服务暴露到所有接口可以写成-p 127.0.0.1:8080:80这样只有宿主机本机能访问局域网其他机器访问不了。这对于跑数据库容器来说是非常实用的安全配置。高位端口 vs 低位端口。宿主机上小于1024的端口通常需要root权限监听虽然Docker会自动帮你处理但如果你的应用要求非root用户运行就可能出问题。建议生产环境尽量避开小端口直接用8080、8000这类高位端口。3.3 容器间DNS解析与跨网络互通自定义bridge网络的另一个大好处是内置DNS解析。Docker会自动将容器名解析成对应的IP地址且这个解析过程是动态的——容器重启后IP变了解析结果也跟着变。这一点配合--network-alias还能实现更高级的用法比如一个容器可以通过多个别名被访问docker run -d --name redis-2 --network demo-net \ --network-alias cache \ redis:7上面这个命令让容器同时拥有redis-2和cache两个可用名称。在微服务架构里你完全可以让多个容器共享同一个网络别名然后用负载均衡或服务发现来消费这个地址实现后端服务的自动切换这个技巧在实际项目里真的很香。还有一个常见需求是让两个不同网络里的容器互通。Docker自己的理念是“网络隔离”不支持直接跨网络通信但你可以通过把同一个容器接入多个网络来实现“中转站”的效果docker network connect demo-net nginx-1 docker network connect another-net nginx-1这样nginx-1就有了两块虚拟网卡两个网络都通。不过这种操作属于应急方案不建议大规模用。真正规划多网络通信的时候还是要从架构上把网络层次理清楚不要让容器身兼数职。4. 常见问题与排查docker网络不通我踩过的坑Docker网络的问题我总结下来不外乎几类容器之间不通、宿主机访问不了容器容器、容器没有外网、端口映射不生效、DNS解析失败。下面逐个说方法和思路。4.1 网络不通的七类典型场景我在线上环境调试Docker网络基本靠这几板斧问题定位的速度非常快。先看容器状态和网络归属docker ps -a docker inspect container_id | grep -A 50 NetworkSettings重点看NetworkSettings下的Networks部分确认容器到底挂在哪个网络上、IP是多少、网关对不对。很多时候所谓的“网络不通”其实是容器挂错了网络两个容器根本不在同一个局域网上那当然连不上。再看宿主机上的网络设备与连通性ip addr ping 172.25.0.1从容器里ping宿主机、ping网关、ping外网逐层排查看断在哪一层。比如容器ping不通外网但能ping通网关那问题多半出在宿主机的转发或DNS配置上。还要看防火墙规则。我在一台新服务器上遇到过最典型的场景是容器启动都正常docker run -p 8080:80也没报错但浏览器访问宿主机8080端口就是不响应。用iptables -L -n一看FORWARD链的默认策略是DROPDocker在宿主机上建立的转发规则被默认丢弃策略给挡住了一部分。解决办法是设置FORWARD链为ACCEPT或者显式放行Docker网段这也是最容易踩的坑。再给一个我自己的工具清单排查时按顺序过一遍容器内ip addr查看IP和网卡状态。容器内ping网关和外网判断网络出口。宿主机bridge网卡和路由表判断网络层是否可达。宿主机iptables -L -n -v查看FORWARD和DNAT规则。容器内cat /etc/resolv.conf判断DNS是否正常。docker logs容器日志排除应用层本身的问题。4.2 排查工具与命令实录日志与网络抓包是最强的排查手段。当我觉得“路由和iptables看起来都对但网络还是不通”的时候最直接的方法是抓包对比——从容器内和宿主机上同时抓看包到底走到了哪一步、是否被丢弃。# 宿主机上抓取容器IP产生的流量 tcpdump -i docker0 host 172.25.0.2 -n -vv # 容器内抓取访问目标网络的流量 docker exec -it nginx-1 tcpdump -i eth0 -n -vv实际工作中我遇到过一种很隐蔽的情况宿主机上有两块网卡Docker默认走的网卡和业务所在的网卡不是同一块导致外部流量进不来。究其原因是路由表没有规划好Docker的默认网桥和物理网卡不在同一路由策略下ip rule和ip route表一塌糊涂。这种场景下你需要检查ip route确认default路由是否指向了正确的网关必要时可以用--dns参数在启动容器时强制指定DNS服务器绕过宿主机DNS转发带来的问题。另外说一个大家都容易忽略的点容器里的resolv.conf。Docker会默认把宿主机的DNS配置写入容器但如果你在宿主机上用了systemd-resolved或者改了/etc/docker/daemon.json里的dns参数容器的DNS解析结果可能会很诡异。典型症状是容器能访问IP地址但域名解析超时或返回错误结果。这种问题的排查思路很简单在容器里cat /etc/resolv.conf看配置在宿主机上用docker network inspect看该网络的DNS设置多半一眼就能发现线索。4.3 docker desktop在Windows/Mac下的特殊表现如果你是在Windows或Mac上用Docker Desktop网络这块和纯Linux环境有一些明显差异。Docker Desktop本质上是跑在一个轻量级虚拟机里的所以你要访问容器端口映射出来的服务用的是localhost但这里的localhost其实是那个虚拟机的端口转发——它在宿主机和你电脑之间又套了一层NAT。这导致一个常见现象容器内ping网关正常但你从Windows上ping容器的IP通常是ping不通的这很正常不必惊慌。我之前在Windows上调试时还遇到过“翻遍防火墙也找不出问题但容器之间就是连接不上”的情况。后来琢磨明白了其实是Docker Desktop内部那个Linux虚拟机的资源分配不够网络栈处理不过来。解决办法很粗暴但有效在Docker Desktop的设置里给虚拟机多分一点CPU和内存然后重启Docker Desktop。对就是这么简单但很多人根本想不到去调这个参数。还有个大坑是Windows宿主机的Hyper-V或WSL2配置问题。如果你启动Docker Desktop时提示虚拟化支持没有检测到或者服务无法启动这往往不是你Docker的配置问题而是Windows的虚拟化平台功能没有开启或者WSL2的内核版本太老。处理方式是在Windows“启用或关闭Windows功能”里打开“虚拟机平台”和“适用于Linux的Windows子系统”然后重启基本就能解决。5. 进阶多主机网络与微服务部署的网络规划单机玩熟之后很多人就想上多主机了。这一节聊聊overlay网络、ipvlan/macvlan的实际用法以及微服务项目里网络规划的一点点经验。5.1 overlay网络实现跨主机容器通信要用overlay网络首先得有一个Docker Swarm集群。初始化集群的命令docker swarm init --advertise-addr 192.168.1.10其他节点执行docker swarm join加入集群之后就可以创建跨主机的overlay网络docker network create \ --driver overlay \ --subnet10.10.0.0/16 \ --attachable \ prod-overlay不加--attachable时普通docker run的容器无法使用这个网络只有Swarm服务能连接。加上之后普通容器也能“蹭”这张跨主机网络了。这一步是我搭环境时最容易忽略的一开始用docker run创建容器接入overlay网络总是失败后来查文档才发现是这个参数的问题。overlay网络底层走的是VXLAN隧道在多个宿主机之间维护一套虚拟的二层网络。它的灵活性体现在你在宿主机A上启动一个Nginx容器在宿主机B上启动一个后端服务容器两个容器都接入prod-overlay那么它们可以直接通过服务名互相访问完全不用关心对方跑在哪台机器上。这种体验对微服务架构来说太关键了服务实例漂移、扩容缩容都不用改网络配置。5.2 微服务项目部署时的网络规划经验我实际部署微服务项目时通常不会把所有服务都丢进一个庞大的网络里而是按“层”和“域”来做隔离。一般我会规划三张网络前端网络承载网关、前端静态服务对外开放端口。后端网络承载业务微服务只对前端网络暴露必要的端口对外完全隔离。数据层网络承载MySQL、Redis、MongoDB等只允许后端网络访问其他网络一律禁止。这三张网络在Docker里的实现方式就是三个自定义bridge网络然后把对应容器分别接入。比如Nginx网关接入前端网络和后端网络业务服务接入后端网络和数据层网络数据库只接入数据层网络。这么做的好处是即便一个节点被攻破攻击者也不会直接获得数据库的访问路径网络级别的隔离给安全上了双保险。我在一个实际项目里就这么干过网关容器挂front-net和backend-net用户服务挂backend-net和>docker network create -d macvlan \ --subnet192.168.1.0/24 \ --gateway192.168.1.1 \ -o parenteth0 \ macvlan-net然后启动容器时指定这个网络容器就会从局域网里动态分配一个IP如果没指定IP的话。这个模式适合那些对网络身份非常敏感的应用比如需要通过IP白名单做访问控制的内部服务。注意它和bridge串了网络之后宿主机访问容器会很麻烦这个是macvlan的原生限制。我再补充一句如果在云厂商的VPC环境里用macvlan你要格外小心。很多云主机的虚拟网络不支持macvlan这种直接把MAC地址暴露出去的模式用了之后可能整个网络就断了。云环境里更推荐overlay或者直接走宿主机端口映射这也是为什么我说“不推荐云上直接上macvlan”的原因。在实际使用中我个人的体会是Docker网络并不复杂复杂的是你没有真正静下心去理解它的设计思路。你只要搞清楚默认的bridge模式、会自定义网络、会排查端口映射和DNS关系就已经超过90%的Docker使用者了。多机部署的时候再补上overlay的姿势日常项目就足够用了。这篇文章是根据我大量实操经验总结出来的遇到的坑和绕过的弯都是真实经历希望能帮你少走几步弯路。