详解虚拟化网络(一)--docker网络单机模型

详解虚拟化网络(一)--docker网络单机模型

本文内容较多,详细介绍docker网络单机模型在各个场景下的实现原理,结构如下:

 

1.Docker核心原理

Docker容器的的本质就是宿主机上的进程.只不过宿主机对它进行了Linux Namesapce隔离,让它看不到外面的世界,利用cgroups实现资源的限制,通过写时复制的机制完成高效的文件操作.同时利用系统调用pivot_root或chroot切换了进程的根目录,把容器镜像挂载为根文件系统rootfs。在rootfs中不仅有要运行的应用程序,还包含了应用的所有依赖库,以及操作系统的目录和文件。rootfs打包了应用运行的完整环境,这样就保证了在开发、测试、线上等多个场景的一致性。上面提及的docker实现核心技术namespace和 cgroups,其实并非新技术,它们是linux的相关技术。而docker对这种技术进行了封装,提高可操作性。

在一般情况下,大多数人会对容器与虚拟机进行比较,为什么会需要容器?容器到底解决的是什么问题?可以认为容器极大的提升了软件的可移植能力。

 

        从官网所给的图片可以看出,容器和虚拟机的最大区别就是,每个虚拟机都有独立的操作系统内核,而容器只是一种特殊的进程,它们共享同一个操作系统内核。直观来说:Docker是虚拟出操作系统,实现多应用之间的隔离,让每个应用觉得自己有一个自己的操作系统,而且彼此之间隔离。而虚拟机的思路则完全不一样,如果进程1和进程2运行于不同的虚拟机,则操作系统都是双份的,它们感觉自己在不同的虚拟电脑上面跑。由于可见,Docker达到了类似虚拟机的效果,但是又没有虚拟机的开销,它虚拟的层次更加高。Docker不虚拟机器,仅仅虚拟应用的运行环境。

        看清了容器的本质,很多问题就容易理解。例如我们执行 docker exec 命令能够进入运行中的容器,好像登录进独立的虚拟机一样。实际上这只不过是利用系统调用setns,让当前进程进入到容器进程的Namesapce中,它就能“看到”容器内部的情况了。

 

2. centos7安装docker

参照官方文档安装方式,输入以下七条命令

$ sudo yum install -y yum-utils device-mapper-persistent-data lvm2

$ sudo yum-config-manager --add-repo \

https://download.docker.com/linux/centos/docker-ce.repo

$ sudo yum-config-manager --enable docker-ce-edge

$ sudo yum-config-manager --enable docker-ce-test

$ sudo yum-config-manager --disable docker-ce-edge

$ sudo yum install -y docker-ce

$ sudo systemctl start docker

 

此时若安装成功,命令行输入docker info,可看到如下信息:

最好从阿里云镜像加速,毕竟如果镜像文件长期从国外下载的话,速度还是挺慢的,网址为https://cr.console.aliyun.com/cn-shanghai/repositories,通过修改daemon配置文件/etc/docker/daemon.json来使用加速器。

执行以下四条命令:

1. sudo mkdir -p /etc/docker

2. sudo tee /etc/docker/daemon.json <<-'EOF'

{

"registry-mirrors": ["自己的容器镜像服务地址"]

}

EOF

3. sudo systemctl daemon-reload

4. sudo systemctl restart docker

 

3. Docker单机网络模型

安装完docker后,接下来就从大多数人关注的焦点:容器的网络通信开始讲起。上面说到,容器是一种特殊的进程,它们共享同一个操作系统内核,Docker使得容器可以达到类似虚拟机的效果。那么就牵涉到网络,如何让容器之间互相连接保持网络通畅,Docker有多种网络模型。对于单机上运行的多个容器,可以使用缺省的bridge网络驱动。而容器的跨主机通信,一种常用的方式是利用Overlay 网络,基于物理网络的虚拟化网络来实现。

本文会在单机上实验展示桥接网络模型,揭示其背后的实现原理。之后文章会演示容器如何利用Overlay 网络和路由模式进行跨主机通信。

我们按照下图创建网络拓扑,让容器之间网络互通,从容器内部可以访问外部资源,同时,容器内可以暴露服务让外部访问。

 

在开始动手实验之前,先简单介绍一下上图bridge网络模型会用到的Linux虚拟化背景网络技术。

(1)Veth Pairs

Veth是成对出现的两张虚拟网卡,从一端发送的数据包,总会在另一端接收到。利用Veth的特性,我们可以将一端的虚拟网卡"放入"容器内,另一端接入虚拟交换机。这样,接入同一个虚拟交换机(Linux bridge就是虚拟交换机:br0)的容器之间就实现了网络互通。这也是docker默认的网络桥接技术,docker在安装完成后会在当前系统环境下,生成默认的docker0虚拟网桥,之后docker容器运行时,会自动生成Veth Pairs,一端在容器内,另一端在宿主机环境下。可以演示如下,先利用docker镜像创建容器,之后查看宿主机和容器网桥的变化。

现在我们来创建和启动两个容器:container1和container2。

 

容器启动后,从宿主机的视角,可以看到网络配置有如下变化:

$ brctl show  //没有brctl(网桥工具)的话,执行yum install bridge-utils -y下载

 

可以看到docker容器运行时,会自动生成Veth Pairs,一端在宿主机环境下,那么另一端我们去容器中查看,容器中显示如下:(容器内没有ip命令的,运行yum -y install initscripts下载)

 

所以Docker Daemon创建了两个veth网络设备,并将veth挂接到docker0网桥上了。

Veth Pairs挂在docker0网桥上,这对于container1和 container2来说,就好比用网线将本地网卡(eth0)与网桥设备docker0的网口连接起来一样。在docker容器网络默认桥接模式中,veth只是在二层起作用。

 

(2)Linux Bridge

交换机是工作在数据链路层的网络设备,它转发的是二层网络包。上面的docker0桥就可以认为是虚拟的网桥。最简单的转发策略是将到达交换机输入端口的报文,广播到所有的输出端口。当然更好的策略是在转发过程中进行学习,记录交换机端口和MAC地址的映射关系,这样在下次转发时就能够根据报文中的MAC地址,发送到对应的输出端口。我们可以认为Linux bridge就是虚拟交换机,连接在同一个bridge上的容器组成局域网,不同的bridge之间网络是隔离的。docker network create [NETWORK NAME]实际上就是创建出虚拟交换机。

从容器角度看来,docker0对于通过veth pair“插在”网桥上的container1和container2来说,首先就是一个二层的交换机的角色:泛洪、维护表,在二层转发数据包;同时由于docker0自身也具有mac地址(这个与纯二层交换机不同),并且绑定了ip(这里是172.17.0.1),因此在 container中还作为container default路由的默认Gateway而存在。

从宿主机视角,docker0可以认为是网卡身份物理交换机提供了由硬件实现的高效的背板通道,供连接在交换机上的主机高效实现二层通信;对于docker0而言,其负责处理二层交换机逻辑以及三层的处理程序其实就是宿主机上的Linux内核 tcp/ip协议栈程序。从宿主机来看,所有docker0从veth(只是个二层的存在,没有绑定ipv4地址)接收到的数据包都会被宿主机看成从docker0这块网卡(绑定172.17.0.1)接收进来的数据包,尤其是在进入三层时,宿主机上的iptables就会 对docker0进来的数据包按照iptables rules进行相应处理。

 

(3)iptables

容器需要能够访问外部世界,同时也可以暴露服务让外界访问,这时就要用到iptables。另外,不同bridge之间的隔离也会用到iptables。

在容器化网络场景,我们经常用到的是在nat表中设置SNAT和DNAT。源地址转换SNAT是发生在数据包离开机器被发送之前。DNAT是对目标地址的转换,需要在路由选择前完成。后面内网地址映射到公网地址,即容器访问外部世界时,再详细说明。

 

4. Docker网络模型单机实验

        通过前面的背景知识介绍,我们就可以开始动手实验了。可能会想,既然容器创建启动时会自动为我们创建好网络,那么实验的目的何在呢?本次将不采用docker默认的网桥的自动分配策略,重新创建新网桥br0,手工将veth pairs加到二端,验证之前的说法。因为涉及到很多系统网络级设置,建议在一个“干净”的虚拟机内操作,以免干扰到工作环境。(文后会有恢复方法)本次使用的实验环境是centos7,达到类似下图的实验网络拓扑。

 

(1):容器间的网络互通

 创建“容器”

从前面的背景知识了解到,容器是由 Namespace + Cgroups + rootfs组成。而且容器创建启动时会自动为我们创建好网络(docker默认的网桥自动分配策略),所以本实验我们可以仅仅创建出Namespace网络隔离环境来达到模拟容器的行为。

用ip netns命令操作容器的网络。

ip netns是常用的namespace的命令,netns是在linux中提供网络虚拟化的一个项目,使用netns网络空间虚拟化可以在本地虚拟化出多个网络环境,目前netns在lxc容器中被用来为容器提供网络。使用netns创建的网络空间独立于当前系统的网络空间,其中的网络设备以及iptables规则等都是独立的,就好像进入了另外一个网络一样。

 

1. 创建出的网络Namesapce并查看

$sudo ip netns add docker0   //增加虚拟网络命名空间

$sudo ip netns add docker1

$ls -l /var/run/netns        //显示虚拟网络命名空间

 

2. 创建Veth pairs并查看

$sudo ip link add veth0 type veth peer name veth1

$sudo ip link add veth2 type veth peer name veth3

$ip addr show

 

 

3. 将Veth的一端放入“容器”

目前二对veth(可认为四张网卡)都在宿主机内,设置Veth一端的虚拟网卡的Namespace,相当于将这张网卡放入“容器”内:

$ sudo ip link set veth0 netns docker0

$ sudo ip link set veth2 netns docker1

 

查看“容器” docker0 内的网卡:

$ sudo ip netns exec docker0 ip addr show

$ sudo ip netns exec docker1 ip addr show

 

此命令意思是在网络Namesapce  docker0的下执行后面跟着的命令,相当于在“容器”内执行命令。可以看到,veth0已经放入了“容器”docker0内。同样使用命令sudo ip netns exec docker1 ip addr show查看“容器”docker1内的网卡。同时,在宿主机上查看网卡ip addr(等同ip a ),发现veth0和veth2已经消失,确实是放入“容器”内了。

 

 

4. 创建bridge

安装bridge管理工具brctl    //sudo apt-get install bridge-utils

创建bridge br0:

$ sudo brctl addbr br0

 

5. 将Veth的另一端接入bridge

$ sudo brctl addif br0 veth1

$ sudo brctl addif br0 veth3

查看接入情况:

$ sudo brctl show

 

可以看到两个网卡veth1和veth3已经“插”在bridge上。

 

6. 为"容器“内的网卡分配IP地址,并激活上线

docker0容器:

sudo ip netns exec docker0 ip addr add 172.18.0.2/24 dev veth0

sudo ip netns exec docker0 ip link set veth0 up

 

docker1容器:

sudo ip netns exec docker1 ip addr add 172.18.0.3/24 dev veth2

sudo ip netns exec docker1 ip link set veth2 up

 

7.Veth另一端的网卡激活

sudo ip link set veth1 up

sudo ip link set veth3 up

 

8. 为bridge分配IP地址,激活

sudo ip addr add 172.18.0.1/24 dev br0

sudo ip link set br0 up

 

 

9. “容器”间的互通测试

我们可以先设置监听br0:

sudo tcpdump -i br0 -n

 

另外开一个新窗口,从容器docker0 ping 容器docker1:

sudo ip netns exec docker0 ping -c 3 172.18.0.3

 

此时br0上监控到的网络流量:

 

可以看到,先是172.18.0.2发起的ARP请求,询问172.18.0.3的MAC地址,然后是ICMP的请求和响应,最后是172.18.0.3的ARP请求。因为接在同一个bridge br0上,所以是二层互通的局域网。同样,从容器docker1 ping 容器docker0也是通的:

 

(2) 从宿主机访问“容器”内网络

在“容器”docker0内启动服务,监听80端口:

sudo ip netns exec docker0 nc -lp 80   //yum install nc -y 没有nc命令的先安装

 

在宿主机上执行telnet,可以连接到docker0的80端口,输入后,看到上图数据已经连通。

telnet 172.18.0.2 80    //yum install telnet-server -y   yum install telnet -y 安装telnet

 

(3):从“容器”内访问外网

1.配置内核参数

允许IP forwarding,即启用网关服务器路由转发功能:

sudo sysctl net.ipv4.conf.all.forwarding=1

 

配置iptables FORWARD规则

首先确认iptables FORWARD的缺省策略:

sudo iptables -L

 

如果缺省策略是DROP,需要设置为ACCEPT:

sudo iptables -P FORWARD ACCEPT

缺省策略的含义是,在数据包没有匹配到规则时执行的缺省动作。

 

2.将bridge设置为“容器”的缺省网关

sudo ip netns exec docker0 route add default gw 172.18.0.1 veth0

sudo ip netns exec docker1 route add default gw 172.18.0.1 veth2

查看“容器”内的路由表:

sudo ip netns exec docker0 route -n

 

可以看出,“容器”内的缺省Gateway是bridge的IP地址,非172.18.0.0/24网段的数据包会路由给bridge。

 

3.配置iptables的SNAT规则

容器的IP地址外部并不认识,如果它要访问外网,需要在数据包离开前将源地址替换为宿主机的IP,这样外部主机才能用宿主机的IP作为目的地址发回响应。

需要注意的问题,内核netfilter会追踪记录连接,在增加了SNAT规则时,系统会自动增加一个隐式的反向规则,这样返回的包会自动将宿主机的IP替换为容器IP。

sudo iptables -t nat -A POSTROUTING -s 172.18.0.0/24 ! -o br0 -j MASQUERADE

上面的命令的含义是:在nat表的POSTROUTING链增加规则,当数据包的源地址为172.18.0.0/24网段,出口设备不是br0时,就执行MASQUERADE动作。MASQUERADE也是一种源地址转换动作,它会动态选择宿主机的一个IP做源地址转换,而SNAT动作必须在命令中指定固定的IP地址。(外网IP地址不稳定的情况下,也可使用MASQUERADE(动态伪装),能够自动的寻找外网地址并改为当前正确的外网IP地址。)

 

4.从“容器”内访问外部地址

sudo ip netns exec docker0 ping -c 3 www.baidu.com

sudo ip netns exec docker1 ping -c 3 www.baidu.com

 

我们确认在“容器”内是可以访问外部网络的。

 

5.附加说明docker默认策略

更加深入一点的话,我们可以通过网络抓包,来查看从容器内部访问外部世界这个过程,到底发生了什么?参照上面的设置,我们从docker自身的网络策略来进行分析,看看docker在创建容器并启动时访问外部世界的过程。这也适用于我们实验。

1.首先确保物理本机可以连接外网

2.docker run -it  busybox

Busybox将众多的UNIX命令集合进一个很小的可执行程序中,可以用来替代GNU fileutils、shellutils等工具集。简单来说,就算你启动了一个新的centos容器并启动,想查看网络信息,运行命令时会发现,好多命令不存在,因为我们下载的镜像文件中并不包含这些命令,docker的轻量化就在于它精简了许多额外的东西,只保留最核心的部分,而Busybox这个镜像正好有我们需要的命令工具集。

由下图可以看到:在容器内部可以访问外网。(busybox 位于 docker0 上)

 

3.查看iptables策略,了解策略原理。

当 busybox 从容器向外 ping 时,数据包是怎样到达 www.baidu.com 的呢?这里的关键就是 NAT。我们查看一下 宿主机 上的 iptables 规则:

 

图中规则是由

iptables -t nat -s -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

生成的。(docker自己会做好这些,手动实验的话,需要自己执行此命令达到上图效果)其含义是:如果网桥 docker0 收到来自 172.17.0.0/16 网段的外出包,把它交给 MASQUERADE 处理。而 MASQUERADE 的处理方式是将包的源地址替换成 host 的地址发送出去,即做了一次网络地址转换(NAT)。

SNAT即源地址转换,能够让多个内网用户通过一个外网地址上网,解决了IP资源匮乏的问题。一个无线路由器也就使用此技术。

在外网IP地址不稳定的情况下,即可使用MASQUERADE(动态伪装),能够自动的寻找外网地址并改为当前正确的外网IP地址。

 

4.开启一个终端运行,监控docker0网卡:

tcpdump -i docker0 -n icmp

 

5.在另一个终端:进容器ping百度docker run -it busybox

ping www.baidu.com

tcpdump -i docker0 -n icmp的结果,发现容器的IP地址交给了MASQUERADE处理

 

6.结束上一个终端,监控物理机网卡

tcpdump -i eth0 -n icmp

发现已经将容器的地址进行转换了,成了物理机的IP地址。172.19.219.91

 

 

可以看到容器内去ping外部世界,经过iptables规则转换后,变成了宿主机去访问外部世界。至于网络源地址转换(NAT)的中间地址103.235.46.39,可以自己手动设置,不设置的话,容器会自动的选择。

借用用一张图来说明这个过程:

 

 

(4)从外部访问“容器”内暴露的服务

配置iptables的DNAT规则

当外部通过宿主机的IP和端口访问容器内启动的服务时,在数据包进入PREROUTING阶段就要进行目的地址转换,将宿主机IP转换为容器IP。同样,系统会为我们自动增加一个隐式的反向规则,数据包在离开宿主机时自动做反向转换。

sudo iptables -t nat -A PREROUTING ! -i br0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.18.0.2:80

上面命令的含义是:在nat表的PREROUTING链增加规则,当输入设备不是br0,目的端口为80时,做目的地址转换,将宿主机IP替换为容器IP。

 

从远程访问“容器”内暴露的服务

在“容器”docker0内启动服务:

sudo ip netns exec docker0 nc -lp 80

在和宿主机同一个局域网的远程主机访问宿主机IP:80

telnet ip 80 后再容器所在的宿主机上可以看到同一个局域网的远程主机发送的信息。

确认可以访问到容器内启动的服务,原理同上。

 

实验环境恢复

删除虚拟网络设备

sudo ip link set br0 down

sudo brctl delbr br0

sudo ip link del veth1

sudo ip link del veth3

然后重启机器iptablers和Namesapce的配置在机器重启后被清除。

 

总结

在本文的虚拟化网络详解(1)docker网络单机模型中,我们在介绍了veth pairs、Linux bridge、iptables等概念后,亲自动手模拟出了docker bridge桥接网络模型,并实验了四种场景的网络互通。实际上docker network 就是使用了上述技术,帮我们创建和维护网络。通过动手实验,相信你对docker bridge网络理解的更加深入。

下一篇我将动手实验容器如何利用Overlay 网络以及路由模式进行跨主机通信,后续对k8s以及openshift网络都会一一实践,所谓万丈高楼平地起,docker作为k8s以及oc的底层实现,弄懂它是我们了解虚拟化网络的开始与基础,请大家持续关注公众号。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值