文章目录
一、引言
一直拖着Docker
网络这一节迟迟未动,直到最近一个问题深深刺激了我。
问题是这样的:我一个SpringBoot
项目有一个通过第三方接口同步数据的功能,起初我本地访问可以访问的通,部署到服务器上的docker容器中就访问超时。排查过程是这样的:
- 起初怀疑是docker宿主机网络慢导致,但深深的又知道不可能比我本地慢,但还是要排查,就采用
curl
方式进行访问,结果正常返回(因为请求是SOAP
方式,这里还是chatGPT
帮我进行转换的) - 其次就是怀疑容器CPU和内存限制(这里也是
chatGPT
提示的),然后就是通过chatGPT
帮助之下查看应用容器的CPU和内存的限制,结果显示没任何限制
// 查看容器的 CPU 限制和实际使用情况
docker stats SYJG-ZXYY
//获取容器的详细信息,包括 CPU 和内存限制
docker inspect SYJG-ZXYY --format='{{json .HostConfig.Resources}}'
- 然后愚笨的百度了一下
为什么我请求部署到docker容器中就很慢,但是curl测试就很快
, 结果看到了一篇关于docker网络的
一篇文章,才算是正式切入问题本质,结合chatGPT
了解docker网络哪方面的影响,DNS或者网络模式,因为是内网IP方式请求,就不存在DNS解析慢的因素,那就只有网络模式了,然后通过修改应用容器的网络模式(直接修改为host网络模式)才得以解决。
反思和总结:
- 这类问题没有合理的怀疑直接通过抓包的方式可能是抓住问题本质的一种方式
- 采用搜索引擎掌握尽可能贴近的关键词,能事半功倍,比如以上:
docker容器网络慢
,要不然就一通搜索之后才能进一步明确关键词 - 大胆合理的怀疑,按照优先顺序进行排查
- docker网络方面知识欠缺是本质,要是掌握这一知识,就能直接定位问题为
docker网络慢的问题
二、网络原理
容器是相对独立的环境,相当于一个小型的 Linux 系统,有自己独立的网络
2.1 Linux veth pair
veth-pair 就是一对的虚拟设备接口,和 tap/tun 设备不同的是,它都是成对出现的。一端连着协议栈,一端彼此相连着。如下图所示:
正因为有这个特性,它常常充当着一个桥梁,连接着各种虚拟网络设备,Docker容器之间的连接就采用veth pair技术
2.2 虚拟网卡Docker0
查看一下宿主机ip
ifconfig/ip addr
结果宿主机有三个网络
lo 127.0.0.1 # 本机回环地址
eth0 172.31.179.120 # 宿主机ip
docker0 172.17.0.1 # docker网桥
lo和eth0在我们的虚拟机启动的时候就会创建,但是docker0在我们安装了docker的时候就会创建。docker0用来和虚拟机之间通信。
问题:Docker 是如何处理容器网络访问的?
我们先启动一个 tomcat 容器来说明。
docker pull tomcat
docker run -d -p 8081:8080 --name tomcat01 tomcat
这里启动了tomcat01,我们再来查看网络
我们发现启动了一个tomcat容器之后,多了一组网卡9: vetha0f1939@if8
,并且是连接到docker0
的
我们进入tomcat01容器,查看容器内部的网卡信息
# 进入tomcat01容器
docker exec -it tomcat01 /bin/bash
安装ip addr
和ping
命令
apt-get update
#ip addr命令
apt install -y iproute2
#ping命令
apt install -y iputils-ping
我们查看tomcat01
内部网络
容器内部有一组8: eth0@if9
网卡信息,恰好和宿主机增加的9: vetha0f1939@if8
网卡信息构成一对,这就充分的体现了veth-pair 是成对出现的。一端连着协议栈,一端彼此相连着
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lt4nsjmu-1688462012492)(https://gitee.com/xmc-space/images/raw/master/markdown/docker%E7%BD%91%E7%BB%9C.png)]
三、容器互联–Link
我们再构建并启动一个tomcat02
容器
docker run -d -p 8082:8080 --name tomcat02 tomcat
系统 | IP | IP查看命令 |
---|---|---|
宿主机 | 192.168.10.110 | ip addr |
tomcat01 | 172.17.0.2 | cat /etc/hosts |
tomcat02 | 172.17.0.3 | cat /etc/hosts |
我们验证其互通情况如下:
- 宿主机可以
ping
通容器tomcat01
、容器tomcat02
- 容器
tomcat01
和容器tomcat02
能相互ping
通,前提是处于同一网络 - 容器tomcat01和容器tomcat02能
ping
通宿主机
但是,容器重新启动之后IP是由docker动态分配的。容器之间采用ip互通显示不可以,这个时候docker --link
就登场了。。。
docker --link
是一个过时的 Docker 命令,它用于在容器之间创建链接(links)。通过链接,一个容器可以访问另一个容器,并在环境变量中自动设置与被链接容器相关的信息。
使用 docker --link
命令的基本语法如下:
docker run --name <container_name> --link <linked_container_name>:<alias> <image>
其中:
<container_name>
是当前要创建的容器的名称。<linked_container_name>
是要链接的容器的名称。<alias>
是要链接容器的别名,可以通过这个别名访问要链接的容器,相当于域名。
我们再构建并启动一个tomcat03容器,使用—link绑定到tomcat02上。
docker run -d -p 8083:8080 --name tomcat03 --link tomcat02 tomcat
然后看看tomcat03的hosts是什么样的
root@926036cb171d:/usr/local/tomcat# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
# tomcat02 服务名
172.17.0.2 tomcat02 97c5254e1212
172.17.0.3 926036cb171d
当运行 docker --link
命令时,Docker 会在被链接的容器上创建一个环境变量,并将其传递给当前容器。这些环境变量包括了被链接容器的 IP 地址、端口以及其他相关信息,以便被链接容器能够被访问和使用。
然而,需要注意的是,docker --link
命令已经不再推荐使用,并且在最新版本的 Docker 中已经不建议使用该功能。相反,更好的做法是使用自定义网络(Custom Networks)来连接容器,或者使用外部服务发现工具(例如 Consul、etcd 等)管理容器之间的通信和依赖关系。这些方法提供了更灵活、可扩展和可维护的方式来管理容器之间的连接和通信。
四、网络模式
Docker 提供了多种网络选项,用于在容器之间进行通信和与主机进行交互。
以下是 Docker 中常见的网络模式:
1. 桥接网络(Bridge Network
):这是 Docker
默认使用的网络模式。在桥接网络中,Docker
使用一个名为docker0
的虚拟网桥来连接容器和主机,每个容器都被分配一个 IP 地址,并且可以通过容器名称或 IP 地址进行相互通信。可以使用 -p
或 --publish
参数将容器内部的端口映射到主机上,以便从主机上的其他服务或外部访问容器。使用--network bridge
指定,默认使用docker0
2. 主机网络(Host Network
):在主机网络模式中,容器与主机共享相同的网络命名空间。这意味着容器使用主机的网络接口和 IP 地址,可以直接访问主机上的服务而无需进行端口映射。请注意,这种模式会牺牲容器的隔离性。使用--network host
指定
3. 空白网络(None Network
):在空白网络模式下,容器没有默认的网络连接。你可以自定义和配置容器的网络设置。使用--network none
指定
4. container模式: 个容器可以直接连接到另一个容器的网络命名空间,从而共享相同的网络栈和网络配置。这意味着两个容器可以使用本地主机地址进行通信,就像它们在同一台物理主机上运行一样
5. 自定义网络(Custom Network
):使用自定义网络模式可以创建用户定义的网络,以便容器在该网络中进行通信。这种方式可以提供更好的隔离和管理容器之间的连接。
网络模式 | 配置 | 说明 |
---|---|---|
桥接网络 | --network bridge | 默认值,在Docker网桥docker0上为容器创建新的网络栈 |
主机网络 | --network host | 容器和宿主机共享Network namespace |
空白网络 | --network none | 不配置网络,用户可以稍后进入容器,自行配置 |
container模 | --net=container:name/id | 容器和另外一个容器共享Network namespace。kubernetes中的pod就是多个容器共享一个Networknamespace. |
自定义网络 | -net=自定义网络 | 用户自己使用network相关命令定义网络,创建容器的时候可以指定为自己定义的网络 |
五、container模式
是的,Docker 还提供了一种网络模式称为 container
模式(也称为 container networking
或 connect to another container's network namespace
)。
在 container
模式下,一个容器可以直接连接到另一个容器的网络命名空间,从而共享相同的网络栈和网络配置。这意味着两个容器可以使用本地主机地址进行通信,就像它们在同一台物理主机上运行一样。
具体步骤如下:
-
创建源容器:首先创建一个源容器,该容器需要暴露服务或提供网络连接给其他容器使用。
docker run --name source_container -d <image>
-
创建目标容器并连接到源容器的网络命名空间:然后创建一个目标容器,并使用
--network container:<source_container>
参数将其连接到源容器的网络命名空间。docker run --name target_container --network container:source_container -d <image>
注意,
<source_container>
是源容器的名称或 ID。
现在,目标容器与源容器共享相同的网络命名空间,它们可以使用本地主机地址进行通信,无需通过网络端口暴露或进行端口映射。
使用 “container” 模式可以实现容器之间的高性能和低延迟通信。然而,要注意容器之间的网络隔离较弱,因为它们共享相同的网络命名空间。请确保在使用 “container” 模式时考虑安全性和隐私方面的问题。
六、自定义网络
4.1 创建网络
要自定义 Docker 网络,可以使用 Docker 的网络管理工具(如 Docker CLI 或 Docker Compose)。下面是一些创建和管理自定义网络的基本步骤:
- 创建自定义网络:
# 将 `<network_name>` 替换为你想要为网络指定的名称。
docker network create <network_name>
下面我们创建mynet网络
[root@Docker110 ~]# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
4a8f52de0dd3a7889a90b156a5472d3014fc92c76056228ca69a0968953f837f
[root@Docker110 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
e3ea29655c3c bridge bridge local
401a8b1ac619 elk_host bridge local
0784764770a2 es_default bridge local
4f5c28919e15 host host local
4a8f52de0dd3 mynet bridge local
d4a377dcf96c none null local
- 运行容器时连接到自定义网络:
# 使用 `--network` 参数将容器连接到指定的自定义网络。
docker run --network=<network_name> <image_name>
下面我们使用自定义的网络构建tomcat容器并连接到mynet网络
[root@Docker110 ~]# docker run -d -p 8081:8080 --name tomcat-net-01 --net mynet tomcat
60a2f054ff0b642b8aa810f60d633306f55622ae4caafe16a1c615f9af151c4d
[root@Docker110 ~]# docker run -d -p 8082:8080 --name tomcat-net-02 --net mynet tomcat
e829344173d7194fe3da64c59150fbbbfc9fee20a8dc80d2b321e2f980ea16d4
查看网络
[root@Docker110 ~]# docker inspect mynet
[
{
"Name": "mynet",
"Id": "4a8f52de0dd3a7889a90b156a5472d3014fc92c76056228ca69a0968953f837f",
"Created": "2023-07-03T23:26:59.290621545+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.168.0.0/16",
"Gateway": "192.168.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"60a2f054ff0b642b8aa810f60d633306f55622ae4caafe16a1c615f9af151c4d": {
"Name": "tomcat-net-01",
"EndpointID": "b021ccbfb67471705050985a8e76621ffbcdeb1b6cdb6d8c2101ad04e4d8ea86",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
},
"e829344173d7194fe3da64c59150fbbbfc9fee20a8dc80d2b321e2f980ea16d4": {
"Name": "tomcat-net-02",
"EndpointID": "92636fd409f8b89845755fbb9f839b905e0a6eacfe82a088c4552b534ed5ac8f",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
我们来测试ping容器名和ip试试,都可以ping通
[root@Docker110 ~]# docker exec -it tomcat-net-01 ping 192.168.0.3
PING 192.168.0.3 (192.168.0.3) 56(84) bytes of data.
64 bytes from 192.168.0.3: icmp_seq=1 ttl=64 time=0.185 ms
64 bytes from 192.168.0.3: icmp_seq=2 ttl=64 time=0.126 ms
64 bytes from 192.168.0.3: icmp_seq=3 ttl=64 time=0.103 ms
[root@Docker110 ~]# docker exec -it tomcat-net-01 ping tomcat-net-02
PING tomcat-net-02 (192.168.0.3) 56(84) bytes of data.
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.079 ms
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=2 ttl=64 time=0.133 ms
-
使用 Docker Compose 创建自定义网络(可选):
<!--compose版本--> version: '3' services: my_service: image: <image_name> <!--指定网络--> networks: - my_network <!--定义网络--> networks: my_network: <!--指定网络驱动程序,不显示指定默认是桥接(bridge)网络驱动程序--> driver: bridge
在 Docker Compose 文件中定义一个自定义网络,并将服务与该网络关联。
-
查看已创建的自定义网络:
docker network ls
运行此命令以列出当前存在的网络,包括自定义网络。
-
删除自定义网络(可选):
docker network rm <network_name>
使用此命令删除不再需要的自定义网络。请注意,只有在没有容器连接到网络时才能成功删除。
通过自定义网络,你可以为容器创建独立的逻辑网络,使其能够相互通信并与主机进行隔离。这对于多个容器之间的内部通信和应用程序架构是有用的。
4.2 Docker网络驱动程序和网络模式区别
Docker 的网络驱动程序和网络模式是两个不同的概念,它们在 Docker 网络中扮演着不同的角色:
1. 网络驱动程序(Network Drivers): Docker 网络驱动程序是用于管理容器网络连接和通信的组件。它定义了容器如何与主机和其他容器进行通信以及如何进行网络隔离。常见的网络驱动程序包括 Bridge、Host、Overlay、MACVLAN 等。选择合适的网络驱动程序可以根据需求提供不同的网络功能和性能特性。
2. 网络模式(Network Modes): Docker 网络模式定义了容器如何加入到主机网络或其他容器之间以实现通信。这些网络模式包括桥接网络(Bridge Network)、主机网络(Host Network)、空白网络(None Network)和自定义网络(Custom Network)等。每种网络模式具有不同的特性和适用场景,用于满足容器之间和容器与主机之间的网络连接需求。
总结来说,Docker 的网络驱动程序是底层的技术组件,用于实现容器网络的功能和性能。而网络模式是通过选择合适的网络设置来定义容器的网络连接方式和隔离级别。网络驱动程序决定了容器网络的行为和特性,而网络模式则决定了容器加入到网络的方式和规则。
七、网络连通
docker0和自定义网络肯定不通,我们使用自定义网络的好处就是网络隔离。
但是在实际的工作中,比如我们部署了mysql使用了一个网段。部署了tomcat使用了另一个网段,两个网段之间肯定是不能相互连通的,但是tomcat和mysql又需要相互连通,我们就要使用网络连通。原理图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e20e92Si-1688462012492)(https://gitee.com/xmc-space/images/raw/master/markdown/%E5%AE%B9%E5%99%A8%E8%BF%9E%E6%8E%A5%E5%88%B0%E7%BD%91%E7%BB%9C.png)]
# ping IP 可以ping通
[root@Docker110 ~]# docker exec -it tomcat04 ping 192.198.0.2
PING 192.198.0.2 (192.198.0.2) 56(84) bytes of data.
64 bytes from 192.198.0.2: icmp_seq=1 ttl=127 time=242 ms
64 bytes from 192.198.0.2: icmp_seq=2 ttl=127 time=267 ms
# ping 服务名 不能ping通
[root@Docker110 ~]# docker exec -it tomcat04 ping tomcat-net-01
ping: tomcat-net-01: Name or service not known
# tomcat04 连接到 mynet 网络
[root@Docker110 ~]# docker network connect mynet tomcat04
# 很神奇吧 能ping通了
[root@Docker110 ~]# docker exec -it tomcat04 ping tomcat-net-01
PING tomcat-net-01 (192.168.0.2) 56(84) bytes of data.
64 bytes from tomcat-net-01.mynet (192.168.0.2): icmp_seq=1 ttl=64 time=0.147 ms
64 bytes from tomcat-net-01.mynet (192.168.0.2): icmp_seq=2 ttl=64 time=0.111 ms
64 bytes from tomcat-net-01.mynet (192.168.0.2): icmp_seq=3 ttl=64 time=0.056 ms
网络连通就是将一个容器和一个网段之间的连通。
比如我前面使用的默认docker0的tomcat04,需要连接到mynet网络。
通过这种方式直接将tomcat04加到了mynet网络中。
八、常见使用命令
- 查看网络
docker network ls
// 返回如下:
NETWORK ID NAME DRIVER SCOPE
06917d5ea73f bridge bridge local
53b7ff1a67d8 host host local
4fc06f4b443c kong_default bridge local
c44b4cd18999 none null local
- 查看Docker容器的网络模式
docker inspect --format='{{.HostConfig.NetworkMode}}' <container_id>
将 <container_id>
替换为要检查的容器的实际ID或名称。执行此命令后,它将返回容器当前使用的网络模式,例如 “bridge”、“host” 或其他自定义网络。
如果执行 docker inspect --format='{{.HostConfig.NetworkMode}}' <container_id>
命令返回的结果是 “default”,那意味着容器正在使用默认网络模式。默认网络模式是指 Docker 使用的基本网络设置,其中容器会被连接到一个称为 “bridge” 的虚拟网络中。在这种模式下,Docker 容器可以通过 NAT (网络地址转换) 访问主机网络和外部网络。
- 查看网络源数据
docker network inspect XXx网络名字
- 删除网络
docker network rm XXx网络名字
- 查看bridge网络的详细信息,并通过grep获取名称项
docker network inspect bridge | grep name
九、总结
- linux veth pair是成对出现的一种虚拟网络设备接口,一端连着网络协议栈,一端彼此相连。
- docker中默认使用docker0网络。docker0就是桥接模式,当然你可以自定义自己桥接模式的网络
- docker0相当于一个路由器的作用,任何一个容器启动默认都是docker0网络。
- docker0是容器和虚拟机之间通信的桥梁。
- 推荐使用自定义网络,更好实现使用服务名的连通方式,避免ip改变的尴尬。
-link
方式已经过时 - 网络之间不能直接连通,网络连通是将一个容器和一个网络之间的连通,实现跨网络操作。上述网络连通就可以解决。