docker的4种网络模型

转自:docker的4种网络模型 - 御用闲人 - 博客园

我们在使用docker run创建Docker容器时,可以用--net参数指定容器的网络模式,Docker有以下4种网络模式:

· host模式,使用--net=host指定。

· container模式,使用--net=container:NAME_or_ID指定。

· none模式,使用--net=none指定。

· bridge模式,使用--net=bridge指定,默认设置。

1、host模式

  众所周知,Docker使用了Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。一个Docker容器一般会分配一个独立的Network Namespace。但如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口和主机名(hostname -I)。

  例如,我们在10.10.101.105/24的机器上用host模式启动一个含有web应用的Docker容器,监听tcp80端口。当我们在容器中执行任何类似ifconfig命令查看网络环境时,看到的都是宿主机上的信息。而外界访问容器中的应用,则直接使用10.10.101.105:80即可,不用任何NAT转换,就如直接跑在宿主机中一样,优点是处理速度快,不用nat转发。虽然共享网络名称空间,但是容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。

总结:与宿主机共享network Namespace,容器与宿主机的主机名、IP一致,但容器的其他方面,如文件系统、进程列表、服务、端口等还是和宿主机隔离的,所以容器内只能看到自己开启的哪些服务、进程等,看不了宿主机或者其他容器的服务、进程等

2 、container(联合网络)模式

  在理解了host模式后,这个模式也就好理解了。这个模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口、主机名(hostname -I)范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表、服务等还是隔离的。两个容器的进程可以通过lo网卡设备通信。(k8s常用网络模型,边车模式)

1.首先启动一个有网络的容器
[root@docker01 ~]# docker run -itd centos69_ssh_df:v3 /bin/bash
347c5934f47b46d8cc54e305313c8c9b84604370aac41bd61535b37a861f002c

[root@docker01 ~]# docker inspect frosty_turing | grep 'IPAddress'
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.6",
                    "IPAddress": "172.17.0.6",

2.接着启动需要使用共享网络的容器
[root@docker01 ~]# docker run -it --network container:frosty_turing centos69_ssh_df:v3 /bin/bash

3.查看容器的ip和主机名,会发现和刚刚启动的容器的主机名、ip都相同
[root@347c5934f47b /]# hostname -I
172.17.0.6

总结:与另一个运行中的容器共享Network Namespace(k8s常用网络模型,边车模式),容器与另一个运行中的容器的主机名、IP一致,但容器的其他方面,如文件系统、进程列表、服务、端口等还是隔离的,谁共享谁的Network Namespace,先到先得

3 、none模式

  这个模式和前两个不同。在这种模式下,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。但有lo回环网络。这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。

[root@docker01 ~]# docker run -it --network none centos69_ssh_df:v3 /bin/bash

2.查看是否有网络
[root@6e47719e0c83 /]# ifconfig 
lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

[root@6e47719e0c83 /]# 

3.查看容器的属性会看到network没有指定ip
[root@docker01 ~]# docker inspect cranky_agnesi 

总结:不为容器配置任何网络功能,但有lo回环网络

4 、bridge模式

  bridge模式是Docker默认的网络设置,此模式会为每一个容器分配Network Namespace、设置IP等,并将一个主机上的Docker容器连接到一个虚拟网桥上。下面着重介绍一下此模式。

4.1 bridge模式的拓扑

  当Docker server启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。接下来就要为容器分配IP了,Docker会从RFC1918所定义的私有IP网段中,选择一个和宿主机不同的IP地址和子网分配给docker0,连接到docker0的容器就从这个子网中选择一个未占用的IP使用。如一般Docker会使用172.17.0.0/16这个网段,并将172.17.42.1/16分配给docker0网桥(在主机上使用ifconfig命令是可以看到docker0的,可以认为它是网桥的管理接口,在宿主机上作为一块虚拟网卡使用)。单机环境下的网络拓扑如下,主机地址为10.10.101.105/24。

Docker完成以上网络配置的过程大致是这样的:

1. 在主机上创建一对虚拟网卡veth pair设备。veth设备总是成对出现的,它们组成了一个数据的通道,数据从一个设备进入,就会从另一个设备出来。因此,veth设备常用来连接两个网络设备。

2. Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0。另一端放在主机中,以veth65f9这样类似的名字命名,并将这个网络设备加入到docker0网桥中,可以通过brctl show命令查看。

3. 从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。
网络拓扑介绍完后,接着介绍一下bridge模式下容器是如何通信的。

4.2 bridge模式下容器的通信

4.2 1、容器跟容器之间的访问

从上述我们知道,在bridge模式下,Docker容器的网络设备,都将加入到docker0网桥中,所以连在同一网桥上的容器可以相互通信。可以理解为docker0是容器的网关,而所有的Docker容器都在同一个网段中。(若出于安全考虑,也可以禁止它们之间通信,方法是在DOCKER_OPTS变量中设置--icc=false,这样只有使用--link才能使两个容器通信)。如下是容器内部的路由,只要是去往172.17.0.0/16的网络,不用路由。(Gateway网关地址下,”*” 表示目标是本主机所属的网络,不需要路由)

[root@localhost ~]# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         gateway         0.0.0.0         UG    100    0        0 ens33
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
192.168.18.0    0.0.0.0         255.255.255.0   U     100    0        0 ens33
[root@localhost ~]#

可以通过brctl show命令查看docker0网桥中的设备。

[root@localhost ~]# brctl show   # 查看连接docker0所有的vethfinder
bridge name     bridge id               STP enabled     interfaces
docker0         8000.024246ae3892       no              veth2631e6b
                                                        veth6e3f1c5
                                                        veth8f4f430

[root@e230f55f0b16 /]# cat /sys/class/net/eth0/iflink   # 在容器中查看使用的vethfinder编号
12
[root@localhost ~]# grep -l 12 /sys/class/net/veth*/ifindex  # 在宿主机中,查看12对应的vethfinder
/sys/class/net/veth2631e6b/ifindex

4.2 2、容器如何访问非容器之外的网络

容器也可以与外部通信,通过主机上的一条Iptable规则实现,如下,这条规则会将源地址为172.17.0.0/16的包(也就是从Docker容器产生的包),并且不是从docker0网卡发出的,进行源地址转换,转换成主机网卡的地址。
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

以访问百度为实际的案例:

假设主机有一块网卡为ens33,IP地址为192.168.18.6/24,网关为192.168.18.1。从主机上一个IP为172.17.0.1/16的容器中ping百度www.baidu.com,通过查看dns解析出是(110.242.68.3) 

[root@e5a339b0c4d7 yum.repos.d]# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 192.168.18.1

[root@e5a339b0c4d7 yum.repos.d]# ping www.baidu.com
ping: unknown host www.baidu.com
[root@e5a339b0c4d7 yum.repos.d]# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 192.168.18.1

[root@e5a339b0c4d7 yum.repos.d]# ping www.baidu.com
PING www.a.shifen.com (110.242.68.3) 56(84) bytes of data.
64 bytes from 110.242.68.3: icmp_seq=1 ttl=127 time=53.3 ms
64 bytes from 110.242.68.3: icmp_seq=2 ttl=127 time=52.5 m

IP包首先通过route路由,route表中获得,目的地址非172.17.0.0地址的IP,默认全部都发送给172.17.0.1网关,所以数据包从容器发往自己的默认网关docker0

[root@e5a339b0c4d7 yum.repos.d]# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         172.17.0.1      0.0.0.0         UG    0      0        0 eth0
172.17.0.0      *               255.255.0.0     U     0      0        0 eth0

包到达docker0后,也就到达了宿主机上,docker0所处在宿主机上,会对刚刚到的数据包(src(172.17.0.0/16地址的IP)dst (110.242.68.3) )处理发给谁处理,所以会查询宿主机的路由表,查看到除了去172.17.0.0/16,192.168.18.0/24 ,都默认给gateway,接着包会转发给ens33,并从ens33发出去(主机的ip_forward转发应该已经打开)。

[root@localhost ~]# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         gateway         0.0.0.0         UG    100    0        0 ens33
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
192.168.18.0    0.0.0.0         255.255.255.0   U     100    0        0 ens33

这时候,Iptable规则就会起作用,意思是只要源地址是172.17.0.0/16的网络要出网,而这个数据包还不是从docker0发出来的,统统对包做SNAT转换,将源地址换为ens33的地址。
这样,在外界看来,这个包就是从192.168.18.6/24上发出来的,Docker容器对外是不可见的。

[root@localhost ~]# iptables-save |grep "172.17.0.0/16"
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

4.2 3、外部机器如何访问容器

将容器内部的端口挂载到宿主机某一个端口,通过Iptable规则,将访问宿主机某一个端口流量引入到容器内部的端口。此条规则对主机ens33收到的目的端口为8081的tcp流量进行DNAT转换,将流量发往172.17.0.2:80。

-A DOCKER ! -i docker0 -p tcp -m tcp --dport 8081 -j DNAT --to-destination 172.17.0.2:80

外面的机器是如何访问Docker容器的服务呢?以访问nginx为实际的案例:

查看本机的8081端口无法访问,查看Iptable规则关于8081是没有的

[root@localhost ~]# iptables-save |grep "80"
[root@localhost ~]# curl 192.168.18.6:8081
curl: (7) Failed connect to 192.168.18.6:8081; 拒绝连接

用下面命令创建一个含有web应用的容器,将容器的80端口映射到主机的8081端口。并能通过192.168.18.6:8081 ,进而访问nginx的80端口

[root@localhost yum.repos.d]# docker pull nginx:1.16
[root@localhost yum.repos.d]# docker image ls -a |grep nginx
nginx               1.16                dfcfd8e9a5d3        23 months ago       127 MB

[root@localhost yum.repos.d]# docker run -d --name web -p 8081:80 dfcfd8e9a5d3
dac63d3282e8e537c4780e59fe91fb66e95f5892882a9f57ba5323fa7b2d2b75

[root@localhost yum.repos.d]# curl 192.168.18.6:8081
<!DOCTYPE html>
......
<h1>Welcome to nginx!</h1>
......

[root@localhost yum.repos.d]#

然后查看Iptable规则的变化,发现多了这样一条规则:

[root@localhost ~]# iptables-save |grep "8081"
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 8081 -j DNAT --to-destination 172.17.0.2:80
[root@localhost ~]#

此条规则就是对主机ens33收到的目的端口为8081的tcp流量进行DNAT转换,将流量发往172.17.0.2:80,也就是我们上面创建的Docker容器。通过宿主机的路由,只要是目的地址是172.17.0.0/16,都给docker0,而docker0是容器网关,它当然知道172.17.0.2是那个容器。所以,外界只需访问192.168.18.6:8081就可以访问到容器中得服务。

[root@localhost yum.repos.d]# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         gateway         0.0.0.0         UG    100    0        0 ens33
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
192.168.18.0    0.0.0.0         255.255.255.0   U     100    0        0 ens33
[root@localhost yum.repos.d]#

除此之外,我们还可以自定义Docker使用的IP地址、DNS等信息,甚至使用自己定义的网桥,但是其工作方式还是一样的。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值