关于《Kubernetes 混合云用Kilo解决NAT节点通讯的问题》的那件事

  1. 1. pre-post()
  2. 2. subject(‘实现目标’)
  3. 3. subject(‘Kilo介绍’)
  4. 4. subject(‘现有问题’)
  5. 5. subject(‘解决问题’)
    1. 5.1. 端口通讯检查
    2. 5.2. 安装 Kilo
    3. 5.3. 配置外部连接IP
    4. 5.4. 关于N2N实现
  6. 6. subject(‘不通过VPN外部直连的可能性’)

pre-post()

最近有在学习K8S相关,同时也将自己的所有服务全部都迁移到了K8S集群上,感受到K8S强大的同时也能明显感受到对于我这种一般用户环境的部署不是很友好,例如建议的高可用集群至少要有 三台 Master, 三台 Worker, 对我这种 穷逼 普通玩家来说是很高昂的,但经过几次实践把生产环境搞炸几次后觉得这是有必要的(哭),但我的想法是用K8S作为我主要部署方式,所以还是非常高昂的,像我这种为了能够管理所有节点但是又不会重度使用K8S的,这里推荐 K3S 来代替K8S作为要求不高的生产环境,不过这篇文章还是用完整的K8S来做,但理论上 K3S 是完全通用的。

subject(‘实现目标’)

利用 Kilo 将带有公网IP的NAT云服务器组成K8S集群,支持 P2P, DDNS (要求有公网IP且需要端口映射)

subject(‘Kilo介绍’)

Kilo 是一个通过 Wireguard 用于建立混合云网络的工具

subject(‘现有问题’)

我的服务器构成是这样的(以下IP皆为虚拟):

Name ifc-IP Location Role Nat
k8s-master 123.123.123.123 Hetzner Master NO
cn-sh01-node 10.0.0.4 QCloud Worker YES
cn-hz01-node 192.168.1.120 Home Worker YES

可以看到我的三台服务器都不是同一个网段甚至都不是同一个服务商的。
所以会有几个问题

  • kube-proxy 无法正常工作转发流量
  • metrics 采集无法工作
  • logs/shell 无法工作

subject(‘解决问题’)

之前是用 Weave 来作为CNI的,然后为了解决 kube-proxy 的一些问题换成了 Flannel, 网上查找一些资料和issues以后发现如果要解决这个问题,就得先将所有节点连起来,连起来的方法就是VPN,然后让通讯流量走VPN接口即可解决,但是按常规理解VPN流量是需要中心服务器转发流量的,那就会导致所有流量转发到同一台服务器,压力和延迟也会非常大不符合需求,然后我去搜了P2P VPN发现 Wireguard 是支持Peer2Peer的,顺势在某个issue里看到有人提到用 Kilo 进行自动组网,并且 Kilo 是支持在 Flannel 之上运行的

端口通讯检查

我是采用 Flannel+Calico+Kilo 的方式设置网络的,所以需要以下端口放通

Port Range Protocol Remark
8285 UDP Flannel
8472 UDP Flannel
51820 UDP Wireguard 默认端口
10250 TCP Kubelet API
30000-32767 TCP+UDP NodePort 服务端口

安装 Kilo

Kubeadm

1
$ kubectl apply -f https://raw.githubusercontent.com/squat/kilo/master/manifests/kilo-kubeadm-flannel.yaml

如果要卸载,直接 kubectl delete 就好了

配置外部连接IP

由于目前尚未支持 NAT to NAT(理论可以实现),所以每个Node都必须具备外部(公网)访问条件,但是你会发现一件事,每个Node都只会拿到网卡的IP,它没办法发现你的外部IP

所以就像 Flannel 通过 flannel.alpha.coreos.com/public-ip-overwrite 来覆写外部通讯IP一样 Kilo 同样也提供了 kilo.squat.ai/force-endpoint 来指定外部通讯连接点,格式是 "IP:PORT" 或者 "[DOMAIN]:PORT",是的,它支持域名,所以就可以实现我们 HOME worker的DDNS需求,只要防火墙放通端口或者路由器转发端口就可以自由通讯了

编辑 cn-hz01-node 节点,在 metadata.annotations 里加入 kilo.squat.ai/force-endpoint: '[home.mydomain.com]:51820',然后过一会通过 kubectl desc node cn-hz01-node 就可以看到它自动解析了域名并且添加了一个新的 kilo.squat.ai/endpoint annotation,值为你域名指向的IP

同样此方法去更改 cn-sh01-node 的 annotations,过一会就可以看到 metrics 信息已经正常显示了(前提是你已经部署了metrics采集)

建议采用 Flannelvxlan 作为后端,不采用 IPSec 等加密后端避免不必要的二次开销

关于N2N实现

目前 Kilo 的N2N实现还在讨论并且已经在计划适配,具体可以在这里看到 https://github.com/squat/kilo/issues/109

subject(‘不通过VPN外部直连的可能性’)

说个题外话,假如全部NODE都是有公网IP但是会有一层NAT的能不能正常通讯呢,这个读过一点Kubernetes的代码,这个是有可能的,就是更改Node的 status.addresses 添加一个 TypeExternalIP 的IP地址,但是这里有个问题就是,你没办法直接编辑或者patch一个node的status值,那么这个addresses是怎么来的呢?

addresses 实际上是通过 cloud-provider 设置的,他读取你的网卡并将其IP设置为 InternalIP 的address, 如果你是GCE, Azure等,他们会去跟平台通讯获取你机器的外网绑定网卡信息,并且设置为 ExternalIP,而 metrics-server 的默认启动参数 --kubelet-preferred-address-types=ExternalIP,InternalIP,Hostname 定义了它会尝试去和外部IP、内部IP、主机名进行通讯,所以如果你是通过云服务商的k8s托管,那么它就会自动设置外部IP,但是手工设置是行不通的,所以如果自己写一个 “Fake” cloud-provider 的话也许也行得通,但其实价值就很低了,不如直接VPN组网来的实在。