使用 k3s 和 tailscale 在甲骨文云和家之间搭建 kubernetes 集群
最近一直在学习 kubernetes(k8s)相关的知识,为了实践学到的知识,选用了 k3s 作为实际部署集群的工具,已经为位于美国的四台虚拟机搭建了一个集群,这四台虚拟机中将一台 2 核 3G 的作为 server,满足了 k3s 的最低硬件要求,目前工作良好。
有了这次成功经验之后,想将位于日本和韩国的甲骨文虚拟机也组成一个集群。 这样子就能更加方便的部署应用,而不用每次手动记住自己的应用部署用 docker compose 在哪一台机器上运行着。经过几天的折腾,发现这几台免费送的虚拟机,不能作为 k3s server 来稳定运行,只要一运行 server,就连 journalctl -f
看日志都卡。通过 grafana 监控收集的数据分析后发现运行 server 后,io 读写就暴涨,怀疑是硬盘太差引起的。
尝试调整 vm.swappiness=10
降低将内存数据写入硬盘的倾向,过段时间 swap 直接满了,最后尝试彻底关闭 swap,依然没有明显的改善,在本机命令行执行 kubectl get node
都是半天才响应。
而作为对比,隔壁另一台相同配置的虚拟机,闲置的时候资源基本没什么波动,颇有一种打工人在摸鱼的感觉。
看来直接在甲骨文虚拟机上面建立带 server 的集群有点难为 1 核 1G 的性能,毕竟是免费送的服务器。
由于手上没有其他更高配置的虚拟机,灵光一闪,决定直接在家里 pve 里面搞一台虚拟机作为 k3s server,然后通过 tailscale 来连通所有的甲骨文机器。需要使用 tailscale 主要有两个原因:
- 家里没有公网 IP,所以甲骨文的虚拟机无法直接和它通信,所以借助万能的 tailscale 来组网,实现一个 k3s over tailscale 的效果。
- 甲骨文给的虚拟机,虽然有公网 ip,外部访问虚拟机 ip 能正确的获得响应,但这个公网 IP 不是直接给虚拟机的,所以虚拟机查看网卡的 ip 得到的是局域网的 ip,所以对于虚拟机来说,它也算没有公网。
tailscale 作为内网穿透利器,有很多免费的中继服务器。并且 k3s 对 tailscale 的支持也是开箱即用的,所以写篇博客记录下整个搭建过程。
一:配置家里的 k3 server
server 位于家中,作为集群的控制中心。通过 pve 的模版 快速创建了一个操作系统,我安装的是 ubuntu 22。
安装完系统之后,ssh 进入创建的虚拟机。然后执行下面的步骤:
-
安装 tailscale:
curl -fsSL https://tailscale.com/install.sh | sh
-
安装 k3s server:
curl -sfL https://get.k3s.io | sh -
-
获取 k3s token:
cat /var/lib/rancher/k3s/server/node-token
, 将结果作为 K3S_TOKEN 保存 -
创建 tailscale auth token,后面 agent 使用 tailscale 连接需要的授权信息,结果作为 TAILSCALE_TOKEN
-
配置 tailscale 的 nameserver(这个步骤似乎可以不做)
-
新增 k3s 配置文件
/etc/rancher/k3s/config.yaml
,里面的内容修改为:
你需要将 ${TAILSCALE_TOKEN}
替换为第四步获得的实际值。
- 修改完配置文件重启服务
sudo systemctl restart k3s
二:验证 k3s server 已经通过 tailscale 连接
接着我们需要确保安装的 k3s 服务正确的使用了 tailscale,这样才能让后面连上 tailscale vpn 的机器都能讲它作为 server。
通过 sudo kubectl get node
你应该能看到一个 master 节点已经就绪,类似:
我这台主机叫 k3s-jp-home-server,它就是当前虚拟机的 hostname
- 通过
sudo kubectl describe node k3s-jp-home-server
查看这个节点的细节信息,留意里面的 ip,我这里获取到的 IP 是 100.91.43.90:
-
获取当前 tailscale 的节点的 IP:
sudo tailscale status
找到这台刚刚注册的这台机器的 ip,不出意外的话这个 ip 就是上面截图的 ip。我们将这个 IP 记做TAILSCALE_IP
-
最后通过
curl -vk https://100.91.43.90:6443/cacerts
(替换成你自己的 ip),应该能看到 k3s server 生成的自签名证书。将https://100.91.43.90:6443
作为K3S_URL
到这里我们就拥有了一个在 tailscale vpn 下面可用的 k8s 主节点。
三:在甲骨文云配置 k3s agent
- 进入甲骨文云管理后台,新建一条规则,允许所有协议(相当于关闭了防火墙)
开放所有端口并不是一个好的安全实践,更好的方式应该是将 k3s 用到的所有端口 单独加入白名单,不过本着先有再优的原则,我们目前没这么做。
- ssh 登陆到虚拟机,删除 ubuntu iptable 加载器,升级 iptable 并重启
可选操作,目的是为了最大程度排除 iptable 各种设定可能导致的集群节点相互之间的通信失败,而且 k3s 对 iptable 的版本也有一定的要求。当然这个设定可能会降低系统的安全性,如果你是安全专家或者 iptables 大佬,可自行决定是否
3. 安装 tailscale: curl -fsSL https://tailscale.com/install.sh | sh
-
运行 tailscale:
tailscale up
这样才能连接到 家里的 server -
通过
tailscale status
确认状态正常,然后通过 curl 确认甲骨文能通过 tailscale 连接到家里的 server:curl -vk https://100.91.43.90:6443/cacerts
能看到自签名证书后,说明甲骨文能连接到家里的 server 了。
- 安装 k3s agent:
-
配置 k3s 使用 tailscale。和配置 server 一样,新增文件
/etc/rancher/k3s/config.yaml
,里面的内容修改为vpn-auth: "name=tailscale,joinKey=${TAILSCALE_TOKEN}"
-
重启 k3s agent 使得刚刚的配置生效:
sudo systemctl restart k3s-agent
四:验证 k3s agent 成功加入集群
回到家里的终端在通过 sudo kubectl get node
应该能看到刚刚加入的机器。
我这里多出来了一台 instance-20220922-2113
同时通过 sudo kubectl describe node instance-20220922-2113
查看加入的 agent 的 ip,应该是 tailscale 提供的 ip 范围(100.64.0.0/10)而不是甲骨文的内网 ip(比如 10.0.0.x)
按照上述方法把自己剩下的甲骨文加到这个集群后的效果:
到这里,我们就把所有的免费甲骨文加到集群里面去了。
五:验证 k3s 集群功能正常
部署一个最简单的 whoami 验证一下集群里面各台机器的联通性 (它的原始文件来自 这里,镜像从 nginx 换成了 whoami,方便查看 pod 信息)
新建一个 traefik.yaml
文件,内容如下:
这个配置大致的意思就是新建五个 pod,然后用名字是 nginx-service 的服务 连接这五个 pod,对外暴露一个 ip,最后通过 ingress 将 http 流量路由到这个 nginx-service 服务。新建五个是因为我有五个 node,所以他们大概率会分散到每台机器上。(k8s 如何将 pod 分配到 node 的策略有一堆配置可以学习呢)
之后 sudo kubectl apply -f traefik.yaml
执行部署。稍等片刻查看部署状态:sudo kubectl get pod -o wide
在 server 的机器上,curl 上面这几个 ip,看到下面的这些信息就表示集群里面的 whoami 部署成功了
能在 server 上直接 curl 这几个子网 ip,而不用在 pod 内部 curl 的原因是 tailscale 路由了子网,具体可以通过命令
ip route show table all
确认。里面有类似下面的就是正确的:这里的这几个 10.42.x.0/24, 就是我上面没个 node 的分配的子网
六:安装 k8s 图形化管理工具 Lens
始终使用命令行管理集群确实很酷,不过对于刚上手的我来说很多命令还是记不住,所以后面找到了一款管理 k8s 的神器: Lens。免费版的功能就够用了。我的主要工作环境是 Mac,下载 mac 版之后需要先将 k3s server 的配置文件复制到 mac 上面,具体步骤官网有:
将
/etc/rancher/k3s/k3s.yaml
复制到位于集群外部的主机上的~/.kube/config
。然后,将server
字段的值替换为你 K3s Server 的 IP 或名称。现在,你可以使用kubectl
来管理 K3s 集群。
七:最终的效果
最后我希望把这个服务发布到外网,然后测试下它的可用性。具体来说就是进入 cloudflare,为刚刚的 nginx 服务增加一条 dns A 记录,这个记录的 IP 可以是任何一个集群里面有公共 ip 的机器。当然你可以选择用一些自动化工具来自动根据 ingress 自动更新 cloudflare dns 记录,比如 external-dns 。
最后你就可以通过 nginx.tomyail.com 访问我部署的这个服务了。
简单的跑了一下 wrk,结果如下:
然后 uptime-kuma 加了一个 http 监控,从家里检测到的数据:
从美国集群的到监控数据
来一个全国测速图
总体来说马马虎虎,不过基本能用,唯一的问题是 http 的延迟不稳定,一会 300ms 以内一会能到 5 秒。
经过一段时间的排查,觉得原因应该出在 traefik 所在节点的位置上。通过下面的命令可以确定 traefik pod 所在的 node,我的就是位于家里。
k3s 默认的 ServiceLB (之前叫 Klipper LoadBalancer) 会把每台机器的 流量转发到 拥有 Traefik 的那台机器上,然后 Traefik 再根据路由规则将流量转发到具体的服务上,因为 traefik 默认是安装在 server 上面的,这导致流量的路径是
简单测试了一下这三个方位的 ping 延迟,这一来一去就无故增加了 200 多 ms,而且家里到国外通过 tailscale 的连接本身也不是特别稳定,这也加剧了网络的波动。
最后的临时解决方案就是位于家里的 server 基本不运行 pod,将 traefik 放到位于日本的服务器上面,这样流量基本就在日本和韩国的这几台服务器之间流转了。
我也在切换后做了一些测试,下面是相关的数据:
首先是 wrk 性能测试:
wrk 的数据居然变差了,没想明白原因。
家里 uptime 的数据,可以看到 09-19 之后,延迟的波动瞬间下来了,切换后响应时间基本在 1s 以内。
从美国的集群的到的数据和家里一致,请求的延迟波动瞬间稳定了。
总结:
以前,我都是通过 docker compose 单独在每台机器上管理服务的,这次通过迁移到 k3s,部署发布的效率高了好多,也不用特意去记住自己的哪个 docker 服务安装在哪一台服务器上。k3s 大大降低了我入门 k8s 的门槛。而 tailscale 降低了自建 wireguard 的门槛。现在终于可以更加有效率的利用好免费的甲骨文云服务器了。而问题解决的过程,chatgpt 比谷歌能更加直接的给出相关的线索,然后我可以利用它给的线索进一步谷歌验证,这极大的加快了我熟悉陌生领域知识的速度。
(番外)使用 ansible 批量安装 k3s agent(可选)
重复登录每台服务器执行相同的操作安装 k3s agent 有点烦人,也不便未来维护。可以使用 ansible 定义一个任务,用来安装 k3s-agent. 下面这个任务只包含了安装 k3s agent 的部分,如果想安装 tailscale,可以用这个 ansible galaxy。
install-k3s-agent.yaml
文件内容:
host.ini
文件内容
./k3s/config.yaml.j2
文件内容
最后在命令行执行 ansible-playbook -i hosts.ini install-k3s-agent.yaml