avatar
@bangbang93

使kubernetes在calico网络插件下和docker swarm共存

10/14/2023, 1:13:00 PM

0x00 前景提要

云主机上跑的小服务越来越多了,而且有些是我很多年前写的,能稳定跑所以也一直没有更新,导致部分程序开始老化,在新版node或者系统环境下或多或少的会出现一些问题。所以之前一直在用的pm2大锅炖方案开始出现一些麻烦,有些新写的程序需要node18,但是有一些年代比较久远的程序,连node16都上不来(比如旧版webpack在node16上和新版openssl有些兼容问题)。最初的方案当然是docker一把梭。

但是我有两台云主机,一台是旧的机器,上面跑的是CentOS 7。另一台是新的机器,在CentOS出事后本来是计划用这台机器过渡,然后切换到Debian。不过后来渐渐的服务多了之后,一台4C8G的机器确实不够用,所以这台机器也就留下来了。

对于一些无状态服务,两台云主机还可以方便的实现HA。鉴于自行托管K8S集群还是稍显麻烦;云服务商的K8S方案每年有要几千块。最初的想法是直接用Docker Swarm了事,反正Swarm集群也可以做HA。所以一顿操作初始化了Docker Swarm集群。Swarm集群没有套traeflk,直接暴露端口然后由外部的nginx转发。

原本的计划是先把旧机器上所有服务全部迁移到新机器上,然后把它重装成Debian。整个迁移过程倒是没什么阻力,挨个服务打成docker镜像即可。

在迁移完成后,我突然觉得,手动管理端口和nginx实在是太烦了,好怀念k8s的ingress,只要一套配置文件,再也不用关心哪个服务什么端口了。考虑再三后,此时我决定还是手动托管K8S集群。

0x01 初始化集群

此时旧服务器上已经没有在运行的服务了,于是直接重装系统。
初始化集群因为已经有外部的rancher,所以直接使用外部的rancher初始化rke2集群,只需要一行命令粘贴即可。因为只有两台服务器,所以选择了单master部署的方案。毕竟云主机不是自托管的服务器,新建虚拟机不要钱。
初始化master也是一路顺风,没有遇到什么特别大的阻碍。之后先迁移了一部分无状态的应用进来。此时还没有加入第二台服务器。

0x02 问题开始

在完成迁移验证后,打算开始把第二台服务器加入集群。整个过程也很快,rancher提供的快速初始化还是一条命令直接粘贴。此时还没有问题,因为所有deployment都是在第二台节点加入前部署的,所以全部pod都在第一台机器上,还没有跨机器,所以我也没有注意此时网络初始化是否有问题。
随后我收到了一条报警,我的zabbix说连不上agent了。然后我突然想起旧服务器上还有一个tinc服务,是我所有服务器间tinc内网的核心节点,托管了所有节点的配置文件,它down了确实会导致部分节点互相找不见了。还好之前为了嫌配置文件同步麻烦,将所有配置文件扔上了nfs在两台服务器间同步。但是有些其他服务器,可能只写了旧服务器的IP,没有写新服务器的IP,所以不会自动切换。但是旧服务器的私钥只能靠备份镜像找回了。于是用备份镜像新建一台主机,把私钥掏回来,恢复tinc内网。
然后我就把所有无状态服务全部非常欢快的迁移进了ingress。
此时我突然发现,节点间互访似乎出现了些问题,如果在旧服务器上访问部署在自己身上的pod,没有任何问题,如果是部署在新服务器上的pod,则不通,ingress返回502。查看ingress的错误日志,是connect timedout。我还以为是新节点有些不干净的网络配置,毕竟这不是干净的全新机器,上面不但有Docker Swarm,甚至可能还有一些自定义的iptables规则。此时重启了一下新服务器,问题依旧。

在简单了解了一下calico的工作原理后,查看了calico-node的日志,发现一直刷Failed to configure VXLAN tunnel device, retrying... error=can't locate created vxlan device vxlan.calico。带着报错Google,只找到了一个https://github.com/projectcalico/calico/issues/3271有点关系,但是我的服务器上压根没有安装NetworkManager,所以和这个issue也毫无关系。

由于以前也没有遇到过类似的问题,而且是报错和网卡相关,是找不到网卡。于是首先想到的就是先对比一下两边的网络配置,然后发现新机器上缺少calico的vxlan.calico网卡。对比了一下相同配置的其他集群,所有节点应该都有这张网卡。这大概就是网络不通的原因了。随后以calico not create vxlan.calico为关键字一顿Google,发现了https://github.com/projectcalico/calico/issues/6920这个issue。这个issue的核心是因为calico检测宿主机ip的默认方法是取找到的第一张非本地回环的网卡(同时会排除docker创建的br)。而我的两台节点由于tinc的存在,确实会比其他集群的节点凭空多一张网卡。于是查看calico-node容器的日志,确实发现dst_node_ip:"192.168.93.2",这个ip是我的tinc网卡的IP,而不是服务器的IP。但是按照issue给出的方法修改时遇到了问题,修改DaemonSet的环境变量后再查看,还是修改前的first-found,修改并没有生效,但是确实触发了pod的重新部署,也就是说在我修改成功后,有人又把它改回来了。那这里应该就是rke2的问题了。于是继续Google。但是有效的issue只找到https://github.com/rancher/rke2/issues/2324,有这个issue那就说明rke2应该是支持修改这个参数的,但是rke2的官方文档里并没有说明如何自定义托管集群。

0x03 峰回路转

在进行若干小时的Google一无所获后,没招了,只好先遍历一下calico的文档,看看它还有没有提供其他修改方法。
于是我终于在https://docs.tigera.io/calico/latest/networking/ipam/ip-autodetection找到了

kind: Installation
apiVersion: operator.tigera.io/v1
metadata:
  name: default
spec:
  calicoNetwork:
    nodeAddressAutodetectionV4:
      interface: eth.*

看了一下集群里,确实有这么一条记录,并且确实是

   nodeAddressAutodetectionV4:
      firstFound: true

于是修改了对应的配置,终于calico-node日志里输出的dst_node_ip是我两台机器的内网ip了。

0x04 问题解决,了吗?

虽然ip对了,但是仔细一看,故障依旧,新节点仍在在刷Failed to configure VXLAN tunnel device, retrying... error=can't locate created vxlan device vxlan.calico,而且vxlan.calico网卡依旧没有创建出来,ingress还是扔502。
内心一凉,还得继续修。但是目前已经没有任何方向了,该搜的报错也都搜过了,或者说报错其实压根没变,也想不出什么新的关键字可以搜索了。
在经过一段时间关键字的随机尝试后,甚至去翻找了calico的源码依旧一无所获。干脆心一横,想着自己创一张网卡,看看它什么反应。于是sudo ip link add vxlan.calico type vxlan id 100走起。终于有反应,calico-node输出了一行[WARNING][60] felix/vxlan_mgr.go 670: "vxlan.calico" exists with incompatible configuration: vni: 4096 vs 100; recreating device,看上去是vxlan id不对,应该是4096,随后改成4096重新创建,又来了一行[WARNING][60] felix/vxlan_mgr.go 670: "vxlan.calico" exists with incompatible configuration: port: 4789 vs 8472; recreating device,那就是端口,最终得到命令ip link add vxlan.calico type vxlan id 4096 dstport 4789。此时又得到一个错误Error: A VXLAN device with the specified VNI already exists.但是用ip link show type vxlan并没有找到任何vxlan的interface。

此时又有一个新的怀疑对象浮出水面,Docker Swarm的overlay网络本质上是个vxlan,是不是它的vxlan id恰好是4096?

0x05 真相大白

既然有怀疑对象了,那就看看,于是执行docker network inspect ingress,发现有一行配置

        "Options": {
            "com.docker.network.driver.overlay.vxlanid_list": "4096"
        },

那就是他了,看来是在之前创建docker swarm网络时,这个节点上并没有部署k8s,所以docker swarm的默认vxlanid也是4096,于是跟calico冲突了。
事已至此,只好先把这个网络删掉。于是在迁移所有依赖的服务之后,删除了这个network。网络刚一删除,vxlan.calico网卡就被自动创建出来了,试了一下网络,集群之间互相通信就正常了。
然后再用docker network create --driver overlay --ingress --subnet=10.0.0.0/24 --gateway=10.0.0.2 --opt com.docker.network.driver.overlay.vxlanid_list=4000 ingress重新把网络创建回来,并且指定vxlanid为4000,docker swarm网络也恢复了。