參考資料: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
:
發現多了兩個這種網卡,而我們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。