Docker网络
公众号首发、欢迎关注
一、什么是ip地址
通常我们看到的ip地址会长这样(由点分割的十进制数):192.168.1.1
其实ip地址是一个32位的二进制数,被分割成4部分,每部分8位。也就是说他们其实长成这样
00000000.00000000.00000000.00000000
每一部分都是8位的二进制数,那他能表示的范围其实就是0~255,并且在一个子网中,第一个0为保留位,最后一个255为广播位,这两位不会划分出去。(比如在局域网中找不到一个完全匹配的ip时,可能就会广播数据报文,广播的地址就会使用255)
所以说,一般情况下,局域网中可以使用的ip为1~254
0.0.0.0 回环地址
255.255.255.255 广播地址
二、划分子网
2.1、为什么要划分子网
划分子网是为了更好的利用ip地址资源,因为ip地址就是上面提到的4个byte字节组成的,一共就有2的32方个,随着互联网的发展,ip地址已经不够用了
现在的解决方案有两种,一种是将ipv4升级成ipv6,第二种就是现在说的划分子网,通过合理划分子网来缓解ip地址枯竭的问题。
2.2、子网分类
子网分类说的是人为的将子网分成A、B、C、D类,每个分类都有两部分组成:网络号+主机号
无论怎么划分,网络号和主机号一共32位是不会变的
A类:8位网络号 24位主机号
B类:16位网络号 16位主机号
C类:24位网络号 8位主机号
2.3、子网划分
子网划分的应用场景:
上面C类子网支持254台主机,B类子网支持6万多台主机。
那假设现在有一个公司,他们公司有几千名员工,如果为他们申请ip的话请问是该申请C类地址还是B类地址呢?
很显然,C类肯定不够用,B类又会浪费掉好几万个ip资源~
这时就得进行子网划分,拿这个例子来说就是我们给该公司申请一个B类地址,然后将B类地址中的部分主机位当作网络ID,这样的话,主机位数就变少了,最新的网络号 = 网络号+从主机位划分出来的网络ID,原来的二级Ip 地址被划分成三级Ip地址,收益就是:实现节省ip地址。
实战子网划分
假设我们对192.168.1.0这个网络号进行子网划分。
然后我们从主机号借用两位当作网络号
# 如果不进行子网分,它是一个C类网络地址,可以承载254台机器
192.168.1.00000000
# 借两位主机位当作网络id来 划分子网,可以得到下面四个子网
192.168.1.00 000000
192.168.1.01 000000
192.168.1.10 000000
192.168.1.11 000000
网络号相同的ip视为在一个局域网中,这样就将原来的一个C类地址划分成四个网络地址,也就能给四个公司使用,而且每个网络中都能分配2的6次方个左右的ip地址。
一个设备有多个ip是正常的,甚至当有它有多个物理网卡,就能有多个公网ip,相应的每个设备也有私网ip,也就是他所在的局域网中的ip(一般是通过DHCP获取的)
想接入互联网就的有公网ip,全球有42亿的ipv4的公网ip,我们的硬件设备默认有一个自己的本地ip,比如192.168..., 当设备连通局域网后,设备的本地ip其实是这个公网ip下的局域网的ip地址之一,这样可以尽最大可能的利用有限的ip网络地址资源(而不用为每一台设备都分配一个独有的公网ip)
2.4、子网掩码
在上一个例子中我们将这个网络号192.168.1.0,通过子网划分,分成了四个网络,如下
192.168.1.00 000000
192.168.1.01 000000
192.168.1.10 000000
192.168.1.11 000000
问题是怎么让机器区分开这是四个网络地址,而不是一个C类地址呢?
这就引出了子网掩码的概念: 子网掩码和ip地址同为32位,子网掩码为1的区域对应着ip地址的网络号部分,子网掩码为0的区域对应着ip地址的主机号部分。
就这个例子来说,它的子网掩码如下
# 192.168.1.0 ==转二进制==> 11000000.10101000.00000001.00000000
# 本来他是C类网络地址:它的网络号为前24位,后8位为主机号
# 然后我们划分子网,从主机位给网络位借两位,也就是前26位是网络号,后6位是主机号
# 对应的子网掩码位:前26位为1,后6位为0。即11111111.11111111.11111111.11000000
# 11111111.11111111.11111111.11000000 =转十进制=>255.255.255.192
如果通过ip和子网掩码得出网络ID呢?
# 两者的二进制表示做与运算
# 相同为1,不同为0
网络ID相同,说明这两个ip在同一个局域子网中
2.5、无分类-构成超网
这里说的无分类-构成超网本质上也是一种ip地址的编址方式,并且它消除了传统的ABCD这种划分子网的概念。
它的编制方式为:网络号/主机号
通常使用这种方式编址的ip长这个样子:192.68.1.1/20
在ipv4中,ip地址的总长度为32位这是不会变的,这里的20表示的是在这总长为32位的地址中,前20为网络号,剩下的12位是主机号
三、网络模式
常见的网络模式有:桥接模式、NAT模式、仅主机模式。比如在vmware中设置虚拟机的网络模式时,可以看到
3.1、桥接
桥接模式的本质是:使用一个虚拟的交换机,将虚拟机和物理机网卡打通。他们同属于一个网段
在这种模式下:有如下特性:
- 虚拟机和物理机同属一个网段
- 虚拟机之间可以相同Ping通
- 虚拟机和物理机之间可以相互ping通
- 只要主机能上网,虚拟机就能上网
3.2、NAT
NAT是一种网络地址转换技术,实现了局域网内的使用自己的本地IP即可联通互联网(NAT会将本地Ip,转换成全球IP)
常见的你家里的wifi,家里按了网线之后会有一个公网ip,但是家里的任何智能设备通过路由器连接上wifi后都能同时上网,因为路由器会分配给设备一个局域网ip,设备使用这个局域网ip上网,其中的转换技术就是NAT技术
有如下特性:
- 主机能上网,虚拟机就能上网
- 虚拟机直接可以互通
3.3、Host
Host,仅主机,即它是存在于主机内部的一个虚拟网络。
这仅主机模式下,有一块虚拟网卡,这块网卡的网段和主机上物理网卡的网段是不相同的。
所有的虚拟机在虚拟网卡的网段下,所以他们彼此直接互通。
默认情况下,虚拟机可以访问到物理机,但是默认情况下虚拟机不能上网的。(也可以通过修改配置让他们上互联网)
3.4、内部网络
在内部网络下,虚拟机和外部完全隔断,只允许虚拟机之间才能相互访问
四、Docker0
Docker网络的核心是Docker0
通过ip addr
可以看到服务器中如下几块网卡
查看公网ip可以在控制台获取或者执行:curl cip.cc
4.1、小实验1:
Docker中启动Nginx容器,问宿主机能通过容器的ip地址ping通它吗?
# 运行镜像
[root@VM-0-15-centos ~]# docker run -d -P --name nginx1 nginx
# 检查容器是否运行起来了
[root@VM-0-15-centos ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
962cf239325d nginx "/docker-entrypoint.…" 5 seconds ago Up 3 seconds 0.0.0.0:32768->80/tcp nginx1
# 进入容器中
[root@VM-0-15-centos ~]# docker exec -it nginx1 /bin/bash
# 查看容器ip地址
root@962cf239325d:/# 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.2 962cf239325d
# 退出容器
root@962cf239325d:/# exit
exit
# ping
[root@VM-0-15-centos ~]# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.064 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.059 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.056 ms
c64 bytes from 172.17.0.2: icmp_seq=4 ttl=64 time=0.058 ms
实验结果很明显:可以ping通!
为什么能ping通呢???
查看容器和宿主机网卡的信息就知道了
宿主机中有一块叫Docker0的网卡如下,它的网络段172.17.0.1/16
容器的网卡信息如下:
可以看到这宿主机和容器其实是在同一个网段的~,所以肯定能通!
4.2、小实验2:
如果我宿主机上的容器中同时运行两个容器,问:
这两个容器之间能通过彼此的ip的相互ping通吗?
# 检查两个容器是否都运行起来了
[root@VM-0-15-centos ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d37b518465a0 tomcat "catalina.sh run" 2 seconds ago Up 2 seconds 0.0.0.0:32772->8080/tcp tomcat2
85859d226c8c f796d3d2c195 "catalina.sh run" 17 minutes ago Up 17 minutes 0.0.0.0:32771->8080/tcp tomcat1
# 查看记录tomcat1的ip
[root@VM-0-15-centos ~]# docker exec -it tomcat1 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
184: eth0@if185: <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 link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
# 查看记录tomcat2的ip
[root@VM-0-15-centos ~]# docker exec -it tomcat2 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
186: eth0@if187: <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
# 使用tomcat2 ping tomcat1
[root@VM-0-15-centos ~]# docker exec -it tomcat2 ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.089 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.066 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.072 ms
^C
--- 172.17.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.066/0.075/0.089/0.014 ms
# 使用tomcat1 ping tomcat2
[root@VM-0-15-centos ~]# docker exec -it tomcat1 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.069 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.061 ms
64 bytes from 172.17.0.3: icmp_seq=3 ttl=64 time=0.070 ms
64 bytes from 172.17.0.3: icmp_seq=4 ttl=64 time=0.070 ms
^C
结论很明显:彼此之间能ping通!
那Docker是怎么做的呢?
我们安装Docker后会带有一个默认的网卡叫做Docker0,它实际上是一个网桥,其中有一端连接在真实的互联网上,使用的的技术是veth-pair桥接技术
此时我的Docker中已经运行了两个容器了,现在去看下宿主机和两个容器的网卡信息。
宿主机:
tomcat1:
tomcat2:
仔细看如上两图中多出来的网卡命名对应关系
# 宿主机多出来的: 185: veth79ccfd1@if184
# 对应容器tomcat1: 184: eth0@if185
# 宿主机多出来的: 187: vethac83886@if186
# 对应容器tomcat2: 186: eth0@if187
这种网卡成对对应的关系,就是借助veth-pair技术实现的
veth-pair就是一对虚拟的网络接口,成对出现,一端连着协议,一端彼此相连。由它充当一个桥梁的作用,连接各个Docker 容器,实现网络数据交互
在当前的案例中之间能Ping的
在不指定容器网络的情况下,默认容器使用的Docker0做网桥
通过下面的命令查看使用Docker0的所有Container
Docker会为容器分配一个可用的ip
分配的ip都在上图规定的subnet子网网段下的格式:172.17.0.2/16
在上面划分子网时有提到这种划分的方式,最后的16为它的网络号,也就是说他还有16位的机器号,也就是2的16次方-2个
五、Docker网络 --link
前面的验证了Docker两个不同的容器可以通过ip直接ping通
--link 可以让我们让两个容器通过服务名直接ping通
# 使用--link这个启动参数,连通不同的容器,实现通过服务名就ping通容器
[root@VM-0-15-centos ~]# docker run -d -P --name tomcat3 --link tomcat2 tomcat
2d8c1363094cd2e09bcd3d3a4e85e0864f32718d38aa4dab9640522c4ea3a87a
# 检查容器是否正常启动了
[root@VM-0-15-centos ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2d8c1363094c tomcat "catalina.sh run" 2 seconds ago Up 2 seconds 0.0.0.0:32773->8080/tcp tomcat3
d37b518465a0 tomcat "catalina.sh run" About an hour ago Up About an hour 0.0.0.0:32772->8080/tcp tomcat2
85859d226c8c f796d3d2c195 "catalina.sh run" About an hour ago Up About an hour 0.0.0.0:32771->8080/tcp tomcat1
# 使用tomcat3ping通tomcat2
[root@VM-0-15-centos ~]# docker exec -it tomcat3 ping tomcat2
PING tomcat2 (172.17.0.3) 56(84) bytes of data.
64 bytes from tomcat2 (172.17.0.3): icmp_seq=1 ttl=64 time=0.093 ms
64 bytes from tomcat2 (172.17.0.3): icmp_seq=2 ttl=64 time=0.068 ms
64 bytes from tomcat2 (172.17.0.3): icmp_seq=3 ttl=64 time=0.069 ms
c64 bytes from tomcat2 (172.17.0.3): icmp_seq=4 ttl=64 time=0.067 ms
^C
--- tomcat2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 0.067/0.074/0.093/0.012 ms
# 反向使用tomcat2 ping tomcat3
[root@VM-0-15-centos ~]# docker exec -it tomcat2 ping tomcat3
ping: tomcat3: Name or service not known
原理:--link实际上是修改了tomcat3的host文件,如下
[root@VM-0-15-centos ~]# docker exec -it tomcat3 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.3 tomcat2 d37b518465a0
172.17.0.4 2d8c1363094c
而tomcat2的host文件中没有这种别名,所以反向通过服务名不能ping通
六、自定义Docker网络
6.1、docker中的网络
查看docker所有的网络:
- briage:桥接网络(也是默认的网络)。通过Docker0作为网桥,实现容器联网互通
- host:和宿主机共享网络(使用同一块网卡)
- none:不配置网络,形成一个全封闭的容器,无法和外网连接
- container:让一个容器共享另一个容器网络相关的namespace,实现让几个容器内网络互通
6.2、自定义网络
默认网络参数
# 启动容器时我们不添加网络相关启动参数,Docker会为我们补全成下面这样
# 而这里指定的 bridge 就是就是docker0
docker run -d -P --name xxx --net bridge imageId
# --net 指定当前容器启动后加入到哪个网络中
自定义
# 创建自定义网络
# --subnet 192.168.0.0/16 指定了子网网络,可以分配2的16次方-2个ip
# --gateway 192.168.0.1 网关路由器的ip地址
[root@VM-0-5-centos ~]# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
a8a84221d4a73a2e2eda9f692c74af75cdbdac440e8c7587b56847c61e1dde4c
# 查看是否创建成功了
[root@VM-0-5-centos ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
a9ff308438a8 bridge bridge local
0d98ae518a10 host host local
a8a84221d4a7 mynet bridge local
cb71d8cdbbca none null local
查看我们自定义的网络详情
查看宿主机网卡信息:可以看到宿主机上多出来了一张虚拟网卡
在自定义的网络中的容器是可以直接通过对方的 容器名 ping通的
七、网络连通
如何让Docker中两个不同网络中的容器互通呢?
见上图,理论上容器1和容器2彼此互通,容器3容器4彼此互通,但是容器12和容器34之间时不互通的,因为他们都不在一个网段中。
具体的作为如下图:
容器2想ping通容器3或者容器4,本质上是需要将容器2加入到mynet 的网络中。
# 使用connect命令,把一个容器连接到一个网络上
# 查看帮助文档
[root@VM-0-5-centos ~]# docker network --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.
# 查看connect帮助文档的使用
[root@VM-0-5-centos ~]# docker network connect --help
Usage: docker network connect [OPTIONS] NETWORK CONTAINER
Connect a container to a network
Options:
--alias strings Add network-scoped alias for the container
--driver-opt strings driver options for the network
--ip string IPv4 address (e.g., 172.30.100.104)
--ip6 string IPv6 address (e.g., 2001:db8::33)
--link list Add link to another container
--link-local-ip strings Add a link-local address for the container
# 将指定容器添加到指定网络
[root@VM-0-5-centos ~]# docker network connect mynet tomcat1
# 检查是否添加成功
[root@VM-0-5-centos ~]# docker inspect mynet
[
{
"Name": "mynet",
"Id": "a8a84221d4a73a2e2eda9f692c74af75cdbdac440e8c7587b56847c61e1dde4c",
"Created": "2020-09-18T09:30:17.769656045+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.168.0.0/16",
"Gateway": "192.168.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"41976989fbea4138554e131d4067d4eb2bcfb3b6a2491be94af2fd5c64c64256": {
"Name": "tomcat1",
"EndpointID": "6894396881ffbbfe0fcf62da1ffcee304218120207f5b5a27f907bb2c306955b",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]