Docker 在启动容器并执行端口映射时,并不会直接操作底层网络硬件,而是依赖 Linux 内核的 netfilter 框架以及用户态工具 iptables 来完成地址转换与流量过滤。每当执行 docker run -p 参数发布端口,Docker 的网桥驱动就会调用 iptables 命令,在 nat 表的 PREROUTING 链和 DOCKER 链中插入 DNAT 规则,将目的地址改写为容器的私有 IP 与端口。与此同时,filter 表的 DOCKER 链和 FORWARD 链决定了这些转发流量是否被允许通过。

Docker 向 iptables 写入了哪些规则
当 Docker 服务启动后,它会创建名为 DOCKER、DOCKER-ISOLATION-STAGE-1 等一系列自定义链。在 nat 表中,PREROUTING 链会跳转至 DOCKER 链,如果匹配到发布的端口,就执行 DNAT 将包发往容器。我们可以用下面命令查看 nat 表规则:
iptables -t nat -L -n --line-numbers
在 filter 表中,FORWARD 链一般会被设置为跳转到 DOCKER-ISOLATION 相关链以及 DOCKER 链。DOCKER 链默认允许已建立连接的包,并对新发起的到容器映射端口的访问做放行。如果宿主机的默认 FORWARD 策略是 DROP,但 Docker 安装时正常配置了规则,那么容器之间以及外部到发布端口的通信依然可用,因为 Docker 显式添加了 ACCEPT 规则。
端口映射的底层逻辑
以发布 8080 到容器 80 为例,Docker 实际写入的 nat 规则类似如下逻辑:在 PREROUTING 阶段,若目的端口为 8080,则作 DNAT 到容器 IP:80。下面是一段等效的 iptables 命令示意:
iptables -t nat -A DOCKER -p tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80
这种做法让外部请求在路由判断前就被改写目的地址,随后包进入 FORWARD 流程。由于容器网络属于 bridge 子网,Linux 内核将其作为普通转发处理,只要 filter 表允许,就能到达容器。理解这一点,就能明白为什么单纯在 INPUT 链加规则拦不住已发布端口的访问,因为流量根本不经过 INPUT 链。
如何在不破坏 Docker 的前提下用 iptables 做限制
很多运维人员习惯用 iptables 直接清空规则或手动改 FORWARD 策略,结果导致 Docker 容器无法互通或外网无法访问。正确做法是利用 Docker 预留的钩子,例如 DOCKER 链之前或之后插入自定义规则,而不是直接删除 Docker 生成的链。下面示例在 filter 表 DOCKER 链前限制某 IP 访问容器映射端口:
iptables -I FORWARD -s 192.168.0.1 -p tcp --dport 8080 -j DROP
该规则在 FORWARD 中优先匹配,若源地址是 192.168.0.1 且目的端口为 8080,则丢弃,不会走到 DOCKER 链的 ACCEPT。这样既保留了 Docker 管理的其他规则,又实现了访问控制。需要注意的是,Docker 重启可能会重置部分规则顺序,因此复杂策略建议写入 systemd 服务或守护进程启动后脚本。
常见误区与排查方法
一个典型误区是认为在 INPUT 链加拒绝规则就能屏蔽容器端口。实际上 Docker 的端口映射流量走 PREROUTING 与 FORWARD,并不进入 INPUT,除非访问的是宿主机自身进程监听的端口。排查时可用 conntrack 与 iptables 计数观察:
iptables -t filter -L DOCKER -v -n iptables -t nat -L DOCKER -v -n
通过查看包计数,可以确认流量是否命中了对应链。若发现计数不增长,说明规则位置错误或网络不在 bridge 模式。另外,使用 Docker 的自定义网络时,内部 DNS 与隔离链也会参与,不能简单用默认 filter 策略一概而论。
总结与协作建议
Docker 与 iptables 的协同核心在于:Docker 是 iptables 规则的自动管理者,而 iptables 是容器网络连通与安全隔离的真正执行者。在生产环境,推荐将防火墙策略集中在 DOCKER 链周边或采用 nftables 兼容层,避免手工冲突。同时,通过 docker network 的 internal 选项与端口发布粒度,可以从更上层减少 iptables 规则复杂度。
docker network create --internal isolated_net
上述命令创建的内部网络不会自动添加 NAT 出口,容器间可通信但无法访问外网,相应 iptables 规则也更简单。掌握 Docker 与 iptables 的分工,才能构建稳定且可控的容器网络环境。