使用 Tailscale 轻松搭建易扩展的跨云 k3s 集群

使用 Tailscale 轻松搭建易扩展的跨云 k3s 集群

上一篇两年前的文章 记 容器编排工具 k3s + Rancher 环境的搭建 记录了 外置 mysql 数据库的 k3s 集群 以及方便的集群管理工具 Rancher 的安装与配置过程。

而如果家中有自建 NAS 、树莓派服务器等 Homelab 设施,以及在各个云服务商均部署有云服务器的这种更为常见的场景,为了设法充分利用这些资源,我在这之后又探索了 使用嵌入式 Etcd 的多 Server 节点高可用部署通过 Tailscale NAT 穿透 建立跨云内网实现跨云 k3s 集群

WireGuard 与 Tailscale

介绍 Tailscale 这个强大的组网工具之前先来简要介绍一下 WireGuard。
WireGuard 是一个用于替代 OpenVPN、L2TP/IPsec 这类传统 VPN 的高性能、现代化且安全的 VPN 协议(官网是这么描述的),它与这些传统 VPN 的主要区别有:

  • WireGuard 在保证核心功能 “加密隧道” 实现的同时切割掉了许多不必要的功能,因此它相当的简洁(代码只有几千行),还有着非常简单的配置步骤
  • WireGuard 可以运行在内核态,避免了与用户态之间来回切换,因此速度非常快,且可以支持很高的带宽
  • WireGuard 没有 Server/Client 之分,连线的两端地位是对等的,因此非常适合用来进行各种拓扑组网

知乎 上有一篇文章详细介绍了 WireGuard 是如何对传统 VPN 进行精简化的;后面我也会看看源码学习学习,毕竟我对能简洁优雅地实现核心功能的东西相当感兴趣。

Tailscale 是一个基于 Wireguard 实现的 mesh 网络构建器。它大致是通过一个集中式 control plane 记录与分发不同节点之间的 wireguard 连接配置,以及一些灵活的打洞黑科技来实现的。
对于大多数用户来说,Tailscale 的主要功能即通过搭建 vpn,将处于不同网络的设备连接在同一个网络下。

k3s 高可用 + 嵌入式 Etcd 集群

节点 A 操作:

curl -sfL https://get.k3s.io | INSTALL_K3S_SKIP_START=true sh -s server --cluster-init
ExecStart=/usr/local/bin/k3s \
    server \
        '--cluster-init' \
        '--tls-san=bwhk3c.verge.toay.io' \
        '--kube-apiserver-arg' \
        'service-node-port-range=1-65535' \
        '--advertise-address' \
        '100.110.1.10' \
        '--node-ip' \
        '100.110.1.10' \
        '--node-external-ip' \
        '154.223.21.101' \
root@lnhks2:~# kubectl get nodes
NAME                STATUS   ROLES                       AGE   VERSION
lnhks2.verge.toay   Ready    control-plane,etcd,master   17s   v1.28.5+k3s1
cat /var/lib/rancher/k3s/server/token

节点 B 操作:

curl -sfL https://get.k3s.io | INSTALL_K3S_SKIP_START=true K3S_TOKEN="K100f0e009f7a9270407795b9de387ac358bd35663d7ee69a967426aaa3548722ed::server:9419304cf3481d654a933db2f24461ba" sh -s server --server https://lnhks2.verge.toay:6443
ExecStart=/usr/local/bin/k3s \
    server \
        '--server' \
        'https://lnhks2.verge.toay:6443' \
        '--advertise-address' \
        '100.110.1.20' \
        '--node-ip' \
        '100.110.1.20' \
        '--node-external-ip' \
        '34.92.159.61' \
root@gchka3:/home/twikor# kubectl get nodes
NAME                STATUS   ROLES                       AGE     VERSION
gchka3.verge.toay   Ready    control-plane,etcd,master   8s      v1.28.5+k3s1
lnhks2.verge.toay   Ready    control-plane,etcd,master   6m17s   v1.28.5+k3s1
root@lnhks2:~# kubectl label node/gchka3.verge.toay node-role.kubernetes.io/worker=true
node/gchka3.verge.toay labeled
root@lnhks2:~# kubectl label node/lnhks2.verge.toay node-role.kubernetes.io/worker=true
node/lnhks2.verge.toay labeled
root@lnhks2:~# kubectl get nodes
NAME                STATUS   ROLES                              AGE     VERSION
gchka3.verge.toay   Ready    control-plane,etcd,master,worker   2m10s   v1.28.5+k3s1
lnhks2.verge.toay   Ready    control-plane,etcd,master,worker   8m19s   v1.28.5+k3s1

若遇到非常奇怪的pod内dns不能正常解析的错误,尝试修改cattle-cluster-agent的dnsPolicy,这也是我在网上翻遍了github issues才找到的解决方法。。。

修改前:

 ~  sudo kubectl get pod -n cattle-system
NAME                                    READY   STATUS    RESTARTS      AGE
cattle-cluster-agent-569cc46d9c-66pdb   1/1     Running   0             83s
cattle-cluster-agent-569cc46d9c-pkljj   0/1     Error     2 (40s ago)   81s
rancher-webhook-5b5665c649-rsx9s        1/1     Running   0             4h30m
INFO: Using resolv.conf: search cattle-system.svc.cluster.local svc.cluster.local cluster.local corgi-ladon.ts.net toaylabs.org.github.beta.tailscale.net verge.toay nameserver 10.43.0.10 options ndots:5
ERROR: https://deploy.cordon.toay.io/ping is not accessible (Could not resolve host: deploy.cordon.toay.io)

修改:

 ~  sudo kubectl edit deployment cattle-cluster-agent -n cattle-system
Warning: spec.template.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key: beta.kubernetes.io/os is deprecated since v1.14; use "kubernetes.io/os" instead
deployment.apps/cattle-cluster-agent edited

同样地:

kubectl edit deployment fleet-agent -n cattle-fleet-system

修改后:

 ~  sudo kubectl get pod -n cattle-system
NAME                                    READY   STATUS    RESTARTS   AGE
cattle-cluster-agent-57ccdb69b4-9q7bl   1/1     Running   0          13s
cattle-cluster-agent-57ccdb69b4-p882r   1/1     Running   0          15s
rancher-webhook-5b5665c649-rsx9s        1/1     Running   0          4h35m

节点 C 操作:

curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.28.5+k3s1 K3S_TOKEN=K100f0e009f7a9270407795b9de387ac358bd35663d7ee69a967426aaa3548722ed::server:9419304cf3481d654a933db2f24461ba K3S_URL=https://bwhk3c.verge.toay:6443 sh -s - --docker --node-ip 100.120.10.1
NAME                 STATUS   ROLES                       AGE     VERSION        INTERNAL-IP     EXTERNAL-IP    OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
bwhk3c.verge.toay    Ready    control-plane,etcd,master   9m46s   v1.28.5+k3s1   100.110.10.1    45.78.28.214   Ubuntu 22.04.3 LTS   5.15.0-25-generic   containerd://1.7.11-k3s2
gchka3.verge.toay    Ready    control-plane,etcd,master   9s      v1.28.5+k3s1   100.111.10.1    34.92.159.61   Ubuntu 22.04.3 LTS   6.2.0-1019-gcp      docker://24.0.5
svc4ubt.verge.toay   Ready    control-plane,etcd,master   7m12s   v1.28.5+k3s1   100.120.10.20   <none>         Ubuntu 22.04.4 LTS   5.15.0-94-generic   docker://24.0.5

Proxmox 配置 Tailscale 域名证书

#!/bin/bash
NAME="$(tailscale status --json | jq '.Self.DNSName | .[:-1]' -r)"
tailscale cert "${NAME}"
pvenode cert set "${NAME}.crt" "${NAME}.key" --force --restart
root@insulator:~# NAME="$(tailscale status --json | jq '.Self.DNSName | .[:-1]' -r)"
root@insulator:~# NAME
-bash: NAME: command not found
root@insulator:~# echo $NAME
insulator-verge-toay.corgi-ladon.ts.net
root@insulator:~# tailscale cert "${NAME}"
pvenode cert set "${NAME}.crt" "${NAME}.key" --force --restart
Wrote public cert to insulator-verge-toay.corgi-ladon.ts.net.crt
Wrote private key to insulator-verge-toay.corgi-ladon.ts.net.key
Setting custom certificate files
Restarting pveproxy
┌─────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────┐
│ filename        │ pveproxy-ssl.pem                                                                                │
├─────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
│ fingerprint     │ 1A:C1:B6:AA:74:28:AE:31:54:E9:CA:5B:5B:A3:58:1B:78:8D:25:E3:66:5E:39:65:E5:3C:15:67:8D:00:89:71 │
├─────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
│ subject         │ /CN=insulator-verge-toay.corgi-ladon.ts.net                                                     │
├─────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
│ issuer          │ /C=US/O=Let's Encrypt/CN=R3                                                                     │
├─────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
│ notbefore       │ 2024-02-18 22:50:04                                                                             │
├─────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
│ notafter        │ 2024-05-18 22:50:03                                                                             │
├─────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
│ public-key-type │ id-ecPublicKey                                                                                  │
├─────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
│ public-key-bits │ 256                                                                                             │
├─────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
│ san             │ - insulator-verge-toay.corgi-ladon.ts.net                                                       │
└─────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────┘

Bonus

Tailscale 实现了一些非常好用的便捷功能

Taildrop

从 Linux 端发送:

tailscale file cp <src-file-loc> <dst-device>:

然后从 Linux 端接收:

tailscale file get <dst-dir>

使用 exitnode

sudo tailscale up --exit-node=<exit-node-ip> --exit-node-allow-lan-access=true

参考链接

  1. cattle-cluster-agent pod内不能解析 解决方法 - Github issue
  2. WireGuard到底好在哪?