Docker 网络
docker安装并启动服务后,会在宿主机中添加一个虚拟网卡。
在Docker服务启动前,使用 ifconfig
或 ip addr
查看网卡信息:
lo
:本机回环网络网卡ens33
或eth0
:本机网卡,下面是阿里云内网地址- 使用
systemctl start docker
启动Docker服务后,会多出一个docker0
网卡。 - 可能有
virbr0
(CentOS安装时如果选择的有相关虚拟化服务,就会多一个以网桥连接的私网地址的virbr0
网卡,作用是为连接虚拟网卡提供NAT访问外网的功能。如果要移除该服务,可以使用yum remove libvirt-libs.x86_64
)
作用:
- 容器间的互联和通信以及端口映射
- 容器IP变动时候可以通过服务名直接网络通信而不受到影响
Docker容器的网络隔离,是通过Linux内核特性 namespace
和 cgroup
实现的。
测试
没有运行任何容器的时候是这个样子的,有lo
,eth0
,docker0
[root@izj6cev682kqg86i4ogj8rz volumes]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
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
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:16:3e:01:8a:da brd ff:ff:ff:ff:ff:ff
inet 172.17.20.142/18 brd 172.17.63.255 scope global dynamic eth0
valid_lft 299860583sec preferred_lft 299860583sec
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN
link/ether 02:42:f1:29:51:33 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global docker0
valid_lft forever preferred_lft forever
当我运行了一个tomcat容器和一个ubuntu容器后,再次ip addr
[root@izj6cev682kqg86i4ogj8rz volumes]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
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
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:16:3e:01:8a:da brd ff:ff:ff:ff:ff:ff
inet 172.17.20.142/18 brd 172.17.63.255 scope global dynamic eth0
valid_lft 299805243sec preferred_lft 299805243sec
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether 02:42:f1:29:51:33 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global docker0
valid_lft forever preferred_lft forever
47: veth26b6577@if46: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP
link/ether 2e:60:70:a8:2f:91 brd ff:ff:ff:ff:ff:ff link-netnsid 0
51: veth4638d32@if50: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP
link/ether 7a:9f:2f:dc:b0:05 brd ff:ff:ff:ff:ff:ff link-netnsid 1
我进入ubuntu容器的内部,因为是阉割版的,需要执行两条命令安装东西
apt-get update
apt-get install -y iproute2
apt install iputils-ping
关于ping命令
安装完了以后执行ip addr
root@90b9a384eb60:/# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
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
50: eth0@if51: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.18.0.3/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever
发现了第二段代码的51: veth4638d32@if50:
和第三段代码的50: eth0@if51:
有着映射关系。
而且每次启动容器都是一对一对的数字出现。
我们在linux服务器上也能ping通ubuntu容器内部分配的私有ip。
当然我们在B容器也能通过这个分配的内网ip连通A容器。运行容器的时候没有指定网络连接方式就是默认的桥接方式。并不是AB两个同期直接通信,两个容器之间通过docker0中转通信。
[root@izj6cev682kqg86i4ogj8rz ~]# ping 172.18.0.3
PING 172.18.0.3 (172.18.0.3) 56(84) bytes of data.
64 bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.051 ms
64 bytes from 172.18.0.3: icmp_seq=2 ttl=64 time=0.054 ms
64 bytes from 172.18.0.3: icmp_seq=3 ttl=64 time=0.051 ms
64 bytes from 172.18.0.3: icmp_seq=4 ttl=64 time=0.055 ms
Docker 网络模式
Docker 的网络模式:
网络模式 | 简介 | 使用方式 |
---|---|---|
bridge | 为每一个容器分配、设置IP等,并将容器连接到一个docker0 虚拟网桥,默认为该模式 |
--network bridge |
host | 容器将不会虚拟出自己的网卡、配置自己的IP等,而是使用宿主机的IP和端口 | --network host |
none | 容器有独立的 Network namespace,但并没有对齐进行任何网络设置,如分配 veth pari 和 网桥连接、IP等 |
--network none |
container | 新创建的容器不会创建自己的网卡和配置自己的IP,而是和一个指定的容器共享IP、端口范围等 | --network container:NAME或者容器ID |
查看某个容器的网络模式
# 通过inspect获取容器信息,最后20行即为容器的网络模式信息
docker inspect 容器ID | tail -n 20
[root@izj6cev682kqg86i4ogj8rz ~]# docker inspect ubuntu02 | tail -n 20
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "37b3214ea181ec7d4a0d0eea126df72f72d7ad1e5e1724b4656bf3cb59acec07",
"EndpointID": "4658c0fc37cd88918ce9f92097d23bbf7f8f3cb04256c0af38bee3b236a252ea",
"Gateway": "172.18.0.1",
"IPAddress": "172.18.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:12:00:03",
"DriverOpts": null
}
}
}
}
]
docker0
Docker 服务默认会创建一个docker0
网桥(其上有一个docker0
内部接口),该桥接网络的名称为 docker0
,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。
Docker默认指定了docker0
接口的IP地址和子网掩码,让主机和容器之间可以通过网桥互相通信。
查看bridge
网络的详细信息,并通过grep
获取名称:可以看到其名称为docker0
docker network inspect bridge | grep name
网络模式——bridge模式
Docker使用Linux桥接,在宿主机虚拟一个Docker
容器网桥(docker0
),Docker启动一个容器时会根据Docker
网桥的网段分配给容器一个IP地址,称为Container-IP
,同时Docker网桥是每个容器的默认网关。因为在同一个宿主机内的容器接入同一个网桥,这样容器之间就能够通过容器的Container-IP
直接通信。docker run
的时候,没有指定--network
的话,默认使用的网桥模式就是bridge
,使用的就是docker0
。在宿主机ifconfig
就可以看到docker0
和自己create
的network
。
网桥docker0
创建一对对等虚拟设备接口,一个叫veth
,另一个叫eth0
,成对匹配:
整个宿主机的网桥模式都是docker0
,类似一个交换机有一堆接口,每个接口叫 veth
,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫做 veth pair
)。
每个容器实例内部也有一块网卡,容器内的网卡接口叫做eth0
。docker0
上面的每个veth
匹配某个容器实例内部的eth0
,两两配对,一一匹配。
例子可以看上面的测试小节
网络模式——host模式
直接使用宿主机的 IP 地址与外界进行通信,不再需要额外进行 NAT 转换。
容器将不会获得一个独立的 Network Namespace,而是和宿主机共用一个 Network space。
容器将不会虚拟出自己的网卡,而是直接使用宿主机的 IP 和端口。
如果在 docker run
命令中同时使用了 --network host
和 -p
端口映射,例如:
docker run -p 8082:8080 --network host tomcat
那么会出现一个警告:WARNING: Published ports are discarded when using host network mode
因为此时已经使用了host
模式,本身就是直接使用的宿主机的IP和端口,此时的-p
端口映射就没有了意义,也不会生效,端口号还是会以主机端口号为主。
正确做法是:不再进行-p
端口映射,或者改用bridge
模式
网络模式——none模式
禁用网络功能。
在none
模式下,并不为docker容器进行任何网络配置。进入容器内,使用 ip addr
查看网卡信息,只能看到 lo
(本地回环网络127.0.0.1
网卡)。
网络模式——container模式
新建的容器和已经存在的一个容器共享网络IP配置,而不是和宿主机共享。
新创建的容器不会创建自己的网卡、IP,而是和一个指定的容器共享IP、端口范围。两个容器除了网络共享,其他的如文件系统、进程列表依然是隔离的。
示例:
docker run -it --name alpine1 alpine /bin/sh
# 指定和 alpine1 容器共享网络
docker run -it --netrowk container:alpine1 --name alpine2 alpine /bin/sh
此时使用 ip addr
查看两台容器的网络,会发现两台容器的eth0
网卡内的IP等信息完全相同。
如果关掉了alpine1
容器,因为alpine2
的网络使用的alpine1
共享网络,所以关掉alpin1
后,alpine2
的eth0
网卡也随之消失了。
自定义网络
容器间的互联和通信以及端口映射。
容器 IP 变动时候可以通过服务名直接网络通信而不受影响。(类似Eureka,通过服务名直接互相通信,而不是写死IP地址)。
:::danger
docker中还有一个 --link
进行容器网络互联,但是已经被标记为过时的,可能会在将来的版本中移除这个功能。推荐使用自定义网络替换link。
:::
- 新建自定义网络
自定义桥接网络(自定义网络默认使用的是桥接网络 bridge
)
docker network create new_network
#指定bridge方式
docker network create -d bridge new_bridge
#详细
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
查看网络列表
docker network ls
创建容器时,指定加入我们自定义的网络中 ```shell docker run -it —name u1 —network mynet newubuntu:1.0 /bin/bash
docker run -it —name u2 —network mynet newubuntu:1.0 /bin/bash
4. 这时候随便进去某个容器,都能通过名字ping对方
```shell
root@89c03e48cf85:/# ping u1
PING u1 (172.19.0.2) 56(84) bytes of data.
64 bytes from u1.new_network (172.19.0.2): icmp_seq=1 ttl=64 time=0.084 ms
64 bytes from u1.new_network (172.19.0.2): icmp_seq=2 ttl=64 time=0.069 ms
64 bytes from u1.new_network (172.19.0.2): icmp_seq=3 ttl=64 time=0.058 ms
网络连通
如果处于两个子网网段,比如docker0的172.18.x.x和自己创建的网络192.168.x.x是不能进行通信的。
下面我们启动三个容器,u1和u2是通过上面创建的mynet启动,d1默认启动,用的网络用的docker0。
在d1里面ping u1,连不通。
[root@izj6cev682kqg86i4ogj8rz ~]# docker run -it --name u1 --network mynet newubuntu:1.0 /bin/bash
root@65e4415ddb7e:/# [root@izj6cev682kqg86i4ogj8rz ~]#
[root@izj6cev682kqg86i4ogj8rz ~]# docker run -it --name u2 --network mynet newubuntu:1.0 /bin/bash
root@866b78daecc8:/# [root@izj6cev682kqg86i4ogj8rz ~]#
[root@izj6cev682kqg86i4ogj8rz ~]# docker run -it --name d1 newubuntu:1.0 /bin/bash
root@a0941b086d6d:/# ping u1
ping: u1: Name or service not known
我们通过通过docker network connect
命令让mynet网络连通a0941b086d6d容器。然后就可以在d1里面ping u1了
[root@izj6cev682kqg86i4ogj8rz ~]# docker network connect mynet a0941b086d6d
[root@izj6cev682kqg86i4ogj8rz ~]# docker exec -it a0941b086d6d /bin/bash
root@a0941b086d6d:/# ping u2
PING u2 (192.168.0.3) 56(84) bytes of data.
64 bytes from u2.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.059 ms
64 bytes from u2.mynet (192.168.0.3): icmp_seq=2 ttl=64 time=0.057 ms
64 bytes from u2.mynet (192.168.0.3): icmp_seq=3 ttl=64 time=0.060 ms
我们查看网络详情docker network inspect mynet
,会发现直接把d1加入到了mynet网络下,并给它分配了一个ip,也就是一个容器两个ip
—link
是不是感觉容器之间通过分配的ip来通信,不太方便,要是这个ip换了不就通信错了吗。我们可以通过名字来和对方通信
#启动一个容器,--name去一个名字叫u1
[root@izj6cev682kqg86i4ogj8rz ~]# docker run -it --name u1 newubuntu:1.0 /bin/bash
#启动另一个容器,u1后台运行没关,这个容器用--link u1,可以通过u1这个名字直接通信u1容器
[root@izj6cev682kqg86i4ogj8rz ~]# docker run -it --name u2 --link u1 newubuntu:1.0 /bin/bash
root@3d69b873181a:/# ping u1
PING u1 (172.18.0.2) 56(84) bytes of data.
64 bytes from u1 (172.18.0.2): icmp_seq=1 ttl=64 time=0.082 ms
64 bytes from u1 (172.18.0.2): icmp_seq=2 ttl=64 time=0.080 ms
#再启动一个容器,用了两个--link,u1:a,冒号后面是别名,可以通过别名访问
[root@izj6cev682kqg86i4ogj8rz ~]# docker run -it --name u3 --link u1:a --link u2:b newubuntu:1.0 /bin/bash
root@a1c729316a7e:/# ping a
PING a (172.18.0.2) 56(84) bytes of data.
64 bytes from a (172.18.0.2): icmp_seq=1 ttl=64 time=0.073 ms
64 bytes from a (172.18.0.2): icmp_seq=2 ttl=64 time=0.061 ms
64 bytes from a (172.18.0.2): icmp_seq=3 ttl=64 time=0.077 ms
:::danger
注意:
—link是单项的连接,u2连接u1,u1并不能连接u2。而且每次启动容器都要—link的配置,已经不推荐使用—link了,推荐自定义网络
:::