Featured image of post 云上云下,资源自由:混合 Kubernetes 集群搭建指南

云上云下,资源自由:混合 Kubernetes 集群搭建指南

云厂商的CVM好贵啊!除了丐版引流招新人入坑,其它稍高点的配置那价格就像在考验智商。作为小小个人如果想搭建一套比较完善的kubernetes集群的话,那资源就捉襟见肘了。如何更经济实惠的应对资源不够这个问题,本文给一点新思路。

基本原理

我曾经基于云上一些CVM搭建了一套k8s集群,简单玩玩尚可。但是如果想搞点花样,比如想加点监控或分析,Prometheus一上,内存就咔咔告警;日志采集分析一下?ES或loki等一上,存储也就喵喵在叫。这阻碍我们学习知识了呢?这不能忍。

看过我之前文章的朋友可能知道,我家里有不少小型计算单元与存储,NUC等小主机几台、NAS几台。这有算力、内存、存储都充足。那么,咱可以优势互补,搭建一个云上云下混合的k8s集群吗?当然可以。

本文将借助tailscale及其相关工具(有比较详细的全套使用指南)构建一个云与家庭一体的混合集群,并且在之上我们使用更轻量的k3s发行版搭建一个k8s集群。这样,我们就可借用云上主机,获得如公网IP,备案等必要的便利,同时需要计算资源时又依托于自己家庭内的低成本CPU内存及存储等,真正物尽其用。接下来和我一起来操作并且验证一下我们的想法吧!

构建网络

不知道你听过WireGuard没有,我在之前一篇文章有过介绍。作为已经被Linux内核集成的一个VPN,它使用上还稍有不便。但我们借助于一款名叫tailscale的软件可快速构建安全可靠的虚拟局域网(VPN)。以下是GPT提供的介绍:

Tailscale是一款基于WireGuard协议开发的虚拟专用网络(VPN)工具,它允许用户将能够访问外网的设备聚合成一个虚拟的局域网,实现设备之间的安全通信。以下是关于Tailscale的详细介绍: 内网穿透:Tailscale可以将不同地点的设备连接起来,实现内网穿透,使得用户可以像在同一个局域网内一样访问这些设备。 设备连接:支持Windows、macOS、Linux、iOS和Android等多个平台,使得不同平台的设备可以轻松连接。 安全性:使用加密协议保护通信内容,确保数据的安全性。

它有强大的自动内网穿透能力,并且即使不能直连也会自动使用中继。据我观察,经常是使用几次中继收发消息后,自动打洞成功走上直连。你要说这么强大的软件,它有什么缺点不?那当然有:

  1. tailscale不是免费的,免费有接入节点限制,还有些高级功能有限。
  2. 中继都在国外,若需要经过它转发性能会有影响。

但是,别慌,这都能很好的解决。有个开源的控制面headscale,我们可以自建它解决官方各种限制。我们还可以自建中继服务DEPER。下面会一一道来,不过为了保证数据安全,首先我们得准备一个SSL证书。

使用ACME.sh自动申请证书

这里假定你已经有一个自己的域名了,我们可以借助于开源的acme.sh快速帮我们申请一个时长3个月的SSL证书,并且可以配置自动续期。这个证书是为了后面headscale服务安全性而用的,它也内置了自动申请证书功能,但目前(2024-10)只支持HTTP认证,这对国内域名不友好。我发现我在腾讯云上使用时不成功,可能是备案相关要求导致。所以下面我们就自己手动通过DNS认证来申请吧。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 安装acme.sh
curl https://get.acme.sh | sh -s email=your-email-address@qq.com

# 方案一:http验证:以standalone模式获取证书(如果网站未备案,在国内机器上操作可能报错,国外机器这里或能成功)
acme.sh --issue --standalone -d example-domain.com

# 方案二:以DNS验证。 我域名托管在cloudflare中,那么就需要设置环境变量。
export CF_Token="你在cloudflare的API token"
export CF_Email="你域名的Email"

acme.sh --issue --dns dns_cf  -d example-domain.com 

如果你想要调试一下,可以先使用测试域名,避免因为一些报错被平台限制申请。这相关信息查看这里。 申请成功后,你将会获得证书相关的文件,把它拷贝出来放到了/home/ubuntu/example-domain.com备用。

搭建tailscale的控制面headscale

安装配置headscale

An open source, self-hosted implementation of the Tailscale control server. 多亏了开源的方案headscale,我们可以免费使用tailscale。查看这里的指引可轻松搭建。上面有容器方案使用起来简单,比如我们基于docker-compose来部署:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
version: "3.7"

services:
  headscale:
    image: headscale/headscale:v0.23
    restart: unless-stopped
    container_name: headscale
    network_mode: host
    volumes:
      - /home/ubuntu/headscale/config:/etc/headscale
      - /home/ubuntu/example-domain.com:/certs/
    command: serve

上面为了简化网络,使用了host模式。重点是这里面的config配置怎么搞?你可以参考官方仓库中的示例稍作修改,主要是一些地址端口等小小变化,限于文章篇幅,以下仅diff出我的修改与原版差异:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
ubuntu@mercedes:~/headscale/config$ diff config.yaml config.yaml.ori
13,14c13
< #server_url: http://127.0.0.1:8080
< server_url: "https://example-domain.com:8812"
---
> server_url: http://127.0.0.1:8080
19,20c18,19
< listen_addr: 0.0.0.0:8812
< #listen_addr: 127.0.0.1:8080
---
> # listen_addr: 0.0.0.0:8080
> listen_addr: 127.0.0.1:8080
80c79
<     enabled: true
---
>     enabled: false
221,222c220,221
< tls_cert_path: "/certs/example-domain.com.crt"
< tls_key_path: "/certs/example-domain.com.key"
---
> tls_cert_path: ""
> tls_key_path: ""

这样我们将配置及前面的SSL证书挂载到容器内供headscale使用。之后docker-compose up -d即可启动,注意检查一下启动日志是否一切正常。

在节点安装tailscale并注册

以我使用的Ubuntu系统,我们可以通过apt安装,其它系统官方也有很详细的安装指南。

1
2
3
4
5
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/jammy.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null 
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/jammy.tailscale-keyring.list | sudo tee /etc/apt/sources.list.d/tailscale.list

sudo apt-get update
sudo apt-get install tailscale

安装完毕后可以输入tailscale version查看一下版本。注意如果太老旧的版本和上面的headscale可能不太兼容哦,用当前最新一般没啥问题。

然后我们将node注册到headscale中:

1
sudo tailscale up --login-server=https://example-domain.com:8812 --accept-routes=true --accept-dns=false

正常会收到一个链接。

1
2
3
To authenticate, visit:

	https://example-domain.com:8812/register/mkey:316fabaca478e6d35190bb8e331004b33c0f8c52c98f202105af41465014a269

打开页面,根据指示在headscale中注册,在注册前,我们可能要先创建一个namespace(新版本叫user)了。

1
2
3
4
5
# 创建一个namespace(user),名叫union。别以为我写错了headscale,前一个是容器名,后一个是命令名
docker exec -it headscale headscale users create union

# 注册
headscale nodes register --user union --key mkey:316fabaca478e6d35190bb8e331004b33c0f8c52c98f202105af41465014a269

然后查看有如下结果:

1
2
# 查看节点列表
docker exec -it headscale  headscale nodes list

可能会输出类似的信息,能看到自己对应主机online了就注册成功了。

开启或搭建中继服务DERP

这一步可选,因为tailscale在全球提供了不少节点。但可惜没有大陆内的,延迟可能会有一定影响。如果你希望更快,我这里给一个我的搭建命令,一行docker命令即可(注意它也同样使用了上面申请的SSL证书)。

1
2
3
4
5
6
7
8
9
docker run --restart always  \
--name derper-with-verify-2024-10-15 \
--network host  \
-v /home/ubuntu/example-domain.com/:/app/certs  \
-v /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock \
-e DERP_CERT_MODE=manual   \
-e DERP_ADDR=:12345   \
-e DERP_DOMAIN=example-domain.com  \
fredliang/derper:latest

这个搭建好后,如果想让headscale使用我们自己搭建的DERP服务,则需要修改一下config。在DERP服务那一块:

1
2
paths:
    - /etc/headscale/derp.yaml

同时把这个文件挂载到容器内。这个文件的示例官方仓库也有,我的是这样的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# /etc/headscale/derp.yaml
regions:
  900:
    regionid: 900
    regioncode: gz-mercedes
    regionname: Tencent Guangzhou
    nodes:
      - name: 900a
        regionid: 900
        hostname: example-domain.com
        stunport: 3478
        stunonly: false
        derpport: 12345

修改完上面headscale相关配置并重启后,我们可以在节点上执行tailscale netcheck看到定义的中继服务器信息。

加入其它节点并验证网络

除了云上我们购买的节点外,现在我们加入自己家里的一些虚拟机进来,让它未来成为混合集群的一部分。比如我想把家里一台Ubuntu24.04TLS作为节点一部分。这块操作和前面完全一致,先安装tailscale再注册到headscale,然后我们使用node list查看:

1
2
3
4
ubuntu@mercedes:~/headscale$ docker exec -it headscale headscale nodes list
ID | Hostname   | Name       | MachineKey | NodeKey | User  | IP addresses                  | Ephemeral | Last seen           | Expiration          | Connected | Expired
1  | union-test | union-test | [j1D6O]    | [aDlUn] | union | 100.64.0.3, fd7a:115c:a1e0::3 | false     | 2024-10-22 06:46:41 | 0001-01-01 00:00:00 | online    | no
2  | outside    | outside    | [TWC9a]    | [MVLAj] | union | 100.64.0.4, fd7a:115c:a1e0::4 | false     | 2024-10-22 06:46:40 | 0001-01-01 00:00:00 | online    | no

如果你按上面部署了自己的DERP,相信速度会快一些。

1
2
3
4
5
6
$ tailscale ping 100.64.0.3
pong from union-test (100.64.0.3) via DERP(gz-mercedes) in 18ms
pong from union-test (100.64.0.3) via DERP(gz-mercedes) in 19ms
pong from union-test (100.64.0.3) via DERP(gz-mercedes) in 19ms
pong from union-test (100.64.0.3) via DERP(gz-mercedes) in 18ms
pong from union-test (100.64.0.3) via DERP(gz-mercedes) in 19ms

也可以使用tailscale status等检查网络状态。

到这里,我们的混合集群的网络部分已经Ready了,现在你可以看到这两台原本互不相干的机器,它们成为了一个局域网,并且是有着当前最安全的加密通信支撑的安全网络。接下来我们继续完成我们的集群部署以及功能验证。

补充题外话:tailscale好处不止于此,作为VPN,借助于它的Router能力,可以让你很方便的从外界安全的访问整个家庭网络,打通一切网络隔阂。鉴于此事于主题无关,就不在此多言啦。

部署集群

经过前文折腾,网络已经就绪。我已经迫不及待想上个集群了,虽然我曾经尝试过部署一个原生的kubernetes集群,但对于咱们小型集群,使用原生的Kubernetes占用资源还是太多了,我更推荐的还是k3s发行版。 我之前文章也提到过如何创建一个k3s集群。下面简单说明一下过程,并且增补一些实用技巧。

master节点

考虑到集群的稳定性,家里或许没有云上持续可靠,所以master节点可以考虑放在云上。但如果这个集群打算跑很久,云上VM你可能不续费的话,那么以家中节点为master也是可行的,要注意备份集群控制面数据即可。

1
2
3
4
5
6
7
8
9
export INSTALL_K3S_CHANNEL=v1.26
export K3S_TOKEN=union-k3s-on-2024-10-23

curl -sfL https://get.k3s.io  | sh -s - server \
--disable=traefik \
--node-ip=100.64.0.4 \
--node-external-ip=<机器外网IP> \
--flannel-iface=tailscale0 \
--tls-san=<机器外网 >

我们在上面的outside(香港)机器上执行如上命令,即可部署一个k8s 1.26版本。对几个参数作个解释:

  • 关闭了默认的traefik。这是我的使用经验,因为未来我们使用它还需要再定义一些参数等(如自动申请证书啥的),故自行管理和部署更好。
  • 节点IP使用的是我们tailscale分配的局域网地址。这样集群内部通信才能走咱们上面的混合网络。
  • node-external-ip指定外网IP,用于LB等暴露时绑定的IP。
  • flannel-iface确保每个节点都使用其 Tailscale接口,不然底层flannel网络不知道应该基于这个设备转发。
  • tls-san需要指定机器的外网IP。为了你的k3s集群api未来可以从外部访问(kubeconfig)。

如果你安装时遇到网络问题进展缓慢,那么可以看下面node的部署,选择国内加速的镜像脚本即可。

你说都2024年10月了,还在用1.26版本?虽然我也尝试了较新的1.30版本,遇到了比较神奇的连接kubernetes api失败的报错,coredns一直起不来。

[INFO] plugin/ready: Still waiting on: “kubernetes”
[INFO] plugin/ready: Still waiting on: “kubernetes”
[INFO] plugin/ready: Still waiting on: “kubernetes”

有issue提可能和内核约莫有些关联,但是咱用的是Ubuntu24.04了,内核不应该低呀。也有issue提到tls-san添加上kubernetes svc ip,尝试均无效。想着换个版本呗?回退到1.26这个我常年用的老版本后,一切正常。此事颇为蹊跷,待我搞完这篇文章再来诊断诊断,在此记下来避免你也同样遇到。

node节点

我把master选择了云上机器,那么node我就选择加入自己家内部的虚拟机啦。我们部署一个node节点,在这里有意切换到一个国内安装脚本。

1
2
3
4
5
6
export INSTALL_K3S_CHANNEL=v1.26
export K3S_TOKEN=union-k3s-on-2024-10-23

curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh |  INSTALL_K3S_MIRROR=cn sh -s - agent --server https://100.64.0.4:6443 \
--flannel-iface=tailscale0 \
--node-ip=100.64.0.3

启动成功后,我们就有了一个可以小小集群了。查看一下:

1
2
3
4
$ sudo k3s kubectl get nodes           
NAME             STATUS   ROLES                  AGE   VERSION
union-test       Ready    <none>                 73s   v1.30.5+k3s1
outside          Ready    control-plane,master   12h   v1.30.5+k3s1

实用技巧

如果你遇到过镜像拉取很慢或者就完全拉不下来,可以为k3s server或agent配置一下代理,根据是master还是node情况来修改/etc/systemd/system/k3s.service.env/etc/systemd/system/k3s-agent.service.env文件,在末尾添加上https代理即可。比如

1
2
K3S_TOKEN='union-k3s-on-2024-10-23'
HTTPS_PROXY=http://<ip>:<port>

然后重启k3s/k3s-agent服务即可。

成果验收

如果上面都操作OK了,集群也调试就绪了。这么辛苦折腾一番,那么是时候收点好处了。我们搞个简单的,部署一个网站,网站实际托管在家里VM上,但是流量入口自云主机。你要问为什么这么搞?那是因为家宽都不让开放80/443端口呀,你得有云上主机才可以。

我们直接部署个nginx来演示,注意这里主动把它调度到家庭VM上(union-test节点)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      nodeName: union-test # 这里为了测试故意将它调度到家庭内部设备上
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80

# k3s默认的servicelb提供了基于VM的LB能力,免费!
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  type: LoadBalancer
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

我们借助k3s中内置了servicelb,它通过iptables一系列规则,帮助你在无需使用云厂商的LB而达到对外暴露服务的目的。然后我们即可以直接访问云上主机的IP地址和端口即可实际访问到家里的计算资源。 跨主机访问Nginx

有这个基础,未来可想象的空间就比较大啦。例如将数据库部署在家庭 NAS 上,将机器学习模型训练任务放到家庭闲置 GPU 上等。咱家里大House不一定非得装小姐姐,也可以放点有用的数据嘛:)

后记

其实k3s的较新版本也直接支持集成tailscale了,并且也支持我们自己部署的headscale作为控制面,详情查看上面链接。不过我们一步步设置过来,对全过程更清晰明白啦!

如此折腾之后,便可充分的利用自建资源,再也不担心搞点研究碰上资源不够的窘境了。未来或许你只需要在云上留下一台最普通的机器即可,连啥负载都可以不放,想想可不要太香,哗哗的省钱。

本篇文章就写到这里,感谢阅读。如果期待看到更多玩法,欢迎点赞鼓励,您的支持是我写作的动力!

我是个爱折腾技术的工程师,也乐于分享。欢迎点赞、关注、分享,更欢迎一起探讨技术问题,共同学习,共同进步。为了获得更及时的文章推送,欢迎关注我的公众号:

扫码关注公众号