学习内容总结来自B站UP主"遇见狂神说"的Docker教学视频: https://www.bilibili.com/video/BV1og4y1q7M4
Docker网络
理解Docker0
在宿主机linux上, 运行ip addr
, 查看当前的网卡信息
(root@Aliyun-Alex:/home/alex)# ip addr
# 打印结果
# 第一个 lo 为回环IP 127.0.0.1
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
# 第二个 eth0 为阿里云内网IP 172.19.67.12
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:16:3e:10:69:3c brd ff:ff:ff:ff:ff:ff
inet 172.19.67.12/20 brd 172.19.79.255 scope global dynamic noprefixroute eth0
valid_lft 310946826sec preferred_lft 310946826sec
inet6 fe80::216:3eff:fe10:693c/64 scope link
valid_lft forever preferred_lft forever
# 第三个 docker0 为Docker使用的IP 172.17.0.1
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:92:a2:eb:c6 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:92ff:fea2:ebc6/64 scope link
valid_lft forever preferred_lft forever
在宿主机安装了docker后, 就会新增一个名为docker0的网卡
启动一个容器
现在运行一个tomcat容器, 尝试linux宿主机ping一下tomcat容器, 看是否能ping通
docker run -d -P --name tomcat01 tomcat
查看tomcat容器中的ip地址信息
docker exec -it tomcat01 ip addr
# 可以看到也有两个地址
# 第一个 lo 为回环IP 127.0.0.1
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
# 第二个 eth0@if161 为容器的IP 172.17.0.2
160: eth0@if161: <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地址172.17.0.2
和宿主机的docker0172.17.0.1
属于同一网段, 那么两者就是能ping通的
(root@Aliyun-Alex:/home/alex)# 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.077 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.059 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.069 ms
64 bytes from 172.17.0.2: icmp_seq=4 ttl=64 time=3.02 ms
64 bytes from 172.17.0.2: icmp_seq=5 ttl=64 time=0.060 ms
^C
--- 172.17.0.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 63ms
rtt min/avg/max/mdev = 0.059/0.656/3.016/1.180 ms
再次回到宿主机linux执行ip addr
, 发现又多了个网卡 161: veth8dd219c@if160
161: veth8dd219c@if160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 16:71:2b:b0:4c:f2 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::1471:2bff:feb0:4cf2/64 scope link
valid_lft forever preferred_lft forever
再启动一个容器
docker run -d -P --name tomcat02 tomcat
在容器中查看ip addr
(root@Aliyun-Alex:/home/alex)# docker exec -it tomcat02 ip addr
# 同样有一个容器内部的IP 162: eth0@if163
162: eth0@if163: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
在宿主机linux执行ip addr
, 发现又多了个网卡 163: veth98c33bb@if162
163: veth98c33bb@if162: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether fa:a3:7c:12:d1:a9 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::f8a3:7cff:fe12:d1a9/64 scope link
valid_lft forever preferred_lft forever
可以看出规律, 每启动一个容器, 就会给容器分配一个IP, 同时宿主机也多一个IP地址, 且这两个IP地址是有对应关系的
在宿主机的网卡名为宿主机IP序号: vethxxx@if容器IP序号
在容器内的网卡名为容器IP序号: eth0@if宿主机IP序号
这就是veth-pair技术, 就是一对虚拟设备接口, 他们都是成对出现的, 一端连着协议, 一端彼此相连, veth-pair可以充当一个桥梁, 连接各种虚拟网络设备
如OpenStack, OVS, 和Docker容器之间的连接, 都是使用的veth-pair技术
由于这两个容器的IP都是在同一网段内(172.17.0.0), 所以可以相互ping通
(root@Aliyun-Alex:/home/alex)# docker exec -it tomcat01 ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.100 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.082 ms
Docker网络桥接图
Docker使用的是Linux的桥接, 宿主机中的docker01是容器间的网桥
只要删除容器, 对应的网桥一对就没了
--link
容器间ping时或连接时, 可否直接通过名字连接, 而不是通过固定写死的ip地址, 即若某个容器的ip地址换了, 不修改连接设置, 依然可以进行连接
可以类比为url的反向解析, 不是直接写死url, 而是给url一个名字, 通过访问名字来访问url, 即使该名字对应的url地址发生了变化, 通过名字也依然可以访问到变化后的url地址
直接使用容器名称, 在容器01中ping容器02, 发生报错
(root@Aliyun-Alex:/home/alex)# docker exec -it tomcat01 ping tomcat02
ping: tomcat02: Name or service not known
通过--link
可以解决这个问题
再启动一个容器 tomcat03, 使用 --link 连接 tomcat01
docker run -d -P --name tomcat03 --link tomcat01 tomcat
进入tomcat03容器中, 直接使用ping tomcat01
, 可以ping通
(root@Aliyun-Alex:/home/alex)# docker exec -it tomcat03 ping tomcat01
PING tomcat01 (172.17.0.2) 56(84) bytes of data.
64 bytes from tomcat01 (172.17.0.2): icmp_seq=1 ttl=64 time=0.113 ms
64 bytes from tomcat01 (172.17.0.2): icmp_seq=2 ttl=64 time=0.072 ms
64 bytes from tomcat01 (172.17.0.2): icmp_seq=3 ttl=64 time=0.073 ms
^C
--- tomcat01 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 72ms
rtt min/avg/max/mdev = 0.072/0.086/0.113/0.019 ms
但是反向在 tomcat01 容器中, 直接使用ping tomcat03
, 却不能ping通
(root@Aliyun-Alex:/home/alex)# docker exec -it tomcat01 ping tomcat03
ping: tomcat03: Name or service not known
查看tomcat03容器信息, 可以看到 Links 属性, 连接了tomcat01
docker inspect tomcat03
# 在输出结果中可以看到 Links 属性, 连接了tomcat01
"Links": [
"/tomcat01:/tomcat03/tomcat01"
],
查看tomcat03的/etc/hosts
文件也可以看到关联了tomcat01, 这就是--link的实质操作, 在host中添加一个连接信息
(root@Aliyun-Alex:/home/alex)# docker exec -it tomcat03 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
172.17.0.2 tomcat01 db68ef67ca80
172.17.0.4 340afdc0bb53
但是现在官方已经不建议使用 --link了, 更加高级的做法是使用自定义网络
自定义网络
查看所有docker网络docker network ls
(root@Aliyun-Alex:/home/alex)# docker network ls
NETWORK ID NAME DRIVER SCOPE
35b249810e67 bridge bridge local
de155dd42929 host host local
c8ffb814685e none null local
网络连接模式
- bridge(桥接模式): docker0就是docker容器间的桥梁
- none: 不配置网络
- host: 和宿主机共享网络
当直接启动容器时, 默认使用的一个参数是 --net bridge
, 这个bridge就是docker0网卡
docker run -d -P tomcat
# 即为
docker run -d -P --net bridge tomcat
我们可以不使用bridge(docker0) ,而是自己创建一个网络
创建网络
# --driver bridge 网络连接模式, 默认为bridge, 因此这里也可以不写这个参数
# --subnet 子网
# --getway 网关
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
# 查看网络
docker network ls
# 结果多了mynet
NETWORK ID NAME DRIVER SCOPE
35b249810e67 bridge bridge local
de155dd42929 host host local
8e7819b11d29 mynet bridge local
c8ffb814685e none null local
查看mynet具体网络信息
(root@Aliyun-Alex:/home/alex)# docker network inspect mynet
[
{
"Name": "mynet",
"Id": "8e7819b11d292e30e36a38095dc4b30f310affc9841b4500f5851c670a7f9b48",
"Created": "2020-07-30T17:41:46.204861397+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": {},
"Options": {},
"Labels": {}
}
]
使用自定义网络, 启动容器
启动两个容器 tomcat01 和 tomcat02
docker run -d -P --name tomcat01 --net mynet tomcat
docker run -d -P --name tomcat02 --net mynet tomcat
再次查看mynet具体网络信息, 可以看到关联的容器
(root@Aliyun-Alex:/home/alex)# docker network inspect mynet
"Subnet": "192.168.0.0/16",
"Gateway": "192.168.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"282a1cd78979de4afcfb24b10fd9c322e21f1eb01879cf5e02918a7dfc9945a4": {
"Name": "tomcat01",
"EndpointID": "2eccabd1b12d555cdbc4fd0a66e9618a3f0df40f6dd7199c94f01caa2b6c1d15",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
},
"680e66da888e5c18cff7aae19f51c5189127405a54547925f4482b955a604e79": {
"Name": "tomcat02",
"EndpointID": "7ce54ee89ba6332ce67920ba687c9af83ba1fd5407aca4e91fcf9e5c23cb5959",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
容器间使用容器名相互ping通
之前使用容器名来ping是ping不同的, 且就算使用了--link
也只能单向使用容器名ping通
现在双向都使用容器名ping通试试看
- 在容器 tomcat01 中ping 容器 tomcat02, 发现可以ping通
(root@Aliyun-Alex:/home/alex)# docker exec -it tomcat01 ping tomcat02
PING tomcat02 (192.168.0.3) 56(84) bytes of data.
64 bytes from tomcat02.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.118 ms
64 bytes from tomcat02.mynet (192.168.0.3): icmp_seq=2 ttl=64 time=0.076 ms
64 bytes from tomcat02.mynet (192.168.0.3): icmp_seq=3 ttl=64 time=0.093 ms
^C
--- tomcat02 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 8ms
rtt min/avg/max/mdev = 0.076/0.095/0.118/0.020 ms
- 在容器 tomcat02 中ping 容器 tomcat01, 发现也可以ping通
(root@Aliyun-Alex:/home/alex)# docker exec -it tomcat02 ping tomcat01
PING tomcat01 (192.168.0.2) 56(84) bytes of data.
64 bytes from tomcat01.mynet (192.168.0.2): icmp_seq=1 ttl=64 time=0.101 ms
64 bytes from tomcat01.mynet (192.168.0.2): icmp_seq=2 ttl=64 time=0.095 ms
^C
--- tomcat01 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 3ms
rtt min/avg/max/mdev = 0.095/0.098/0.101/0.003 ms
发现通过自定义网络比docker0使用起来更加方便, 推荐使用自定义网络
网络连通
在前面的步骤中, mynet
网络上连接了两个容器, 分别是tomcat01和tomcat02, 他们的网段是192.168.0.0
我们现在使用默认的网络docker0再创建两个容器tomcat03和tomcat04, 他们的网段是172.17.0.0
docker run -d -P --name tomcat03 tomcat
docker run -d -P --name tomcat04 tomcat
现在的网络结构如下:
(root@Aliyun-Alex:/home/alex)# docker exec -it tomcat02 ping tomcat03
ping: tomcat03: Name or service not known
直接尝试连接, 发现tomcat02并不能ping通03
但是我们可以做一个操作, 如果将tomcat02能与Docker0连接, 那么就能和tomcat03和tomcat04连接
在docker network
命令中有一个connect
命令
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.
这个命令可以将一个网络连接入一个容器中
(root@Aliyun-Alex:/home/alex)# 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
将网络docker0连接容器tomcat02
docker network connect bridge tomcat02
查看tomcat02的信息, 找到网络属性, 发现该容器有两个网络属性(mynet
和bridge
)和两个IP(192.168.0.3
和172.17.0.4
)
docker inspect tomcat02
# 打印结果
"bridge": {
...
"NetworkID": "35b249810e679e94e48b0dce387ad422fe4241c3335f82355cf919a56d96bc0c",
"EndpointID": "cf79813e3322df01cd9e693a0e42d803a26d0f2bf29762e82c5d28c9fa900710",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.4",
...
},
"mynet": {
...
"NetworkID": "8e7819b11d292e30e36a38095dc4b30f310affc9841b4500f5851c670a7f9b48",
"EndpointID": "7ce54ee89ba6332ce67920ba687c9af83ba1fd5407aca4e91fcf9e5c23cb5959",
"Gateway": "192.168.0.1",
"IPAddress": "192.168.0.3",
...
}
或者查看网络 docker0 的信息, 可以看到有三个容器连接上了, tomcat02/tomcat03/tomcat04
docker inspect bridge
# 打印结果
"Containers": {
"1152c9fa07e21798589e2f36de0dc9529b104b65f95fe4749b54b557cf51fa0c": {
"Name": "tomcat03",
...
"IPv4Address": "172.17.0.2/16",
},
"680e66da888e5c18cff7aae19f51c5189127405a54547925f4482b955a604e79": {
"Name": "tomcat02",
...
"IPv4Address": "172.17.0.4/16",
},
"daf280a8066191e42a4e80168ecb0bc285e9ef92a01723c3d862a0b18b2d38f8": {
"Name": "tomcat04",
...
"IPv4Address": "172.17.0.3/16",
}
}
此时网络结构如下:
现在测试tomcat02去ping容器tomcat03或者tomcat04
(root@Aliyun-Alex:/home/alex)# docker exec -it tomcat02 ping tomcat03
ping: tomcat03: Name or service not known
发现依然ping不通, 回想一下, 想起了之前说过了Docker0这个网卡默认情况不支持直接通过容器名去ping
那么测试用tomcat02去ping容器tomcat03的IP地址172.17.0.2
或者tomcat04的IP地址172.17.0.3
, 发现可以ping通
(root@Aliyun-Alex:/home/alex)# docker exec -it tomcat02 ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.134 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.080 ms
^C
--- 172.17.0.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 48ms
rtt min/avg/max/mdev = 0.080/0.107/0.134/0.027 ms
那么反过来, 我们将tomcat03连接到mynet网络下, 在自定义的网络中, 是可以实现通过容器名去ping的
docker network connect mynet tomcat03
现在网络结构如下:
在测试用tomcat03去ping容器tomcat01, 发现可以ping通
(root@Aliyun-Alex:/home/alex)# docker exec -it tomcat03 ping tomcat01
PING tomcat01 (192.168.0.2) 56(84) bytes of data.
64 bytes from tomcat01.mynet (192.168.0.2): icmp_seq=1 ttl=64 time=0.112 ms
64 bytes from tomcat01.mynet (192.168.0.2): icmp_seq=2 ttl=64 time=0.091 ms
^C
--- tomcat01 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 2ms
rtt min/avg/max/mdev = 0.091/0.101/0.112/0.014 ms
那么结论就是:
想要不同网段的容器间连接, 需要通过docker network connect network_name docker_name
将容器加入到目标网络中即可, 且目标网络最好是自定义的网络, 在自定义的网络中才能直接使用容器名ping通, 而默认的网络docker0(bridge)中只能使用IP地址ping通