WireGuard 系列文章(八):基于 WireGuard 的 K8S CNI Kilo 简介

本文最后更新于:2024年7月24日 晚上

系列文章前情提要:

  1. WireGuard 系列文章(一):什么是 VPN
  2. WireGuard 系列文章(二):WireGuard 简介 - 快速、现代、安全的 VPN 隧道
  3. WireGuard 系列文章(三):WireGuard 安装
  4. WireGuard 系列文章(四):WireGuard 快速上手
  5. WireGuard 系列文章(五):Netmaker 简介 - 创建和管理 WireGuard 网络的平台
  6. WireGuard 系列文章(六):Netmaker 安装
  7. WireGuard 系列文章(七):使用 WireGuard 和 Netmaker 创建 Full Mesh 网络

接下来介绍 WireGuard 和 Kubernetes 的整合 – 一个基于 WireGuard 的 K8S 网络插件 – Kilo。

Kilo 是一个建立在 WireGuard 上的 多云 overlay 网络,专为 Kubernetes 设计。

Kilo 概述

Kilo 通过提供一个加密的 三层网络 来连接集群中的节点,该 网络可以跨越数据中心和公共云。由 Kilo 创建的 Pod 网络总是完全连接的,即使节点在不同的网络中或在 NAT 后。通过允许不同位置的节点池安全地通信,Kilo 可以实现多云集群的操作。Kilo 的设计允许客户端通过 VPN 连接到集群,以便安全地访问集群上运行的服务。除了创建多云集群(即一个集群跨越多朵公有云),Kilo 还支持创建多集群服务,即跨不同 Kubernetes 集群的服务。

Kilo 工作原理

Kilo 使用 WireGuard 在集群中的不同节点之间创建一个网格(mesh)。

Kilo agent(kg) 运行在集群中的每个节点上,为 VPN 设置公钥和私钥,以及在位置之间路由数据包所需的规则。

Kilo 既可以作为一个完整的、独立的 K8S 网络插件,也可以作为目前安装在集群上的集群网络解决方案的补充。这意味着,如果集群使用 Flannel 进行网络连接,那么可以将 Kilo 安装在集群顶部,以使位于不同位置(location,如不同的云)的节点池能够连接;Kilo 负责各 location 之间的网络,Flannel 负责各 location 内的网络。

Kilo Annotions

Kilo 安装在 K8S 集群后,通过 K8S Node 的 Annotations(注释) 来实现相应的配置。

以下注释可以添加到任何 Kubernetes Node 对象,以配置 Kilo 网络。

Name type examples
kilo.squat.ai/force-endpoint host:port 55.55.55.55:51820, example.com:1337
kilo.squat.ai/force-internal-ip CIDR 55.55.55.55/32, "-",""
kilo.squat.ai/leader string "", true
kilo.squat.ai/location string gcp-east, lab
kilo.squat.ai/persistent-keepalive uint 10
kilo.squat.ai/allowed-location-ips CIDR 66.66.66.66/32

force-endpoint

为了在位置(location)之间创建链接,Kilo 要求每个位置至少有一个节点有一个端点(就是公网地址 + 端口),即 host:port 组合,可以从其他位置路由。如果位置在不同的云提供商或不同的私有网络中,那么端点的主机部分应该是一个公开可访问的 IP 地址,或一个解析为公共 IP 的 DNS 名称,以便其他位置可以将包路由到它。运行在每个节点上的 Kilo 代理将使用启发式来自动检测节点的外部 IP 地址,并正确配置其端点;然而,在某些情况下,可能需要显式地配置端点以使用,例如:

  • 在网卡上没有自动的公共 IP: 在一些云提供商上,通常为节点分配一个公共 IP 地址,但为网卡只自动配置私有网络地址;在这种情况下,应该指定分配的公网 IP 地址;(国内公有云都是这种情况,所以在国内公有云上使用 Kilo,一定要配置 force-endpoint
  • 多个公网 IP 地址: 如果一个节点有多个公网 IP 地址,但有一个是首选 IP 地址,则指定首选 IP 地址;
  • IPv6: 如果一个节点有公共 IPv4 地址和 IPv6 地址,并且 Kilo 网络应该在 IPv6 上运行,那么应该指定 IPv6 地址;
  • 动态 IP 地址: 如果一个节点有一个动态分配的公网 IP 地址,例如一个从网络提供商租用的 IP,那么可以给出一个动态 DNS 名称。
  • 覆盖端口: 如果一个节点应该监听一个特定的端口,这个端口不同于网格默认的 WireGuard 端口(比如:52181),那么这个注释可以用来覆盖端口;这可能很有用,例如,可以确保在同一个端口转发 NAT 网关后运行的两个节点可以分别分配不同的端口。

force-internal-ip

Kilo 使用节点的内部 IP 地址将数据包发送到同一逻辑位置的节点。运行在每个节点上的 Kilo 代理将使用启发式来自动检测该节点的私有 IP 地址;然而,在某些情况下,可能需要显式地配置 IP 地址,例如:

  • 多个私有 IP 地址: 如果一个节点有多个私有 IP 地址,但有一个是首选 IP 地址,则需要指定首选 IP 地址(典型如一个私有 IP 用于业务,一个私有 IP 用于存储;或者是在本地虚拟机环境下,一个私有 IP 是 Host,一个私有 IP 是 NAT);
  • IPv6: 如果一个节点同时拥有私有 IPv4 和 IPv6 地址,且 Kilo 网络运行在 IPv6 上,则应指定 IPv6 地址。
  • 使用“-”或“”禁用私网 IP: 节点有私网地址和公网地址,但忽略该私网地址。

leader

默认情况下,Kilo 在数据中心粒度上创建一个网络网格。这意味着从每个位置选择一个领导节点作为边缘服务器,并作为到其他位置的网关;网络拓扑结构将是 leader 之间的 full mesh。Kilo 以稳定和确定的方式自动为每个位置选择 leader,以避免网络配置中的混乱,同时优先考虑已知拥有公共 IP 地址的节点。在某些情况下,手动选择一个位置的 leader 可能是可取的,例如:

  • 防火墙: Kilo 需要一个开放的 UDP 端口,默认为 51820,以在位置之间进行通信;如果只有一个节点被配置为开放该端口,那么该节点应该被给予 leader 注释;
  • 带宽: 如果集群中的某些节点有更高的带宽或更低的延迟 Internet 连接,那么这些节点应该被给予 leader 注释。

Note:

在一个位置内的多个节点可以被给予 leader 注释;在这种情况下,Kilo 将从一组注释节点中选择一个 leader。

location

Kilo 允许位于不同逻辑或物理位置的节点相互路由数据包。为了知道要创建什么连接,Kilo 需要知道每个位置上有哪些节点。Kilo 会试图从拓扑结构 topology.kubernetes.io/region label 中推断出每个节点的位置。如果节点没该有标签,例如运行裸金属集群或在不受支持的云提供商(或自己搭建的 K8S 集群)上,则应该指定位置注释。

Note:

所有没有定义位置的节点将被认为处于默认位置""

persistent-keepalive

在某些部署中,集群节点可能位于 NAT 或防火墙 后面,例如位于普通路由器后面的边缘节点。在这种情况下,NAT 转换后的节点可以向 NAT 转换后的网络之外的节点发送报文,但是外部的节点只有在 NAT 映射有效的情况下才能向 NAT 转换后的网络发送报文。为了让 NAT 后的节点接收到来自 NAT 后网络之外的节点的报文,它必须通过定期向这些节点发送报文(即发送 keepalive 报文)来维护 NAT 映射关系。这些 keepalive 报文的发送频率可以通过在 NAT 后的节点上设置 persistent-keepalive 注释来控制。被注释的节点将使用指定的值作为其所有对等体的 persistent-keepalive 间隔,请参阅 WireGuard 文档中的 NAT 和防火墙穿越

allowed-location-ips

通过注释位置中的任何节点,可以将 allowed-location-ips 添加到位置。在一个位置添加允许的位置 ip,使得这些 ip 也可以从其他位置路由。

在一个有两个位置 A 和 B 的 Kilo 部署示例中,可以从位置 B 的节点和 pod 访问位置 A 的打印机。此外,Kilo Peers 可以使用位置 A 的打印机。

附加模式

现有集群的管理员如果不希望改变现有的网络解决方案,可以以附加模式运行 Kilo。

在这种模式下,Kilo 将向集群添加高级特性,如 VPN 和多集群服务,同时将 CNI 管理和本地网络委托给集群的当前网络提供商。Kilo 目前支持在 flannel 上运行。

例如,要在运行 Flannel 的 Typhoon 集群上运行 Kilo:

1
2
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/crds.yaml
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/kilo-typhoon-flannel.yaml

VPN

Kilo 还允许 Kubernetes 集群外的对等体(peer)连接到该 VPN,允许集群应用程序安全地访问外部服务,并允许开发人员和支持人员安全地调试集群资源。

为了声明一个对等点,首先定义一个 Kilo peer 资源:

1
2
3
4
5
6
7
8
9
10
11
cat <<'EOF' | kubectl apply -f -
apiVersion: kilo.squat.ai/v1alpha1
kind: Peer
metadata:
name: squat
spec:
allowedIPs:
- 10.5.0.1/32
publicKey: GY5aT1N9dTR/nJnT1N2f4ClZWVj0jOAld0r8ysWLyjg=
persistentKeepalive: 10
EOF

这个配置可以应用到本地的 WireGuard 接口,例如 wg0,让它在kgctl 工具的帮助下访问集群:

1
2
kgctl showconf peer squat > peer.ini
sudo wg setconf wg0 peer.ini

Kilo 网络分析

可以使用 kgctl命令行工具分析 Kilo 网络的拓扑和配置。

例如,可以使用 graph 命令生成 Graphviz 格式的网络图:

1
kgctl graph | circo -Tsvg > cluster.svg

示例 Kilo 网络拓扑

Kilo 网络拓扑

Kilo 允许定制加密网络的拓扑结构。集群管理员可以指定加密的网络应该是每个节点之间的 full mesh,还是应该是直接相互通信的不同节点池之间的网格。这使得加密网络可以服务于几个目的,例如:

  • 在拥有不安全私有网络的云提供商上,可以在节点之间创建一个完整的网格,以保护所有集群流量;
  • 运行在不同云提供商中的节点可以通过在两个云之间创建一条链接而加入到单个集群中(这也是后面要实战的主要内容);
  • 更普遍的是,不安全的链接可以被加密,而安全的链接可以保持快速且未封装。

Logical Groups 逻辑组

默认情况下,Kilo 在集群中的不同逻辑位置之间创建一个网格,例如数据中心、云提供商等。Kilo 会利用 kubernetes 拓扑 topology.kubernetes.io/region label 推断节点的位置。此外,Kilo 支持通过设置命令行标志 --topology-label=<label> 来使用自定义拓扑标签。如果这个标签没有设置,那么可使用 kilo.squat.ai/location 标注。 例如,为了将谷歌 Cloud 和 AWS 中的节点连接到一个单独的集群中,管理员可以使用下面的代码片段在名称中对所有具有 GCP 的节点进行注释:

1
2
3
for node in $(kubectl get nodes | grep -i gcp | awk '{print $1}'); do 
kubectl annotate node $node kilo.squat.ai/location="gcp";
done

在这种情况下,Kilo 会这么做:

  • 将所有带有GCP annotion 的节点分组到一个逻辑位置;
  • 分组所有没有标注的节点将被分组到默认位置;和
  • 在每个 location 选出一个 leader,并在他们之间建立联系。

使用 kgctl 分析集群将产生如下结果:

kgctl graph | circo -Tsvg > cluster.svg

Kilo 网络拓扑模型 - Point-to-point

Full Mesh

创建一个 full mesh 是逻辑网格的逻辑简化,其中每个节点都在自己的组中。Kilo 以命令行标志的形式为这个拓扑提供了一个快捷方式:--mesh-granularity=full. 当指定了 full mesh 粒度时,Kilo 将网络配置为所有节点间的流量都使用 WireGuard 加密。

使用 kgctl 分析集群将产生如下结果:

kgctl graph | circo -Tsvg > cluster.svg

Kilo 网络拓扑模型 - FullMesh

混合

kilo.squat.ai/location 标注可以将一些 full mesh 节点和一些按逻辑位置分组的节点混合在一起。例如,如果一个集群包含一组节点在谷歌云计算和没有安全的私有网络的一组节点,例如一些裸露的金属节点,然后在谷歌云节点可以放置在一个逻辑组,而裸金属节点可以形成一个完整的网。 这可以通过运行以下命令来实现:

1
2
for node in $(kubectl get nodes | grep -i gcp | awk '{print $1}'); do kubectl annotate node $node kilo.squat.ai/location="gcp"; done
for node in $(kubectl get nodes | tail -n +2 | grep -v gcp | awk '{print $1}'); do kubectl annotate node $node kilo.squat.ai/location="$node"; done

使用 kgctl 分析集群将产生如下结果:

kgctl graph | circo -Tsvg > cluster.svg

Kilo 网络拓扑模型 - 混合网络 1

如果集群在 AWS 中也有节点,那么可以使用以下代码片段:

1
2
3
for node in $(kubectl get nodes | grep -i aws | awk '{print $1}'); do kubectl annotate node $node kilo.squat.ai/location="aws"; done
for node in $(kubectl get nodes | grep -i gcp | awk '{print $1}'); do kubectl annotate node $node kilo.squat.ai/location="gcp"; done
for node in $(kubectl get nodes | tail -n +2 | grep -v aws | grep -v gcp | awk '{print $1}'); do kubectl annotate node $node kilo.squat.ai/location="$node"; done

这反过来会生成如下图表:

kgctl graph | circo -Tsvg > cluster.svg

Kilo 网络拓扑模型 - 混合网络 2

总结

Kilo 是一个建立在 WireGuard 上的 多云 overlay 网络,专为 Kubernetes 设计。通过它,可以在多云上建立一个快速、现代、安全的 K8S 集群。🎉🎉🎉