type
status
date
slug
summary
tags
category
icon
password
AI summary

Docker端口映射的实现机制

引言

在学习dubbo的过程中,我们了解到了dubbo优雅停机的流程:
  1. provider会先从注册中心把自己下线
  1. consumer监听到provider下线的通知,会主动和provider所在的server断开连接
于是我想看看当前我们某个server上到底有多少个连接。不过在观察连接的过程中触及了一些知识盲区,所以记录总结一下。

宿主机上的连接之谜

宿主机上的连接

由于该dubbo服务在宿主机上映射的端口是23090,我们用netstat -anp | grep ":23090"看看宿主机上的连接情况:
这里的连接数量明显和实际情况不符,并且对端IP都比较奇怪,因为我们自身的网段规划是10.20.0.0/16。通过docker network inspect bridge看到这些IP应该都是bridge网络分配给对应容器的。另外还有一点比较奇怪的是,这些连接都来自于同一个进程——docker-proxy。从进程名称来看应该和docker相关。从当前的信息只能看到这里,接着我们进入第一个容器看看。
我们的应用都是跑在docker里的,并且network用的是默认的bridge模式,所以在宿主机上并未找到我们期望看到的连接。因为在bridge模式下,容器的网络被隔离在自己的网络命名空间中。

容器里的连接

进入容器,我们用netstat -anp | grep "172.17.0.7:20880"看一下容器内的连接情况(容器内的dubbo监听的端口是20880,172.17.0.7是该容器分配到的IP)。172.17.0.1是网关,也就是容器内访问宿主机的ip地址。为了更好的展示,下面我用两个命令把连接分成了两类:
  1. 目的ip为172.17.0.1,也就是容器和宿主机的连接
  1. 和其他宿主机上的连接(其实最终也都是宿主机上运行的各种容器的连接)

容器和宿主机的连接

这个是同宿主机上其他容器和20880建立的连接,172.17.0.1是gateway,也就是容器和宿主机通信的ip

容器和其他宿主机的连接

这个是容器和不同宿主机上运行的其他容器建立的连接

宿主机上的连接

看完了上面一堆的连接信息,我们回到最开始在宿主机上看到的两条连接,看看它的另一端是什么。第一条连接的端口是58882,我们通过lsof -i:58882来查看一下对应的进程是什么?

docker-proxy

巧了,又是docker-proxy。从docker-proxy的名称以及启动参数我们大概能盲猜出它的作用:监听宿主机上的23090端口,接受任意ip,并将请求代理到172.17.0.7:20880上。诶,这不就是docker的端口映射功能(--port)吗?我们再看看这个docker-proxy进程关联的连接:
你有没有发现,列出的4个连接中,上面2个连接是我们在容器里看到的,下面2个连接是我们在宿主机上看到的。
到这里,我们大概能猜出docker的端口映射的实现逻辑:docker-proxy会监听宿主机的端口,当接收到对应端口的连接信息时,docker-proxy会再建立一个自身和对应容器ip(172.17.0.7)和端口(20880)的连接,并把从前一条连接接收到的数据都转发到后一条连接,这也是一个典型的代理链路。所以可以看到上面除了LISTEN之外,其他连接都是成对出现的。
并且看起来同宿主机上的不同容器间通过非docker0访问时,是会走docker-proxy链路的。

DNAT

而在不同宿主机上发起网络请求,并不是走的docker-proxy,而是用到了DNAT。在linux下是通过iptables来实现的,直接把宿主机上对应端口的请求转发到对应容器。我们可以通过iptables -L -n -t nat命令来验证
确实看到一条宿主机上23090端口->172.17.0.7:20880的转发规则。并且适用于除了docker0外的所有网卡。

实验

我们再来通过几个实验来验证一下

宿主机上用localhost/127.0.0.1通信

走的docker-proxy。可以看到telnet和docker-proxy建立了一条通信连接,docker-proxy又建立了一条和对应容器通信的连接。

宿主机上用eth0通信

走了iptables,从宿主机上看不到docker-proxy的连接
在容器内可以看到对应链接

从宿主机上的其他容器(172.17.0.4)内用eth0通信

走的docker-proxy。这个感觉应该是由于DNAT规则上的!docker0不匹配导致的。

从不同宿主机上的其他容器内用eth0通信

走的iptables

用bridge网桥分配的网卡通信

无论是从宿主机上发起的,还是同个宿主机上的容器内发起的。这个都是直接通过ip port请求的,无转发逻辑。

总结

没想到在学习dubbo优雅停机的过程中对docker的端口映射有了一定的了解。最开始在使用端口映射这种方式的时候,运维还曾经提过可能对性能产生一定的影响。不过从这里看来,走DNAT的应该没有太多性能损耗,因为都是在内核态。反而是同一个宿主机上,通过docker-proxy转发可能存在一些性能问题(比如存在内核态到用户态的内存拷贝)。
在探究端口映射的过程中,发现对于iptables也不是很熟悉。于是又去恶补了一些iptables的逻辑。这其实会把这个战线拉得比较长。不过还在是有不少收获。

参考

  1. 网络地址转换:DNAT和SNAT有啥区别?分别用于什么场景?
  1. 容器端口映射到主机端口探究
  1. 四、Docker 网络原理、分类及容器互联配置
  1. docker端口映射查看 docker 端口映射原理
  1. docker 网络之端口映射不完全探索
  1. 一篇文章讲明白 Docker 网络原理
  1. Introduction to Container Networking
  1. 聊聊容器网络和 iptables
  1. iptables详解(10):iptables自定义链
  1. 20200414-iptables-principle-introduction
  1. Linux防火墙配置工具iptables中MASQUERADE的含义
  1. iptables详解(1):iptables概念
  1. 【Docker】关于Docker网络隔离与通信详解
优雅停机之Spring Cloud Gateway优雅停机之Dubbo
Loading...
黑微狗
黑微狗
一只普通的干饭汪🍚
Latest posts
RocketMQ 4.6.0 Message Trace 功能异常排查
2025-4-8
browser-use 项目核心原理
2025-3-28
关于怎么搭建一个这样的blog
2025-3-28
关于怎么给blog搞一个自定义的域名
2025-3-28
Excel导入需求升级——支持内嵌图片导入
2025-3-28
mysql流式查询中的一个坑
2025-3-28