什么是Docker0
使用ip addr
命令看一下网卡:
root@KitDevVps:~# 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: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
inet xxx.xxx.xxx.xxx/23 brd xxx.xxx.xxx.xxx scope global dynamic ens3
valid_lft 55158sec preferred_lft 55158sec
inet6 xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/64 scope global dynamic mngtmpaddr noprefixroute
valid_lft 2591896sec preferred_lft 604696sec
inet6 xxxx::xxxx:xxxx:xxxx:xxxx/64 scope link
valid_lft forever preferred_lft forever
3: ens7: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:8c:85:1a:f3 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:8cff:fe85:1af3/64 scope link
valid_lft forever preferred_lft forever
30: vethxxcaxxa@if29: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 xxxx::xxxx:xxxx:xxxx:xxxx/64 scope link
valid_lft forever preferred_lft forever
其中lo是本地回环地址,docker0就是docker0地址,也就是docker的地址172.17.0.1。
docker使用的是桥接模式,使用的技术是evth-pair技术,后面会解释。
Docker如何处理容器的网络访问
比如有两个容器,容器A要去访问容器B,该如何访问?使用127.0.0.1吗?还是写docker0地址?
我们运行起一个centos01来,并且在它内部执行命令ip addr
:
root@KitDevVps:~# docker run -d -it --name centos01 centos
d07fb4674ffe04e7ef60540485c1c890faccffeb8f40c401ccf59965f1cb8760
root@KitDevVps:~# docker sp
docker: 'sp' is not a docker command.
See 'docker --help'
root@KitDevVps:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d07fb4674ffe centos "/bin/bash" 7 seconds ago Up 6 seconds centos01
1cdd55fd90c5 nginx "/docker-entrypoint.…" 2 days ago Up 47 hours 0.0.0.0:80->80/tcp nginx1
root@KitDevVps:~# docker exec -it centos01 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
115: eth0@if116: <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
可以看到里面除了lo之外还有个115: eth0@if116,这是什么东西?可以发现我们每个容器启动的时候会得到一个eth0,这个容器就是eth0@if116,这是Docker给它分配的。
尝试在本机能否ping通容器内部,我们在本地ping一下eth0@if116,也就是172.17.0.3:
root@KitDevVps:~# 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.104 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.093 ms
64 bytes from 172.17.0.3: icmp_seq=3 ttl=64 time=0.076 ms
64 bytes from 172.17.0.3: icmp_seq=4 ttl=64 time=0.073 ms
64 bytes from 172.17.0.3: icmp_seq=5 ttl=64 time=0.087 ms
...
...
^C
--- 172.17.0.3 ping statistics ---
22 packets transmitted, 22 received, 0% packet loss, time 21508ms
rtt min/avg/max/mdev = 0.064/0.085/0.114/0.019 ms
发现是可以ping通的。现在已经得知主机可以ping通容器,那容器能否ping通容器?再run一个centos02试试:
root@KitDevVps:~# docker exec -it centos02 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
117: eth0@if118: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.4/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
failed to resize tty, using default size
用它来ping centos01(172.17.0.3):
[root@1ebe741425b2 /]# 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.103 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.112 ms
64 bytes from 172.17.0.3: icmp_seq=3 ttl=64 time=0.075 ms
64 bytes from 172.17.0.3: icmp_seq=4 ttl=64 time=0.089 ms
64 bytes from 172.17.0.3: icmp_seq=5 ttl=64 time=0.108 ms
64 bytes from 172.17.0.3: icmp_seq=6 ttl=64 time=0.146 ms
64 bytes from 172.17.0.3: icmp_seq=7 ttl=64 time=0.076 ms
^C
--- 172.17.0.3 ping statistics ---
7 packets transmitted, 7 received, 0% packet loss, time 129ms
rtt min/avg/max/mdev = 0.075/0.101/0.146/0.024 ms
到现在,docker0的地址是172.17.0.1,centos01的地址是172.17.0.3,centos02的地址是172.17.0.4。docker0就像是一个路由器,centos01和02就像是路由器下的设备,它们在同一个网段内,可以互相ping通。
此时再在本机使用ip addr
:
发现多了两个这种网卡,而我们run起来的centos01和02的网卡分别为 115: eth0@if116 和
117: eth0@if118。它们似乎有某种关联。
可以发现容器的网卡都是一对一对的,这就是前面说的evth-pair技术。evth-pair就是一对虚拟设备接口,它们都是成对出现的,一端连着协议,一端彼此相连,所以可以通信。evth-pair可以充当一个桥梁。
这是B站up主狂神画的简单示意图,本来linux是ping不通容器的,但利用evth-pair在两边都搞了一个接口,接口通过一种协议可以通信,所以相当于架了一个桥,让它们可以通信。
两个容器看似是直接ping通的,实际是通过docker0来ping通的。docker0就像路由器,每run出一个容器来,就把它的地址在这个路由器上注册。
--link与docker network
如果服务宕机重启,分配的ip地址发生了改变,如何进行通信?最好是可以不指定ip地址,而使用服务名称这个标识来进行通信。比如有很多mssql服务,服务名就叫mssql,但它们的ip地址都不一样,如果可以通过服务名来ping,就很好了。一个服务挂了重启,整个项目就不用重启。就是做到项目不重启,还能把数据库替换掉。因为docker重启是会更换ip的。我们希望能够通过名字访问,解决这个问题。
可以通过比较老的--link
来解决。
先尝试centos01能否直接通过名字ping通centos02:
root@KitDevVps:~# docker exec -it centos01 ping centos02
ping: centos02: Name or service not known
是ping不通的。我们将两个网络进行连接就可以解决了。我们run一个centos03,使用--link
连接上centos02,然后直接在centos03中ping centos02这个名字:
root@KitDevVps:~# docker run -d -it --name centos03 --link centos02 centos
ad50e83c3041eb76d4905670a8cc91bc23c60d3933b5d5f51f702983e9f5711d
root@KitDevVps:~# docker exec -it centos03 ping centos02
PING centos02 (172.17.0.4) 56(84) bytes of data.
64 bytes from centos02 (172.17.0.4): icmp_seq=1 ttl=64 time=0.146 ms
64 bytes from centos02 (172.17.0.4): icmp_seq=2 ttl=64 time=0.097 ms
64 bytes from centos02 (172.17.0.4): icmp_seq=3 ttl=64 time=0.128 ms
64 bytes from centos02 (172.17.0.4): icmp_seq=4 ttl=64 time=0.097 ms
^C
--- centos02 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 45ms
rtt min/avg/max/mdev = 0.097/0.117/0.146/0.021 ms
发现是可以ping通的。但是02ping不通03:
root@KitDevVps:~# docker exec -it centos02 ping centos03
ping: centos03: Name or service not known
因为centos02根本不知道centos03是什么东西,没有给它配置。想让02ping通03,肯定要对02进行一些配置。
我们使用命令docker network ls
来查看当前的网络。先看一下docker network
怎么用:
root@KitDevVps:~# docker network -h
Flag shorthand -h has been deprecated, please use --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.
然后docker network ls
:
root@KitDevVps:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
1375b7ef4bbc bridge bridge local
b29eab4db971 host host local
acfcd6eaf888 none null local
可以看到上面有个bridge,我们可以使用docker network inspect 1375b7ef4bbc
来查看这个bridge的详细信息:
root@KitDevVps:~# docker network inspect 1375b7ef4bbc
...
"Name": "bridge",
"Id": "1375b7ef4bbcf53a812fc8397787f17f2b47ea412292daea3324e016c48f5738",
"Created": "2020-07-03T02:06:46.264143987Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"1cdd55fd90c550de6b2ad1544df5ae08b50c85d06d2a9f2e416bf0dbd0ac164d": {
"Name": "nginx1",
"EndpointID": "16c89d4f905cb8437a5a539064358a37d6ca09e46d5af653db09e639d2ff56ef",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"1ebe741425b2c80226fe265d170ed0567a6321ea7d7c4b3f4ab787985b2a6308": {
"Name": "centos02",
"EndpointID": "6f468d178b612079095203cd0df4e58705069ead019ab72011646259dac67cec",
"MacAddress": "02:42:ac:11:00:04",
"IPv4Address": "172.17.0.4/16",
"IPv6Address": ""
},
"ad50e83c3041eb76d4905670a8cc91bc23c60d3933b5d5f51f702983e9f5711d": {
"Name": "centos03",
"EndpointID": "f54109b911f200c1afaad50d5459bb67a778fd5e64358f6344d2891e463d0598",
"MacAddress": "02:42:ac:11:00:05",
"IPv4Address": "172.17.0.5/16",
"IPv6Address": ""
},
"d07fb4674ffe04e7ef60540485c1c890faccffeb8f40c401ccf59965f1cb8760": {
"Name": "centos01",
"EndpointID": "89a3ea298b219971b9aa477efe67506e333a4d410083d56659fce3a6edbc259e",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
}
},
...
可以看到上面这一部分的containers中有我们正在运行的所有容器和他们的ip。运行容器不指定ip的时候它就会随机分配一个ip。
docker03实际上就是在本地配置了docker02的配置。
然后我们使用docker inspect 容器id
来查看我们的centos03,因为我们用它link了02,我们看一下03里面有没有什么link的线索:
结果我就不放了,因为没有什么线索。
我们cat一下03中的/etc/hosts文件,看看是不是在这里面配置了。这个就跟windows的hosts文件性质是一样的:
root@KitDevVps:~# docker exec -it centos03 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.4 centos02 1ebe741425b2
172.17.0.5 ad50e83c3041
在倒数第二行我们看到了centos02和它的ip地址以及它的id。这样我们就了解它的原理了,直接写在了hosts文件中,所以我们ping centos02或者02的id,可以直接ping到。02中的hosts文件是没有03的,我们可以手动写入,但这样就要重启容器才能生效,太麻烦,不弄了。
实际上这种--link
的方式已经不推荐使用了。docker0不支持容器名,我们需要自定义网络,不使用docker0。