容器系列之虚拟化网络

名称空间:UTS、User、Mount、IPC、PID、NET

Linux的3.12内核支持6种Namespace:

    1. UTS: hostname
    2. IPC: 进程间通信 
    3. PID: "chroot"进程树
    4. NS: 挂载点,首次登陆Linux
    5. NET: 网络访问,包括接口
    6. USER: 将本地的虚拟user-id映射到真实的user-id

NET:网络名称空间
描述:主要是网络设备、协议栈等实现,假设物理机上有四块网卡,需要创建两个名称空间,这些设备可以单独关联给某个空间所使用的,如第一个网卡分配给第一个名称空间使用,其他就看不见这个设备了,一个设备一般只能授予一个空间,同样有四个网卡就可以使用四个名称空间,使得每个名称空间都可以配置IP地址与外界进行通信。
  如果名称空间的数量超过物理网卡数量,每个名称空间内部的进程也是需要通过网络进行通信,应该如何上报,可以使用模拟技术,linux设备支持两种内核级的模拟,是二层设备和三层设备,网卡就是一个二层设备,工作在链路层,能够封装报文实现各设备之间报文转发的实现,这功能是完全可以在Linux之上利用内核中对二层虚拟设备的支持,创建虚拟网卡接口,而且这种虚拟网卡接口很独特,每个网络接口设备是成对出现的,可以模拟为一根网线的两头,其中一头可以插在主机之上,另一头插在交换机之上进行模拟,相当于一个主机连接到交换机上去了,而linux内核源生就支持模拟二层网络设备,使用软件来构建一个交换机。
  如果有两个名称空间,那么两台主机就像连接到同一个交换机上进行通信,如果配置的网络地址在同一个网段就可以直接进行通讯了。这就是虚拟化的网络。
OVS: OpenVSwitch 可以模拟高级的网络技术,二层交换,甚至三层网络设备,vlan,,不属于Linux内核组件,要额外安装,由cisco众多公司所构建的,有云计算的浪潮下,构建网络是比较复杂的,然后才是网络之上所承载的主机,才能通讯,这个网络虚拟化所实现的功能,需要软件硬件结合起来实现,而且把传统意义上的网络平面,控制平面,传输平面等,隔离开来,集中到一个设备之上实现全局的调度,实现SDN,软件定义网络
单节点上容器通讯:同一个物理机上的两个容器,或者两个名称空间要通讯,就是在主机上建立一个虚拟的交换机,让两个容器各自使用纯软件的方式,建一对虚拟网卡,一半在交换机上,一半在容器上,从而实现单节点上容器进行通讯,但是也有比较复杂的情况,有可能会出现有两个软交换机的情况,连接不同的容器,这时两个软交换机要连接,需要再做一块网卡,一头在交换机1上,另一头在交换机2之上,如果不同交换机之间要实现路由转发,就需要在两能交换机上加一台路由器,linux内核自身可以当作路由器来使用,打开转发或者使用iptables规则,但是路由器是一个三层的设备,在linux内核直接使用一个单独的名称空间就可以实现,就是再做一个容器当作路由器来使用,当是要模拟出网卡来让它们建立关联关系


多节点:另一台主机上的一个容器,与1号主机上的容器进行通信,vmware实现不同主机上的虚拟机之间的通讯可以使用桥接的方式,就是把物理网卡当作交换机来使用,所有一台主机上的容器都到一个物理网卡来,通过MAC地址来确定交给那个容器,如果是到物理机的,就给物理机,也就是虚拟机里也有自身的独特的MAC地址,所以数据包来时可以区别各
个设备,把物理网卡当作交换机来使用,把报文转发给各容器,如果报文目标是物理网卡时,需要虚拟出一个软网卡作为物理网卡的使用,这样就没有虚拟交换机概念,所以两台主机上的虚拟机要使用桥接通讯时,都是连接到各自主机上的物理网卡的的,但是这种通讯方式要实现有很大的代价,因为所有容器的桥接都在同一个平面中,很容易产生风暴,所以在大规模的虚拟机或容器的使用场景中使用桥接不太好,除非能隔离得很好(桥接)


Nat技术:如图中C3与C6通讯,C3是虚拟网卡,C3网卡与物理网卡物理地址不在同一个网段中,C3把网关指向S2,把S3当作宿主机的一个网卡来使用,IP地址与C3在同一个网段,把C3的网关指向S2,然后在物理机上打开核心转发功能,所以当C3与C6通讯时,先转给s2,再到达内核,内核判定查路由列不是自己要到另一个主机上的C6,这时报文回不来,因为C3和C4是一个私有地址,如果要报文能够回来,最后到报文送走物理机之前,要把源IP地址修改成物理网卡的IP地址,这样C5或者C6回复物理主机的IP就可以了,通过NAT表的查询是C3的访问,就把报文送给C3,这就使用NAT实现跨主机之间的通讯,但是这里有一个很大的问题,C6也可能是NAT的模式下工作,也就是说它也是使用私有地址的,如果C6要被访问只能把它暴露出去,在物理机的能外网卡上明确说明某个端口是提供服务的,如果要C4能够访问C6,就要先访问C6所在的宿主机的物理地址,再使用H2做dnat发给C6,但是C4发送报文时是通过SNAT出来的,C4也是隐藏在NAT背后的,发出去的报文要其他的主机可以响应就应该改写源地址。所以在跨服务主机实现两个虚拟机之间的通讯要实现两级的NAT操作,从C4到C6,首先C4出去就SNAT,到到C6要使用到DNAT,这样的效率不会高,但是网络比较容易管理。
Overlay Network: 叠加网络,是NAT和桥接的一个解决方案,有多个物理主机,在虚拟机上做一个虚拟的桥,让各虚拟机连接到虚拟桥上,通信时借用物理网络来完成报文的隧道转发,从而实现C1可以直接看见C5或C6,物理主机本来就是使用物理网络连接在一起的,C1与物理网络不在同一个地址段内,但是C1与C5是在同一地址段内的,C1发送报文时,先发送给虚拟机,假设它是知道C5是不要本地的物理主机上的,以是报文要从物理网卡发送出去,但是要做隧道转发,也就是C1的报文源IP地址是C1,目标地址是C5,然后再封装一个IP包头的首部源地址是C1所在物理主机的IP地址,目标地址是C5所在物理主机的IP地址,当报文送到C5所在的物理机,把报文拆完第一层后,第二层的目标地址就是C5的,就直接交给本地的软交换机,再交给C5,C1与C5之间的通讯直接源地址和目标地址就是各自双方,但是它寄于别的网络,本地自身就是一个三层的网络,应该封装二层,但是没有封装,又封装三层四层报文,就是一个TCP或者UDP的首部,再封装一个首部实现一个两级的三层封装,从而完成报文的转发

 

docker网络:bridge、host、none
bridge:桥接时网络,并不是物理桥,把本机上创建一个纯粹的软交换机dokcer0,也可以当作网卡来使用,每启动一个容器就可以给容器分配一段网卡的地址,一半在容器上,一半在docker0桥上,veth176661b这种在机器可以看到的无论容器还是KVM时,每次创建网卡时,都是创建一对的,一半放在虚拟机上,一半放在软交换机上,相当于一根网线连接着两个设备一样。

[root@node1 ~]# yum install bridge-utils
[root@node1 ~]# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.0242e583526b	no		veth176661b
						            	veth260f195
						            	veth989df86
						            	vethfd38da6
[root@node1 ~]# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 00:0c:29:0f:73:2d brd ff:ff:ff:ff:ff:ff
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default 
    link/ether 02:42:e5:83:52:6b brd ff:ff:ff:ff:ff:ff
10: veth989df86@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default 
    link/ether 82:e0:c3:c8:3f:6f brd ff:ff:ff:ff:ff:ff link-netnsid 0
12: veth260f195@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default 
    link/ether 8a:7c:c5:27:2e:a9 brd ff:ff:ff:ff:ff:ff link-netnsid 1
18: vethfd38da6@if17: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default 
    link/ether da:a4:45:f7:b1:cc brd ff:ff:ff:ff:ff:ff link-netnsid 2
22: veth176661b@if21: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default 
    link/ether ea:b5:29:d7:66:8d brd ff:ff:ff:ff:ff:ff link-netnsid 4
veth989df86@if9 可以在网卡里查阅,if9是在容器中的另一半的地址

Nat桥:docker创建时默认就是nat桥,是使用Iptables来实现的

[root@node1 ~]# iptables -t nat -vnL
Chain PREROUTING (policy ACCEPT 131 packets, 28983 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    9   444 DOCKER     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT 122 packets, 26871 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 475 packets, 45022 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 DOCKER     all  --  *      *       0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT 475 packets, 45022 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0    
in: 从使用接口进来,只要不出docker0出去,源地址来自于172.17.0.0/16的,无论到达任何主机0.0.0.0/0,都要做地址伪装MASQUERADE,相当于SNAT,
而且是自动实现SNAT,也就是自动选择一个最合适物理地址当作源地址,所以docker0桥默认就是nat桥

Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 RETURN     all  --  docker0 *       0.0.0.0/0            0.0.0.0/0 

  

容器中网络通讯情况
a. 同一个宿主机中,使用同一个docker0中的软交换机进行通讯

[root@node1 ~]# docker exec -it web1 /bin/sh
/ # ifconfig 
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02  
          inet addr:172.17.0.2  Bcast:172.17.255.255  Mask:255.255.0.0
[root@node1 ~]# docker exec -it b1 /bin/sh
/ # ifconfig 
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:04  
          inet addr:172.17.0.4  Bcast:172.17.255.255  Mask:255.255.0.0
/ # ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=44.547 ms

使用物理机来访问
[root@node1 ~]# 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=33.1 ms

b. 跨主机通讯
在同一个宿方机之间的容器通讯可以实现,但是跨主机就会产生问题,因为docker本身就是一个nat bridge,对外来说是不可见的,要实现不同主机之间的容器的实现通讯,就要做dnat,把接口中发布出来的,假设物理主机上有一个物理网卡,开通一个端口然后提供对外服务,外部主机访问容器中的服务时,使用dnat的方式转到容器中的虚拟网卡中,提供服务。
但是存在一个问,如果在同一台宿主机上,起了两个容器分别是两个nginx的web服务,但是对外的IP只有一个,只能使用端口来区分,假设nginx1使用80,另一个nginx2就只能使用非80的端口,这时client访问的出现问题,因为默认访问就要给80,如果是非80端口就请求不到。
如查使用ovetlay network叠加网络方式就可以直接使用隧道来承载,直接访问就可以了,可以不用对地址进行映射。一般的跨主机之间的虚拟机访问方式桥接、nat的。
容器特殊功能,在容器内部有6个隔离的名称空间,user,mount,pid,uts,net,ipc,每个容器都有自身独立的资源,假设让每个容器都有隔离而独立的user,mount,pid,而uts,net,ipc这三个资源是共享使用的,拥有同一个网卡,同一组网络协议栈,有同一个主机名和域名,对外使用同一个IP地址,优点是如第一个容器使用的tomcat服务,第二个容器的是redis服务时,如果tomcat要访问redis中的数据时,是同一个协议栈,之前如果是隔离的,通过127来访问是不可以的,实现有自己独立隔离的名称空间,却又共享一部分名称空间



docker中的host模式
物理机也有名称空间,也让容器使用物理机的名称空间,容器和容器之间可以共享名称空间,所以也可以共享物理服务器的名称空间来使用,如可以让第一个容器直接使用物理机的名称空间,也就是容器中修改网卡中的信息是直接改物理机中的网卡的信息的,第二个容器可以使用桥接,所以第一个容器就拥有了管理网络的特权host就是让容器使用宿主机的网络名称空间

docker中的none模式
设定容器使用none网络表示没的网络,相当于没有网卡,只有Loop接口,有些容器可能只需要不向外通讯的

docker网络模型

closed container   封闭式网络接口,只有Loop接口
bridged container  桥接,使用容器接口连接到docker0上,默认设置的
jonied container   联盟式容器网络,一部分名称空间是隔离的,文件系统,用户,pid是各自的,其他三个是共享的,
                   两个容器可以使用loop接口来通讯
开放式容器网络     直接共享物理机的网络接口
[root@node1 ~]# docker network inspect bridge|grep bridge.name
            "com.docker.network.bridge.name": "docker0",   #bridge网络所关联的是docker0

 

 

posted @ 2018-10-07 20:28  Reid21  阅读(1232)  评论(0编辑  收藏  举报