Docker的docker0网络

参考资料:B站狂神视频教程
https://www.bilibili.com/video/BV1og4y1q7M4?p=34

什么是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

UTOOLS1593935145952.png

发现多了两个这种网卡,而我们run起来的centos01和02的网卡分别为 115: eth0@if116 和
117: eth0@if118。它们似乎有某种关联。

可以发现容器的网卡都是一对一对的,这就是前面说的evth-pair技术。evth-pair就是一对虚拟设备接口,它们都是成对出现的,一端连着协议,一端彼此相连,所以可以通信。evth-pair可以充当一个桥梁。

UTOOLS1593935496102.png

这是B站up主狂神画的简单示意图,本来linux是ping不通容器的,但利用evth-pair在两边都搞了一个接口,接口通过一种协议可以通信,所以相当于架了一个桥,让它们可以通信。

UTOOLS1593936383414.png

两个容器看似是直接ping通的,实际是通过docker0来ping通的。docker0就像路由器,每run出一个容器来,就把它的地址在这个路由器上注册。

UTOOLS1593936601114.png

--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。

原文地址:https://www.cnblogs.com/Kit-L/p/13246782.html