Docker 端口映射原理

发布于:2025-03-31 ⋅ 阅读:(21) ⋅ 点赞:(0)

在 Docker 中,默认情况下容器无法直接与外部网络通信。 为了使外部网络能够访问容器内的服务,Docker 提供了端口映射功能,通过将宿主机的端口映射到容器内的端口,外部可以通过宿主机的IP和端口访问容器内的服务

以下通过动手演示, 安装一个Flask容器, 解释端口映射从外部访问容器的原理

安装一个Flask容器

文件结构

.
├── Dockerfile
└── app.py

Dockerfile

FROM rockylinux:9.3

RUN dnf update -y && \
    dnf install -y python3 python3-pip && \
    dnf clean all

WORKDIR /app

COPY app.py /app/app.py

RUN pip3 install --no-cache-dir flask

EXPOSE 5000

CMD ["python3", "app.py"]

app.py

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

构建镜像

docker build -t flask-app:1.0 .

启动容器, 进行端口映射

docker run -d -p 80:5000 flask-app:1.0

从外部访问 (192.168.52.203是我的虚拟机IP)

# curl 192.168.52.203
Hello

端口映射原理

当执行docker run -d -p 80:5000 flask-app:1.0时, Docker会配置iptables规则来实现端口映射, 流程如下:

1. iptables PREROUTING 链处理, 做DNAT转换

外部请求到达宿主机时, iptables的PREROUTING链处理该请求, 根据DNAT规则将目标IP和端口(192.168.52.203:5000)替换为容器的IP和端口(172.17.0.2:5000)

# iptables -t nat -L PREROUTING
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  anywhere             anywhere             ADDRTYPE match dst-type LOCAL

# iptables -t nat -L DOCKER
Chain DOCKER (2 references)
target     prot opt source               destination
RETURN     all  --  anywhere             anywhere
RETURN     all  --  anywhere             anywhere
DNAT       tcp  --  anywhere             anywhere             tcp dpt:http to:172.17.0.2:5000

2. DNAT转换后的数据包转发到容器

经过DNAT转换后的数据包会发送到虚拟网桥docker0, 再通过veth设备转发到容器的虚拟网络接口(eth0)

宿主机的路由表
# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.52.2    0.0.0.0         UG    100    0        0 ens160
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
192.168.52.0    0.0.0.0         255.255.255.0   U     100    0        0 ens160

3. 容器接受请求并返回响应, 响应报文通过iptables POSTROUTING链处理, 做SNAT转换

容器接受请求并返回响应,响应报文先发送到 docker0, 再通过 SNAT(MASQUERADE)规则,将源IP和端口(172.17.0.2:5000)替换为宿主机的IP和端口,最后发送回客户端

# iptables -t nat -L POSTROUTING -v
Chain POSTROUTING (policy ACCEPT 2387 packets, 151K bytes)
 pkts bytes target     prot opt in     out     source               destination
   89  5558 MASQUERADE  all  --  any    !docker0  172.17.0.0/16        anywhere

附: wireshark抓包结果

  • 客户端IP: 192.168.52.202
  • 运行容器的宿主机IP: 192.168.52.203
  • 容器IP: 172.17.0.2

抓宿主机网卡的包

tcpdump -i ens160 -nn tcp and not port 22 -w ens160.pcap

请添加图片描述
抓docker0的包

tcpdump -i docker0 -nn tcp -w docker0.pcap

请添加图片描述

参考

Docker 容器如何访问外部网络