Kubernetes 入门到实践:借助 WireGuard 跨云搭建 K3s 集群环境

写在前面

最近看从《跨云厂商部署 k3s 集群》这篇文章中找到了又一种可以穷开心的花样玩法,于是乎就有了这篇文章。刚好最近各大云服务商搞起了双 11 活动大促,像腾讯云又可以几十块钱买台三年的小鸡玩了。那么问题来了,每个云服务商只能活动价购买一台,最终我们云主机就分布在不同云服务商上,得不到充分地利用,那么有没有可能将它们整合起来输出算力呢?当然是可以的啦!那就是利用 WireGuard 组网搭建 Kubernetes 集群。

由于前面提到文章中使用的版本已经过时了,那我就基于现在最新的软件版本按文章的方式开搞吧。

PS: 搞完回来了,通过自己动手后发现一个硬道理,有事没事真的要多看软件的官方文档或多水下官方的社区(如 GayHub),其实官方已经提供足够详细的文档,社区里已经有人早过踩过了几种坑了。想要柳暗花明,就多看官方文档吧。

环境准备

软件版本
Ubuntu20.04
Docker20.10
WireGuardv1.0.20200513
K3sv1.23.14+k3s1

我在腾讯云、Vultr 上准备了几台预装 Ubuntu 20.04 的云主机,当然可以是任意云服务商的云主机,只需具备公网访问、可以运行 Linux 系统即可。

云服务商公网 IP配置节点名称节点角色OS-IMAGEKERNEL-VERSIONCONTAINER-RUNTIME
腾讯云42.193.XXX.XXX4C4Gk3s-node-01control-plane,masterUbuntu 20.04 LTS5.4.0-96-genericdocker://20.10.13
Vultr45.63.YYY.YYY1C1Gk3s-node-02agent/workerUbuntu 20.04.3 LTS5.4.0-131-genericdocker://20.10.11
Vultr13.22.ZZZ.ZZZ1C1Gk3s-node-03agent/workerUbuntu 20.04.5 LTS5.4.0-122-genericdocker://20.10.12

安装 Docker

sudo apt intall docker.io -y

安装 WireGuard

需要确保在每个节点上安装 WireGuard 软件,在 Ubuntu 20.04 的安装细节如下:

# 切换 root 权限
sudo -i

# 更新软件源
apt update

# 安装 WireGuard 软件
apt install wireguard resolvconf -y

# 开启 IPV4 IP 转发
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p

在这里只需要完成 WireGuard 的正确安装即可,无需配置和启动,其他事情就可以交给 K3s 完成配置组网,我们要做的是尽情使唤 K3s 干活,还要它把活干漂亮。其实 K3s 也早为我们准备好了,只需简单配置启动参数即可。

跨云搭建 K3s 集群

由于我的云主机分布在不同的云服务商,所以不能通过服务商的内网环境互相访问,这里需要借助 WireGuard 完成异地组网。由于 K3s 通过 Flannel 已经集成了 WireGuard,所以我们可以通过一些简单的配置即可轻松完成组网。

You need to install WireGuard on every node, both server and agents before attempting to leverage the WireGuard flannel backend option. The wireguard backend will be removed from v1.26 in favor of wireguard-native backend natively from Flannel.

在开始之前建议大家先通读官方指引,只有搞清楚了来龙去脉,才能拨开迷雾。其中有提及不同版本的 K3s 配置参数有所差异,值得注意的是从 v1.26 开始启动参数由原先的 flannel-backend: wireguard  变更为  flannel-backend: wireguard-native

我们可以参考上一篇文章《Kubernetes 入门到实践:搭建 K3s 集群初体验 》中的部署命令。

安装 K3s Server

在启动每个 Server 时需要额外增加以下启动参数,便可激活 WireGuard:

-node-external-ip <SERVER_EXTERNAL_IP> --flannel-backend wireguard-native --flannel-external-ip

K3s Server 完整的启动过程如下:

# 指定 K3s 版本
export INSTALL_K3S_VERSION=v1.23.14+k3s1
# 只安装不启动
export INSTALL_K3S_SKIP_START=true
# 为您添加到集群的每个节点设计一个独特的名称:https://docs.rancher.cn/docs/k3s/installation/installation-requirements/_index#先决条件
export K3S_NODE_NAME=k3s-node-01
# 获取节点的公网 IP,或手动设置
export PUBLIC_IP=`curl -sSL https://ipconfig.sh`
##
# 自定义启动执行命令:
# --docker:启用 Docker 运行时
# --disable servicelb:(可选)禁用 servicelb
# --disable traefik:(可选)禁用 traefik
# --node-ip $PUBLIC_IP --node-external-ip $PUBLIC_IP:设置节点的公网 IP,节点间使用公网 IP 组网通信
# --flannel-backend wireguard-native --flannel-external-ip:启用 Flannel CNI 并使用 WireGuard 组网
export INSTALL_K3S_EXEC="--docker --disable servicelb --disable traefik --node-ip $PUBLIC_IP --node-external-ip $PUBLIC_IP --flannel-backend wireguard-native --flannel-external-ip"
# 使用阿里云镜像源脚本安装
curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn sh -
# 手动启动 K3s 服务,由于上面已配置 INSTALL_K3S_SKIP_START=true
systemctl enable --now k3s
# 查看 K3s 服务状态
systemctl status k3s
# 查看 metrics-server 运行日志,方便定位问题
kubectl logs -f pod/metrics-server-5bb8d5f679-btt96 -n kube-system

安装 K3s Agent

在启动每个 Agent 时需要额外增加以下启动参数,便可激活 WireGuard:

-node-external-ip <AGENT_EXTERNAL_IP>

K3s Agent 完整的启动过程如下:

# 基本参数与 Server 启动参数保持一致
export INSTALL_K3S_VERSION=v1.23.14+k3s1
export INSTALL_K3S_SKIP_START=true
# 只需要配置 Agent 特定的参数即可
export K3S_NODE_NAME=k3s-node-02
export PUBLIC_IP=`curl -sSL https://ipconfig.sh`
export INSTALL_K3S_EXEC="--docker --node-ip $PUBLIC_IP --node-external-ip $PUBLIC_IP"
# 设置了 K3S_URL,它将默认为“agent”。如果未设置 K3S_URL,它将默认为“server”
export K3S_URL=
# 用于将 server 或 agent 加入集群的共享 secret
# 在主节点上获取:cat /var/lib/rancher/k3s/server/node-token
export K3S_TOKEN=K105a308b09e583fccd1dd3a11745826736d440577d1fafa5d9dbaf5213a7150f5f::server:88e21efdad8965816b1da61e056ac7c4
# 由于我的节点在 Vultr 上,这里走官方源脚本安装,国内的主机仍可使用阿里云镜像源脚本安装
# curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn sh -
curl -sfL https://get.k3s.io | sh -
# 手动启动 K3s 服务,由于上面已配置 INSTALL_K3S_SKIP_START=true
systemctl enable --now k3s-agent
# 查看 K3s 服务状态
$ systemctl status k3s-agent
● k3s-agent.service - Lightweight Kubernetes
Loaded: loaded (/etc/systemd/system/k3s-agent.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2022-11-19 12:23:59 UTC; 50s ago
Docs: https://k3s.io
Process: 707474 ExecStartPre=/bin/sh -xc ! /usr/bin/systemctl is-enabled --quiet nm-cloud-setup.service (code=exited, status=0/SUCCESS)
Process: 707476 ExecStartPre=/sbin/modprobe br_netfilter (code=exited, status=0/SUCCESS)
Process: 707477 ExecStartPre=/sbin/modprobe overlay (code=exited, status=0/SUCCESS)
Main PID: 707478 (k3s-agent)
Tasks: 14
Memory: 255.6M
CGroup: /system.slice/k3s-agent.service
└─707478 /usr/local/bin/k3s agent

Nov 19 12:23:59 vultr k3s[707478]: I1119 12:23:59.986059 707478 network_policy_controller.go:163] Starting network policy controller
Nov 19 12:24:00 vultr k3s[707478]: I1119 12:24:00.057408 707478 network_policy_controller.go:175] Starting network policy controller full sync goroutine
Nov 19 12:24:00 vultr k3s[707478]: I1119 12:24:00.698216 707478 kube.go:133] Node controller sync successful
Nov 19 12:24:00 vultr k3s[707478]: I1119 12:24:00.702931 707478 kube.go:331] Overriding public ip with '45.63.XXX.XXX' from node annotation 'flannel.alpha.coreos.com/public-ip-overwrite'
Nov 19 12:24:00 vultr k3s[707478]: time="2022-11-19T12:24:00Z" level=info msg="Wrote flannel subnet file to /run/flannel/subnet.env"
Nov 19 12:24:00 vultr k3s[707478]: time="2022-11-19T12:24:00Z" level=info msg="Running flannel backend."
Nov 19 12:24:00 vultr k3s[707478]: I1119 12:24:00.865979 707478 wireguard_network.go:78] Watching for new subnet leases
Nov 19 12:24:00 vultr k3s[707478]: I1119 12:24:00.866258 707478 wireguard_network.go:172] Subnet added: 10.42.0.0/24 via 42.193.XXX.XXX:51820
Nov 19 12:24:00 vultr k3s[707478]: I1119 12:24:00.956299 707478 iptables.go:260] bootstrap done
# 查看集群节点信息,会发现 k3s-node-02 的 INTERNAL-IP 为公网 IP
$ kubectl get nodes -owide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k3s-node-01 Ready control-plane,master 19m v1.23.14+k3s1 10.0.20.12 42.193.XXX.XXX Ubuntu 20.04 LTS 5.4.0-96-generic docker://20.10.13
k3s-node-02 Ready <none> 14m v1.23.14+k3s1 45.63.YYY.YYY 45.63.YYY.YYY Ubuntu 20.04.3 LTS 5.4.0-131-generic docker://20.10.11

关于 metrics-server 问题

metrics-server
  无法获取指标,是由于kubelet-preferred-address-types
值首选是 InternalIP,而云服务器的 InternalIP 为内网 IP,不同云厂商的内网 IP 段不同,无法通讯

问题就是 metrics-server 无法获取 CPU、内存等利用率核心指标,需要人工干预下配置。在刚发布的新版本 **v1.23.14+k3s1** 中修正了启用 flannel-external-ip=true 选项后会动态调整 -kubelet-preferred-address-types=ExternalIP,InternalIP,Hostname 地址的优先级顺序。

下面将展开说说这个特性的调整对 K3s 集群的影响:

在 v1.23.13+k3s1 版本及更旧的版本

查看下默认配置:

# 无法获取 CPU、内存等利用率核心指标
$ kubectl top node
Error from server (ServiceUnavailable): the server is currently unable to handle the request (get nodes.metrics.k8s.io)

$ kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
k3s-node-01 133m 3% 2232Mi 56%
k3s-node-02 <unknown> <unknown> <unknown> <unknown>

$ kubectl describe pod/metrics-server-d76965d8f-t2sll -n kube-system | grep -i types
--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname

需要修改 metrics-server 的 manifests,使用以下命令在线编辑 metrics-server 的 manifests:

kubectl -n kube-system edit deploy metrics-server

调整以下执行参数后保存退出:

    spec:
containers:
- args:
- --cert-dir=/tmp
- --secure-port=10250
- - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
+ - --kubelet-preferred-address-types=ExternalIP
- --kubelet-insecure-tls
- --kubelet-use-node-status-port
- --metric-resolution=15s

保存后稍等资源重新调度后,这样就可以让 metrics-server 使用公网 IP 来和 node 通信了。重新查看核心指标:

$ kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
k3s-node-01 259m 6% 2269Mi 57%
k3s-node-02 203m 20% 534Mi 54%

$ kubectl top pods -A
NAMESPACE NAME CPU(cores) MEMORY(bytes)
default nginx-85b98978db-659cl 0m 5Mi
default nginx-85b98978db-tt2hh 0m 5Mi
default nginx-85b98978db-zr47g 0m 2Mi
kube-system coredns-d76bd69b-k8949 4m 15Mi
kube-system local-path-provisioner-6c79684f77-nc2xn 1m 7Mi
kube-system metrics-server-d76965d8f-t2sll 6m 25Mi

v1.23.14+k3s1 版本及更新的版本

  1. Describe pod/metrics-server-, look for ARGS and check those scenarios:-kubelet-preferred-address-types=InternalIP,ExternalIP,Hostnamewhen flannel-external-ip=false
  2. Do same steps just change flannel-external-ip: true and look for-kubelet-preferred-address-types=ExternalIP,InternalIP,Hostnamewhen flannel-external-ip=true

⚠️  注意事项 ⚠️

(不要问为什么,你试试)

  • 安全组防火墙需要放行相关端口
    • TCP 6443:K3s Server 端口
    • TCP 10250metrics-server 服务端口,用于 K3s Server 与 Agent 通信收集指标,否则会无法获取 CPU、内存等利用率核心指标
    • UDP 51820:开启 flannel-backend: wireguard-native 默认端口,Flannel 后端使用 WireGuard 的端口
    • TCP 30000-32767:K8s NodePort 范围,方便外网调试
  • 选配启动参数
    • --tls-san
      • 在 TLS 证书中添加其他主机名或 IP 作为主机备用名称
      • 即在公网环境下允许通过公网 IP 访问控制、操作远程集群
      • 或者部署多个 Server 并使用 LB 进行负责,就需要保留公网地址
    • --disable servicelb
    • --disable traefik
  • 禁用未使用上的组件,节约性能
    • Service Load Balancer
      • K3s  提供了一个名为  Klipper Load Balancer的负载均衡器,它可以使用可用的主机端口。 允许创建  LoadBalancer  类型的  Service,但不包括  LB  的实现。某些  LB  服务需要云提供商,例如  Amazon EC2。相比之下,K3s service LB使得可以在没有云提供商的情况下使用  LB  服务。
      • 要禁用嵌入式 LB,请使用 --disable servicelb 选项启动每个 Server。
    • Traefik Ingress Controller
      • Traefik 是一个现代的 HTTP 反向代理和负载均衡器。启动 Server 时,默认情况下会部署 Traefik。Traefik ingress controller 将使用主机上的 80 和 443 端口(即这些端口不能用于 HostPort 或 NodePort)。
      • 要禁用它,请使用 --disable traefik 选项启动每个 server。

验证 K3s 跨云集群及网络

验证跨云集群

$ kubectl get nodes -owide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k3s-node-01 Ready control-plane,master 69m v1.23.14+k3s1 10.0.20.12 42.193.XXX.XXX Ubuntu 20.04 LTS 5.4.0-96-generic docker://20.10.13
k3s-node-02 Ready <none> 63m v1.23.14+k3s1 45.63.XXX.XXX 45.63.YYY.YYY Ubuntu 20.04.3 LTS 5.4.0-131-generic docker://20.10.11
k3s-node-03 Ready <none> 16m v1.23.14+k3s1 13.22.ZZZ.ZZZ 13.22.ZZZ.ZZZ Ubuntu 20.04.5 LTS 5.4.0-122-generic docker://20.10.12
$ kubectl create deploy whoami --image=traefik/whoami --replicas=3
deployment.apps/whoami created

$ kubectl get pod -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
whoami-84d974bbd6-57bnt 1/1 Running 0 10m 10.42.1.4 k3s-node-02 <none> <none>
whoami-84d974bbd6-hlhdq 1/1 Running 0 10m 10.42.2.2 k3s-node-03 <none> <none>
whoami-84d974bbd6-g894t 1/1 Running 0 10m 10.42.0.6 k3s-node-01 <none> <none>

$ kubectl create deploy nginx --image=nginx --replicas=3
deployment.apps/nginx created

$ kubectl get pod -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
whoami-84d974bbd6-hlhdq 1/1 Running 0 82m 10.42.2.2 k3s-node-03 <none> <none>
whoami-84d974bbd6-g894t 1/1 Running 0 82m 10.42.0.6 k3s-node-01 <none> <none>
whoami-84d974bbd6-57bnt 1/1 Running 0 82m 10.42.1.4 k3s-node-02 <none> <none>
nginx-85b98978db-ptvcb 1/1 Running 0 32s 10.42.1.5 k3s-node-02 <none> <none>
nginx-85b98978db-m2nlm 1/1 Running 0 32s 10.42.2.3 k3s-node-03 <none> <none>
nginx-85b98978db-fs8gk 1/1 Running 0 32s 10.42.0.17 k3s-node-01 <none> <none>

验证跨云网络

通过集群内置的 CoreDNS、Service、Pod 相互间调试网络,验证不同节点的网络是否可达。

开始前先快速创建一个名为 whoami 的 Service:

$ kubectl expose deploy whoami --type LoadBalancer --port 80 --external-ip 42.193.XXX.XXX
service/whoami exposed

$ kubectl get svc -owide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 75m <none>
whoami LoadBalancer 10.43.77.192 42.193.XXX.XXX 80:32064/TCP 12s app=whoami

$ kubectl describe svc whoami
Name: whoami
Namespace: default
Labels: app=whoami
Annotations: <none>
Selector: app=whoami
Type: LoadBalancer
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.43.77.192
IPs: 10.43.77.192
External IPs: 42.193.XXX.XXX
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 32064/TCP
Endpoints: 10.42.0.6:80,10.42.1.4:80,10.42.2.2:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>

那怎么测试 Service 的负载均衡效果呢?

因为 Service、Pod 的 IP 地址都是 Kubernetes 集群的内部网段,所以我们需要用 kubectl exec 进入到 Pod 内部(或者 ssh 登录到集群的任一节点),再通过 curl 等工具来访问 Service。

得助于集群内置的 CoreDNS,我们可以在集群内部通过域名的方式访问相应的 Service、Pod:

  • Service 对象的域名完全形式是“对象.名字空间.svc.cluster.local”,但很多时候也可以省略后面的部分,直接写“对象.名字空间”甚至“对象”就足够了,默认会使用对象所在的名字空间(比如这里就是 default
    • 比如 whoamiwhoami.default
  • Kubernetes 也为每个 Pod 分配了域名,形式是“IP 地址.名字空间.pod.cluster.local”,但需要把 IP 地址里的 . 改成 -
    • 比如 10.42.2.2,它对应的域名就是 10-42-2-2.default.pod

这样我们就无需再关心 Service、Pod 对象的 IP 地址,只需要知道它的名字,就可以用 DNS 的方式去访问后端服务了。

通过外网访问

$ curl http://42.193.XXX.XXX:32064
Hostname: whoami-84d974bbd6-57bnt
IP: 127.0.0.1
IP: 10.42.1.4
RemoteAddr: 10.42.0.0:42897
GET / HTTP/1.1
Host: 42.193.XXX.XXX:32064
User-Agent: curl/7.68.0
Accept: */*

$ curl http://42.193.XXX.XXX:32064
Hostname: whoami-84d974bbd6-hlhdq
IP: 127.0.0.1
IP: 10.42.2.2
RemoteAddr: 10.42.0.0:3478
GET / HTTP/1.1
Host: 42.193.XXX.XXX:32064
User-Agent: curl/7.68.0
Accept: */*

$ curl http://42.193.XXX.XXX:32064
Hostname: whoami-84d974bbd6-g894t
IP: 127.0.0.1
IP: 10.42.0.6
RemoteAddr: 10.42.0.1:3279
GET / HTTP/1.1
Host: 42.193.XXX.XXX:32064
User-Agent: curl/7.68.0
Accept: */*

通过外网访问 Service,经过多次请求后,可以发现从主节点 [http://42.193.XXX.XXX:32064](http://42.193.XXX.XXX:32064) 作为访问入口,可以负载到不同节点上的 Pod 并正确响应了。

通过集群各节点访问 Service CLUSTER-IP

root@k3s-node-01:~# curl 10.43.77.192:80
Hostname: whoami-84d974bbd6-g894t
IP: 127.0.0.1
IP: 10.42.0.6
RemoteAddr: 10.42.0.1:22291
GET / HTTP/1.1
Host: 10.43.77.192
User-Agent: curl/7.68.0
Accept: */*

root@k3s-node-01:~# curl 10.43.77.192:80
Hostname: whoami-84d974bbd6-57bnt
IP: 127.0.0.1
IP: 10.42.1.4
RemoteAddr: 10.42.0.0:23957
GET / HTTP/1.1
Host: 10.43.77.192
User-Agent: curl/7.68.0
Accept: */*

root@k3s-node-01:~# curl 10.43.77.192:80
Hostname: whoami-84d974bbd6-hlhdq
IP: 127.0.0.1
IP: 10.42.2.2
RemoteAddr: 10.42.0.0:26130
GET / HTTP/1.1
Host: 10.43.77.192
User-Agent: curl/7.68.0
Accept: */*

通过节点间直接访问 Service,经过多次请求后,也可以正常负载到不同节点上的 Pod 并正确响应了。

从集群内部访问 Service、Pod

$ kubectl exec -it nginx-85b98978db-ptvcb -- sh
# curl whoami
Hostname: whoami-84d974bbd6-g894t
IP: 127.0.0.1
IP: 10.42.0.6
RemoteAddr: 10.42.1.5:36010
GET / HTTP/1.1
Host: whoami
User-Agent: curl/7.74.0
Accept: */*

# curl whoami.default
Hostname: whoami-84d974bbd6-57bnt
IP: 127.0.0.1
IP: 10.42.1.4
RemoteAddr: 10.42.1.5:33050
GET / HTTP/1.1
Host: whoami.default
User-Agent: curl/7.74.0
Accept: */*

# curl whoami.default.svc.cluster.local
Hostname: whoami-84d974bbd6-hlhdq
IP: 127.0.0.1
IP: 10.42.2.2
RemoteAddr: 10.42.1.5:57358
GET / HTTP/1.1
Host: whoami.default.svc.cluster.local
User-Agent: curl/7.74.0
Accept: */*

经过多方面的网络验证,证明使用 Flannal 集成 WireGuard 的多云组网环境是可用的,可以放心玩耍了。

参考链接


原文地址:https://y0ngb1n.github.io/a/setup-k3s-cluster-multicloud-with-wireguard.html

如果你觉得内容还算实用,欢迎点赞分享给你的朋友,在此谢过。

如果你想更快的看到后续内容的更新,请戳 “点赞”、“分享”、“喜欢”,这些免费的鼓励将会影响后续有关内容的更新速度。


感谢您的阅读,本文由 杨斌的博客 版权所有。如若转载,请注明出处:杨斌的博客(https://y0ngb1n.github.io/a/setup-k3s-cluster-multicloud-with-wireguard.html
Kubernetes 入门到实践:搭建 K3s 集群初体验