Docker网络
在 Docker 中,不同容器之间的网络访问原理取决于容器所使用的网络模式。下面是 Docker 中常见的两种网络模式下容器间网络访问的原理:
-
桥接模式(Bridge):
- 在桥接模式下,Docker 使用 Linux 桥接技术创建一个虚拟的网络桥接设备,所有容器都连接到该桥接设备上。
- 每个容器都被分配一个唯一的 IP 地址,并且它们可以通过这些 IP 地址相互通信。
- Docker 主机上运行的容器可以使用 NAT(Network Address Translation)机制来实现与外部网络的通信。
- 当容器之间需要通信时,数据包会在它们之间通过 Linux 桥接设备转发。
-
主机模式(Host):
- 在主机模式下,容器与宿主机共享网络命名空间,它们直接使用宿主机的网络栈和接口。
- 因此,在主机模式下,容器之间的网络访问就像是它们运行在同一台主机上的进程之间的通信,无需进行额外的网络转发或 NAT。
- 容器可以直接使用宿主机上的 IP 地址和端口,因此在主机模式下,容器之间的通信速度可能会更快,但也可能会导致端口冲突和网络配置更改的困扰。
我们主要使用docker的桥接模式
bash
#准备工作,清空当前的容器与镜像
docker rm -f $(dokcer ps -a -q)
docker rmi -f $(dokcer ps -aq)
使用ip addr 查看网络状态
bash
[root@hcss-ecs-8f46 ~]# ip addr
#本地回环网络
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
#网络地址,也是云服务器所在的地址
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether fa:16:3e:0f:69:6a brd ff:ff:ff:ff:ff:ff
inet 192.168.2.3/20 brd 192.168.15.255 scope global dynamic noprefixroute eth0
valid_lft 314842758sec preferred_lft 314842758sec
inet6 fe80::f816:3eff:fe0f:696a/64 scope link
valid_lft forever preferred_lft forever
#docker 创建的网络
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:87:87:66:38 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:87ff:fe87:6638/64 scope link
valid_lft forever preferred_lft forever
1、微服务这么多,会有无数的ip地址,所以可以通过容器的名称访问容器网络,docker每启动一个容器,就会创建一个ip。
bash
[root@hcss-ecs-8f46 ~]# docker run -itd --name web-01 centos
Unable to find image 'centos:latest' locally
latest: Pulling from library/centos
7a0437f04f83: Already exists
Digest: sha256:dbbacecc49b088458781c16f3775f2a2ec7521079034a7ba499c8b0bb7f86875
Status: Downloaded newer image for centos:latest
3c3f2c1f573c6bf504dd91f6b2946e496644cd8e7a599a249beb71d8fc68a083
[root@hcss-ecs-8f46 ~]# docker exec -it web-01 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
#这个是docker启动容器是产生的ip,它归docker0管
54: eth0@if55: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
# 容器外部可以ping通容器内部的ip地址
[root@hcss-ecs-8f46 ~]# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.041 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.034 ms
1、分析容器外部ping容器内部的网络原理
bash
[root@hcss-ecs-8f46 ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether fa:16:3e:0f:69:6a brd ff:ff:ff:ff:ff:ff
inet 192.168.2.3/20 brd 192.168.15.255 scope global dynamic noprefixroute eth0
valid_lft 314841874sec preferred_lft 314841874sec
inet6 fe80::f816:3eff:fe0f:696a/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:87:87:66:38 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:87ff:fe87:6638/64 scope link
valid_lft forever preferred_lft forever
55: veth1da7586@if54: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether d2:72:78:49:50:2b brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::d072:78ff:fe49:502b/64 scope link
valid_lft forever preferred_lft forever
[root@hcss-ecs-8f46 ~]# docker exec -it web-01 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
54: eth0@if55: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
-
启动容器后,会自动创建IP地址
-
网路容器外与容器内的网络是配对的。例如我们新运行的centos,外部分配的网络是"55: veth1da7586@if54:",容器内部存在网络地址"54: eth0@if55: "
-
总结
- 只要启动一个容器,就会默认分配一对网卡
- 这对网络是虚拟接口"veth-pair",它是一对虚拟设备接口,成对出现,一端连着协议栈,一端彼此相连
- 就好比一个桥梁,连接容器内外
-
inet 172.17.0.2/16 brd 172.17.255.255 总共可以创建 255*255个容器,再多就没有网络地址了。
2、容器删除或重启后,IP变化,如何解决容器间的网络访问问题
- 方案1、--link,但不方便,几乎不用。
bash
# 通过参数 --link在容器启动时就链接到另一个容器网络中,就可以通过名称ping了
docker run -itd --name web-02 --link web-01 centos
docker exec -it web-02 ping web-01
# 但是反向仍ping不通
[root@hcss-ecs-8f46 ~]# docker exec -it web-01 ping web-02
ping: web-02: Name or service not known
# 底层原理:
# 与本机的域名解析过程类似,host 127.0.0.1 www.badu.com
# --links 相当于hosts 添加了一条记录。
- 方案2、自定义网络。
bash
[root@hcss-ecs-8f46 ~]# docker network --help
Usage: docker network COMMAND
Manage networks
Commands:
connect Connect a container to a network
create Create a network
disconnect Disconnect a container from a network
inspect Display detailed information on one or more networks
ls List networks
prune Remove all unused networks
rm Remove one or more networks
Run 'docker network COMMAND --help' for more information on a command.
# docker0的特点
1.它是默认的
2.域名访问不通
3.通过--link域名通了,但容器删了就又不行了
bash
[root@hcss-ecs-8f46 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
3e1cb1f23c44 bridge bridge local
3783048b4c7a host host local
eddad10b8bca none null local
[root@hcss-ecs-8f46 ~]# docker network inspect 3e1cb1f23c44
[
{
"Name": "bridge",
"Id": "3e1cb1f23c447860fa017d76195a53b025cb117a7e0ef4bea77ba41d9b378b39",
"Created": "2024-03-13T10:46:36.060713723+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
#网络配置 网段 /16代表后俩位数字可以 故总网段数为255*255
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
# 之前创建的容器,都在contain内中
"Containers": {
"3c3f2c1f573c6bf504dd91f6b2946e496644cd8e7a599a249beb71d8fc68a083": {
"Name": "web-01",
"EndpointID": "390419366b591b79b84e7f662a40f440d1fb439478f11756f049828d21b5c247",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"995d9af8e8c729ef1a5bf19a0ddd9c2a79d30d825a1b94a562d8e9e1585e3a50": {
"Name": "web-02",
"EndpointID": "b0d57efdd0cf62641c0e6ec8c0ea9eb52e0a6a688aeac7b67c3bd7dbfe6d26f9",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
创建自定义网络
create命令创建一个新的网络 myDockerStudy
bash
docker network create --driver bridge --subnet 192.169.0.0/16 --gateway 192.169.0.1 myDockerStudyNet
[root@hcss-ecs-8f46 ~]# docker network inspect myDockerStudyNet
[
{
"Name": "myDockerStudyNet",
"Id": "cb2029ad79edbe63a178253584d997e1b91586c1ccb6adebbf36123ea511452a",
"Created": "2024-03-17T11:45:42.577493481+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.169.0.0/16",
"Gateway": "192.169.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
通过网络指定使用自己创建的网络.
bash
docker run -itd --name web-01-net --net myDockerStudyNet centos
docker run -itd --name web-02-net --net myDockerStudyNet centos
[root@hcss-ecs-8f46 ~]# docker network inspect cb2
[
{
"Name": "myDockerStudyNet",
"Id": "cb2029ad79edbe63a178253584d997e1b91586c1ccb6adebbf36123ea511452a",
"Created": "2024-03-17T11:45:42.577493481+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.169.0.0/16",
"Gateway": "192.169.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"f112d82ae67e66fab583c6986ccaf87b489a49a4e56652e6c83d4a2e6d126160": {
"Name": "web-02-net",
"EndpointID": "dfdae007633db3dd06e82a900857b3157a021a5277a5272eaabfb41d77cb927f",
"MacAddress": "02:42:c0:a9:00:03",
"IPv4Address": "192.169.0.3/16",
"IPv6Address": ""
},
"fa6d1332e302d27350969c1f64f87e2c378adbc182971899e35d8ab1052b376e": {
"Name": "web-01-net",
"EndpointID": "5d35bd6354a686152d3a4e8ada3e33cf74628b105476dbd6239316aa0250cd5c",
"MacAddress": "02:42:c0:a9:00:02",
"IPv4Address": "192.169.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
使用自身创建的网络,容器间可以互相ping的
bash
##ip
[root@hcss-ecs-8f46 ~]# docker exec -it web-01-net ping 192.169.0.3
PING 192.169.0.3 (192.169.0.3) 56(84) bytes of data.
64 bytes from 192.169.0.3: icmp_seq=1 ttl=64 time=0.071 ms
##容器名称
[root@hcss-ecs-8f46 ~]# docker exec -it web-01-net ping web-02-net
PING web-02-net (192.169.0.3) 56(84) bytes of data.
64 bytes from web-02-net.myDockerStudyNet (192.169.0.3): icmp_seq=1 ttl=64 time=0.049 ms
##反ping
[root@hcss-ecs-8f46 ~]# docker exec -it web-02-net ping web-01-net
PING web-01-net (192.169.0.2) 56(84) bytes of data.
64 bytes from web-01-net.myDockerStudyNet (192.169.0.2): icmp_seq=1 ttl=64 time=0.048 ms
总结:使用自定义网络后。容器之间就可以通过容器名称互相访问了。ip无论发生怎样的变化,也不会产生错误。
3、容器与多网络联通
通过以上的操作,我们现在共有2个网络。分别是docker0与我们的自定义网络myDockerStudyNet。那么这两个网路能否互相ping通呢
bash
# 无法ping通
[root@hcss-ecs-8f46 ~]# docker exec -it web-01 ping 192.169.0.3
PING 192.169.0.3 (192.169.0.3) 56(84) bytes of data.
^C
--- 192.169.0.3 ping statistics ---
6 packets transmitted, 0 received, 100% packet loss, time 145ms
所以自定义网络之间库存时互不相通的,我们使用自定义网络的好处就是网络隔离。
如果在同一个网络下,有些程序猿的恶意代码就不能防止了,所以在部署时。使用网络隔离,创建两个桥接网卡,比如订单业务(里面的数据库、redis、mq,都在order-net网络下),其他业务在其他网络。
那么关键问题出现了,如何成功让web-01-net ping web-01?
bash
#使用docker network connect命令
[root@hcss-ecs-8f46 ~]# docker network connect --help
Usage: docker network connect [OPTIONS] NETWORK CONTAINER
Connect a container to a network
Options:
--alias strings Add network-scoped alias for the container
--driver-opt strings driver options for the network
--ip string IPv4 address (e.g., "172.30.100.104")
--ip6 string IPv6 address (e.g., "2001:db8::33")
--link list Add link to another container
--link-local-ip strings Add a link-local address for the container
# 执行
[root@hcss-ecs-8f46 ~]# docker network connect myDockerStudyNet web-01
[root@hcss-ecs-8f46 ~]# docker network inspect myDockerStudyNet
[
{
"Name": "myDockerStudyNet",
"Id": "cb2029ad79edbe63a178253584d997e1b91586c1ccb6adebbf36123ea511452a",
"Created": "2024-03-17T11:45:42.577493481+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.169.0.0/16",
"Gateway": "192.169.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"09479e1e460ff05ca7da8c810d9a36bde71f923952e5dbb27e8497482fc2e7a1": {
"Name": "web-01",
"EndpointID": "12d3797952ae9f5a34fb3dc5dee8d034529abda6da8e33f86a1061a4d38f6840",
"MacAddress": "02:42:c0:a9:00:04",
"IPv4Address": "192.169.0.4/16",
"IPv6Address": ""
},
"f112d82ae67e66fab583c6986ccaf87b489a49a4e56652e6c83d4a2e6d126160": {
"Name": "web-02-net",
"EndpointID": "dfdae007633db3dd06e82a900857b3157a021a5277a5272eaabfb41d77cb927f",
"MacAddress": "02:42:c0:a9:00:03",
"IPv4Address": "192.169.0.3/16",
"IPv6Address": ""
},
"fa6d1332e302d27350969c1f64f87e2c378adbc182971899e35d8ab1052b376e": {
"Name": "web-01-net",
"EndpointID": "5d35bd6354a686152d3a4e8ada3e33cf74628b105476dbd6239316aa0250cd5c",
"MacAddress": "02:42:c0:a9:00:02",
"IPv4Address": "192.169.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
**会发现web-01同时存在于docker0,myDockerStudyNet两个网络中。且该容器拥有了两个ip地址。**myDockerStudyNet中的3个容器就可以互相访问了。
总结
如果要夸网络操作别的容器,使用docker network connect命令。最终即使是天一样多的网络,也可以清晰且任意的在各个网络中访问