在安装docker时,会自动在host主机上创建三个网络,用docker network ls
可以进行查看:
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
b271af4d6598 bridge bridge local
95f888208994 host host local
5705b1b4107d none null local
none网络
none网络就是什么都没有的网络,挂在none网络下面的容器除了了lo,没有其他任何的网卡,在创建容器的时候可以通过--network=none
指定容器使用none网络。
$ docker run -it --network=none busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
可以看到该容器只有一块网卡lo,封闭意味着隔离,一些对安全性要求高并且不需要联网的应用可以使用 none 网络。
host网络
连接到host网络的容器共享host的网络栈,容器的网络配置跟host的完全相同:
$ docker run -it --network=host busybox
/ # ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 00:0c:29:09:e3:78 brd ff:ff:ff:ff:ff:ff
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue
link/ether 02:42:9b:0d:23:8b brd ff:ff:ff:ff:ff:ff
31: vethb6ac730@if30: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue master docker0
link/ether be:0d:67:bb:5f:9c brd ff:ff:ff:ff:ff:ff
33: veth5819fa7@if32: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue master docker0
link/ether 8a:a7:7c:de:29:16 brd ff:ff:ff:ff:ff:ff
35: veth7c93491@if34: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue master docker0
link/ether 9a:19:ce:26:aa:ab brd ff:ff:ff:ff:ff:ff
/ # hostname
ubuntu
容器中能看到host的所有网卡,连hostname也跟host一样。
直接使用 Docker host 的网络最大的好处就是性能,如果容器对网络传输效率有较高要求,则可以选择 host 网络。当然不便之处就是牺牲一些灵活性,比如要考虑端口冲突问题,Docker host 上已经使用的端口就不能再用了。
Docker host 的另一个用途是让容器可以直接配置 host 网路。比如某些跨 host 的网络解决方案,其本身也是以容器方式运行的,这些方案需要对网络进行配置,比如管理 iptables
bridge网络
docker在安装时会创建一个名为docker0的linux bridge。如果容器启动的时候不指定–network,那么容器会默认挂在docker0上。
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02429b0d238b no
现在docker0上没有任何网络设备,我们创建一个容器看看。
root@ubuntu:~# docker run -d httpd
3f8da19c11f089cce5b3f85bc016f3403db0ad4af71a7abea0b696afd1886411
root@host1:~# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02429b0d238b no vethe631bd9
root@ubuntu:~# docker exec -it 3f8da19c11f0 bash
root@3f8da19c11f0:/usr/local/apache2# ip a
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
52: eth0@if53: <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
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
可以看到一个新的网络设备挂在在docker0上,vethe631bd9
就是新创建的httpd容器的虚拟网卡。看了容器内部的网卡为eth0@if53
,为什么从外面看到的和容器内部看到的网卡不一样呢?
实际上
vethe631bd9
和eth0@if53
是一对veth pair。veth pair是一种成对出现的网络设备,可以理解为一根网线的两端,一端(eth0@if53
)在容器内,另一端(vethe631bd9
)挂在docker0上,其效果就是相当于(eth0@if53
)也挂在docker0上。
还看到eth0@if53
已经配置了IP为172.17.0.2
,为什么是这个网段,我们通过docker network inspect bridge
看一下bridge网络的配置:
$ docker network inspect bridge
[
{
"Name": "bridge",
"Id": "b271af4d65980ecfa171fdad89636864ec076f45fb8c8558310916bedc7ecda0",
"Created": "2018-01-22T17:51:21.295766604+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
......
原理bridge网络的Subnet(子网)是172.17.0.0/16
,Gateway(网关)是172.17.0.1
,这个网关就是docker0。
在容器启动的过程中,docker会自动从172.17.0.0/16
中分配一个IP。
自定义容器网络
docker提供了三种user-defined网络驱动:bridge,overlay和macvlan。后两种用于创建跨主机的网络。
可以使用bridge驱动创建类似bridge的网络:
$ docker network create --driver bridge my_net
5e9efda5449ba5a581e9fb91e3afbb1621bacd823b0ad08ef79b5d3cd368e1da
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
b271af4d6598 bridge bridge local
95f888208994 host host local
5e9efda5449b my_net bridge local
5705b1b4107d none null local
$ brctl show
bridge name bridge id STP enabled interfaces
br-5e9efda5449b 8000.02426b4ed3ee no
docker0 8000.02429b0d238b no vethe631bd9
这样我们成功穿创建了一个my_net的网络,新增加了一个网桥br-5e9efda5449b
,并且5e9efda5449b
是my_net网络的短ID。
通过docker network inspect my_net
命令查看my_net网络的配置信息:
$ docker network inspect my_net
[
{
"Name": "my_net",
"Id": "5e9efda5449ba5a581e9fb91e3afbb1621bacd823b0ad08ef79b5d3cd368e1da",
"Created": "2018-01-23T15:44:17.670486371+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
.......
docker自动为my_net网络分配了子网和网关。
同时我们也可以自己指定subnet和gateway,在创建网络的时候加上--subnet
和--gateway
参数就可以了:
$ docker network create --driver bridge --subnet 172.19.0.0/24 --gateway 172.19.0.1 my_net2
1ded797aeede01408e2a70295e7b327a06233ac4e799b7da7e0913db1f41d8dd
$ brctl show
brctl show
bridge name bridge id STP enabled interfaces
br-1ded797aeede 8000.0242bfebcd26 no
$ ifconfig br-1ded797aeede
br-1ded797aeede Link encap:以太网 硬件地址 02:42:bf:eb:cd:26
inet 地址:172.19.0.1 广播:172.19.0.255 掩码:255.255.255.0
UP BROADCAST MULTICAST MTU:1500 跃点数:1
接收数据包:0 错误:0 丢弃:0 过载:0 帧数:0
发送数据包:0 错误:0 丢弃:0 过载:0 载波:0
碰撞:0 发送队列长度:0
接收字节:0 (0.0 B) 发送字节:0 (0.0 B)
$ docker network inspect my_net2
[
{
"Name": "my_net2",
"Id": "1ded797aeede01408e2a70295e7b327a06233ac4e799b7da7e0913db1f41d8dd",
"Created": "2018-01-23T15:55:51.894463236+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.19.0.0/24",
"Gateway": "172.19.0.1"
}
......
到这里,我们自己指定了网络。my_net2对应的网桥是br-1ded797aeede
。
容器要使用新的网络,可以用--network
指定:
$ docker run -it --network=my_net2 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
56: eth0@if57: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:13:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.19.0.2/24 brd 172.19.0.255 scope global eth0
valid_lft forever preferred_lft forever
容器分到的地址是172.19.0.2
,那么能不能给容器指定一个静态IP呢?通过--ip
参数就可以给容器指定静态IP:
$ docker run -it --network=my_net2 --ip 172.19.0.6 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
58: eth0@if59: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:13:00:06 brd ff:ff:ff:ff:ff:ff
inet 172.19.0.6/24 brd 172.19.0.255 scope global eth0
valid_lft forever preferred_lft forever
到这里,我们给容器指定了静态IP:172.19.0.6
注意:只有通过
--subnet
创建的网络才能指定静态IP
同主机网络的通信
上面的两个busybox容器都挂在my_net2上,我们测试以下能否互通:
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
56: eth0@if57: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:13:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.19.0.2/24 brd 172.19.0.255 scope global eth0
valid_lft forever preferred_lft forever
/ # ping 172.19.0.6 -c 3
PING 172.19.0.6 (172.19.0.6): 56 data bytes
64 bytes from 172.19.0.6: seq=0 ttl=64 time=0.224 ms
64 bytes from 172.19.0.6: seq=1 ttl=64 time=0.192 ms
64 bytes from 172.19.0.6: seq=2 ttl=64 time=0.336 ms
--- 172.19.0.6 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.192/0.250/0.336 ms
同一网络中的容器、网关之间都是互通的
不同网络中的容器不能互通,通过docker network connect
命令可以实现:
$ docker network connect my_net2 3f8da19c11f0
$ docker exec -it 3f8da19c11f0 bash
root@3f8da19c11f0:/usr/local/apache2# ping 172.19.0.6 -c 3
PING 172.19.0.6 (172.19.0.6): 56 data bytes
64 bytes from 172.19.0.6: icmp_seq=0 ttl=64 time=0.162 ms
64 bytes from 172.19.0.6: icmp_seq=1 ttl=64 time=0.196 ms
64 bytes from 172.19.0.6: icmp_seq=2 ttl=64 time=0.160 ms
--- 172.19.0.6 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.160/0.173/0.196/0.000 ms
果然,能够互通了