
点击上方 云原生CTO,选择 设为星标
优质文章,每日送达

「【只做懂你de云原生干货知识共享】」
如何部署具有内置灾难恢复功能的跨云 Kubernetes 集群

本文介绍了如何运行跨越混合云环境的单个 Kubernetes 集群,使其具有故障恢复能力。它将解释为什么这是必要的,以及如何使用 MicroK8s、WireGuard 和 Netmaker 来实现这个架构。好的,准备好了吗?
Kubernetes 很难,但你知道什么更难吗?多云、多集群 Kubernetes,这是您在生产中运行 Kubernetes 时不可避免地要处理的问题。
通常,您将至少为任何生产设置部署两个集群:一个作为实时环境,另一个用于故障转移(灾难恢复)。这可能会让你想知道:
“为什么我需要两个集群来处理灾难恢复?我以为 Kubernetes 有分布式架构?”
正确的!Kubernetes 是分布式的!它分布在单个数据中心/区域内。除此之外……没有那么多。
通常,要拥有“高度可用”的基础架构,您最终会部署两个(或更多)集群,最重要的是您必须部署自动化工具来在集群之间移动和复制应用程序,以及某种机制来处理当集群出现故障时进行故障转移。听起来很有趣,对吧?
坚持下去,我们将通过一种不那么痛苦的方式来处理灾难恢复(以及混合工作负载,就此而言),这种方法不需要额外的工具,并且可以在单个集群中执行。
分布式集群——MicroK8s 和 Netmaker
存在三个限制通常会阻止您跨环境使用单个集群:
「Etcd」:它是集群的大脑,不能容忍延迟。在地理上分离的环境中运行它是有问题的。
「网络」:集群节点需要能够直接且安全地相互通信。
「延迟」:对于企业应用程序来说,高延迟是不可接受的。如果基于微服务的应用程序跨越多个环境,您最终可能会获得次优的性能。
我们可以用 MicroK8s 和 Netmaker 解决所有三个问题:
「Etcd」:Etcd 是 Kubernetes 的默认数据存储,但它不是唯一的选择。MicroK8s默认运行 Dqlite。Dqlite 具有延迟容忍性,允许您运行相距很远的主节点而不会破坏集群。
「网络」:Netmaker 易于与 Kubernetes 集成,并通过 WireGuard 创建扁平、安全的网络,供节点进行通信。
「延迟」:Netmaker 是可用的最快的虚拟网络平台之一,因为它使用内核 WireGuard,对网络性能的降低可以忽略不计(与 OpenVPN 等选项不同)。此外,我们可以使用 Kubernetes 的内置放置策略将应用程序组合到同一数据中心的节点上,从而消除跨云延迟问题。
所以,现在我们有了答案。通过运行 MicroK8s 和 Netmaker,您可以消除复杂的、传统的、多集群部署。您可以用更少的工作和更简单的架构获得相同的结果。
话不多说,让我们付出行动吧!
设置我们的环境
我们将使用三个环境。这确保了如果任何一个环境出现故障,我们的master节点仍然可以形成共识。
你会注意到我们不会区分master和worker。那是因为在 MicroK8s 中,每个节点都有一个控制平面的副本,所以真的没有区别。我们的集群布局
数据中心:2个节点(datacenter1、datacenter2)
DigitalOcean(区域 1):1 个节点(do1)
DigitalOcean(区域 2):1 个节点(do2)
我们有两个数据中心节点和两个云节点,可用于故障转移。
我们将 DigitalOcean 用于我们的云节点,因为它们的带宽成本最低。根据您的云提供商的不同,数据传输成本可能会快速增加。DigitalOcean 的带宽定价非常合理,您应该能够运行您的集群而根本不会产生额外的成本。
我们所有的节点都运行 Ubuntu 20.04,在运行本教程之前,每个节点都应该安装 WireGuard。
apt install wireguard wireguard-tools
节点 1:seed
SSH 到您的第一个节点,该节点将充当集群的“seed”,因为它将设置 Netmaker 并建立将在其他节点上运行的网络。此节点应可公开访问。我们正在使用do1:
ssh root@do1
snap install microk8s --classic
microk8s enable dns ingress storage
您会注意到我们使用的是内置 MicroK8s 存储。对于生产设置,您可能需要更强大的东西,比如另一个 MicroK8s 插件 openebs。接下来,确保您已将通配符 DNS 设置为指向这台机器。例如,在 Route53 中,您可以为 *.kube.mydomain.com 创建一条记录,指向该机器的公共 IP。
完成后,让我们设置一些证书:
microk8s kubectl create namespace cert-manager
microk8s kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v0.14.2/cert-manager.yaml
然后创建并应用以下 clusterissuer.yaml,用您的电子邮件替换 EMAIL_ADDRESS 占位符:
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
namespace: cert-manager
spec:
acme:
# The ACME server URL
server: https://acme-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: EMAIL_ADDRESS
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-prod
# Enable the HTTP-01 challenge provider
solvers:
- http01:
ingress:
class: public
证书管理器需要几分钟才能可用,因此在执行上述步骤后,此命令不会立即成功:
microk8s kubectl apply -f clusterissuer.yaml
现在,我们已准备好部署 Netmaker。首先,wget模板:
wget https://raw.githubusercontent.com/gravitl/netmaker/develop/kube/netmaker-template.yaml
接下来,插入您希望 Netmaker 拥有的域。这必须是您上面设置的 DNS 的子域,例如,如果您的外部负载均衡器指向 *.kube.mydomain.com,您可以选择 nm.kube.mydomain.com。然后模板将在顶部添加以下子域:dashboard.nm.kube.mydomain.com、api.nm.kube.mydomain.com 和 grpc.nm.kube.mydomain.com。
sed -i ‘s/NETMAKER_BASE_DOMAIN/<your base domain>/g’ netmaker-template.yaml
现在,安装 Netmaker!
microk8s kubectl create ns nm
microk8s kubectl config set-context --current --namespace=nm
microk8s kubectl apply -f netmaker-template.yaml -n nm
大约需要 3 分钟才能让所有pod出现。启动后,转到仪表板(“microk8s kubectl get ingress”以查找域):
microk8s kubectl get ingress nm-ui-ingress-nginx
创建一个用户并登录。您将看到一个默认网络:

删除它(转到编辑→删除)并创建一个新的,我们将其称为 microk8s。确保 IP 范围不与 microk8s 重叠。我们给我们的 10.101.0.0/16:

现在我们需要一个密钥让我们的节点安全地连接到这个网络。单击“Access Keys”,生成一个新密钥(给它大量使用,例如 1000),然后单击创建。确保复制并保存您的访问令牌下的值。这只会出现一次。

这就是我们在 Netmaker 服务器中需要做的所有事情。现在我们可以使用netclient设置我们的节点,该代理处理每台机器上的网络。
但首先,MicroK8s 警告!MicroK8s 要求节点主机名可以相互访问才能正常运行。例如,如果您登录到您的机器并看到 root@mymachine,则 mymachine 应该是其他机器的可解析地址。只要我们正确设置了主机名,Netmaker 就会处理这个问题。
节点主机名的格式应为 nodename.networkname。由于我们的节点位于 microk8s 网络上,因此每个主机名都必须采用 .microk8s 格式。
在我们的“seed"节点上,让我们将主机名设置为do1.microk8s。这样我们就知道它是一个数字海洋节点。
hostnamectl set-hostname do1.microk8s
好的,现在我们已准备好部署 netclient:
wget https://github.com/gravitl/netmaker/releases/download/v0.5.11/netclient && chmod +x netclient
./netclient join -t <YOUR_TOKEN> --dns off --daemon off --name $(hostname | sed -es/.microk8s//)
您现在应该拥有nm-microk8s界面:
wg show
#example output
#interface: nm-microk8s
# public key: AQViVk8J7JZkjlzsV/xFZKqmrQfNGkUygnJ/lU=
# private key: (hidden)
# listening port: 51821
我们可以将 netclient 部署为 systemd 守护进程,但我们将使用集群守护进程来管理我们的 netclient。这使我们能够使用 Kubernetes 处理网络更改和升级。
wget https://raw.githubusercontent.com/gravitl/netmaker/develop/kube/netclient-template.yaml
sed -i ‘s/ACCESS_TOKEN_VALUE/< your access token value>/g’ netclient-template.yaml
microk8s kubectl apply -f netclient-template.yaml
这个守护进程接管 netclient 的管理并执行“签入”。如果一切顺利,您应该会看到类似于以下内容的日志:
root@do1:~# microk8s kubectl logs netclient-<id>
2021/07/13 17:11:16 attempting to join microk8s at grpc.nm.k8s.gravitl.com:443
2021/07/13 17:11:16 node created on remote server...updating configs
2021/07/13 17:11:16 retrieving remote peers
2021/07/13 17:11:16 starting wireguard
2021/07/13 17:11:16 joined microk8s
Checking into server at grpc.nm.k8s.gravitl.com:443
Checking to see if public addresses have changed
Local Address has changed from to 210.97.150.30
Updating address
2021/07/13 17:11:16 using SSL
Authenticating with GRPC Server
Authenticated
Checking In.
Checked in.
该节点现在也应该在 UI 中可见。

节点 2 到 X
对于所有后续节点,我们的任务很简单。在每个节点上运行这些步骤(一次一个,而不是并行),并耐心等待:您不想在任何先前的步骤有时间完成处理之前匆忙完成这些步骤。
「0. 更改主机名」
在加入节点上,运行:
hostnamectl set-hostname <nodename>.microk8s
「加入网络」
使用您在seed节点上使用的相同命令和密钥来安装 netclient 并加入网络:
wget https://github.com/gravitl/netmaker/releases/download/v0.5.11/netclient && chmod +x netclient
./netclient join -t <YOUR_TOKEN> --daemon off --dns off --name $(hostname | sed -es/.microk8s//)
使用wg show确认节点已加入网络:
root@datacenter2:~# wg show
interface: nm-microk8s
public key: 2xUDmCohypHcCD5dZukhhA8r6BGWN879J8vIhrcwSHg=
private key: (hidden)
listening port: 51821
peer: lrZkcSzWdgasgegaimEYnrr5CgopcEAIP8m3Q1M7+hiM=
endpoint: 192.168.88.151:51821
allowed ips: 10.101.0.3/32
latest handshake: 41 seconds ago
transfer: 736 B received, 2.53 KiB sent
persistent keepalive: every 20 seconds
peer: IUobp84wipq44aFGP0SLuRhdSsDWvcxvBFefeRCE=
endpoint: 210.97.150.30:51821
allowed ips: 10.101.0.1/32
latest handshake: 57 seconds ago
transfer: 128.45 MiB received, 9.03 MiB sent
persistent keepalive: every 20 seconds
「2.生成Join命令」
在“seed”节点上,运行microk8s add-node。复制包含由 Netmaker 创建的 WireGuard IP 地址的命令:
root@do1:~# microk8s add-node
From the node you wish to join to this cluster, run the following:
microk8s join 209.97.147.27:25000/14e3a77f1584cb42323f39ce8ece0852/be5e4c7be0c6
If the node you are adding is not reachable through the default interface you can use one of the following:
microk8s join 210.97.150.27:25000/14e3a77f1584bc42323f39ce8ece0852/be5e4c7eb0c6
microk8s join 10.17.0.5:25000/14e3a77f1584bc42323f39ce8ece0852/be5e4c7eb0c6
microk8s join 10.108.0.2:25000/14e3a77f1584bc42323f39ce8ece0852/be5e4c7eb0c6
microk8s join 10.101.0.1:25000/14e3a77f1584bc42323f39ce8ece0852/be5e4c7eb0c6
「3.加入集群」
在加入节点上:
microk8s 加入 10.101.0.1:25000/14e3a77f1584bc42323f39ce8ece0852/be5e4c7eb0c6
等待节点加入网络。以下是一些要运行的命令,它们将帮助您确定节点是否健康:
「microk8s kubectl get nodes」:应显示处于就绪状态的节点
「microk8s kubectl get pods -A」:所有 pod 都应该运行
「microk8s logs netclient-」:获取该节点上netclient的日志
对要添加到集群的每个节点重复步骤 0 到 3。要有耐心。在继续下一步/机器之前,等待每个节点加入网络和集群。
在此过程结束时,您的集群和 Netmaker 实例应如下所示:
root@do1:~/kube# microk8s kubectl get nodes -o wide
NAME STATUS VERSION INTERNAL-IP EXTERNAL-IP
do2.microk8s Ready v1.21.1-3+ba 10.101.0.2 <none>
datacenter1.microk8s Ready v1.21.1-3 +ba 10.101.0.3 <无>
do1.microk8s Ready v1.21.1-3+ba 10.101.0.1 <none>
datacenter2.microk8s Ready v1.21.1-3+ba 10.101.0.4 <无>
恭喜!您有一个跨云的 Kubernetes 集群,如果环境出现故障,它将转移工作负载。
DR 测试
现在我们已经设置了集群,我们可以测试一个 DR 场景,看看它是如何发挥作用的。让我们设置一个在数据中心运行的应用程序。首先,添加一些节点标签,以便我们知道哪个节点是哪个:
microk8s kubectl label nodes do1.microk8s do2.microk8s location=cloud
microk8s kubectl label nodes datacenter1.microk8s datacenter2.microk8s location=onprem
现在,我们将部署一个位于我们数据中心的 Nginx 应用程序:
wget https://raw.githubusercontent.com/gravitl/netmaker/develop/kube/example/nginx-example.yaml
#BASE_DOMAIN should be your wildcard, ex: app.example.com
#template will add a subdomain, ex: nginx.app.example.com
sed -i ‘s/BASE_DOMAIN/<your base domain>/g’ nginx-example.yaml
microk8s kubectl apply -f nginx-example.yaml
运行“get pods”以查看所有实例都在数据中心运行。这是由于部署中的“节点亲和性”标签所致。它对带有标签「location=onprem」 的节点具有亲和力。
root@do1:~# k get po -o wide | grep nginx
nginx-deployment-cb796dbc7-h72s8 1/1 Running 0 2m53s 10.1.99.68 datacenter1.microk8s <none> <none>
nginx-deployment-cb796dbc7-p5bhr 1/1 Running 0 2m53s 10.1.99.67 datacenter1.microk8s <none> <none>
nginx-deployment-cb796dbc7-pxpvw 1/1 Running 0 2m53s 10.1.247.3 datacenter2.microk8s <none> <none>
nginx-deployment-cb796dbc7-7vbwz 1/1 Running 0 2m53s 10.1.247.4 datacenter2.microk8s <none> <none>
nginx-deployment-cb796dbc7-x862w 1/1 Running 0 2m53s 10.1.247.5 datacenter2.microk8s <none> <none>
转到入口域,您应该会看到 Nginx 欢迎屏幕:

模拟故障
在这种情况下,模拟故障非常容易。让我们关闭数据中心节点:
root@datacenter2:~# microk8s stop
root@datacenter1:~# microk8s stop
集群需要一段时间才能意识到节点丢失。默认情况下,Kubernetes 将在节点处于“NotReady”状态后等待 5 分钟,然后才开始重新调度 Pod。
对于正常运行时间非常关键的场景,您可以更改参数以使其更快地发生。检查节点状态:
root@do2:~# k get nodes
NAME STATUS ROLES AGE VERSION
do2.microk8s Ready <none> 77m v1.21.1-3+ba118484dd39df
do1.microk8s Ready <none> 106m v1.21.1-3+ba118484dd39df
datacenter1.microk8s NotReady <none> 62m v1.21.1-3+ba118484dd39df
datacenter2.microk8s NotReady <none> 40m v1.21.1-3+ba118484dd39df
最终,您应该会在云节点上看到旧 Pod 终止和新 Pod 调度:

就像以前一样,我们的网页完好无损:

因此,在没有多个集群或自定义自动化的情况下,我们成功设置了一个集群来为我们处理 DR!
结论
我们在这里学到了什么?
「DR 等场景历来需要多集群部署」
「多集群模型不是绝对必要的」
「您可以使用单个集群启用多集群模式」
「启用这些模式需要像 MicroK8s 和 Netmaker 这样的工具」
这种方法还支持许多其他用例。例如,您可以将应用程序突发到云中,将节点部署到边缘,或使用单个节点访问云环境中的资源。我们在这里列出了其中的一些模式。
在部署此类系统时,还需要考虑许多陷阱。网络可能会变得复杂,我们没有介绍可能出错的地方、如何修复它或如何优化此系统。
参考:
「https://itnext.io/how-to-deploy-a-cross-cloud-kubernetes-cluster-with-built-in-disaster-recovery-bbce27fcc9d7」




