Docker的应用运行在容器中,其相互之间或与外部之间是如何通信的,涉及到哪些知识点,本文对相关内容进行整理。因网络这块牵涉的面较多,因此只从日常使用或理解的角度出发,过于专业的就不深入探讨了。
1. Docker默认的网络拓扑
在Docker笔记(二):Docker管理的对象 中,介绍了Docker通过一些驱动程序来实现容器之间或容器与外部的互联,包括bridge(默认的虚拟网桥形式),host(与主机共享网络栈),overlay(跨Docker Daemon容器间的互联),macvlan(为容器分配mac地址),none(禁用所有网络)等。
默认情况下,Docker启动时会创建一个虚拟网桥 docker0,可以理解为一个软件交换机。当创建一个 Docker 容器的时候,会创建一对 veth pair 接口(当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包)。这对接口一端在容器内,即 eth0 ;另一端在宿主机本地并被挂载到 docker0 网桥,名称以veth 开头,如 veth340c305,docker0会在挂载到它上面的网口之间进行转发,从而实现主机与容器之间及容器与容器之间的相互通信。Docker默认的网络拓扑图如下:
我们可以在宿主机上通过ifconfig
查看相关的网络接口,
~$ ifconfigdocker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255 inet6 fe80::42:46ff:fe26:ce0b prefixlen 64 scopeid 0x20<link> ether 02:42:46:26:ce:0b txqueuelen 0 (Ethernet) RX packets 16868344 bytes 127838098551 (127.8 GB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 17929275 bytes 137867853738 (137.8 GB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0veth340c305: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet6 fe80::50f7:7ff:fe8f:6e72 prefixlen 64 scopeid 0x20<link> ether 52:f7:07:8f:6e:72 txqueuelen 0 (Ethernet) RX packets 8093606 bytes 126893792744 (126.8 GB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 8795102 bytes 10834735399 (10.8 GB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0veth6c803b7: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet6 fe80::1045:4cff:fe66:7f5a prefixlen 64 scopeid 0x20<link> ether 12:45:4c:66:7f:5a txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 140 bytes 9832 (9.8 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
通过brctl show
可查看网络接口的挂载情况,
~$ brctl showbridge name bridge id STP enabled interfacesdocker0 8000.02424626ce0b no veth340c305 veth6c803b7
由上可看出网络接口veth340c305,veth6c803b7都挂在虚拟网桥docker0上。
2. 容器与外部的互联
我们前面的许多容器启动命令都有添加类似 -p 8080:8080 的参数,以指定将宿主机端口映射到容器端口,从而通过访问 宿主机IP:宿主机端口
的地址来访问对应端口的容器服务。 端口映射的完整格式为 宿主机IP:宿主机端口:容器端口
,其中前两个是可以两者都取,或只取其一
宿主机IP:宿主机端口:容器端口
:将指定宿主机IP的一个指定端口映射到容器端口,如192.168.40.205:8090:8080
宿主机IP::容器端口
:将指定宿主机IP的一个随机端口映射到容器端口上,如果宿主机有多个IP,则可以通过这种格式指定绑定其中一个宿主机IP,随机端口范围为49000~49900
宿主机端口:容器端口
:将宿主机所有网络接口IP的指定端口映射到容器端口上,8090:8080
等效于0.0.0.0:8090:8080
(0.0.0.0
即表示所有网络接口地址)
可以使用 docker port 容器ID或名称 容器端口
或docker ps
命令来查看端口映射情况,如
~$ docker port test-dev 80800.0.0.0:32768~$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES696a76944e72 cnbots:dev "/bin/sh -c '/usr/lo…" 23 minutes ago Up 23 minutes 0.0.0.0:32768->8080/tcp test-dev
在容器启动时,可以多次使用 -p 来指定映射多个端口。
如果不指定具体的宿主机端口,则可以使用 -P(大写)来分配一个宿主机的随机端口(范围为49000~49900), 如docker run -d -P --name test-dev test:dev
,然后通过docker port 容器ID或名称 容器端口
或docker ps
命令来查看具体映射到了哪个端口。
3. 容器之间的互联
同一个Docker Daemon下的容器,彼此之间是可以通过容器IP互相访问的(如何查看容器IP?用docker inspect 容器ID或名称
命令),如果要实现两个容器之间可以通过容器名直接访问,则可以通过自建一个docker网络。
# 创建一个自定义网络,-d 表示网络类型,可以为bridge(网桥,软件交换机),或overlay(跨Docker Daemon容器间的互联)~$ docker network create -d bridge my-net0c97fc265ed1cab67d84b9376d6914c9558419c73bb5abc040e75c945cd99f0a# 启动一个centos容器centos1,通过 --network 指定自定义网络~$ docker run -it --name centos1 --network my-net centos:7.3.1611 bash[root@3dcf507bd12a /]# # 再启动一个centos容器centos2(打开另一个窗口),指定同一个自定义网络~$ docker run -it --name centos2 --network my-net centos:7.3.1611 bash[root@16dcce660a89 /]# # 在centos1容器中直接ping centos2[root@3dcf507bd12a /]# ping centos2PING centos2 (172.19.0.2) 56(84) bytes of data.64 bytes from centos2.my-net (172.19.0.2): icmp_seq=1 ttl=64 time=0.111 ms64 bytes from centos2.my-net (172.19.0.2): icmp_seq=2 ttl=64 time=0.058 ms# 在centos2容器中直接ping centos1[root@16dcce660a89 /]# ping centos1PING centos1 (172.19.0.3) 56(84) bytes of data.64 bytes from centos1.my-net (172.19.0.3): icmp_seq=1 ttl=64 time=0.061 ms64 bytes from centos1.my-net (172.19.0.3): icmp_seq=2 ttl=64 time=0.054 ms
由上可见通过自定义网桥连接的容器可以通过容器名称互相访问。如果需要多个容器之间互联,则可以使用Docker Compose。
4. 配置容器的DNS
如果要自定义所有容器的DNS,则可以在 /etc/docker/daemon.json 中增加
{ "dns" : [ "114.114.114.114", "8.8.8.8" ]}
也可以在启动容器时通过参数指定单个容器的DNS配置,--dns=IP_ADDRESS
,这会将指定DNS的地址添加到容器的 /etc/resolv.conf 文件中,让容器用这个DNS服务器来解析所有不在 /etc/hosts 中的主机名。
5. Docker网络的底层实现
容器的网络访问控制,主要是通过Linux上的iptables防火墙 来实现与管理的。
1. 容器访问外部网络 容器访问外部网络,需要通过本地系统的转发,可以通过如下命令查看转发是否打开
$sysctl net.ipv4.ip_forwardnet.ipv4.ip_forward = 1# 为1为打开,为0则未打开,可通过如下命令打开,也可以在Docker服务启动时通过参数--ip-forward=true打开$sysctl -w net.ipv4.ip_forward=1
容器所有到外部网络的访问,源地址都会被 NAT 成本地系统的 IP 地址。这是使用 iptables 的源地址伪装操作实现的,
~# iptables -t nat -nLChain POSTROUTING (policy ACCEPT)target prot opt source destination MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
上述规则将所有源地址在 172.17.0.0/16 的网段(容器IP所在网段),目标地址为任意网段(包括外部网络)的流量动态伪装为从系统网卡发出。MASQUERADE 跟传统 SNAT 的好处是它能动态从网卡获取地址。
2. 外部访问容器
通过 -p 或 -P 指定端口映射,允许外部访问容器端口,实质也是在本地的 iptable 的 nat 表中添加相应的规则,如
~# iptables -t nat -nLChain DOCKER (2 references)target prot opt source destination DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:3306 to:172.17.0.2:3306DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:11090 to:172.17.0.3:11090
这里的规则映射了 0.0.0.0 ,意味着将接受主机来自所有网络接口的流量。
3. 容器之间的访问 容器之间能互相访问,需要满足两个条件:1)容器的网络拓扑是否已经互联,默认情况下容器都连接到docker0网桥上,默认是互联的。2)本地系统的防火墙iptables是否允许通过。当容器启动时通过–link互联时,也是在iptables中创建对应规则来实现。
6. 总结
本文整理了Docker网络相关知识,对容器之间及容器与外部之间的通信机制应该有了一定的了解。除了默认的网络实现,Docker还提供了网络的配置及自定义网络,出于篇幅,本文介绍到这,后续再补充。
相关阅读
Docker笔记(一):什么是Docker
Docker笔记(二):Docker管理的对象 Docker笔记(三):Docker安装与配置 Docker笔记(四):Docker镜像管理 Docker笔记(五):整一个自己的镜像 Docker笔记(六):容器管理
Docker笔记(七):常用服务安装——Nginx、MySql、Redis
Docker笔记(八): 数据管理
我的微信公众号:jboost-ksxy (一个不只有技术干货的公众号,欢迎关注,及时获取更新内容)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?