29. 多集群通信

本指南将引导您安装和配置 Linkerd,以便两个集群可以与托管在两个集群上的服务通信。 这里有很多活动的部分和概念,因此通读我们的介绍很有价值, 该介绍解释了它在幕后是如何工作的。 在本指南结束时,您将了解如何在不同集群上的服务之间分配流量。

在较高级别,您将:

  1. 安装 Linkerd,在具有共享信任锚的两个集群上。
  2. 准备 集群。
  3. 链接 集群。
  4. 安装 demo。
  5. 暴露 demo 服务, 以控制可见性。
  6. 验证 集群的安全性。
  7. 拆分流量 ,将从源集群 (west) 上的 pod 的流量拆分到目标集群 (east)。

前提条件

  • 两个集群。我们将在本指南中将它们称为 eastwest。 在您浏览本指南时,请跟随 博客文章! 为开发执行此操作的最简单方法是在您的笔记本电脑上本地运行一个 kindk3d 集群, 并在云提供商(例如 AKS) 上远程运行一个集群。
  • 这些集群中的每一个都应配置为 kubectl contexts。 我们建议您使用 eastwest的名称, 以便您可以按照本指南进行操作。 使用 kubectl rename contexts 很容易,所以不要觉得你需要永远保持这种命名方式。
  • 两个集群上的提升权限。我们将创建服务帐户并授予扩展权限, 因此您需要能够在测试集群上执行此操作。
  • 应安装 Linkerdviz 扩展,以便运行 stat 命令、 查看 GrafanaLinkerd 仪表板并 运行 linkerd multicluster gateways 命令。
  • 支持 east 集群中的 LoadBalancer 类型的服务。 查看集群提供商的文档或查看 inlets。 这是 west 集群将用于通过网关与 east 通信的内容。

安装 Linkerd

install
Two Clusters

Linkerd 需要在所有相互通信的集群中的安装之间存在共享 trust anchor。 这用于加密集群之间的流量并授权到达网关的请求,以便您的集群不对公共互联网开放。 我们需要生成凭据并将它们用作 install 命令的配置,而不是让 linkerd 生成所有内容。

我们喜欢使用 step CLI 来生成这些证书。 如果您更喜欢 openssl,请随意使用它! 要使用 step 生成信任锚,您可以运行:

step certificate create root.linkerd.cluster.local root.crt root.key \
  --profile root-ca --no-password --insecure

该证书将构成所有集群之间的共同信任基础。 每个代理都将获得此证书的副本,并使用它来验证从对等方收到的证书, 作为 mTLS 握手的一部分。有了共同的信任基础, 我们现在需要生成一个证书,可以在每个集群中使用该证书向代理颁发证书。 如果您想更深入地了解这一切是如何运作的,请查看 深入了解

我们生成的信任锚是一个自签名证书,可用于创建新证书(证书颁发机构)。 要使用信任锚生成 issuer credentials,请运行:

step certificate create identity.linkerd.cluster.local issuer.crt issuer.key \
  --profile intermediate-ca --not-after 8760h --no-password --insecure \
  --ca root.crt --ca-key root.key

集群中的 identity 服务将使用您在此处生成的 certificatekey 来生成每个单独代理使用的证书。 虽然我们将在本指南的每个集群上使用相同的颁发者凭据, 但最好为每个集群使用不同的颁发者凭据。 通读证书文档以了解更多详细信息。

有了有效的 trust anchorissuer credentials, 我们现在就可以在您的 westeast 集群上安装 Linkerd

linkerd install \
  --identity-trust-anchors-file root.crt \
  --identity-issuer-certificate-file issuer.crt \
  --identity-issuer-key-file issuer.key \
  | tee \
    >(kubectl --context=west apply -f -) \
    >(kubectl --context=east apply -f -)

install 的输出将应用于每个集群并启动! 您可以使用 check 来验证一切是否成功。

for ctx in west east; do
  echo "Checking cluster: ${ctx} .........\n"
  linkerd --context=${ctx} check || break
  echo "-------------\n"
done

准备集群

preparation
Preparation

为了在集群之间路由流量,Linkerd 利用了 Kubernetes services, 因此您的应用程序代码无需更改,也无需学习任何新内容。 这需要一个网关组件,将传入请求路由到正确的内部服务。 网关将通过 LoadBalancer 类型的 Service 暴露给公共互联网。 仅允许通过 LinkerdmTLS(具有共享信任锚)验证的请求通过此网关。 如果您有兴趣,我们将更详细地说明为什么这在 多集群 Kubernetes 架构 中很重要。

要在 westeast 上安装多集群组件,您可以运行:

for ctx in west east; do
  echo "Installing on cluster: ${ctx} ........."
  linkerd --context=${ctx} multicluster install | \
    kubectl --context=${ctx} apply -f - || break
  echo "-------------\n"
done
install
Components

网关安装在 linkerd-multicluster 命名空间中,是一个简单的 pause container, 已注入 Linkerd 代理。 在入站端,Linkerd 负责验证连接是否使用了作为信任锚一部分的 TLS 证书,然后处理出站连接。 此时,Linkerd proxy 像数据平面中的任何其他代理一样运行,并将请求转发到正确的服务。 通过运行以下命令确保网关成功启动:

for ctx in west east; do
  echo "Checking gateway on cluster: ${ctx} ........."
  kubectl --context=${ctx} -n linkerd-multicluster \
    rollout status deploy/linkerd-gateway || break
  echo "-------------\n"
done

通过运行以下命令仔细检查负载均衡器是否能够分配公共 IP 地址:

for ctx in west east; do
  printf "Checking cluster: ${ctx} ........."
  while [ "$(kubectl --context=${ctx} -n linkerd-multicluster get service \
    -o 'custom-columns=:.status.loadBalancer.ingress[0].ip' \
    --no-headers)" = "<none>" ]; do
      printf '.'
      sleep 1
  done
  printf "\n"
done

每个集群现在都在运行多集群控制平面(multicluster control plane)并准备启动镜像服务。 我们现在想要将集群链接在一起!

链接集群

link-clusters
Link

为了让 westeast 镜像服务,west 集群需要有凭据, 以便它可以监视要暴露的 east 服务。 毕竟,您不希望任何人能够内省集群上运行的内容! 凭据包括用于验证服务镜像的服务帐户以及 允许监视服务的 ClusterRoleClusterRoleBinding。 总的来说,服务镜像组件使用这些凭证来观察 east 或目标集群上的服务, 并从自身(west)添加/删除它们。 作为 linkerd multicluster install 的一部分添加了一个默认设置, 但是如果您想为每个集群拥有单独的凭据, 您可以运行 linkerd multicluster allow

下一步是将 west 链接到 east。 这将创建一个 credentials secretLink resourceservice-mirror controller。 凭证密钥包含一个 kubeconfig,可用于访问目标(east)集群的 Kubernetes APILink resource 是配置服务镜像的自定义资源, 包含网关地址(gateway address)网关标识(gateway identity) 和在确定要镜像哪些服务时使用的标签选择器等内容。 服务镜像控制器使用 Linksecret 在 目标集群上查找与给定标签选择器匹配的服务, 并将它们复制到源(本地)集群中。

要将 west 集群链接到 east 集群,请运行:

linkerd --context=east multicluster link --cluster-name east |
  kubectl --context=west apply -f -

Linkerd 将查看您当前的 east context, 提取包含服务器位置(server location)以及 CA 包的 cluster 配置。 然后它将获取 ServiceAccount token 并 将这些配置合并到一个 kubeconfigsecret 中。

再次运行 check 将确保服务镜像已经 发现了这个 secret 并且可以到达 east

linkerd --context=west multicluster check

此外,east 网关现在应该显示在列表中:

linkerd --context=west multicluster gateways

安装测试服务

test-services
Topology

是时候测试这一切了! 第一步是添加一些我们可以镜像的服务。 要将这些添加到两个集群,您可以运行:

for ctx in west east; do
  echo "Adding test services on cluster: ${ctx} ........."
  kubectl --context=${ctx} apply \
    -k "github.com/linkerd/website/multicluster/${ctx}/"
  kubectl --context=${ctx} -n test \
    rollout status deploy/podinfo || break
  echo "-------------\n"
done

您现在将拥有一个 test 命名空间,在每个集群中运行两个部署 - frontendpodinfopodinfo 在每个集群中的配置略有不同,具有不同的名称和颜色,以便我们可以知道请求的去向。

要立即从 west 集群中查看它的样子,您可以运行:

kubectl --context=west -n test port-forward svc/frontend 8080
west-podinfo
West Podinfo

通过 http://localhost:8080 提供的 podinfo 登录页面, 您现在可以看到它在 west 集群中的外观。 或者,运行 curl http://localhost:8080 将返回一个类似于以下内容的 JSON 响应:

{
  "hostname": "podinfo-5c8cf55777-zbfls",
  "version": "4.0.2",
  "revision": "b4138fdb4dce7b34b6fc46069f70bb295aa8963c",
  "color": "#6c757d",
  "logo": "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif",
  "message": "greetings from west",
  "goos": "linux",
  "goarch": "amd64",
  "runtime": "go1.14.3",
  "num_goroutine": "8",
  "num_cpu": "4"
}

请注意,message 引用了 west 集群名称。

暴露 services

为确保敏感服务(sensitive services)不被镜像并且 集群性能受到服务的创建删除的影响,我们要求显式暴露服务。 出于本指南的目的,我们将把 podinfo 服务从 east 集群导出到 west 集群。 为此,我们必须首先导出 east 集群中的 podinfo 服务。 你可以通过添加 mirror.linkerd.io/exported 标签来做到这一点:

kubectl --context=east label svc -n test podinfo mirror.linkerd.io/exported=true

查看服务镜像控制器刚刚创建的服务!

kubectl --context=west -n test get svc podinfo-east

architecture 中,您会记得服务镜像组件所做的不仅仅是移动服务。 它还管理镜像服务上的端点。 要验证设置是否正确,您可以检查 west 的端点并验证它们 是否与 east 网关的公共 IP 地址匹配。

kubectl --context=west -n test get endpoints podinfo-east \
  -o 'custom-columns=ENDPOINT_IP:.subsets[*].addresses[*].ip'
kubectl --context=east -n linkerd-multicluster get svc linkerd-gateway \
  -o "custom-columns=GATEWAY_IP:.status.loadBalancer.ingress[*].ip"

此时,我们可以从 west 集群中访问 east 中的 podinfo 服务。 这需要对客户端进行网格化,因此让我们从前端 pod 中运行 curl

kubectl --context=west -n test exec -c nginx -it \
  $(kubectl --context=west -n test get po -l app=frontend \
    --no-headers -o custom-columns=:.metadata.name) \
  -- /bin/sh -c "apk add curl && curl http://podinfo-east:9898"

你会看到 greeting from east 的消息! 来自在 west 运行的 frontend pod 的请求被透明地转发到 east。 假设您仍然在上一步进行端口转发, 您也可以从浏览器访问 http://localhost:8080/east。 刷新几次,您也可以从 linkerd viz stat 中获取指标。

linkerd --context=west -n test viz stat --from deploy/frontend svc

我们还提供了一个 grafana 仪表板来了解这里发生的事情。 您可以通过运行 linkerd --context=west viz dashboard 并转到 http://localhost:50750/grafana/ 来访问它。

grafana-dashboard
Grafana

安全

默认情况下,请求将通过公共互联网。 Linkerd 跨集群扩展其自动 mTLS, 以确保通过公共互联网进行的通信是加密的。 如果您想深入了解如何验证这一点,请查看docs。 但是,要快速检查,您可以运行:

linkerd --context=west -n test viz tap deploy/frontend | \
  grep "$(kubectl --context=east -n linkerd-multicluster get svc linkerd-gateway \
    -o "custom-columns=GATEWAY_IP:.status.loadBalancer.ingress[*].ip")"

tls=true 告诉你请求正在被加密!

除了确保您的所有请求都被加密之外,阻止任意请求进入您的集群也很重要。 我们通过验证请求来自 mesh 中的客户端来做到这一点。 为了进行这种验证,我们依赖集群之间的共享信任锚。 要查看当客户端在 mesh 之外时会发生什么,您可以运行:

kubectl --context=west -n test run -it --rm --image=alpine:3 test -- \
  /bin/sh -c "apk add curl && curl -vv http://podinfo-east:9898"

流量拆分

with-split
Traffic Split

让服务自动出现在集群中并能够明确地处理它们是非常有用的, 但是这仅涵盖操作多个集群的一个用例。 多集群的另一个场景是故障转移。 在故障转移场景中,您没有时间更新配置。 相反,您需要能够不理会应用程序,而只需更改路由即可。 如果这听起来很像我们进行 canary 部署的方式,那么您是对的!

TrafficSplit 允许我们定义多个服务之间的权重并在它们之间拆分流量。 在故障转移场景中,您希望缓慢执行此操作,以确保不会因为 增加的延迟而使其他集群过载或跳闸任何 SLO。 为了让这一切都适用于我们的场景, 让我们在 westeast 中的 podinfo 服务之间进行拆分。 要配置它,您将运行:

cat <<EOF | kubectl --context=west apply -f -
apiVersion: split.smi-spec.io/v1alpha1
kind: TrafficSplit
metadata:
  name: podinfo
  namespace: test
spec:
  service: podinfo
  backends:
  - service: podinfo
    weight: 50
  - service: podinfo-east
    weight: 50
EOF

podinfo 的任何请求现在将有 50% 的时间转发到 podinfo-east 集群, 另外 50% 的时间会转发到本地 podinfo 服务。 发送到 podinfo-east 的请求最终会出现在 east 集群中, 因此我们现在已经有效地使从 westeast50% 以上的流量失败了。

如果您仍在运行 port-forward, 则可以将浏览器转到 http://localhost:8080。 刷新页面应该显示两个集群。 或者,对于命令行方法,curl localhost:8080 会 给你一条来自 westeast 的问候消息。

podinfo-split
Cross Cluster Podinfo

您还可以通过指标观察发生的情况。要查看事物的源头(west),您可以运行以下命令:

linkerd --context=west -n test viz stat trafficsplit

也可以通过运行以下命令从目标(east)侧观察:

linkerd --context=east -n test viz stat \
  --from deploy/linkerd-gateway \
  --from-namespace linkerd-multicluster \
  deploy/podinfo

甚至还有一个 dashboard!运行 linkerd viz dashboard 并将浏览器发送到 localhost:50750

podinfo-split
Cross Cluster Podinfo

清理

要清理多集群控制平面,您可以运行

for ctx in west east; do
  linkerd --context=${ctx} multicluster uninstall | kubectl --context=${ctx} delete -f -
done

如果您还想删除 Linkerd 安装,请运行:

for ctx in west east; do
  linkerd --context=${ctx} uninstall | kubectl --context=${ctx} delete -f -
done