如何使用Cluster Mesh进行多区域Kubernetes Pod通信

144 阅读9分钟

由于AWS、GCP和Azure提供的服务,开发跨越多个地区的应用程序已经变得相对容易。这很好,因为缓慢的应用程序会扼杀企业。这些应用有一个共同的问题:它们不被多区域数据库架构所支持。

在这篇博客中,我将为让Kubernetes pods在多区域部署中相互对话的问题提供一个解决方案。

让我备份一下:CockroachDB经常被部署在Kubernetes内部。这是因为CockroachDB是一个单一的二进制文件,这使得它非常适合在容器和Kubernetes中运行(你可以在这里阅读更多信息)。然而,当我们希望在多个地区或云供应商之间部署CockroachDB时,这给我们带来了一些挑战。Kubernetes的设计是为我们的容器化工作负载提供一个pod网络,以便相互之间进行交流。这个网络通常不会暴露在Kubernetes集群之外。这是一个问题,因为所有的CockroachDB pods都需要相互交流。

一个典型的部署模式是,每个区域都有自己的Kubernetes集群。这样做是非常合理的,可以确保延迟降到最低,而且如果网络中断,工作节点也不会成为 "孤岛"。这对CockroachDB来说是个问题,因为pod网络在局域网上是不能路由的,当然也不能跨区域看到。我们如何克服这个问题呢?

Kubernetes是一个允许整合许多不同组件的框架。从网络的角度来看,可以按照容器网络接口(CNI)标准来开发接口。这允许我们使用不同的网络插件来为我们提供不同的能力。

在这种情况下,我们需要集群间的pod路由。由超大规模公司开发的CNI允许通过在虚拟网络上给pod一个IP地址来实现。这很有帮助,但是如果我们不在公有云中,或者想在不同的云供应商中对单一的CNI进行标准化呢?在这里,我们可以使用Cilium,这个CNI有一个叫做Cluster Mesh的功能,它允许我们将不同的Kubernetes集群 "网格化",并进行跨集群的pod-to-pod通信。让我们来看看如何更详细地实现这一目标。

设置多区域的Azure网络配置和虚拟机

我们需要做的第一件事是准备基础设施。在这个例子中,我们将使用Azure,但你可以使用其他云,如AWS或GCP,甚至是它们的混合。

为了使其可重复,我使用了Azure CLI 来创建所有的资源,首先是网络。我们创建一个资源组,这只是一个逻辑容器,用来存储我们所有的资源。虽然一个资源组属于一个特定的区域,但它可以包含其他区域的资源,就像我们的情况一样。

az group create --name $rg --location $loc1

我们现在在我们选择的每个区域创建一个虚拟网络。我们给每个虚拟网络一个地址前缀,以及地址前缀范围内的一个子网。所有的区域都没有重叠的地址空间,这对确保路由工作的预期和所有CockroachDB节点能够进行通信非常重要。

az network vnet create -g $rg -l $loc1 -n crdb-$loc1 --address-prefix 10.1.0.0/16 \
    --subnet-name crdb-$loc1-sub1 --subnet-prefix 10.1.1.0/24
az network vnet create -g $rg -l $loc2 -n crdb-$loc2 --address-prefix 10.2.0.0/16 \
    --subnet-name crdb-$loc2-sub1 --subnet-prefix 10.2.1.0/24
az network vnet create -g $rg -l $loc3 -n crdb-$loc3 --address-prefix 10.3.0.0/16 \
    --subnet-name crdb-$loc3-sub1 --subnet-prefix 10.3.1.0/24

默认情况下,虚拟网络是不能相互通信的。因此,我们需要创建虚拟网络对等物,每个区域两个。

az network vnet peering create -g $rg -n $loc1-$loc2-peer --vnet-name crdb-$loc1 \
    --remote-vnet crdb-$loc2 --allow-vnet-access --allow-forwarded-traffic --allow-gateway-transit
az network vnet peering create -g $rg -n $loc2-$loc3-peer --vnet-name crdb-$loc2 \
    --remote-vnet crdb-$loc3 --allow-vnet-access --allow-forwarded-traffic --allow-gateway-transit
az network vnet peering create -g $rg -n $loc1-$loc3-peer --vnet-name crdb-$loc1 \
    --remote-vnet crdb-$loc3 --allow-vnet-access --allow-forwarded-traffic --allow-gateway-transit
az network vnet peering create -g $rg -n $loc2-$loc1-peer --vnet-name crdb-$loc2 \
    --remote-vnet crdb-$loc1 --allow-vnet-access --allow-forwarded-traffic --allow-gateway-transit
az network vnet peering create -g $rg -n $loc3-$loc2-peer --vnet-name crdb-$loc3 \
    --remote-vnet crdb-$loc2 --allow-vnet-access --allow-forwarded-traffic --allow-gateway-transit
az network vnet peering create -g $rg -n $loc3-$loc1-peer --vnet-name crdb-$loc3 \
    --remote-vnet crdb-$loc1 --allow-vnet-access --allow-forwarded-traffic --allow-gateway-transit

在Azure中,网络适配器本身就是一种资源。这些适配器与一个特定的子网和虚拟机相关。

az network public-ip create --resource-group $rg --location $loc1 --name crdb-$loc1-ip1 --sku standard
az network public-ip create --resource-group $rg --location $loc1 --name crdb-$loc1-ip2 --sku standard
az network public-ip create --resource-group $rg --location $loc1 --name crdb-$loc1-ip3 --sku standard
az network public-ip create --resource-group $rg --location $loc2 --name crdb-$loc2-ip1 --sku standard
az network public-ip create --resource-group $rg --location $loc2 --name crdb-$loc2-ip2 --sku standard
az network public-ip create --resource-group $rg --location $loc2 --name crdb-$loc2-ip3 --sku standard
az network public-ip create --resource-group $rg --location $loc3 --name crdb-$loc3-ip1 --sku standard
az network public-ip create --resource-group $rg --location $loc3 --name crdb-$loc3-ip2 --sku standard
az network public-ip create --resource-group $rg --location $loc3 --name crdb-$loc3-ip3 --sku standard
az network nic create --resource-group $rg -l $loc1 --name crdb-$loc1-nic1 --vnet-name crdb-$loc1 --subnet crdb-$loc1-sub1 --network-security-group crdb-$loc1-nsg --public-ip-address crdb-$loc1-ip1
az network nic create --resource-group $rg -l $loc1 --name crdb-$loc1-nic2 --vnet-name crdb-$loc1 --subnet crdb-$loc1-sub1 --network-security-group crdb-$loc1-nsg --public-ip-address crdb-$loc1-ip2
az network nic create --resource-group $rg -l $loc1 --name crdb-$loc1-nic3 --vnet-name crdb-$loc1 --subnet crdb-$loc1-sub1 --network-security-group crdb-$loc1-nsg --public-ip-address crdb-$loc1-ip3
az network nic create --resource-group $rg -l $loc2 --name crdb-$loc2-nic1 --vnet-name crdb-$loc2 --subnet crdb-$loc2-sub1 --network-security-group crdb-$loc2-nsg --public-ip-address crdb-$loc2-ip1
az network nic create --resource-group $rg -l $loc2 --name crdb-$loc2-nic2 --vnet-name crdb-$loc2 --subnet crdb-$loc2-sub1 --network-security-group crdb-$loc2-nsg --public-ip-address crdb-$loc2-ip2
az network nic create --resource-group $rg -l $loc2 --name crdb-$loc2-nic3 --vnet-name crdb-$loc2 --subnet crdb-$loc2-sub1 --network-security-group crdb-$loc2-nsg --public-ip-address crdb-$loc2-ip3
az network nic create --resource-group $rg -l $loc3 --name crdb-$loc3-nic1 --vnet-name crdb-$loc3 --subnet crdb-$loc3-sub1 --network-security-group crdb-$loc3-nsg --public-ip-address crdb-$loc3-ip1
az network nic create --resource-group $rg -l $loc3 --name crdb-$loc3-nic2 --vnet-name crdb-$loc3 --subnet crdb-$loc3-sub1 --network-security-group crdb-$loc3-nsg --public-ip-address crdb-$loc3-ip2
az network nic create --resource-group $rg -l $loc3 --name crdb-$loc3-nic3 --vnet-name crdb-$loc3 --subnet crdb-$loc3-sub1 --network-security-group crdb-$loc3-nsg --public-ip-address crdb-$loc3-ip3

网络配置的最后一部分是网络安全组。这些组从网络角度控制对资源的访问。在这个演示中,我们将允许访问SSH的22号端口、访问Kubernetes API的6443号端口以及Kubernetes的NodePort范围30000-32767。

第一步:在每个区域创建一个NSG。

az network nsg create --resource-group $rg --location $loc1 --name crdb-$loc1-nsg
az network nsg create --resource-group $rg --location $loc2 --name crdb-$loc2-nsg
az network nsg create --resource-group $rg --location $loc3 --name crdb-$loc3-nsg

第二步:允许SSH访问

在每个NSG中创建一个规则,允许SSH访问虚拟机,使我们能够通过SSH和k3sup部署Kubernetes。

az network nsg rule create -g $rg --nsg-name crdb-$loc1-nsg -n NsgRuleSSH --priority 100 \
    --source-address-prefixes '*' --source-port-ranges '*' \
    --destination-address-prefixes '*' --destination-port-ranges 22 --access Allow \
    --protocol Tcp --description "Allow SSH Access to all VMS."

az network nsg rule create -g $rg --nsg-name crdb-$loc2-nsg -n NsgRuleSSH --priority 100 \
    --source-address-prefixes '*' --source-port-ranges '*' \
    --destination-address-prefixes '*' --destination-port-ranges 22 --access Allow \
    --protocol Tcp --description "Allow SSH Access to all VMS."

az network nsg rule create -g $rg --nsg-name crdb-$loc3-nsg -n NsgRuleSSH --priority 100 \
    --source-address-prefixes '*' --source-port-ranges '*' \
    --destination-address-prefixes '*' --destination-port-ranges 22 --access Allow \
    --protocol Tcp --description "Allow SSH Access to all VMS."

第三步。允许Kubernetes API访问

在所有区域创建一个规则,允许访问每个集群的Kubernetes API。这将使我们能够在每个集群中创建所需的资源来运行CockroachDB。

az network nsg rule create -g $rg --nsg-name crdb-$loc1-nsg -n NsgRulek8sAPI --priority 200 \
    --source-address-prefixes '*' --source-port-ranges '*' \
    --destination-address-prefixes '*' --destination-port-ranges 6443 --access Allow \
    --protocol Tcp --description "Allow Kubernetes API Access to all VMS."

az network nsg rule create -g $rg --nsg-name crdb-$loc2-nsg -n NsgRulek8sAPI --priority 200 \
    --source-address-prefixes '*' --source-port-ranges '*' \
    --destination-address-prefixes '*' --destination-port-ranges 6443 --access Allow \
    --protocol Tcp --description "Allow Kubernetes API Access to all VMS."

az network nsg rule create -g $rg --nsg-name crdb-$loc3-nsg -n NsgRulek8sAPI --priority 200 \
    --source-address-prefixes '*' --source-port-ranges '*' \
    --destination-address-prefixes '*' --destination-port-ranges 6443 --access Allow \
    --protocol Tcp --description "Allow Kubernetes API Access to all VMS."

第四步。允许NodePort访问

创建一个规则,开放对Kubernetes NodePort范围的访问,以允许访问我们暴露的任何资源。

az network nsg rule create -g $rg --nsg-name crdb-$loc1-nsg -n NsgRuleNodePorts --priority 300 \
    --source-address-prefixes '*' --source-port-ranges '*' \
    --destination-address-prefixes '*' --destination-port-ranges 30000-32767 --access Allow \
    --protocol Tcp --description "Allow Kubernetes NodePort Access to all VMS."

az network nsg rule create -g $rg --nsg-name crdb-$loc2-nsg -n NsgRuleNodePorts --priority 300 \
    --source-address-prefixes '*' --source-port-ranges '*' \
    --destination-address-prefixes '*' --destination-port-ranges 30000-32767 --access Allow \
    --protocol Tcp --description "Allow Kubernetes NodePort Access to all VMS."

az network nsg rule create -g $rg --nsg-name crdb-$loc3-nsg -n NsgRuleNodePorts --priority 300 \
    --source-address-prefixes '*' --source-port-ranges '*' \
    --destination-address-prefixes '*' --destination-port-ranges 30000-32767 --access Allow \
    --protocol Tcp --description "Allow Kubernetes NodePort Access to all VMS."

请记住,这只是一个演示,并不是为生产使用而设计的,所以在现实世界的场景中要考虑更多的限制性规则。下面是一个描述这些资源的图表。

NSG Rules for Azure Deployment

基础设施的最后一个元素是九个虚拟机,它们将支持Kubernetes的安装。每个区域将部署三个虚拟机。

区域一。

az vm create \
  --resource-group $rg \
  --location $loc1 \
  --name crdb-$loc1-node1 \
  --image UbuntuLTS \
  --nics crdb-$loc1-nic1 \
  --admin-username ubuntu \
  --generate-ssh-keys

az vm create \
  --resource-group $rg \
  --location $loc1 \
  --name crdb-$loc1-node2 \
  --image UbuntuLTS \
  --nics crdb-$loc1-nic2 \
  --admin-username ubuntu \
  --generate-ssh-keys

az vm create \
  --resource-group $rg \
  --location $loc1 \
  --name crdb-$loc1-node3 \
  --image UbuntuLTS \
  --nics crdb-$loc1-nic3 \
  --admin-username ubuntu \
  --generate-ssh-keys

区域二。

az vm create \
  --resource-group $rg \
  --location $loc2 \
  --name crdb-$loc2-node1 \
  --image UbuntuLTS \
  --nics crdb-$loc2-nic1 \
  --admin-username ubuntu \
  --generate-ssh-keys

az vm create \
  --resource-group $rg \
  --location $loc2 \
  --name crdb-$loc2-node2 \
  --image UbuntuLTS \
  --nics crdb-$loc2-nic2 \
  --admin-username ubuntu \
  --generate-ssh-keys

az vm create \
  --resource-group $rg \
  --location $loc2 \
  --name crdb-$loc2-node3 \
  --image UbuntuLTS \
  --nics crdb-$loc2-nic3 \
  --admin-username ubuntu \
  --generate-ssh-keys

区域三。

az vm create \
  --resource-group $rg \
  --location $loc3 \
  --name crdb-$loc3-node1 \
  --image UbuntuLTS \
  --nics crdb-$loc3-nic1 \
  --admin-username ubuntu \
  --generate-ssh-keys

az vm create \
  --resource-group $rg \
  --location $loc3 \
  --name crdb-$loc3-node2 \
  --image UbuntuLTS \
  --nics crdb-$loc3-nic2 \
  --admin-username ubuntu \
  --generate-ssh-keys

az vm create \
  --resource-group $rg \
  --location $loc3 \
  --name crdb-$loc3-node3 \
  --image UbuntuLTS \
  --nics crdb-$loc3-nic3 \
  --admin-username ubuntu \
  --generate-ssh-keys

K3s Kubernetes的部署

在演示中,我们将使用k3s作为Kubernetes分布。这是一个 轻量级的、经过CNCF认证的Kubernetes版本,以单个二进制文件的形式运行。这使得它易于部署和快速启动。部署工具k3sup(称'番茄酱')可以用来将k3s部署到我们的虚拟机上。K3s有一个服务器代理架构。在这个演示中,每个区域将部署一个服务器和两个代理。每个节点将运行Kubernetes的所有角色(控制平面、etcd、worker)。在生产环境中,建议将这些角色分离到独立的基础设施上。

作为安装过程的一部分,Flannel的默认CNI被禁用,以允许部署Cilium。这将是提供连接集群的能力并允许跨集群网络通信的网络供应商。我们可以用以下命令下载并安装k3sup。

curl -sLS https://get.k3sup.dev | sh
sudo install k3sup /usr/local/bin/
k3sup --help

然后我们可以使用Azure CLI将服务器节点的IP地址存储为环境变量,然后部署k3s。

MASTERR1=$(az vm show -d -g $rg  -n crdb-$loc1-node1 --query publicIps -o tsv)
k3sup install \
  --ip=$MASTERR1 \
  --user=ubuntu \
  --sudo \
  --cluster \
  --k3s-channel stable \
  --k3s-extra-args '--flannel-backend none --disable-network-policy' \
  --merge \
  --local-path $HOME/.kube/config \
  --context=$clus1

然后用同样的方法把它k3s部署到代理上。

AGENT1R1=$(az vm show -d -g $rg  -n crdb-$loc1-node2 --query publicIps -o tsv)
k3sup join \
  --ip $AGENT1R1 \
  --user ubuntu \
  --sudo \
  --k3s-channel stable \
  --server \
  --server-ip $MASTERR1 \
  --server-user ubuntu \
  --sudo \
  --k3s-extra-args '--flannel-backend=none --disable-network-policy'
AGENT2R1=$(az vm show -d -g $rg  -n crdb-$loc1-node3 --query publicIps -o tsv)
k3sup join \
  --ip $AGENT2R1 \
  --user ubuntu \
  --sudo \
  --k3s-channel stable \
  --server \
  --server-ip $MASTERR1 \
  --server-user ubuntu \
  --sudo \
  --k3s-extra-args '--flannel-backend=none --disable-network-policy'

现在我们只需要对其他两个区域重复前面的两个步骤。

如何使用Cilium Cluster Mesh

在这个演示中,Cilium CLI被用来部署CNI和配置Cluster Mesh。在生产环境中,可以使用GitOps方法来自动部署。Cilium CLI可以通过以下命令下载和安装。

curl -L --remote-name-all https://github.com/cilium/cilium-cli/releases/latest/download/cilium-linux-amd64.tar.gz{,.sha256sum}
sha256sum --check cilium-linux-amd64.tar.gz.sha256sum
sudo tar xzvfC cilium-linux-amd64.tar.gz /usr/local/bin
rm cilium-linux-amd64.tar.gz{,.sha256sum}

Cilium Cluster Mesh控制平面的架构是基于etcd的。每个集群都维护自己的etcd实例。这包含了该集群的当前状态,来自多个集群的状态从不在etcd中混合。每个集群通过一组代理暴露自己的etcd。在其他集群中运行的代理连接到代理,观察集群状态的变化,并将多集群的变化复制到他们自己的集群中。访问受到TLS证书的保护。从其他集群对一个集群的etcd的访问始终是只读的。这确保了一个集群的故障不会传播到其他集群。配置是通过一个简单的Kubernetes秘密资源进行的,该资源包含远程etcd代理的地址信息,以及集群名称和访问etcd代理所需的证书。

Cilium Cluster Mesh

荚的IP路由是多集群功能的主要能力。它允许跨集群的pod通过其pod IP到达对方。Cilium可以在几种模式下运行,执行pod IP路由。所有这些模式都能够执行多集群吊舱IP路由。

隧道模式将吊舱发出的所有网络数据包封装在一个所谓的封装头中。封装头可以由一个VXLAN或Geneve帧组成。然后,这个封装帧通过标准的UDP数据包头进行传输。这个概念类似于VPN隧道。

Cilium Cluster Mesh

在底层网络上永远看不到吊舱的IP。网络只看到工作节点的IP地址。这可以简化安装和防火墙规则。

所需的额外网络头将减少网络的理论最大吞吐量。具体的成本取决于配置的MTU,与使用MTU9000的巨型帧相比,使用1500的传统MTU时,成本会更加明显。

CockroachDB在多个地区的部署

CockroachDB的github资源库 中,有一个Python脚本,可以帮助在多个地区自动部署CockroachDB。我们需要用前面步骤中创建的Kubernetes上下文和区域的名称来更新Python脚本。

contexts = { 'eastus': 'crdb-k3s-eastus', 'westus': 'crdb-k3s-westus', 'northeurpoe': 'crdb-k3s-northeurope',}
regions = { 'eastus': 'eastus', 'westus': 'westus', 'northeurope': 'northeurope',}

保存之后,就可以运行该脚本了。这个脚本在三个区域内部署了运行CockroachDB所需的所有资源。一旦完成,还有最后一个步骤要完成。

在每个Kubernetes集群中,都有一个CoreDNS的部署,它负责在其部署的Kubernetes集群中进行名称解析。但是,如果不对包含CoreDNS配置的ConfigMap进行修改,它就无法解析来自其他Kubernetes集群的名称。该配置可以被更新,以包括其他两个集群的转发器,因此一个集群中的pod可以解析另一个集群中的pod名称。这是CockroachDB所需要的,集群中的所有节点都必须能够相互通信。下面是每个Kubernetes集群中所需要的变化的例子。

westus.svc.cluster.local:53 {       # <---- Modify
          log
          errors
          ready
          cache 10
          forward . IP1 IP2 IP3 {      # <---- Modify
          }
      }
      }
      northeurope.svc.cluster.local:53 {       # <---- Modify
          log
          errors
          ready
          cache 10
          forward . IP1 IP2 IP3 {      # <---- Modify
          }
      }

将更新的ConfigMaps应用到每个集群中,集群就应该建立起来了。

多区域的网络原则

当你踏上CockroachDB的多区域集群之旅时,记得在开始部署前不要忘记一些基本的网络原则。

- 连接性,你的集群中所有的pod都需要能够相互对话,所以要选择一个能够允许这样做的解决方案。改变它将是破坏性的!DNS!

- 连接性是一件事,但能够执行名称解析也是一个很高的要求。每个集群都有自己的DNS部署,并且不知道其他集群的情况。因此,请确保你也为这个问题做好计划。

一旦你把这些问题解决了,就会很顺利了。这里有一个github repo的链接,其中包括更详细的步骤说明,以及你在自己的订阅中部署这个解决方案所需的所有代码。