目录
一.docker网络实现原理
虚拟机平台的网卡分析
- 我们使用ifconfig命令查看安装docker-ce的虚拟机平台有,如下信息
[root@localhost ~]# ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.18.0.1 netmask 255.255.0.0 broadcast 172.18.255.255
ether 02:42:24:fe:93:22 txqueuelen 0 (Ethernet)
RX packets 2652 bytes 139971 (136.6 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3407 bytes 15146807 (14.4 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.43.101 netmask 255.255.255.0 broadcast 192.168.43.255
inet6 fe80::ba30:ef5e:b59f:4f1c prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:ce:5f:24 txqueuelen 1000 (Ethernet)
RX packets 45 bytes 5797 (5.6 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 92 bytes 12139 (11.8 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1 (Local Loopback)
RX packets 68 bytes 5916 (5.7 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 68 bytes 5916 (5.7 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
virbr0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 192.168.122.1 netmask 255.255.255.0 broadcast 192.168.122.255
ether 52:54:00:7a:bf:df txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@localhost ~]#
- 其中eth0是真实存在的适配器,用来连接物理机网卡;
- 而lo则是虚拟出来的回环网卡,用来检测tcp/ip协议是否生效;
- 对于virbr0虚拟桥接网卡,linux自身集成了一个虚拟化功能,被镶嵌在内核当中
docker0网桥分析
- docker使用linux桥接,在宿主机虚拟一个容器网——docker 0,Docker启动一个容器时会根据docker网桥的网段分配给容器一个IP地址,称为Container-IP。
- 同时Docker网桥也是每个容器的默认网关。因为在同一个宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的container-IP直接通信。
- Docker网桥是宿主机虚拟出来的,并不是真实存在的网络设备,外部网络是无法寻址到的,意味着外部网络无法直接通过Container-IP访问到容器。如果容器希望外部访问能够访问到,可以通过映射容器端口到宿主主机(端口映射)。
虚拟机平台容器网络架构图
- 启动docker 0和ens33之间通过NAT地址转换技术,使得后面的容器IP映射到ens33的地址,再通过不同的端口映射区分不同的容器。
二.docker的四类网络模式
- 安装docker时,docker会自动创建三个网络,bridge(bridge创建容器默认连接到此网络)、none、host
[root@cloud ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
9c7af129bb18 bridge bridge local
6679de44fc2e host host local
b1e4b6deb58f none null local
[root@cloud ~]#
host模式
- 如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network NameSpace,而是和宿主机共用一个Network NameSpace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。但是,容器的其他方面,比如文件系统、进程列表等还是和宿主机隔离的。
- 使用host模式的容器可以直接使用宿主机的IP地址与外界通信,容器内部的服务端口可以使用宿主机的端口,不需要要进行NAT,Host的最大的优势就是网络性能比较好,但是docker host上已经使用的端口,就不能再用了,网络的隔离性不好。
container模式
- container模式指定新创建的容器和已经存在的容器共享一个Network NameSpace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP地址,而是和指定的一个容器共享IP地址、端口范围。同样,两个容器除了网络方面,其他如文件系统,进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。
none模式
- 使用none模式,docker容器拥有自己的network namesapce,但是,并不为docker容器进行任何网络配置。也就是说,这个docker容器没有网卡、Ip、路由等信息。需要我们自己为docker容器添加网卡、配置IP地址等
- 在none模式下容器只有lo回环网络,没有其他网卡。none模式可以在容器创建时通过 --network=none来指定。这种网络没有办法联网。封闭的网络能够很好的保证容器的安全性。
bridge模式
- 当docker进程启动时,会在主机上创建一个名为docker 0的虚拟网桥,此主机上启动的docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就可以通过交换机连在一个二层网络中。
- 从docker 0子网中分配一个给容器使用,并且设置docker 0的IP地址位容器的默认网关。在主机上创建一对虚拟网卡veth pair设备,docker将veth pair设备的一端放在新创建的容器中,并且命名为eth0(容器的网卡),另外一端放在主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到docker 0网桥中。可以通过brctl show命令查看。
- bridge模式是docker的默认网络模式。不写--net参数,就是bridge模式。使用docker run -p时,docker实际是在iptables做了DNAT规则,实现端口转发功能。可以使用iptables -t nat -vnL 查看。
当我们使用多个node时,我们可以使用k8s,进行多节点容器管理
上述四种网络模式的配置说明
docker网络模式 | 配置 | 说明 |
host模式 | -net=host | 容器和宿主机共享network namespace |
container模式 | -net=container:NAME_or_ID | 容器和另外一个容器共享Network namespace。kubernetes中的pod就是多个容器共享一个network namespace |
none模式 | -net=none | 容器有独立的network namespace,但是并没有对其进行任何网络设置,如分配veth pair和网桥连接,配置IP等 |
bridge模式 | -net=bridge | (默认为该模式) |
三.docker用户自定义网络使用命令
自定义网络
- 除了默认的名为docker 0的bridge网络,还可以创建自己的bridge网络或overlay网络。
- bridge网络用于在本机内容器之间的互通
- overlay网络用于不同主机内容的容器之间的互通
- 本文主要对bridge网络说明,overlay网络在搭建k8s管理多节点时会详细说明
注:当我们使用如下命令,创建一个bridge网络下的指定IP地址的容器时,会发现使用bridge无法支持指定IP
docker run -itd --name test1 --network bridge --ip 172.17.0.10 centos:7 /bin/bash
- 一个bridge类型的网络
[root@cloud ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
9c7af129bb18 bridge bridge local
6679de44fc2e host host local
b1e4b6deb58f none null local
[root@cloud ~]# docker network create -d bridge my-net
ae3d32337feb2daea7c51b47020323a43c946ef041a664296438f594d66265a8
[root@cloud ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
9c7af129bb18 bridge bridge local
6679de44fc2e host host local
ae3d32337feb my-net bridge local
b1e4b6deb58f none null local
[root@cloud ~]#
##docker network ls列出docker的网络类型
##docker network create -d指定要创建什么类型的网络 ,后面在加自定义的网络名称
##假如不使用-d参数,会创建bridge网络
docker network create -d bridge my-net 等同于 docker network create my-net
- 查看自定义网络的源数据
[root@cloud ~]# docker network inspect my-net ##使用inspect查看详细信息
[
{
"Name": "my-net",
"Id": "ae3d32337feb2daea7c51b47020323a43c946ef041a664296438f594d66265a8",
"Created": "2020-04-16T09:42:28.530895411+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.19.0.0/16", ##此处docker为该网卡分配了一个子网
"Gateway": "172.19.0.1" ##定义了网关
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
[root@cloud ~]#
- 创建一个bridge网络并且指定子网
##创建一个bridge类型的网络,定义子网段,定义网络名称
[root@cloud ~]# docker network create --subnet=172.20.0.0/16 my-net_172_20
cd3c2d0fcfde4ab138944078bffcfec9265abc5ed3894a033b20828addc6d7af
[root@cloud ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
9c7af129bb18 bridge bridge local
6679de44fc2e host host local
ae3d32337feb my-net bridge local
cd3c2d0fcfde my-net_172_20 bridge local
b1e4b6deb58f none null local
##查看该网络的源信息
[root@cloud ~]# docker network inspect my-net_172_20
[
{
"Name": "my-net_172_20",
"Id": "cd3c2d0fcfde4ab138944078bffcfec9265abc5ed3894a033b20828addc6d7af",
"Created": "2020-04-16T09:49:08.7236899+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.20.0.0/16" ##子网段成功设置
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
[root@cloud ~]#
- 创建一个网络时,可以使用--label添加元数据
#在创建一个网络时使用--label,为此网络指定一些元数据信息
#如下
[root@cloud ~]# docker network create \
> --label network_name=my-net_label \ ##指定网络名称元数据
> --label project=LB_nginx \ ##指定项目名称元数据
> my-net_label
1db0bc5d23308fa582dea32c044ec910e23e5f6c7012dd9a51751dd1a0ef3e5d
[root@cloud ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
9c7af129bb18 bridge bridge local
6679de44fc2e host host local
ae3d32337feb my-net bridge local
cd3c2d0fcfde my-net_172_20 bridge local
1db0bc5d2330 my-net_label bridge local
b1e4b6deb58f none null local
[root@cloud ~]# docker network inspect my-net_label
[
{
"Name": "my-net_label",
"Id": "1db0bc5d23308fa582dea32c044ec910e23e5f6c7012dd9a51751dd1a0ef3e5d",
"Created": "2020-04-16T09:57:10.419307733+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.21.0.0/16",
"Gateway": "172.21.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": { ##此处是我们指定label信息
"network_name": "my-net_label",
"project": "LB_nginx"
}
}
]
[root@cloud ~]#
- 删除自定网络
[root@cloud ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
9c7af129bb18 bridge bridge local
6679de44fc2e host host local
ae3d32337feb my-net bridge local
cd3c2d0fcfde my-net_172_20 bridge local
1db0bc5d2330 my-net_label bridge local
b1e4b6deb58f none null local
[root@cloud ~]# docker network rm my-net ##使用docker network rm + 自定义网络名称删除
my-net
[root@cloud ~]# docker network rm my-net_172_20
my-net_172_20
[root@cloud ~]# docker network rm my-net_label
my-net_label
[root@cloud ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
9c7af129bb18 bridge bridge local
6679de44fc2e host host local
b1e4b6deb58f none null local
[root@cloud ~]#
定义容器网络并且通信
- 我们可以将现有的容器连接到一个或者多个网络。容器可以连接到使用不同网络驱动程序的网络,比如连接到bridge网络或者overlay网络。连接后,容器可以使用其他容器的IP地址或名称进行通信。
上图中表明的意思是,在一台docker主机docker host上有三个容器,且三个容器分别连接到两个不同的网络中
容器container1连接到默认的桥接网络bridge
容器container2连接到默认的网络的bridge,同时它还连接到自定义的桥接网络my-net中
容器container3连接到自定义的桥接网络my-net中
- 先创建并且运行两个容器container1和container2
[root@cloud ~]# docker run -itd --name=container1 centos:7
499930b14be84a6307e1e539d1c1f9d7c10c8cc7a01ebe3228d27388d4bd3732
[root@cloud ~]# docker run -itd --name=container2 centos:7
52c71b93d4cc6a2f5898b3147043a5becf0bdd79e2198b563b52273ee44031c4
[root@cloud ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
52c71b93d4cc centos:7 "/bin/bash" 4 seconds ago Up 3 seconds container2
499930b14be8 centos:7 "/bin/bash" 12 seconds ago Up 11 seconds container1
[root@cloud ~]#
- 创建一个隔离的桥接网络my-net
[root@cloud ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
9c7af129bb18 bridge bridge local
6679de44fc2e host host local
b1e4b6deb58f none null local
//--subnet指定子网
[root@cloud ~]# docker network create -d bridge --subnet 172.20.0.0/16 my-net
7b4bef68576ca21991dfb132a96c8db458beaeb84a7fa084b5fe88055aa90644
[root@cloud ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
9c7af129bb18 bridge bridge local
6679de44fc2e host host local
7b4bef68576c my-net bridge local
b1e4b6deb58f none null local
[root@cloud ~]#
- 将容器container2假如my-net网络中
##使用connect连接网络
[root@cloud ~]# docker network connect my-net container2
[root@cloud ~]# docker network inspect my-net
[
{
"Name": "my-net",
"Id": "7b4bef68576ca21991dfb132a96c8db458beaeb84a7fa084b5fe88055aa90644",
"Created": "2020-04-16T11:03:46.664703847+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.20.0.0/16"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"52c71b93d4cc6a2f5898b3147043a5becf0bdd79e2198b563b52273ee44031c4": {
"Name": "container2", ##此处信息显示添加成功
"EndpointID": "f3d6b544d66216f1e914cffae302f895b8b816bc348fefbfe88bb6549e20d4d4",
"MacAddress": "02:42:ac:14:00:02",
"IPv4Address": "172.20.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
[root@cloud ~]#
- 创建并且运行container3容器并且指定网络
[root@cloud ~]# docker run -itd --name=container3 --network=my-net --ip=172.20.0.100 centos:7
77565a1449d2095d75b02698445364cb4aaaeeb99d18384305980b58758c9e12
##--network指定网络类型
##--ip指定固定的IP地址
- 进入container2容器查看网卡信息
[root@cloud ~]# docker exec -it container2 /bin/bash
[root@52c71b93d4cc /]# yum install net-tools -y ##下载网络命令工具
[root@52c71b93d4cc /]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.18.0.3 netmask 255.255.0.0 broadcast 172.18.255.255
ether 02:42:ac:12:00:03 txqueuelen 0 (Ethernet)
RX packets 6214 bytes 15333342 (14.6 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 4405 bytes 302412 (295.3 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.20.0.2 netmask 255.255.0.0 broadcast 172.20.255.255
ether 02:42:ac:14:00:02 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
RX packets 61 bytes 5883 (5.7 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 61 bytes 5883 (5.7 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@52c71b93d4cc /]#
在container2中可以看到eth0和eth1两张虚拟网卡信息
这是因为,我们一开始运行这个容器的时候,被连接到默认的网络上,后来我们又执行了
docker network connect
命令,把它连接到了my-net
网络上了,所以这使其有两个网络,并且有两个网卡。
- 容器间的通信
[root@cloud ~]# docker exec -it container1 /bin/bash
[root@499930b14be8 /]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.18.0.2 netmask 255.255.0.0 broadcast 172.18.255.255
ether 02:42:ac:12:00:02 txqueuelen 0 (Ethernet)
RX packets 6816 bytes 15372396 (14.6 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 4508 bytes 302345 (295.2 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@499930b14be8 /]# 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.073 ms
^C
--- 172.18.0.3 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.073/0.073/0.073/0.000 ms
[root@499930b14be8 /]# ping 172.20.0.100
PING 172.20.0.100 (172.20.0.100) 56(84) bytes of data.
^C
--- 172.20.0.100 ping statistics ---
7 packets transmitted, 0 received, 100% packet loss, time 5999ms
[root@499930b14be8 /]#
上面我们进入container1容器,container1可以ping通container2,但是ping不同container3。
因为 container3
和container1
并没有任何共同的网络,所以他们是无法通信的。
- 从网络中断开一个容器的连接
docker network disconnect 网络名 容器名