名称空间的六种分别是:UTC,User,Mount,ipc,pid,Net。网络的名称空间在早些时候就已经添加到内核当中,现在的内核上,基本上是支持的。

网络名称空间主要用于协议栈,网络设备的隔离实现。

一般情况,一个网卡设备只能关联到一个名称空间上,并且都会配置ip地址与外部通讯。假如现在名称空间数量超过物理网卡数量,每一个名称空间内部的进程也需要通过网络进行通讯。 当然,我们是不可能有那么多的网卡设备,而用纯软件的方式模拟一组设备使用。

虚拟化网络#

linux内核支持2层和三层设备来模拟,物理的网卡也就是2层设备,工作在链路层能够封装物理报文,实现在各个设备之间进行报文转发的组件。而这个功能完全可以在linux之上用内核当中对虚拟设备的支持创建虚拟网卡接口的。

而这种网卡接口很独特,是成对出现。可以模拟为一根网线的两头,而linux内核原生支持二层虚拟网桥设备,也就是软件交换机。一头在名称空间,一头在交换机,就相当于模拟了让一个主机链接到交换机上来。如下:

docker-26

假如有多个一样的名称空间,各自创建虚拟的网卡,一半留在名称空间上,一半留在交换机上,模拟接入机制,就能实现网络功能。如果配置的网络在同一个网段就完成了网络通讯,这种方式就是虚拟化网络。从网络设备通讯到网卡都是用纯软件的方式实现,并且我们能够在一组主机上用纯软件的方式实现,这种实现就是网络虚拟化技术中的一种简单实现。

如著名的OVS(openVswitch)用纯软件实现,并且能够模拟网络的高级功能,如vlan,vxlan,qos,sdn等,都用纯软件实现,额外安装即可。

容器网络#

如果是在同一个物理机上的两个容器/名称空间,需要通讯就需要在其物理机之上创建一个交换机,两个容器各自创建一个虚拟网卡,一边在容器之上,一边在交换机上,从而建立通讯联系。如下:

docker-27

两个交换机内链接交换机的容器各自需要通讯的时候,相互通讯就需要在做一个虚拟网卡,将他们互相链接。

桥接

但是,如果期望经过路由转发,就需要在linux之上打开路由转发,或者通过iptables实现。

那么,如果现在是第一个host上的namespace1期望与其他host的namespace通讯,按照之前的虚拟机物理桥接方式后,将物理网卡当交换机来使用。所有发往sw1和sw2(上图)的上的容器的通讯先到达物理网卡,物理网卡根据mac地址来判断是最终到达哪里。如果是发给物理网卡本身,就会转交到另外一个的虚拟网卡,这个网卡接收物理网卡报文。而物理网卡本身是交换机。类似于这种。如下:

docker-28

但是这种方式所有的容器都是桥接的,就容器产生网络风暴,隔离也是问题。

net网络

如果不桥接还要与外部通信,也可以使用net网络。

如果此时namspace2要与namespace3通信,namspace2与host网段也不在同一个网段,namspace2的网关指向sw1,并且在物理主机打开核心转发。

当namspace2和namespace3通讯的时候,先送往sw1,在到达物理内核,物理机查看路由表发出去。其中,namspace2在离开物理机host之前将源ip改成物理网卡的ip地址 。namespace3回复给host物理机的Ip即可。如下图:

docker-29

这种方式是经过了连击NAT实现,NAT后的ip要想被访问是需要暴露出去的,这就需要DNAT。

如上,namespace2访问namespace3的时候,namespace2出去的时候是做了SNAT,实际上访问的namespace3的host主机地址。而namespace3要想被访问到就需要做DNAT。

这样一来namespace2访问namespace3都是隔着两层,这种方式效率也是很低的,并且互相都不知道真实的ip。但是,这种方式网络是易于管理的。

当然,还有一种网络,比桥接和NAT实现都快速,这种网络就是叠加网络。

在多个物理机有虚拟桥,通讯的时候借助每个物理机器上的物理网卡完成报文隧道转发,通讯中互相就能看到自己。如上图中,namespace2访问namespace3。在叠加网络中namespace2和namespace3是在同一个网段中的,当namespace2访问namespace3时候,网桥是知道namespace3在哪里的

namespace2访问namespace3是做隧道转发的,原地址是namespace2,目标地址是namespace3,报文中还封装了各个namespace位于物理host主机的ip地址。当拆分第一层后就到了namespace的地址了。而后交给软交换到达目标。

参考flannel

docker网络#

docker安装完成后自动提供了三种网络

[marksugar@linuxea.com142 ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
c46b68610246        bridge              bridge              local
ffc5941362d9        host                host                local
1f143e6b01ea        none                null                local

bridge是net桥接网络,本机之上创建docker0交换机和网卡使用

3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN 
    link/ether 02:42:d0:06:23:b8 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 scope global docker0
       valid_lft forever preferred_lft forever

而后启动的容器,就会自动给分配一对虚拟网卡。一半在容器,一半在网桥。

本机网桥

72: veth350c423@if71: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether be:99:b4:ad:8b:b0 brd ff:ff:ff:ff:ff:ff link-netnsid 1

并且还会被关联到docker0上。使用brctl show查看

brctl 安装:yum install bridge-utils -y

[marksugar@linuxea.com_Node ~]$ brctl show
bridge name bridge id       STP enabled interfaces
docker0     8000.0242671ebf16   no      veth350c423
                                        veth869790e

ip link show查看对应的接口

[marksugar@linuxea.com_Node ~]$ ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 88:88:2f:d9:89:67 brd ff:ff:ff:ff:ff:ff
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default 
    link/ether 02:42:67:1e:bf:16 brd ff:ff:ff:ff:ff:ff
4: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether fa:d9:57:90:8c:c8 brd ff:ff:ff:ff:ff:ff
62: veth869790e@if61: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default 
    link/ether 96:7d:73:13:1e:41 brd ff:ff:ff:ff:ff:ff link-netnsid 0
72: veth350c423@if71: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default 
    link/ether be:99:b4:ad:8b:b0 brd ff:ff:ff:ff:ff:ff link-netnsid 1

而容器内网卡也就是虚拟另一半

[marksugar@linuxea.com_Node ~]$ docker exec -it 5idocker sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
71: eth0@if72: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
/ # ping 172.17.0.1
PING 172.17.0.1 (172.17.0.1): 56 data bytes
64 bytes from 172.17.0.1: seq=0 ttl=64 time=0.092 ms
64 bytes from 172.17.0.1: seq=1 ttl=64 time=0.061 ms
64 bytes from 172.17.0.1: seq=2 ttl=64 time=0.097 ms
64 bytes from 172.17.0.1: seq=3 ttl=64 time=0.093 ms
64 bytes from 172.17.0.1: seq=4 ttl=64 time=0.086 ms
64 bytes from 172.17.0.1: seq=5 ttl=64 time=0.108 ms
64 bytes from 172.17.0.1: seq=6 ttl=64 time=0.082 ms

docker0桥默认是一个nat桥,每创建一个容器并启动并分配地址后,就会生成一个iptables规则

[marksugar@linuxea.com_Node ~]$ iptables -t nat -vnL
...省略...
Chain POSTROUTING (policy ACCEPT 3317K packets, 231M bytes)
 pkts bytes target     prot opt in     out     source               destination         
   10   660 MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0    
...省略...   

只要不是从docker0桥出去的,源地址来自172.17.0.0/16 ,无论到达哪里,都要做地址伪装,也就是SNAT(MASQUERADE),并且自动SNAT(自动选择).

容器网络访问#

这个此前创建的容器,并没有选择网络,默认是bridge网络,docker0桥在物理机上,而后创建的5idocker容器,容器内有eth0,另外一侧在物理机的docker0交换机。

docker-30

如果在同一个物理机内的同一个docker0交换机内这个5idocker容器是可以被访问到的。如上图所示:

现在run一个busybox,ip是172.17.0.4,访问到此前的httpd是没有任何问题的,如下

[marksugar@linuxea.com_Node ~]$ docker run --name client -it busybox:latest sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
73: eth0@if74: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.4/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
/ # wget -O - -q http://172.17.0.3
helo www.linuxea.com

当然,本地物理机访问也是没有问题的。如果没有SNAT,就只是一个内部的主机桥。

[marksugar@linuxea.com_Node ~]$ curl 172.17.0.3
helo www.linuxea.com

跨主机容器访问#

那么现在,如果其他的host主机默认访问这个5idocekr是无法访问的,默认的桥下的容器想要在此桥下被访问只能是DNAT,而DNAT是看不到后端的5idocker的。此刻要想被外部客户端访问就需要DNAT,客户端在访问网卡80端口时候,转发至docker0交换机上,而后到5idocker。如下图

docker-31

当使用这种默认的桥接,只能在添加DNAT规则便于可以被外部客户端访问。如果此时启动多个web容器,正常亲下对外通讯的物理机器只有一个ip地址,也只有一个网卡,在做DNAT那就不能使用同一个端口。但是,如果不是同一个端口,那只能使用端口映射的方式对外提供业务。

如果是叠加的方式就不需要在物理机做端口映射,直接通过隧道访问对端ip和端口

Host#

此前,我们知道。每一个容器都有独立隔离的6个名称空间,user,mount,pid,UTS,ipc,net。

而每个容器都有user,mount,pid,而UTS,ipc,net是容器间共享的。这样一来,除了user,mount,pid这些是互不干扰的,其他是拥有同一组网卡,同一组协议栈,同一个主机名和域名,同一个ip地址。如果此时两个容器通讯,使用同一个lo接口通讯,就可以使用127.0.0.1访问。这样就有一部分隔离的名称空间,一部分是共享的名称空间。

docker-32

而这种Host的方式是直接共享物理机器的网络。如果创建容器的时候,默认都是桥接网络,nat网络模式。

这些信息可以通过docker network inspect [bridge|host]查看

None#

none意味着没有网络。不设置网络,只有lo接口。一般运行一些不需要网络的程序,充当临时使用或者客户端等。