K8S 网络设计原则
- 每个 Pod 都拥有一个独立 IP 地址,Pod 内所有容器共享该 IP 地址。
- 集群内所有 Pod 都在一个直接连通的扁平网络中,可通过 IP 直接访问。
- 即:所有容器之间无需 NAT 就可以直接互相访问;所有 Node 和所有容器之间无需 NAT 就可以直接互相访问;容器自己看到的 IP 跟其他容器看到的一样。
K8S 网络规范
CNI 是由 CoreOS 提出的一个容器网络规范。已采纳规范的包括 Apache Mesos,Cloud Foundry,Kubernetes,Kurma 和 RKT。
CNI 规定了一个容器 Runtime 和网络插件之间的简单的契约。这个契约通过 JSON 的语法定义了 CNI 插件所需要提供的输入和输出。
CNI 插件提供两个功能:
- 一个用来将网络接口加入到指定网络。
- 另一个用来将其移除。
这两个接口分别在容器被创建和销毁的时候被调用。容器 Runtime 首先需要获得一个网络命名空间以及一个容器 ID,然后连同一些 CNI 配置参数传给网络驱动。
接着网络驱动会将该容器连接到网络并将分配的 IP 地址以 JSON 的格式返回给容器 Runtime。
K8S 网络插件要求
K8S 对网络插件的要求总的来讲主要有两个最基本的,分别是:
- 要能够为每一个 Node 上的 Pod 分配互相不冲突的 IP 地址。
- 要所有 Pod 之间能够互相访问。
K8S 网络实现方案
K8S网络实现方案有如下几种:
隧道方案
隧道方案在 IaaS 层的网络中应用也比较多,将 Pod 分布在一个大二层的网络规模下。网络拓扑简单,但随着节点规模的增长复杂度会提升。
代表方案:
- **Weave:**UDP 广播,本机建立新的 BR,通过 PCAP 互通。
- **Open vSwitch:**基于 VxLan 和 GRE 协议,但是性能方面损失比较严重。
- **Flannel:**UDP 广播,VxLan。
- **Racher:**IPsec。
路由方案
路由方案一般是从 3 层或者 2 层实现隔离和跨主机容器互通的,出了问题也很容易排查。
代表方案:
- **Calico:**基于 BGP 协议的路由方案,支持很细致的 ACL 控制,对混合云亲和度比较高。
- **Macvlan:**从逻辑和 Kernel 层来看隔离性和性能优的方案,基于二层隔离,所以需要二层路由器支持,大多数云服务商不支持,所以混合云上比较难以实现。
K8S Pod 的网络创建流程
K8S Pod 的网络创建流程如下:
- 每个 Pod 除了创建时指定的容器外,都有一个 Kubelet 启动时指定的基础容器。
- Kubelet 创建基础容器,生成 Network Namespace。
- Kubelet 调用网络 CNIdriver,根据配置调用具体的 CNI 插件。
- CNI 插件给基础容器配置网络。
- Pod 中其他的容器共享基础容器的网络。
Flannel 网络
Flannel 简介
Flannel 是 CoreOS 团队针对 Kubernetes 设计的一个网络规划服务,简单来说,它的功能是让集群中的不同节点主机创建的 Docker 容器都具有全集群唯一的虚拟 IP 地址。
在默认的 Docker 配置中,每个节点上的 Docker 服务会分别负责所在节点容器的 IP 分配。这样导致的一个问题是,不同节点上容器可能获得相同的 IP 地址。
Flannel 的设计目的就是为集群中的所有节点重新规划 IP 地址的使用规则,从而使得不同节点上的容器能够获得“同属一个内网”且“不重复的”IP 地址,并让属于不同节点上的容器能够直接通过内网 IP 通信。
Flannel 实质上是一种“覆盖网络(overlay network)”,也就是将 TCP 数据包装在另一种网络包里面进行路由转发和通信。
目前已经支持 UDP、Vxlan、Host-gw、Aws-vpc、Gce 和 Alloc 路由等数据转发方式,默认的节点间数据通信方式是 UDP 转发。
**适用场景:**不需要隔离 Pod,集群规模小。
**设计思想:**为每一个 Node 节点分配 IP 网段,使 Node 之间 IP 不重复,Pod 之间直接使用 IP 访问。
**设计优势:**网络模型简单,安装配置相对容易,成熟度高,适合大多数用例的环境。
Flannel 对网络要求提出的解决办法
互相不冲突的 IP:
- Flannel 利用 Kubernetes API 或者 etcd 用于存储整个集群的网络配置,根据配置记录集群使用的网段。
- Flannel 在每个主机中运行 Flanneld 作为 Agent,它会为所在主机从集群的网络地址空间中,获取一个小的网段 Subnet,本主机内所有容器的 IP 地址都将从中分配。
在 Flannel Network 中,每个 Pod 都会被分配唯一的 IP 地址,且每个 K8S Node 的 Subnet 各不重叠,没有交集。
Pod 之间互相访问:
- Flanneld 将本主机获取的 Subnet 以及用于主机间通信的 Public IP 通过 etcd 存储起来,需要时发送给相应模块。
- Flannel 利用各种 Backend 机制,例如 UDP,Vxlan 等等,跨主机转发容器间的网络流量,完成容器间的跨主机通信。
Flannel 架构原理
Flannel 架构图:
各个组件的解释如下:
**Cni0:**网桥设备,每创建一个 Pod 都会创建一对 Veth Pair。其中一端是 Pod 中的 eth0,另一端是 cni0 网桥中的端口(网卡)。
Pod 中从网卡 eth0 发出的流量都会发送到 cni0 网桥设备的端口(网卡)上。
**注:**cni0 设备获得的 IP 地址是该节点分配到的网段的第一个地址。
**Flannel.1:**Overlay 网络的设备,用来进行 Vxlan 报文的处理(封包和解包)。不同 Node 之间的 Pod 数据流量都从 Overlay 设备以隧道的形式发送到对端。
**Flanneld:**Flannel 在每个主机中运行 Flanneld 作为 Agent,它会为所在主机从集群的网络地址空间中,获取一个小的网段 Subnet,本主机内所有容器的 IP 地址都将从中分配。
同时 Flanneld 监听 K8S 集群数据库,为 Flannel.1 设备提供封装数据时必要的 Mac,IP 等网络数据信息。
不同 Node 上的 Pod 的通信流程:
- Pod 中产生数据,根据 Pod 的路由信息,将数据发送到 cni0。
- cni0 根据节点的路由表,将数据发送到隧道设备 Flannel.1。
- Flannel.1 查看数据包的目的 IP,从 Flanneld 获得对端隧道设备的必要信息,封装数据包。
- Flannel.1 将数据包发送到对端设备。节点的网卡接收到数据包,发现数据包为 Overlay 数据包,解开外层封装,并发送内层封装到 Flannel.1 设备。
- Flannel.1 设备查看数据包,根据路由表匹配,将数据发送给 cni0 设备。
- cni0 匹配路由表,发送数据给网桥上对应的端口。