Docker 提供了 overlay driver,使用户可以创建基于 VxLAN 的 overlay 网络。VxLAN 可将二层数据封装到 UDP 进行传输,VxLAN 提供与 VLAN 相同的以太网二层服务,但是拥有更强的扩展性和灵活性。linux下是使用了net namespace来隔离docker创建的overlay网络。
Docker 网络模型如下:
Sandbox
一个Sandbox包含了一个容器网络栈的配置。其中包括了对容器的网卡,路由表以及对DNS设置的管理。通常,一个Sandbox的实现可以是一个Linux Network Namespace,一个FreeBSD Jail或者其他类似的东西。一个Sandbox可以包含多个处于不同Network的Endpoint。
Endpoint
Endpoint将一个Sandbox加入一个Network。Endpoint的实现可以是一个veth对,一个Open vSwitch internal port或者其他类似的东西。一个Endpoint只能属于一个Network和一个Sandbox。
Network
Network是一个能够互相通信的Endpoint的集合。Network的实现可以是一个Linux网桥,一个VLAN等等。
Docker swarm中的服务发现:
docker-dns.png每一个docker容器都有一个域名解析器,用来把域名查询请求转发到docker engine;docker engine
内部dns的服务器收到请求后就会在发出请求的容器所在的所有网络中检查域名对应的是不是一个容器或者是服务,如果是,docker引擎就会从存储的key-value建值对中查找这个容器名、任务名、或者服务名对应的IP地址,并把这个IP地址或者是服务的虚拟IP地址返回给发起请求的域名解析器。如果目的容器或服务和源容器不在同一个网络里面,Docker引擎会把这个DNS查询转发到配置的默认DNS服务。
上图展示了task1.client请求两个不同资源dns返回的不同结果
Docker swarm 中的LB分为两种情况:
1. Ingress Load Balancing
2. Internal Load Balancing
简要介绍测试环境下docker swarm中的网络分布情况:
环境:
swarm-a(manager node):10.10.8.92
swarm-b(work node):10.10.8.93
swarm-c(work node):10.10.8.94
在docker swarm集群创建的开始,docker 会给每台host创建除了docker0以外的两个网络,分是bridge类型(docker_gwbridge网桥)和overlay类型(ingress)的网络,我们可以使用如下命令自建overlay网络,结果如下:
docker network create --driver overlay mynet (后续会有用到)
- ingress网络的IPAM( IP Address Management)分配如下:
- mynet自建的overlay会使用docker自动分配的IPAM:
- 建完的overlay网络的同时会在host本地创建对应的Net Namespace如下:
注意1:要是想看到容器创建的两个Net Namespace需要执行
ln -s /var/run/docker/netns /var/run/netns
1. Ingress Load Balancing
1)、部署一个service使用默认的ingress网络:
docker service create --name web_ingress_lb --replicas=2 --publish 8090:80 httpd
- 部署的两个容器分别处在a和b节点上:
- service:web_ingress_lb的网络连接结构图如下:
Swarm mode下,docker会创建一个默认的overlay网络—ingress network。Docker也会为每个worker节点创建一个特殊的net namespace(sandbox)-- ingress_sbox。ingress_sbox有两个endpoint,一个用于连接ingress network,另一个用于连接local bridge network docker_gwbridge。Ingress network的IP空间为10.255.0.0/16,所有router mesh的service都共用此空间。
2)、Ingress Load Balancing实现方式:
1>宿主机网络通过worker节点IP和service published port来访问服务。比如:上面服务定义-p 8090:80,可以通过workerIP:8090 访问服务
ingress-2.png2>每个节点iptables中NAT表定义规则,对于匹配published的宿主机端口(8090)的数据,将其dst IP转换成ingress_sbox中的ip:172.18.0.2。使数据包转发到ingress_sbox的ns中交给该ns处理做下一步的转发。
ingress-3.png3>Ingress_sbox是swarm为每个节点默认创建的net namespace,用于连接ingress overlay network。此处会设置mangle表,将dst port为8090的数据做标记(fwmark)。同时做DNAT转换成vip地址使数据包能正常转发到ingress的ns中,该vip由ingress_sbox的ipvs做负载转发。
ingress-4.png4>Ingress_sbox会设置kernel中的LVS模块,将标记fwmark的包LB到各个实际IP中,默认round-robin算法,forware为VS/NAT方式。容器底层间通过overlay网络互连通信。
- 数据包在这一步进入ingress的ns后怎么实现到后端真实容器上呢?我们猜想下ingress想要转发就需
要有各个节点容器对应的ingress veth pair网卡的mac地址才能做转发是吧,好的那我们来看下ingress的ns空间中的fdb(linux bridge forward db)信息。
- 查看b节点上web_ingress_lb.1容器的mac地址信息
这样一来即使容器的副本没有落到host上我们仍可以通过这种转发方式来访问到服务。这应该就是routing mesh吧!
ingress-7.png5>Service的各个容器会将dst port为8080的数据的dst port转换成80,从而访问到真实的服务。
可以看到一个请求到主机端口8090之后, 数据包的流向如下所示:
主机端口8090 => Ingress-sbox-VIP:8090 => 容器Ingress-sbox => IPVS分发到containers。
大家可以看到访问主机之后数据包流到了一个特殊的Sandbox容器里, 这个容器和我们的容器共享一个Ingress网络,通过Iptables和IPVS等重定向到了最终容器之上。 达到了服务在任何一台主机的8090端口都可达的目的。
2. Internal Load Balancing
1)、部署一个service使用我们自己创建的mynet网络:
docker service create --name web_mynet --replicas=2 --network=mynet --publish 8080:80 httpd
部署的两个容器分别处在a和c节点上:
mynet-1.png
--publish #--在这里的用意是将容器内部的服务暴露到host上这样我们就可以访问这个services,一般情况下我们在swarm中部署service后容器中的网络只有一张网卡使用的是docker0网络,当我们将服务发布出去后,swarm会做如下操作:
- 给容器添加三块网卡eth0和eth1,eth2,eth0连接overlay类型网络名为ingress用于在不同主机间通信,eth1连接bridge类网络名为docker_gwbridge,用于让容器能访问外网。eth2连接到我们自己创建的mynet网络
上,同样的作用也是用于容器之间的访问(区别于eth2网络存在dns解析即服务发现功能)。 - swarm各节点会利用ingress overlay网络负载均衡将服务发布到集群之外。
结合例子如下:
2)、查看web_mynet.1容器和mynet网络命名空间的网卡情况:
mynet-2.png$docker exec web_mynet.1.kammwchnoeend86w3e5pho88i ip add
上面的命令可以查看a节点上的容器的网络有四张网卡eth0和eth1,eth2和lo,eth2网卡可以看出其
对应的veth pair为mynet网络中的veth0,eth1的网卡比较容易找到在host上对应的veth pair
$ip netns exec 1-j6s2r8ahdh ip add
查看mynet网络命名空间下的网卡情况。
$ip netns exec 1-j6s2r8ahdh brctl show
查看mynet网络空间下网桥挂载情况可以看出veth0挂到了br0网桥上。
3)、查看web_mynet.1容器和ingress\ingress_sbox网络命名空间的网卡对应情况:
mynet-3.png- 获取mynet和ingress网络的vxlan-id:
## 执行如下命令查看mynet空间下vxlan0网卡所带的vlan-id:
$ip netns exec 1-j6s2r8ahdh ip -d l show vxlan0
可以看mynet网络下vlan-id 为4097,ingress网络空间同样操作可以得到vlan-id为4096
mynet-4.pngswarm-c节点上的情况也是差不多就不操作了,好了我们来画下网络连接的大致图:
image.png
可以看到这里ingress_sbox和创建容器的ns共用一个ingress网络空间。
4)、 Internal Load Balancing实现方式:
有两种实现方式dns rr和vip形式,在dns rr 的情况下可能会存在一定是的问题,当容器重启后
dns的解析会存在一定时间的延迟。vip则是由vip+内核ipv来实现。docker swarm默认使用的是vip,
这里就以vip的形式来解析。(想要了解dns rr的可以看文章后面的参考链接都是大牛写的)
VIP形式下的流量路径:
swarm-vip.jpg1> 同处于网络mynet中的容器可以通过service域名或者VIP来访问service;通过域名访问时,容
器会访问docker engine中内置的DNS服务,从而获取VIP。
2> CNM网络模型中一个容器对应一个sandbox,也即容器的net namespace。我们查web_mynet.1
容器的sandbox中iptables的mangle表的配置情况:mangle表中OUTPUT链,将destIP==VIP的包标
记fwmark。
操作流程如下:
通过busybox服务做dns解析,可以发现该服务后端挂载的容器和该服务对应的
VIP地址。web_mynet服务对应的VIP为10.0.0.6。
mynet-5.png
- 进入web_mynet.1容器的ns:
$docker inspect container_id/container_name | grep -i sandbox
$nsenter --net=SandboxKey(/var/run/docker/netns/xxxx) sh
mynet-6.png
mynet-7.png3>web_mynet.1的 Sandbox中会设置kernel中的LVS模块,将标记fwmark的包LB到各个实际IP中,默认round-robin算法,VS/NAT方式。容器底层间通过overlay网络互连通信。
在web_mynet.1的ns中执行如下获取LB信息:
$ipvsadm -L
- 简单的来说就是在web_mynet.1容器中定义好了web_mynet服务的vip数据包的标签和LB,然后数据
包通过容器本地路由从eth2接口出去,进入到mynet的ns中:
-
带有具体目容器的MAC数据包进入mynet的ns后,由mynet网络中的fdb来进行转发:
mynet-9.png
总结下:
在Internal Load Balancing也就是文中我们自建的mynet overlay网络中,我们会看到创
建的service会同时应用到两个网络环境里面去,为何要这样呢?
原因是swarm自带ingress不具备有服务发现的功能,而容器的生命周期又是不固定的,
service每次的消亡和启用都会改变容器内部的ip地址以及vip地址,那么集群中服务之间
的通信势必会造成问题,这里有人会说,要使多个service之间能够互相通信可以将所有
的service都publish出去,然后通过routing mesh 访问,这样是没错也能行得通,但是存
在一个缺点,那就是不安全,我们仅仅只需要的是将最终提供服务的端口publish即可。
那么不publish所有的service需要做到以下几点:
-
让service通过简单的方法访问其他service
-
当service副本的ip发生变化时,不会影响访问该service的其他service
-
当service的副本数发生变化时,不会影响访问该service的其他service
这其实就是服务发现,docker swarm提供了这些功能将LB和服务发现集成在一起,通过
服务发现service的使用者不需要知道service运行在哪里,ip是多少有多少个副本,就能
实现集群内service与service的通信以及LB。
这里我理解的是ingress是单单提供LB实现routing mesh而mynet是服务发现和LB的结合
所以上文中Internal Load Balancing中的数据流应该分成两种情景如下:
1、当一个外部请求到主机端口8080之后, 数据包的流向如下所示:
主机端口8080 => Ingress-sbox-VIP:8080 => 容器Ingress-sbox => IPVS分发到containers。
2、处于 同mynet网络的service内部通信时:
处于 同mynet网络的test service(busybox容器)发起访问web_mynet域名的请求=>请求转发到docker engine内置的DNS解析web_mynet的vip=>进入mynet的ns根据目的地址为vip,进行路由到web_mynet的ns=>web_mynet将VIP数据包打上标签,并通过ipvs来负载到后端对应的容器=>带有具体容器MAC信息的数据包进入mynet的ns,由mynet中的fdb来做转发走tunnel出去。
文章中存在不足之处希望路过的大牛多踩踩^^!
参考:
https://zhuanlan.zhihu.com/p/25954203
https://www.jianshu.com/p/4433f4c70cf0
网友评论