30. 与 StatefulSets 的多集群通信
Linkerd
的多集群(multi-cluster)
扩展通过在集群之间“镜像”
服务信息来工作。
目标集群中导出的服务将被镜像为 clusterIP
副本。
默认情况下,每个导出的服务都将镜像为 clusterIP
。
当运行需要 headless
服务的工作负载时,
例如 StatefulSets,
Linkerd
的多集群扩展可以配置为支持 headless
服务以保留服务类型。
导出的 headless
服务将在源集群中镜像为 headless
,
保留 DNS
记录创建等功能和寻址单个 Pod
的能力。
本指南将引导您安装和配置 Linkerd
以及支持 headless
服务的多集群扩展,
并将举例说明如何在目标集群中部署 StatefulSet
。
部署后,我们还将研究如何从源集群中
的客户端
与目标集群
的 StatefulSet
中的任意 Pod
进行通信。
有关 headless
服务的多集群支持如何工作的更详细概述,
请查看多集群通信。
前提条件
- 两个
Kubernetes
集群。它们将被称为east
和west
, 其中east
分别是“源”
集群和“west”
是目标集群。 这些可以在任何云或本地环境中,本指南将使用 k3d 配置两个本地集群。 smallstep/CLI
为Linkerd
安装生成证书。linkerd:stable-2.11.0
来安装Linkerd
。
为了帮助创建和安装集群,有一个 demo
存储库可用。
在整个指南中,我们将使用存储库中的脚本,但您可以在不克隆或使用脚本的情况下继续操作。
安装具有 headless 支持的 Linkerd 多集群
为了开始我们的 demo
并查看实践中的所有内容,我们将经历一个多集群场景,
其中 east
集群中的 pod
将尝试与来自 west
集群的任意 pod
通信。
第一步是在本地机器上克隆 demo
存储库。
# clone example repository
$ git clone git@github.com:mateiidavid/l2d-k3d-statefulset.git
$ cd l2d-k3d-statefulset
第二步包括创建两个名为 east
和 west
的 k3d
集群,其中 east
集群是源,west
集群是目标。
创建集群时,我们需要一个共享的信任根。幸运的是,您刚刚克隆的存储库包含一些脚本,可以大大简化一切。
# create k3d clusters
$ ./create.sh
# list the clusters
$ k3d cluster list
NAME SERVERS AGENTS LOADBALANCER
east 1/1 0/0 true
west 1/1 0/0 true
创建集群后,我们将安装 Linkerd
和 multi-cluster
扩展。
最后,一旦两者都安装完毕,我们需要将两个集群链接在一起,以便镜像它们的服务。
为了启用对 headless
服务的支持,
我们将额外的 --set "enableHeadlessServices=true
flag 传递给 linkerd multicluster link
。
和以前一样,这些步骤是通过提供的脚本自动执行的,可随时查看!
# Install Linkerd and multicluster, output to check should be a success
$ ./install.sh
# Next, link the two clusters together
$ ./link.sh
完美的!如果你已经做到了这一点,没有任何错误,那么这是一个好兆头。 在下一章中,我们将部署一些服务并了解通信是如何工作的。
Pod-to-Pod: 从 east, 到 west
完成安装步骤后,我们现在可以专注于 pod
到 pod
的通信。
首先,我们将部署我们的 pod
和服务
:
- 我们将在
east
和west
中对默认命名空间进行mesh
划分。 - 在
west
,我们将部署一个nginx StatefulSet
和它自己的headless
服务nginx-svc
。 在
east
,我们的脚本将部署一个 curlpod
,然后将用于 curlnginx service
。# deploy services and mesh namespaces $ ./deploy.sh # verify both clusters # # verify east $ kubectl --context=k3d-east get pods NAME READY STATUS RESTARTS AGE curl-56dc7d945d-96r6p 2/2 Running 0 7s # verify west has headless service $ kubectl --context=k3d-west get services NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 10m nginx-svc ClusterIP None <none> 80/TCP 8s # verify west has statefulset # # this may take a while to come up $ kubectl --context=k3d-west get pods NAME READY STATUS RESTARTS AGE nginx-set-0 2/2 Running 0 53s nginx-set-1 2/2 Running 0 43s nginx-set-2 2/2 Running 0 36s
在我们继续之前,让我们看一下 nginx-svc
的端点对象:
$ kubectl --context=k3d-west get endpoints nginx-svc -o yaml
...
subsets:
- addresses:
- hostname: nginx-set-0
ip: 10.42.0.31
nodeName: k3d-west-server-0
targetRef:
kind: Pod
name: nginx-set-0
namespace: default
resourceVersion: "114743"
uid: 7049f1c1-55dc-4b7b-a598-27003409d274
- hostname: nginx-set-1
ip: 10.42.0.32
nodeName: k3d-west-server-0
targetRef:
kind: Pod
name: nginx-set-1
namespace: default
resourceVersion: "114775"
uid: 60df15fd-9db0-4830-9c8f-e682f3000800
- hostname: nginx-set-2
ip: 10.42.0.33
nodeName: k3d-west-server-0
targetRef:
kind: Pod
name: nginx-set-2
namespace: default
resourceVersion: "114808"
uid: 3873bc34-26c4-454d-bd3d-7c783de16304
我们可以看到,基于端点对象,该服务具有三个端点,每个端点都有一个地址(或 IP
),
其主机名对应于一个 StatefulSet pod
。
如果我们直接对这些端点中的任何一个进行 curl
,我们将得到答复。
我们可以通过将 curl pod
应用于 west
集群来测试这一点:
$ kubectl --context=k3d-west apply -f east/curl.yml
$ kubectl --context=k3d-west get pods
NAME READY STATUS RESTARTS AGE
nginx-set-0 2/2 Running 0 5m8s
nginx-set-1 2/2 Running 0 4m58s
nginx-set-2 2/2 Running 0 4m51s
curl-56dc7d945d-s4n8j 0/2 PodInitializing 0 4s
$ kubectl --context=k3d-west exec -it curl-56dc7d945d-s4n8j -c curl -- bin/sh
/$ # prompt for curl pod
如果我们现在 curl
这些实例之一,我们将得到一个 response
。
# exec'd on the pod
/ $ curl nginx-set-0.nginx-svc.default.svc.west.cluster.local
"<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>"
现在,让我们做同样的事情,但这次是从 east
集群。我们将首先导出服务。
$ kubectl --context=k3d-west label service nginx-svc mirror.linkerd.io/exported="true"
service/nginx-svc labeled
$ kubectl --context=k3d-east get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 20h
nginx-svc-west ClusterIP None <none> 80/TCP 29s
nginx-set-0-west ClusterIP 10.43.179.60 <none> 80/TCP 29s
nginx-set-1-west ClusterIP 10.43.218.18 <none> 80/TCP 29s
nginx-set-2-west ClusterIP 10.43.245.244 <none> 80/TCP 29s
如果我们查看 endpoints
对象,我们会注意到一些奇怪的东西,
nginx-svc-west
的端点将具有相同的主机名,但每个主机名将指向我们上面看到的服务之一:
$ kubectl --context=k3d-east get endpoints nginx-svc-west -o yaml
subsets:
- addresses:
- hostname: nginx-set-0
ip: 10.43.179.60
- hostname: nginx-set-1
ip: 10.43.218.18
- hostname: nginx-set-2
ip: 10.43.245.244
这就是我们在教程开始时概述的内容。
来自目标集群(west)
的每个 pod
都将被镜像为一个 clusterIP
服务。
我们稍后会看到为什么这很重要。
$ kubectl --context=k3d-east get pods
NAME READY STATUS RESTARTS AGE
curl-56dc7d945d-96r6p 2/2 Running 0 23m
# exec and curl
$ kubectl --context=k3d-east exec pod curl-56dc7d945d-96r6p -it -c curl -- bin/sh
# we want to curl the same hostname we see in the endpoints object above.
# however, the service and cluster domain will now be different, since we
# are in a different cluster.
#
/ $ curl nginx-set-0.nginx-svc-west.default.svc.east.cluster.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
如您所见,我们得到了相同的回复! 但是,nginx
在不同的集群中。那么,幕后发生了什么?
- 当我们镜像
headless
服务时,我们为每个pod
创建了一个clusterIP
服务。 由于服务创建DNS
记录, 因此使用来自目标的hostname
命名每个端点为我们提供了这些pod FQDN
(nginx-set-0.(...).cluster.local
)。 Curl
将pod DNS
名称解析为IP
地址。在我们的例子中,这个IP
是10.43.179.60
。- 一旦请求在进行中,
linkerd2-proxy
就会拦截它。 它查看IP
地址并将其与我们的clusterIP
服务相关联。 服务本身指向网关,因此代理将请求转发到目标集群网关。这是通常的多集群场景。 - 目标集群中的网关查看请求并查找原始目标地址。
在我们的例子中,由于这是一个
“端点镜像(endpoint mirror)”
, 它知道它必须转到同一集群中的nginx-set-0.nginx-svc
。 - 请求再次由网关转发到
pod
,然后返回响应。
就是这样!您现在可以跨集群向 pod
发送请求。
查询 3
个 StatefulSet pod
中的任何一个都应该有相同的结果。
清理
要进行清理,您可以使用 k3d CLI
完全删除两个集群:
$ k3d cluster delete east
cluster east deleted
$ k3d cluster delete west
cluster west deleted