kubernetes 网络

Docker 网络

Docker 网络是通过 Bridge 来实现的,每个容器都会创建一个虚拟网卡对(Veth Pair),一个插在容器里,一个插在 docker0 这个网桥上(交换机),这样容器之间就可以互相通信了。

Docker 跨主机通信是通过 VXLAN 来实现的,Virtual Extensible LAN,每个宿主机会创建一个虚拟网络设备 VTEP (VXLAN Tunnel End Point),这个网络设备可以将 docker0 的 IP 包进行二次封装(加上 VTEP 的 MAC 地址),完全由 Linux 内核进行封装和解封装,不需要经过用户态。docker0 和 VTEP 都是基于 MAC 地址进行转发的,属于交换机。

Kubernetes 网络

由于 Docker 跨主机网络需要做复杂的地址转换,所以 k8s 提出了一个自己的网络模型 ”IP-per-pod“,能够很好的适应集群的网络需求。它有如下四点基本要求:

  • 集群里的每个 Pod 都会有唯一的一个 IP 地址。

  • Pod 里的所有容器共享这个 IP 地址。

  • 集群里的所有 Pod 都属于同一个网段。

  • Pod 直接可以基于 IP 地址直接访问另一个 Pod,不需要做麻烦的网络地址转换(NAT)。

网络模型图:

因为 Pod 都具有独立的 IP 地址,相当于一台虚拟机,而且直连互通,也就可以很容易地实施域名解析、负载均衡、服务发现等工作,以前的运维经验都能够直接使用,对应用的管理和迁移都非常友好。

CNI

CNI 就是实现上述网络模型的标准:Container Networking Interface。

依据实现技术的不同,CNI 插件可以大致上分成“Overlay”“Route”和“Underlay”三种。

  • Overlay 的原意是“覆盖”,是指它构建了一个工作在真实底层网络之上的“逻辑网络”,把原始的 Pod 网络数据封包,再通过下层网络发送出去,到了目的地再拆包。因为这个特点,它对底层网络的要求低,适应性强,缺点就是有额外的传输成本,性能较低。

  • Route 也是在底层网络之上工作,但它没有封包和拆包,而是使用系统内置的路由功能来实现 Pod 跨主机通信。它的好处是性能高,不过对底层网络的依赖性比较强,如果底层不支持就没办法工作了。

  • Underlay 就是直接用底层网络来实现 CNI,也就是说 Pod 和宿主机都在一个网络里,Pod 和宿主机是平等的。它对底层的硬件和网络的依赖性是最强的,因而不够灵活,但性能最高。

选型

Flannel( https://github.com/flannel-io/flannel/ )是最早的一种 Overlay 模式的网络插件,使用 UDP 和 VXLAN 技术。Flannel 简单易用,是 Kubernetes 里最流行的 CNI 插件,但它在性能方面表现不是太好,所以一般不建议在生产环境里使用。

Calico( https://github.com/projectcalico/calico )是一种 Route 模式的网络插件,使用 BGP 协议(Border Gateway Protocol)来维护路由信息,性能要比 Flannel 好,而且支持多种网络策略,具备数据加密、安全隔离、流量整形等功能。

Cilium( https://github.com/cilium/cilium )是一个比较新的网络插件,同时支持 Overlay 模式和 Route 模式,它的特点是深度使用了 Linux eBPF 技术,在内核层次操作网络数据,所以性能很高,可以灵活实现各种功能。在 2021 年它加入了 CNCF,成为了孵化项目,是非常有前途的 CNI 插件。

CNI 插件工作方式

Fannel 插件工作方式如图,这也是 Docker 跨主通信的方式:

Calico 插件的工作方式:

可以在 Calico 的网站( https://www.tigera.io/project-calico/ )上找到它的安装方式,我选择的是“本地自助安装(Self-managed on-premises)”,可以直接下载 YAML 文件:

wget https://projectcalico.docs.tigera.io/manifests/calico.yaml

Calico 的安装非常简单,只需要用 kubectl apply 就可以(记得安装之前最好把 Flannel 删除):

kubectl apply -f calico.yaml

安装之后查看一下 Calico 的运行状态,它是在 kube-system 的 Namespace 中。

kubectl get pod -n kube-system

我们创建三个 Nginx 来进行实验:

kubectl create deploy ngx-dep --image=nginx:alpine --replicas=3

我们看到了它们的 IP 地址,分别是 10.10.171.* ,10.10.219.*

然后我们来看看 Pod 里的网卡情况,你会发现虽然还是有虚拟网卡,但宿主机上的网卡名字变成了 calica17a7ab6ab@if4,而且并没有连接到“cni0”网桥上:

这是 Calico 的工作模式导致的正常现象。因为 Calico 不是 Overlay 模式,而是 Route 模式,所以它就没有用 Flannel 那一套,而是在宿主机上创建路由规则,让数据包不经过网桥直接“跳”到目标网卡去。

来看一下节点上的路由表就能明白:

假设 Pod A“10.10.219.67”要访问 Pod B“10.10.219.68”,那么查路由表,知道要走“cali051dd144e34”这个设备,而它恰好就在 Pod B 里,所以数据就会直接进 Pod B 的网卡,省去了网桥的中间步骤。

Calico 的网络架构示意图,你可以再对比 Flannel 来学习:

至于在 Calico 里跨主机通信是如何路由的,你完全可以对照着路由表,一步步地“跳”到目标 Pod 去(提示:tunl0 设备)。

tunl0 设备待更新 ~~

小结:

  • Kubernetes 使用的是“IP-per-pod”网络模型,每个 Pod 都会有唯一的 IP 地址,所以简单易管理。

  • CNI 是 Kubernetes 定义的网络插件接口标准,按照实现方式可以分成“Overlay”“Route”和“Underlay”三种,常见的 CNI 插件有 Flannel、Calico 和 Cilium。

  • Flannel 支持 Overlay 模式,它使用了 cni0 网桥和 flannel.1 设备,本机通信直接走 cni0,跨主机通信会把原始数据包封装成 VXLAN 包再走宿主机网卡发送,有性能损失。

  • Calico 支持 Route 模式,它不使用 cni0 网桥,而是创建路由规则,把数据包直接发送到目标网卡,所以性能高。

Kubernetes 三层网络方案(重点)

Flannel host-gw

Flannel 有两种模式: VXLANhost-gw. VXLAN 在 Docker 面试题那里讲过, 这里讲 Flannel 的 host-gw 模式和 Calico 项目.

我们先来看一下 Flannelhost-gw 模式, 它的工作原理比较简单, 用一张图片就可以说清楚:

假设现在, Node1 上的 Infra-container-1 要访问 Node2 上的 Infra-container-2. 当你设置 Flannel 使用 host-gw 模式之后, Flannel 会在宿主机上创建这样一条规则, 以 Node1 为例子:

$ ip route
...
10.244.1.0/24 via 10.168.0.3 dev eth0

这条规则的含义是: 目的 IP 地址属于 10.244.1.0/24 网段的 IP 包, 应该经由本机的 eth0 设备发出, 下一跳地址是 10.168.0.3. 从 host-gw 示意图我们可以看出, 这个下一跳地址对应的正是我们的目的宿主机 Node2.

那么接下来, 当 IP 包从 网络层->数据链路层二次封装为数据链路层的帧 之后, eth0 设备就会使用 **下一跳地址对应的 MAC 地址, 作为该数据帧的目的 MAC 地址. 显然, 这个 MAC 地址, 正是 Node2 的 MAC 地址.

也就是说, 容器发出的 IP 包进入了宿主机之后, 又被封装成了帧(数据链路层), 然后使用 MAC 地址来将这个帧发送到下一跳.

Flannel 子网和主机的信息, 都是保存在 ETCD 中的. Flannel 只需要 Watch 这些数据的变化, 然后实时更新路由表即可.

通过上面的叙述, 可以看出, host-gw 模式能够正常工作和核心, 就在于 IP 包在封装成帧发送出去的时候, 会使用路由表里面的下一跳来设计 MAC 地址. 这样 IP 包就会经过二层网络到达目的宿主机. 所以说, Flannel host-gw 模式必须要求集群宿主机之间是二层连通的.

通过二层网络转发的性能损耗大约在 10% 左右.

当然, 宿主机之间二层不连通的情况也是广泛存在的. 比如, 宿主机分布在了不同子网(VLAN)里. 这样宿主机之间就需要通过 IP 地址来进行通信, 而不是 MAC 地址, 也就是说是通过三层网络转发 IP 包.

Calico

BGP

Calico 项目就是使用最广泛的三层网络方案, 它和 Flannel host-gw 模式几乎是一样的, 路由规则也是:

<目的容器 IP 地址段> via <网关的 IP 地址> dev eth0

其中, 网关的 IP 地址, 正是目的容器所在的宿主机的 IP 地址. 而正如前所述, 这个三层网络方案得以正常工作的核心, 是为每个容器的 IP 地址, 找到它对应的下一跳的网关. Flannel host-gw 与 Calico 的最大区别就是在如何找到下一跳网关这个问题上.

  • Flannel host-gw: 通过 ETCD 来维护子网和主机信息, Flannel 只需监听资源变化
  • Calico: 通过 BGP(Border Gateway Protocol) 即边界网关协议

BGP 是 Linux 内核原生支持的, 用在维护大规模数据系统中, 维护不同 自治系统 之间的路由信息的, 无中心的路由协议.

在这个图中, 我们有两个自治系统(Autonomous System, AS): AS1 和 AS2. 如果这两个自治系统内的主机要通过 IP 地址来进行通信, 我们就必须使用路由器将这两个自治系统连接起来(不在一个子网下, 如果在同一个子网那么直接使用网关即可). 比如, AS1 里面的主机 10.10.0.2 要访问 AS2 里面的主机 172.17.0.3, 它发出的 IP 包就会先到达 AS1 上的路由器 Router1. 此时, Router1 的路由表里面有这样一条规则: 目的地址是 127.17.0.2 的包, 应该经过 Router1 的 C 接口, 发往 Router2.

像上面这样负责把自治系统连接在一起的路由器, 我们称之为 边界网关.

BGP 的优点就在于扩展性好, 可以加入一些业务策略, 但是运维复杂.

BGP不使用传统的内部网关协议(IGP)的指标, 而是基于路径, 网络策略或规则集来决定路由. 因此, 它更适合被称为矢量性协议, 而不是路由协议, 通俗的说就是将接入到机房的多条线路(如电信、联通、移动等)融合为一体, 实现多线单IP.

接下来, 我们回到 Calico 项目上来. Calico 项目架构由三个部分组成:

  • Calico 的 CNI 插件. 详解见 CNI 插件笔记.
  • Felix. 它是一个 DaemonSet, 负责在宿主机上插入路由规则.
  • BIRD. 它就是 BGP 的客户端, 专门负责在集群里分发路由规则信息.

除了对路由信息的维护方式之外, Calico 项目于 Flannel host-gw 模式的另一个不同之处, 就是它不会在宿主机上创建任何网桥设备. 这时候, Calico 的工作方式, 可以用一幅示意图来形容, 其中绿色实线标出的路径, 就是一个 IP 包从 Node1 上的 Container1, 到达 Node2 上的 Container4 的完整路径. calixxxxxxxx 只是 Veth Pair 设备, 唯一作用就是插入到 Router 接口上, 然后插入到容器中, 每个容器都有一个.

BGP 协议传输的消息, 可以简单理解为: 我是宿主机 192.168.1.3, 10.233.2.0/24 网段的容器都在我这里, 这些容器的下一条地址是我(MAC 地址/IP 地址).

不过需要注意的是, Calico 维护的网络在默认配置下, 每台宿主机上的 BGP Client 都需要跟其他节点交换路由信息(Node-To-Node Mesh), 随着节点数量 N 的增加, 这些连接的数量就会以 N^2 的规模快速增长, 从而给集群本身的网络带来巨大的压力. 所以在大规模集群中, 需要用到 Router Reflector 模式.

Router Reflecter 模式下, Calico 会指定一个或者几个专门的节点, 来负责跟所有节点建立 BGP 连接从而学习到全局的路由规则. 而其他节点, 只需要跟这几个专门的节点交换路由信息, 就可以获得整个集群的路由规则信息了.

Calico IPIP 模式

如果集群宿主机之间二层不连通(不在一个子网下), 需要打开 IPIP 模式.

使用 IP 隧道 (IP tunnel) 设备, 路由规则变成:

10.233.2.0/24 via 192.168.2.2 tunl0

IPIP 封包方式: 将原来 Node1-container1 发送到 Node2-container4 的 IP 包封装起来, 作为新 IP 包的 payload(负载). 新 IP 包的 Header 目的地址就是 Node2 的地址. 也就是说将容器之间的 IP 包封装起来, 加宿主机之间的 IP Header.

Linux支持的五种ip隧道,可以通过ip tunnel help查看.

  • ipip: 即IPv4 in IPv4, 在IPv4报文的基础上再封装一个IPv4报文.
  • gre: 即通用路由封装(Generic Routing Encapsulation), 定义了在任意一种网络层协议上封装其他任意一种网络层协议的机制,IPv4和IPv6都适用.
  • sit: 和ipip类似. 不同的是sit是用IPv4报文封装IPv6报文, 即IPv6 over IPv4.
  • isatap: 即站内自动隧道寻址协议(Intra-Site Automatic Tunnel Addressing Protocol), 和sit类似, 也是用于IPv6的隧道封装.
  • vti: 即虚拟隧道接口(Virtual Tunnel Interface), 是cisco提出的一种IPsec隧道技术.

待更新~

posted @ 2023-03-03 11:10  kohn  阅读(46)  评论(0)    收藏  举报