梦越 發表於 2019-9-9 15:52:00

Kubernetes网络之Flannel工作原理

<p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>1、Docker网络模式<ul><li>1.1 bridge网络的构建过程</li><li>1.2 外部访问</li></ul></li><li>2、Kubernetes网络模式<ul><li>2.1 同一个Pod中容器之间的通信</li><li>2.2 不同Pod中容器之间的通信</li><li>2.3 Flannel在K8S中运行的整体过程<ul><li>1)设置集群网络</li><li>2)设置Node节点上的子网</li><li>3)在每个Node上启动flanneld</li><li>4)创建虚拟网卡</li><li>5)创建Docker网桥</li><li>6)修改路由表</li></ul></li><li>2.4 数据传递过程<ul><li>1)源容器向目标容器发送数据,数据首先发送给docker0网桥</li><li>2)docker0网桥接受到数据后,将其转交给flannel.1虚拟网卡处理</li><li>3)flannel.1接受到数据后,对数据进行封装,并发给宿主机的eth0</li><li>4)对在flannel路由节点封装后的数据,进行再封装后,转发给目标容器Node的eth0</li><li>5)目标容器宿主机的eth0接收到数据后,对数据包进行拆封,并转发给flannel.1虚拟网卡;</li><li>6)flannel.1 虚拟网卡接受到数据,将数据发送给docker0网桥;</li><li>7)最后,数据到达目标容器,完成容器之间的数据通信。</li></ul></li></ul></li></ul></div><p></p>
<h2 id="1docker网络模式">1、Docker网络模式</h2>
<blockquote>
<p>在讨论Kubernetes网络之前,让我们先来看一下Docker网络。Docker采用插件化的网络模式,默认提供bridge、host、none、overlay、maclan和Network plugins这几种网络模式,运行容器时可以通过–network参数设置具体使用那一种模式。</p>
</blockquote>
<ul>
<li>
<p>bridge:这是Docker默认的网络驱动,此模式会为每一个容器分配Network Namespace和设置IP等,并将容器连接到一个虚拟网桥上。如果未指定网络驱动,这默认使用此驱动。</p>
</li>
<li>
<p>host:此网络驱动直接使用宿主机的网络。</p>
</li>
<li>
<p>none:此驱动不构造网络环境。采用了none 网络驱动,那么就只能使用loopback网络设备,容器只能使用127.0.0.1的本机网络。</p>
</li>
<li>
<p>overlay:此网络驱动可以使多个Docker daemons连接在一起,并能够使用swarm服务之间进行通讯。也可以使用overlay网络进行swarm服务和容器之间、容器之间进行通讯,</p>
</li>
<li>
<p>macvlan:此网络允许为容器指定一个MAC地址,允许容器作为网络中的物理设备,这样Docker daemon就可以通过MAC地址进行访问的路由。对于希望直接连接网络网络的遗留应用,这种网络驱动有时可能是最好的选择。</p>
</li>
<li>
<p>Network plugins:可以安装和使用第三方的网络插件。可以在Docker Store或第三方供应商处获取这些插件。</p>
</li>
</ul>
<p>在默认情况,Docker使用bridge网络模式,bridge网络驱动的示意图如下,此文以bridge模式对Docker的网络进行说明。<br>
<img src="http://image.ssgeek.com/20190909-01.png" alt="" loading="lazy"></p>
<h3 id="11-bridge网络的构建过程">1.1 bridge网络的构建过程</h3>
<p>1)安装Docker时,创建一个名为docke0的虚拟网桥,虚拟网桥使用“10.0.0.0 -10.255.255.255 “、”172.16.0.0-172.31.255.255″和“192.168.0.0——192.168.255.255”这三个私有网络的地址范围。<br>
通过 ifconfig 命令可以查看docker0网桥的信息:<br>
<img src="http://image.ssgeek.com/20190909-02.png" alt="" loading="lazy"><br>
通过 docker network inspect bridge 可以查看网桥的子网网络范围和网关:<br>
<img src="http://image.ssgeek.com/20190909-03.png" alt="" loading="lazy"><br>
2)运行容器时,在宿主机上创建虚拟网卡veth pair设备,veth pair设备是成对出现的,从而组成一个数据通道,数据从一个设备进入,就会从另一个设备出来。将veth pair设备的一端放在新创建的容器中,命名为eth0;另一端放在宿主机的docker0中,以veth为前缀的名字命名。通过 brctl show 命令查看放在docker0中的veth pair设备<br>
<img src="http://image.ssgeek.com/20190909-04.png" alt="" loading="lazy"></p>
<h3 id="12-外部访问">1.2 外部访问</h3>
<p>bridge的docker0是虚拟出来的网桥,因此无法被外部的网络访问。因此需要在运行容器时通过-p和-P参数对将容器的端口映射到宿主机的端口。实际上Docker是采用 NAT的 方式,将容器内部的服务监听端口与宿主机的某一个端口port 进行绑定,使得宿主机外部可以将网络报文发送至容器。<br>
1)通过-P参数,将容器的端口映射到宿主机的随机端口:</p>
<pre><code>$ docker run -P {images}
</code></pre>
<p>2)通过-p参数,将容器的端口映射到宿主机的制定端口:</p>
<pre><code>$ docker run -p {hostPort}:{containerPort} {images}
</code></pre>
<h2 id="2kubernetes网络模式">2、Kubernetes网络模式</h2>
<p>Kubernetes与Docker网络有些不同。Kubernetes网络需要解决下面的4个问题:</p>
<ul>
<li>集群内:</li>
<li>容器与容器之间的通信</li>
<li>Pod和Pod之间的通信</li>
<li>Pod和服务之间的通信</li>
<li>集群外:</li>
<li>外部应用与服务之间的通信</li>
</ul>
<p>因此,Kubernetes假设Pod之间能够进行通讯,这些Pod可能部署在不同的宿主机上。每一个Pod都拥有自己的IP地址,因此能够将Pod看作为物理主机或者虚拟机,从而能实现端口设置、命名、服务发现、负载均衡、应用配置和迁移。为了满足上述需求,则需要通过集群网络来实现。</p>
<p>在本文主要分析容器与容器之间,以及Pod和Pod之间的通信;Pod和服务之间,以及外部应用与服务之间的通信请参考《Kubernetes-核心资源之Service》和《Kubernetes-核心资源之Ingress》。</p>
<h3 id="21-同一个pod中容器之间的通信">2.1 同一个Pod中容器之间的通信</h3>
<p>这种场景对于Kubernetes来说没有任何问题,根据Kubernetes的架构设计。Kubernetes创建Pod时,首先会创建一个pause容器,为Pod指派一个唯一的IP地址。然后,以pause的网络命名空间为基础,创建同一个Pod内的其它容器(–net=container:xxx)。因此,同一个Pod内的所有容器就会共享同一个网络命名空间,在同一个Pod之间的容器可以直接使用localhost进行通信。</p>
<h3 id="22-不同pod中容器之间的通信">2.2 不同Pod中容器之间的通信</h3>
<p>对于此场景,情况现对比较复杂一些,这就需要解决Pod间的通信问题。在Kubernetes通过flannel、calic等网络插件解决Pod间的通信问题。本文以flannel为例说明在Kubernetes中网络模型,flannel是kubernetes默认提供网络插件。Flannel是由CoreOS团队开发社交的网络工具,CoreOS团队采用L3 Overlay模式设计flannel, 规定宿主机下各个Pod属于同一个子网,不同宿主机下的Pod属于不同的子网。</p>
<p>flannel会在每一个宿主机上运行名为flanneld代理,其负责为宿主机预先分配一个子网,并为Pod分配IP地址。Flannel使用Kubernetes或etcd来存储网络配置、分配的子网和主机公共IP等信息。数据包则通过VXLAN、UDP或host-gw这些类型的后端机制进行转发。<br>
<img src="http://image.ssgeek.com/20190909-05.png" alt="" loading="lazy"></p>
<h3 id="23-flannel在k8s中运行的整体过程">2.3 Flannel在K8S中运行的整体过程</h3>
<h4 id="1设置集群网络">1)设置集群网络</h4>
<p>flannel默认使用etcd作为配置和协调中心,首先使用etcd设置集群的整体网络。通过如下的命令能够查询网络配置信息:</p>
<pre><code>$ etcdctl ls /coreos.com/network/config
</code></pre>
<h4 id="2设置node节点上的子网">2)设置Node节点上的子网</h4>
<p>基于在etcd中设置的网络,flannel为每一个Node分配IP子网。<br>
获取子网列表</p>
<pre><code>$ etcdctl ls /coreos.com/network/subnets
</code></pre>
<p>获取子网信息</p>
<pre><code>$ etcdctl ls /coreos.com/network/subnets/{IP网段}
</code></pre>
<h4 id="3在每个node上启动flanneld">3)在每个Node上启动flanneld</h4>
<p>flannel在每个Node上启动了一个flanneld的服务,在flanneld启动后,将从etcd中读取配置信息,并请求获取子网的租约。所有Node上的flanneld都依赖etcd cluster来做集中配置服务,etcd保证了所有node上flanned所看到的配置是一致的。同时每个node上的flanned监听etcd上的数据变化,实时感知集群中node的变化。flanneld一旦获取子网租约、配置后端后,会将一些信息写入/run/flannel/subnet.env文件。</p>
<pre><code>$ cat /var/run/flannel/subnet.env
</code></pre>
<p><img src="http://image.ssgeek.com/20190909-06.png" alt="" loading="lazy"></p>
<h4 id="4创建虚拟网卡">4)创建虚拟网卡</h4>
<p>在Node节点上,会创建一个名为flannel.1的虚拟网卡。</p>
<pre><code>$ ip addr show flannel.1
</code></pre>
<p><img src="http://image.ssgeek.com/20190909-07.png" alt="" loading="lazy"></p>
<h4 id="5创建docker网桥">5)创建Docker网桥</h4>
<p>并为容器配置名为docker0的网桥,实际是通过修改Docker的启动参数–bip来实现的。通过这种方式,为每个节点的Docker0网桥设置在整个集群范围内唯一的网段,从保证创建出来的Pod的IP地址是唯一。</p>
<pre><code>$ ip addr show docker0
</code></pre>
<p><img src="http://image.ssgeek.com/20190909-08.png" alt="" loading="lazy"></p>
<h4 id="6修改路由表">6)修改路由表</h4>
<p>flannel会对路由表进行修改,从而能够实现容器跨主机的通信。</p>
<pre><code>$ route -n
</code></pre>
<p><img src="http://image.ssgeek.com/20190909-09.png" alt="" loading="lazy"></p>
<h3 id="24-数据传递过程">2.4 数据传递过程</h3>
<p>在源容器宿主机中的数据传递过程:</p>
<h4 id="1源容器向目标容器发送数据数据首先发送给docker0网桥">1)源容器向目标容器发送数据,数据首先发送给docker0网桥</h4>
<p>在源容器内容查看路由信息:</p>
<pre><code>$ kubectl exec -it -p {Podid} -c {ContainerId} -- ip route
</code></pre>
<h4 id="2docker0网桥接受到数据后将其转交给flannel1虚拟网卡处理">2)docker0网桥接受到数据后,将其转交给flannel.1虚拟网卡处理</h4>
<p>docker0收到数据包后,docker0的内核栈处理程序会读取这个数据包的目标地址,根据目标地址将数据包发送给下一个路由节点:<br>
查看源容器所在Node的路由信息:</p>
<pre><code>$ ip route
</code></pre>
<h4 id="3flannel1接受到数据后对数据进行封装并发给宿主机的eth0">3)flannel.1接受到数据后,对数据进行封装,并发给宿主机的eth0</h4>
<p>flannel.1收到数据后,flannelid会将数据包封装成二层以太包。<br>
Ethernet Header的信息:</p>
<ul>
<li>From:</li>
<li>To:</li>
</ul>
<h4 id="4对在flannel路由节点封装后的数据进行再封装后转发给目标容器node的eth0">4)对在flannel路由节点封装后的数据,进行再封装后,转发给目标容器Node的eth0</h4>
<p>由于目前的数据包只是vxlan tunnel上的数据包,因此还不能在物理网络上进行传输。因此,需要将上述数据包再次进行封装,才能源容器节点传输到目标容器节点,这项工作在由linux内核来完成。<br>
Ethernet Header的信息:</p>
<ul>
<li>From:</li>
<li>To:</li>
</ul>
<p>IP Header的信息:</p>
<ul>
<li>From:</li>
<li>To:</li>
</ul>
<p>通过此次封装,就可以通过物理网络发送数据包。<br>
在目标容器宿主机中的数据传递过程:</p>
<h4 id="5目标容器宿主机的eth0接收到数据后对数据包进行拆封并转发给flannel1虚拟网卡">5)目标容器宿主机的eth0接收到数据后,对数据包进行拆封,并转发给flannel.1虚拟网卡;</h4>
<h4 id="6flannel1-虚拟网卡接受到数据将数据发送给docker0网桥">6)flannel.1 虚拟网卡接受到数据,将数据发送给docker0网桥;</h4>
<h4 id="7最后数据到达目标容器完成容器之间的数据通信">7)最后,数据到达目标容器,完成容器之间的数据通信。</h4>
<p>文章来源:<br>
公众号(k8s技术栈)http://1t.click/aesU</p><br><br>
来源:https://www.cnblogs.com/ssgeek/p/11492150.html
頁: [1]
查看完整版本: Kubernetes网络之Flannel工作原理