docker容器端口映射的原理

首先我们通过 docker run -p 1234:80 -d nginx,启动一个nginx容器,宿主机的1234端口映射到nginx容器的80端口,然后我们打印一下iptables的 nat 表里面的规则 :

root@ubuntutest1:~# iptables -t nat -nL
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0           

Chain DOCKER (2 references)
target     prot opt source               destination         
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:1234 to:172.17.0.3:80

它完整地展示了 Docker 如何利用 iptables 的 NAT (网络地址转换) 功能来实现其核心网络特性:端口映射容器访问外网

接下来我们来逐链、逐行地深度解析这份“网络蓝图”。

整体概览

  • -t nat:我们查看的是 nat 表。这个表专门用于修改数据包的源/目标 IP 地址或端口,是实现端口转发、共享上网的关键。
  • -n:以数字形式显示 IP 地址和端口,不进行 DNS 解析,输出更快、更清晰。
  • **-L:列出 (List) 当前表中的所有链和规则。

各链详解

1. Chain PREROUTING (数据包到达时,路由决策前)
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL
  • policy ACCEPT:这是这条链的默认策略。如果数据包没有匹配到任何规则,默认就接受它。
  • target DOCKER:这是规则的动作。如果数据包匹配了这条规则,它就会被跳转到名为 DOCKER 的自定义链去继续处理。
  • prot opt all --:匹配所有协议。
  • source 0.0.0.0/0:匹配任何源 IP 地址。
  • destination 0.0.0.0/0:匹配任何目标 IP 地址。
  • ADDRTYPE match dst-type LOCAL:这是这条规则最关键的匹配条件。dst-type LOCAL 表示数据包的目标地址是本机上的一个 IP 地址(比如你主机的 eth0 网卡的 IP,或者 127.0.0.1)。

这条规则的整体含义:

“任何发往本机 IP 地址的数据包,都不要按默认流程处理了,请将它送到 DOCKER 自定义链里去进行特殊处理。”

为什么这么做?
因为外部访问 http://<你的主机IP>:1234 的请求,其目标 IP 就是你主机的 IP。Docker 需要拦截这些请求,判断它们是不是要发给容器的。这是实现端口映射的第一步。


2. Chain INPUT (发往本机进程的包)
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

这条链是空的。这意味着发往本机进程的数据包(比如你用 curl 127.0.0.1)将直接由内核的默认策略 ACCEPT 处理,不受 Docker 的 NAT 规则影响。这很合理,因为发往本机进程的流量通常不需要被转发到容器。


3. Chain OUTPUT (从本机发出的包)
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

这条规则和 PREROUTING 非常相似,但它在 OUTPUT 链上。

  • !127.0.0.0/8:注意这里的 !,代表“非”。这条规则匹配的目标地址是本机 IP,但排除127.0.0.0/8 网段(也就是 127.0.0.1127.255.255.255)。

这条规则的整体含义:

“任何从本机发出、且目标地址是本机 IP(但不是回环地址 127.x.x.x)的数据包,也请送到 DOCKER 自定义链去处理。”

为什么这么做?
这主要是为了处理“主机访问自己的容器”的场景。比如,你在主机上执行 curl <你的主机IP>:1234。这个请求从本机发出,目标地址也是本机 IP,它需要被 Docker 拦截并转发到容器。而 curl 127.0.0.1:1234 则不受此规则影响,它通常有其他处理方式(比如通过 docker0 网桥直接路由)。


4. Chain POSTROUTING (数据包离开时)
Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0           
  • target MASQUERADE:这是一个特殊的 SNAT (Source NAT, 源地址转换) 动作。它会将数据包的源 IP 地址修改为数据包离开时所用网络接口的 IP 地址。这就像一个伪装,让外部世界觉得数据包是从这台主机发出的,而不是从后面的容器发出的。
  • source 172.17.0.0/16:这是匹配条件。172.17.0.0/16 是 Docker 默认的网桥 docker0 的网段。所有容器都会从这个网段获得 IP 地址(比如你的 172.17.0.3)。

这条规则的整体含义:

“任何从 172.17.0.0/16 网段(即所有容器)发出的数据包,在离开主机时,都将其源 IP 地址伪装成主机的 IP 地址。”

为什么这么做?
这是为了让容器能够访问外部网络(比如 ping google.com)。

  1. 容器 172.17.0.3 发送一个数据包给 8.8.8.8
  2. 数据包到达主机的 POSTROUTING 链。
  3. MASQUERADE 规则匹配成功,数据包的源 IP 从 172.17.0.3 被修改成主机的公网 IP(比如 203.0.113.10)。
  4. Google 的服务器收到包,并回复给 203.0.113.10
  5. 主机收到回复后,内核会记得这个连接是之前伪装过的,于是会自动把目标 IP 再改回 172.17.0.3,然后转发给容器。
    没有这条规则,容器发出的包源 IP 是私有 IP,互联网上的路由器不知道如何回复,容器就无法与外界通信。这是实现容器访问外网的关键。

5. Chain DOCKER (Docker 的自定义规则)
Chain DOCKER (2 references)
target     prot opt source               destination         
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:1234 to:172.17.0.3:80

这个 DOCKER 链就是 PREROUTINGOUTPUT 链将数据包送来的地方。(2 references) 表明有两条规则(即 PREROUTINGOUTPUT 中的规则)跳转到了这个链。

  • target RETURN

    • 这是一个非常重要的动作。RETURN 意味着“立即返回”,即跳出当前链(DOCKER 链),回到原来调用它的链(PREROUTINGOUTPUT)的下一条规则继续处理。
    • 它匹配所有数据包 (all -- 0.0.0.0/0 0.0.0.0/0)。
    • 这实际上是一个**“默认通过”的兜底策略**。它的意思是:“如果一个数据包进入了 DOCKER 链,但它不匹配下面任何一条具体的 DNAT 规则,那就把它放回去,按正常流程处理。”
  • target DNAT

    • 这是实现端口映射的核心。
    • prot opt tcp --:只匹配 TCP 协议。
    • tcp dpt:1234:匹配目标端口为 1234 的 TCP 包。
    • to:172.17.0.3:80:这是 DNAT 的核心。将匹配到的数据包的目标 IP 和端口,修改为 172.17.0.380 端口。

这条规则的整体含义:

“对于进入 DOCKER 链的数据包:

  1. 首先,有一条 RETURN 规则。它匹配所有数据包。这意味着,如果数据包不匹配下面的任何规则,它就会直接返回。
  2. 接着,检查数据包是不是 TCP 协议,并且目标端口是不是 1234。如果是,就执行 DNAT,将目标地址 :1234 改为 172.17.0.3:80,然后处理结束。
  3. 如果数据包的目标端口不是 1234(比如是 5678),它就不会匹配 DNAT 规则,但会匹配第一条 RETURN 规则,然后被返回给 PREROUTING 链,继续它的正常旅程。”

完整流程:一个外部请求的生命周期

现在,我们把所有规则串联起来,模拟一个外部用户访问 http://<你的主机IP>:1234 的完整过程。假设你的容器 IP 是 172.17.0.3,并且它内部运行着一个 Nginx 服务在监听 80 端口。

  1. 请求到达:一个来自互联网的 TCP 数据包,目标 IP 是你主机的 IP,目标端口是 1234
  2. 进入 PREROUTING
    • 数据包匹配 ADDRTYPE match dst-type LOCAL 规则。
    • targetDOCKER,所以数据包被跳转到 DOCKER
  3. 进入 DOCKER
    • 数据包首先匹配 RETURN 规则吗?是的,但 iptables 会继续检查下面的规则。
    • 数据包匹配下一条规则:它是 TCP 协议,目标端口是 1234
    • targetDNAT,数据包的目标 IP 和端口被修改为 172.17.0.3:80
    • DNAT 执行完毕,DOCKER 链的处理结束。
  4. 路由决策:内核现在看到这个数据包的目标 IP 是 172.17.0.3。它查询路由表,发现这个 IP 属于 docker0 网桥。
  5. 进入 FORWARD
    • 因为数据包的目标 IP 不是本机,所以它进入 FORWARD 链(filter 表)。
    • Docker 在 FORWARD 链中也设置了规则,通常会允许 172.17.0.0/16 网段内的流量通过。数据包被 ACCEPT
  6. 转发到容器:内核将数据包通过 docker0 网桥发送给 IP 为 172.17.0.3 的容器。
  7. 容器响应:容器里的 Nginx 进程处理请求,并返回一个响应数据包。这个包的源 IP 是 172.17.0.3:80,目标 IP 是外部用户的 IP。
  8. 进入 POSTROUTING
    • 响应包离开主机时,经过 POSTROUTING 链。
    • 它匹配 source 172.17.0.0/16 规则。
    • targetMASQUERADE,响应包的源 IP 被伪装成主机的公网 IP
  9. 返回用户:响应包被发送回互联网上的用户。用户完全不知道背后有容器的存在,他一直以为是在和你的主机通信。

总结

这份 iptables 规则清单是 Docker 网络实现的精髓。它通过一个清晰、分阶段的流程,巧妙地利用 Linux 内核的 iptables/netfilter 框架,实现了:

  • 端口暴露:通过 PREROUTING + DOCKER 链中的 DNAT 规则,将主机端口的访问精准地转发到容器内部端口。
  • 外部访问:通过 POSTROUTING 链中的 MASQUERADE 规则,让容器能够“伪装”成主机访问外部网络。
  • 主机访问:通过 OUTPUT 链的规则,确保从主机自身发出的、访问主机 IP 的请求也能被正确地转发到容器。

理解了这些规则,你就掌握了 Docker 网络最核心的原理,对于排查网络问题(如端口不通、容器无法上网等)将有极大的帮助。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值