K3s 跨云集群
更新于 2023.2.12
背景
计划搭建 K8s 集群用于提供比较稳定的服务。
我拥有多台云厂商的机器,稳定性非常高,但是内存都比较低,尤其是运行 Spring Boot 服务时会不够用。我也拥有多台内网设备,计算性能和内存都比云服务器高得多,但是不稳定。所以计划借助 K8s,使用云服务器作为控制平面和外部流量入口,后端服务运行在内网机器上。当某一台内网机器挂掉的时候,K8s 可以及时将流量切到另一台机器的服务上,从而保障服务整体的可用性。
因为云服务器的内存实在太小了,所以使用 K3s。K3s 作为轻量级的 K8s 发行版,对资源的占用非常低,适合边缘计算等场景。
因为只有一台云服务器,所以这台服务器将成为整个集群的单点。但是云服务器一般还是比较靠谱的,整体可用性也还能接受。
因为云服务器只有 1Mbps 的带宽,所以集群只能跑一些后端应用。更大带宽、负载均衡等方法尽管能解决这个问题,但是开销是难以承受的。不过可以把前端应用部署在 OSS 之类的地方。
机器信息
控制节点: 阿里云ECS 2c2g, 1Mbps 工作节点: 8c8g 及以上 操作系统: ArchLinux
Tailscale 打通网络
因为集群中存在内网节点,云服务器无法直接和内网机器通讯。为了解决这个问题,需要用到 VPN 搭建虚拟的局域网。这里我选用 Tailscale, 它具有高性能的同时,易用性比 ZeroTier 更好。我还自己搭建了 Derper 服务器,用于协助握手和流量转发。具体见 Tailscale 自建 & 旁路由。
通过 Tailscale,云服务器和每台内网机器都会分配到一个虚拟的 IP,机器之间通过这个 IP 即可直接通信。
K3s 部署
在云服务器上执行下列安装命令。其中 $MY_TAILSCALE_IP
是云服务器的 Tailscale 虚拟 IP。--disable traefik
表示禁用默认安装的 Traefik 网关,因为我想安装 APISIX。--node-taint node-role.kubernetes.io/control-plane:NoSchedule
为云服务器打上了污点,避免容器被调度到云服务器上。这是因为云服务器的内存较小,尽量不要运行多余的容器。
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn sh -s - --node-external-ip $MY_TAILSCALE_IP --flannel-backend=wireguard-native --flannel-external-ip --node-taint node-role.kubernetes.io/control-plane:NoSchedule --disable traefik
然后执行 cat /var/lib/rancher/k3s/server/node-token
,获取 K3s 的 token。
执行 export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
,可以考虑把这句命令放进 .zshrc
。也可以拷贝。
在内网机器上执行下列安装命令。其中 $CLOUD_TAILSCALE_IP
是云服务器的 Tailscale 虚拟 IP, $MY_TAILSCALE_IP
是内网机器的 Tailscale 虚拟 IP,$NODE_TOKEN
是 K3s 集群的 token。
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn K3S_URL=https://$CLOUD_TAILSCALE_IP:6443 K3S_TOKEN=$NODE_TOKEN sh -s - --node-external-ip $MY_TAILSCALE_IP
安装 APISIX 及 dashboard
helm repo add apisix https://charts.apiseven.com
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm install apisix apisix/apisix \
--set gateway.type=LoadBalancer \
--set gateway.tls.enabled=true \
--set ingress-controller.enabled=true \
--create-namespace \
--namespace ingress-apisix \
--set ingress-controller.config.apisix.serviceNamespace=ingress-apisix \
--set ingress-controller.config.apisix.adminAPIVersion=v3 \
--set ingress-controller.config.kubernetes.enableGatewayAPI=true \
--kubeconfig /etc/rancher/k3s/k3s.yaml
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.5.0/standard-install.yaml --namespace ingress-apisix
helm install apisix-dashboard apisix/apisix-dashboard --create-namespace --namespace ingress-apisix
将自己的域名 DNS 解析到云服务器的 ip 上。比如自己拥有的域名是 a.com
,云服务器域名是 ali.a.com
。还需要进行泛域名解析,将 *.ali.a.com
解析到 ali.a.com
上。这样,可以用不同的 HTTP Host 区分不同的服务。比如 apisix.ali.a.com
用来表示 APISIX Dashboard, whoami.ali.a.com
表示访问 whoami
服务。
准备 *.ali.a.com
证书。可以使用 ACME.sh
脚本进行免费申请。写入 ~/.k8s/apisix/cert.yaml
。需要把 cert 和 key 经过 base64 编码后放入对应的位置。
apiVersion: v1
data:
cert: 123
key: 456
kind: Secret
metadata:
name: apisix-secret
namespace: ingress-apisix
定义 apisix.ali.a.com
到 APISIX Dashboard 的路由。写入 ~/.k8s/apisix/dashboard.yaml
。其中 apisix.ali.a.com
替换成自己的域名。
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
name: apisix-dashboard
namespace: ingress-apisix
spec:
loadbalancer:
type: ewma
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: apisix-dashboard
namespace: ingress-apisix
spec:
http:
- name: apisix-dashboard
match:
hosts:
- apisix.ali.a.com
paths:
- /*
backends:
- serviceName: apisix-dashboard
servicePort: 80
---
apiVersion: apisix.apache.org/v2
kind: ApisixTls
metadata:
name: apisix-dashboard
namespace: ingress-apisix
spec:
hosts:
- apisix.ali.a.com
secret:
name: apisix-secret
namespace: ingress-apisix
然后执行 kubectl apply -f ~/.k8s/apisix
就可以通过 https://apisix.ali.a.com
访问 APISIX Dashboard 了。
最后部署一个 Whoami 服务。其他后端服务的部署也比较类似。
写入 ~/.k8s/whoami/whoami.yaml
。其中 apisix.ali.a.com
替换成自己的域名。
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami
spec:
replicas: 3
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: traefik/whoami
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
name: whoami
spec:
type: NodePort
ports:
- name: http
targetPort: 80
port: 80
nodePort: 30163
selector:
app: whoami
---
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
name: whoami
spec:
loadbalancer:
type: ewma
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: whoami
spec:
http:
- name: whoami
match:
hosts:
- whoami.ali.a.com
paths:
- /*
backends:
- serviceName: whoami
servicePort: 80
---
apiVersion: apisix.apache.org/v2
kind: ApisixTls
metadata:
name: whoami
spec:
hosts:
- "whoami.ali.a.com"
secret:
name: apisix-secret
namespace: ingress-apisix
然后执行 kubectl apply -f ~/.k8s/whoami
就可以通过 https://whoami.ali.a.com
访问 Whoami 服务了。
参考
https://icloudnative.io/posts/deploy-k3s-cross-public-cloud/ https://docs.k3s.io/zh/