网络通信 频道

云原生二十篇|Docker网络篇

  本文介绍Docker的网络,包括网桥,Overlay等。

  第一部分:Docker网络

  Docker网络需要处理容器之间,容器与外部网络和VLAN之间的连接,设置之初相对复杂,随着容器化的发展,Docker网络架构采用容器网络模型方案(CNM),支持拔插式的驱动方式来提供网络拓扑。

  1、详解(1)CNM

  Docker的网络架构设计规范是CNM,CNM规定了基本组成要素:

  沙盒:是一种独立的网络栈,包括以太网接口,端口,路由以及DNS配置

  终端(EP):虚拟网络接口,负责创建连接,将沙盒连接到网络

  网络:网桥的软件实现

  (2)Libnetwork

  Libnetwork是CNM的标准实现,支持跨平台,3个标准的组件和服务发现,基于Ingress的容器负载均衡,以及网络控制层和管理层的功能。

  (3)网络模式网桥(Bridge):Docker默认的容器网络驱动,容器通过一对veth pair连接到docker0网桥上,由Docker为容器动态分配IP及配置路由、防火墙规则等,具体详解可以查看第二部分;

  Host:容器与主机共享同一Network Namespace,共享同一套网络协议栈、路由表及iptables规则等,执行docker run --net=host centos:7 python -m SimpleHTTPServer 8081,然后查看看网络情况(netstat -tunpl) :

  可以看出host模型下,和主机上启动一个端口没有差别,也不会做端口映射,所以不能启动的服务在主机端口范围内不能冲突;

  Overlay:多机覆盖网络是Docker原生的跨主机多子网网络方案,主要通过使用Linux bridge和vxlan隧道实现,底层通过类似于etcd或consul的KV存储系统实现多机的信息同步,具体详解可以看第二部分;

  Remote:Docker网络插件的实现,可以借助Libnetwork实现网络自己的网络插件;

  None:模式是最简单的网络模式,它会使得Docker容器完全隔离,无法访问外部网络。在None模式下,容器不会被分配IP地址,也无法与其他容器和主机通信,可以尝试执行docker run --net=none centos:7 python -m SimpleHTTPServer 8081,然后curl xxx.com应该是无法访问的。

  第二部分:网桥和Overlay详解

  Docker中最常用的两种网络是网桥和Overlay,网桥是解决主机内多容器通讯,Overlay是解决跨主机多子网网络,下面我们来详细了解一下这两种网络模式。

  1、网桥(Bridge)

  网桥是什么?同tap/tun、veth-pair一样,网桥是一种虚拟网络设备,所以具备虚拟网络设备的所有特性,比如可以配置IP、MAC等,除此之外,网桥还是一个二层交换机,具有交换机所有的功能。

  (1)创建Docker daemon启动时会在主机创建一个Linux网桥(默认为docker0),容器启动时,Docker会创建一对veth-pair(虚拟网络接口)设备,veth设备的特点是成对存在,从一端进入的数据会同时出现在另一端,Docker会将一端挂载到docker0网桥上,另一端放入容器的Network Namespace内,从而实现容器与主机通信的目的。

  (2)查看网桥执行docker network ls,输出:

  (3)查看网桥的详细信息先执行docker run -d --name busybox-1 busybox echo "1"和docker run -d --name busybox-2 busybox echo "2",然后执行docker inspect bridge,可以看到输出网桥IPv4Address,MacAddress和EndpointID等:

  (4)探测网桥是否正常可以进入busybox-2容器,执行ping 172.17.0.2,输出(可见是可以通的):

  (5)端口映射基于上面我们已经了解容器与容器之间的通讯,那么Docker端口映射是如何通讯的呢?先执行 docker run -d -p 8000:8000 centos:7 python -m SimpleHTTPServer 建立映射关系,然后查看 iptables,执行iptables -t nat -nvL:

  可以看出只要是非docker0进来的数据包(如eth0进来的数据),都是8000直接转到172.17.0.5:8000,可以看出这里是借助iptables实现的。

  (6)网桥模式下的Docker网络流程

  容器与容器之前通讯是通过Network Namespace, bridge和veth pair这三个虚拟设备实现一个简单的二层网络,不同的namespace实现了不同容器的网络隔离让他们分别有自己的ip,通过veth pair连接到docker0网桥上实现了容器间和宿主机的互通;

  容器与外部或者主机通过端口映射通讯是借助iptables,通过路由转发到docker0,容器通过查询CAM表,或者UDP广播获得指定目标地址的MAC地址,最后将数据包通过指定目标地址的连接在docker0上的veth pair设备,发送到容器内部的eth0网卡上;

  容器与外部或者主机通过端口映射通讯对应的限制是相同的端口不能在主机下重复映射;

  2、Overlay

  在云原生下集群通讯是必须的,当然Docker提供多种方式,包括借助Macvlan接入VLAN网络,另一种是Overlay。那什么是Overlay呢?指的就是在物理网络层上再搭建一层网络,通过某种技术再构建一张相同的逻辑网络。

  (1)原理

  VXLAN

  在讲原理之前先了解一下VXLAN网络,什么VXLAN网络?VXLAN全称是Visual eXtensible Local Area Network,本质上是一种隧道封装技术,它使用封装/解封装技术,将L2的以太网帧(Ethernet frames)封装成L4的UDP数据报(datagrams),然后在L3的网络中传输,效果就像L2的以太网帧在一个广播域中传输一样,实际上是跨越了L3网络,但却感知不到L3网络的存在。那么容器B发送请求给容器A(ping)的具体流程是怎样的?

  VXLAN

  1.容器B执行ping,流量通过BridgeA的veth接口发送出去,但是这个时候BridgeB并不知道要发送到哪里(BridgeB没有MAC与容器A的IP映射表),所以BridgeB将通过VTEP解析ARP协议,确定MAC和IP以后,将真正的数据包转发给VTEP,带上VTEP的MAC地址

  2.VTEP-B收到数据包,通过Swarm的集群的网络信息中知道目标IP是容器B

  3.VTEP-B将数据包封装为VXLAN格式(数据包中存储了VXLAN的ID,记录其映射关系)

  4.实际底层VTEP-B将数据包通过主机B的UDP物理通道将VXLAN数据包封装为UDP发送出去

  5-6.通过隧道传输(UDP端口:4789),数据包到达VTEP-A,VTEP-A解析数据包读取其中的VXLAN的ID,确定发送到哪个网桥

  7.VTEP-A继续解包和封包,将数据从UDP中拆解出来,重新组装网络协议包,发送给BridgeA

  8.BridgeA收到数据,通过veth发给容器A,回包的过程就是反向处理

  (2)创建(Overlay)执行docker swarm init,然后创建test-net(docker network create --subnet=10.1.1.0/24 --subnet=11.1.1.0/24 -d overlay test-net),查看网络创建情况:

  发现最后一行test-net创建成功。然后创建一个sevice,replicas等于2来看看网络情况,执行(docker service create --name test --network test-net --replicas 2 centos:7 sleep infinity),由于有两台物理机器,可以看看网络和服务情况:

  (3)查看网络详情并测试创建成功后,可以查看一下网络详情,执行docker network inspect test-net,输出如下:

  可以看到两个网络地址(Containers的值)分别是10.1.1.7和11.1.1.12,然后登陆到第一台机器(10.1.1.7),执行ping 11.1.1.12 -c 1发现可以成功,那么继续在第二台(11.1.1.12)抓包看看输出:

  从上述的抓包可以看出test.2.5j5bm8m0g96enm3ltf7172rt4.test-ne往当前服务发送ICMP报文并成功响应。

  (4)验证VXLAN隧道传输数据为了第一节的原理:通过VXLAN隧道传输,于是抓包,先在10.1.1.12容器上启动python -m SimpleHTTPServer,然后在10.1.1.7上发送curl命令curl '11.1.1.12:8000',同时在10.1.1.12容器所在的主机2抓包udp端口4789,执行tcpdump -i any port 4789,输出如下:

  可以看出协议的确是从udp端口4789传输的,使用VXLAN。

  第三部分:服务发现和Ingress

  1、服务发现

  Docker支持自定义配置DNS服务发现,执行docker run -it --name test1 --dns=8.8.8.8 --dns-search=dockercerts.com alpine sh,输出:

  可以看出配置dns,实际是修改/etc/resolv.conf配置。

  2、Ingress

  对于集群,Docker Swarm提供类似K8S的Ingress模式,在Swarm集群内的任何宿主机节点都可以访问对应的容器服务,执行样例docker service create --name test --replicas 2 -p 5000:80 nginx,可以分别在Swarm集群的主机中看到对应的端口5000,如下:

  其底层是通过Sevice Mesh四层路由网络实现,原理和Docker本身端口映射类似,可以参考iptables -nvL端口查看,其中负载均衡的实现可以了解下图。

  参考

  (1)https://zhuanlan.zhihu.com/p/558785823

  (2)https://www.cnblogs.com/oscar2960/p/16536891.html

  (3)https://www.jianshu.com/p/e3a87c76aab4?utm_campaign=maleskine&utm_cnotallow=note&utm_medium=seo_notes&utm_source=recommendation

0
相关文章