Skip to content

容器网络

Docker 网络负责容器之间、容器到宿主机、宿主机到容器、容器到外部的通信。单机 Docker 里最常用的是 bridge 模式,生产上再根据服务需要选用 host、none 或自定义网络。

默认网络和 docker0

安装 Docker 后会有几个内置网络:

bash
docker network ls
网络驱动说明
bridgebridge默认桥接网络,不指定网络时容器连到这里
hosthost直接使用宿主机网络栈
nonenull没有网络接口

docker0 是宿主机上的一个 Linux bridge,容器通过 veth pair(虚拟网线)接到这个网桥上,再经宿主机转发和 NAT 访问外部网络。

自定义 bridge 网络

默认 bridge 网络上容器之间只能通过 IP 互通,不能用容器名解析。自定义 bridge 网络解决了这个问题:

bash
docker network create app-net

docker run -dit --name app1 --network app-net alpine sh
docker run -dit --name app2 --network app-net alpine sh

docker exec app1 ping -c 3 app2  # 自定义网络内可以用容器名解析

实际项目里给每个单机项目建独立网络,不把所有容器堆到默认 bridge。这样排查端口、DNS、容器互通时边界更清楚。

端口映射

容器内服务默认只在容器网络里可见。宿主机或外部要访问容器服务,需要发布端口:

bash
docker run -d \
  --name web \
  -p 8080:80 \
  nginx:1.26
写法含义
-p 8080:80宿主机所有地址的 8080 → 容器 80
-p 127.0.0.1:8080:80只允许宿主机本机访问
-p 192.168.10.10:8080:80只绑定指定宿主机 IP

端口映射的链路是:客户端 → 宿主机端口 → Docker NAT 规则 → 容器端口。直接 -p 8080:80 暴露在所有网卡上,公网机器上很容易把不该暴露的服务暴露出去。只给本机代理访问时,绑定 127.0.0.1 更安全。

Docker 和防火墙的关系

Docker 发布端口时会在宿主机 iptables 里写转发规则。表现是宿主机上并没有进程直接监听 8080,但外部访问 宿主机IP:8080 可以转到容器 80。

bash
iptables -t nat -S | grep DOCKER       # Docker 写入的 NAT 规则
iptables -S DOCKER-USER 2>/dev/null    # Docker 预留的用户自定义过滤入口

DOCKER-USER 是 Docker 给用户预留的过滤链,流量进入 Docker 自己的转发规则前会先经过这里。需要限制外部来源访问容器端口时,规则写在这里比直接改 Docker 自动生成的链更稳——Docker 重启不会覆盖 DOCKER-USER

示例:只允许管理网段访问已发布的容器端口:

bash
iptables -I DOCKER-USER -s 192.168.10.0/24 -j RETURN
iptables -A DOCKER-USER -j DROP

在 ufw 的 Ubuntu 上,Docker 发布端口可能绕过普通 ufw 入站规则。公网机器上遇到"ufw 没放行但容器端口能访问"时,原因是 Docker 的 NAT 规则在 ufw 之前生效。排查方向优先看 Docker NAT 规则和 DOCKER-USER

firewalld、ufw、iptables-nft 混用时,表面上命令都能执行,实际生效顺序可能和预期不同——这是网络排错里容易绕进去的地方。

容器访问宿主机

Linux 上容器访问宿主机服务,常见方式:

方式说明
网桥网关 IP默认 bridge 通常是 172.17.0.1
host.docker.internal--add-host 显式添加
host 网络容器直接使用宿主机网络
bash
docker run --rm \
  --add-host host.docker.internal=host-gateway \
  curlimages/curl:8.8.0 \
  curl -I http://host.docker.internal:8080

host-gateway 会把宿主机网关地址写进容器 /etc/hosts,本地调试很方便。生产里要确认应用为什么需要反向访问宿主机,避免把服务依赖关系绕得太隐蔽。

host 和 none 模式

host 模式下容器直接使用宿主机网络,没有独立网络命名空间:

bash
docker run --rm --network host nginx:1.26

它少了一层 NAT 和端口映射,网络性能更好,但容器进程直接占用宿主机端口,隔离更弱。适合对网络性能、多端口监听有特殊要求的服务。普通 Web/API 服务用 bridge 加端口映射更清楚。

none 模式下容器只有 loopback,没有外部网络:

bash
docker run --rm --network none alpine ip addr

适合离线任务、只读文件处理、构建任务——不需要外部网络时,none 模式能减少不必要的网络暴露面。

自定义网段和网段冲突

默认 Docker 网段可能和公司内网、VPN、K8s Pod 网段冲突。创建网络时可以指定网段:

bash
docker network create \
  --subnet 172.30.10.0/24 \
  --gateway 172.30.10.1 \
  app-net

网段冲突的表现是容器能访问部分地址,部分地址路由异常。VPN、办公网、K8s Pod 网段、Docker 网段最好统一记录,不然排查网络问题会很绕。

常见排查

bash
docker network ls
docker network inspect app-net
docker port web
docker inspect web --format '{{json .NetworkSettings.Ports}}'
ss -lntp | grep 8080                          # 宿主机上确认端口到底由谁监听
iptables -t nat -S | grep DOCKER              # 端口映射对应的 NAT 规则
现象检查方向
容器访问不了外网DNS、默认路由、宿主机 ip_forward、防火墙
外部访问不了容器-p 映射、监听地址、防火墙、安全组
容器之间不通是否在同一网络、容器名解析、应用监听地址
端口冲突宿主机端口是否已被占用
访问到了旧容器端口映射、反向代理 upstream、DNS 缓存
防火墙没放行但端口可访问Docker NAT 规则、DOCKER-USER、firewalld/ufw 生效顺序

容器网络故障里,核心是先确认三件事:容器内服务是否在监听、宿主机端口是否映射、流量有没有实际到容器。三件事分开看,比盯着应用日志猜要稳。