随着企业信息系统越来越多地采用微服务架构,如何在多集群环境中实现服务的高效、安全地跨集群访问成为了一个重要的挑战。Istio 作为一种流行的服务网格解决方案,提供了丰富的功能来支持跨集群服务的无缝连接。
在部署和使用多集群服务网格时有以下难点:
本文将深入探讨如何在多集群多网格的 Istio 部署中,通过实施 SPIRE 联邦和东西向网关暴露服务的方式,实现跨集群的无缝访问。通过一系列配置和部署示例,本文旨在为读者提供一个清晰的指南,帮助理解和解决多集群服务网格部署中遇到的常见问题和挑战。
Istio 文档中根据集群、网络、控制平面、网格、信任域及租户等维度划分了多种部署模型,我将其总结并附上适用场景说明如下表所示。
维度 | 单一配置 | 多元配置 | 适用场景说明 |
---|---|---|---|
集群 | 一个集群托管所有服务与控制平面。 | 跨多个集群分布服务,可以共享或分离控制平面。 | 单集群适用于资源需求较小、管理相对简单的环境;多集群适合于需要高可用性、地理冗余或遵守数据驻留政策的大型组织。 |
网络 | 所有服务在单一网络内通信,无需跨网络通信。 | 服务跨越多个网络,需通过 Istio 网关进行通信。 | 单网络适用于网络简单、无复杂跨网络通信需求的场景;多网络适合在多云、混合云环境中部署,或需要跨行政边界部署的场景。 |
控制平面 | 一个控制平面管理所有服务。 | 每个控制平面管理一个或多个集群,增强隔离与可用性。 | 单控制平面适用于小型至中型部署,易于管理;多控制平面适用于大规模部署,需要高度的容错能力和安全隔离。 |
网格 | 所有服务在一个连续的服务网格中。 | 服务网格之间通过联盟进行通信,适用于不同组织或区域。 | 单网格适用于组织内部密切协作的服务;多网格适合于需要隔离不同业务线或合作伙伴间的服务,或实施强隔离的大型组织。 |
信任域 | 所有服务使用同一套密钥和证书体系。 | 不同信任域使用不同的密钥和证书,需进行信任链交换。 | 单信任域适用于信任级别统一的环境;多信任域适用于需要严格隔离、满足不同安全级别需求的复杂组织或多方合作场景。 |
租户 | 整个网格为单一租户或用户服务。 | 通过命名空间隔离,支持多个租户在同一网格中运行服务。 | 单租户适用于所有资源和服务由单一组织管理的场景;多租户适用于云服务提供商或需要在同一物理基础设施上运营多个客户的场景。 |
选择合适的部署模型需要考虑到实际的业务需求、安全要求、管理复杂度以及成本等因素。在生产环境中,往往是对多种部署模型的组合使用。
下表展示了在实际应用中如何结合不同的部署模型来满足更复杂的业务和技术需求:
混合部署模型 | 描述 | 适用场景 |
---|---|---|
多集群 + 多网格 + 多控制平面 | 不同的集群可以配置成不同的网格,每个网格都有自己的控制平面。通过网格联邦共享服务和策略。 | 适合大型组织,其中不同的业务单位需要独立运行并管理自己的服务,同时需要一定级别的服务共享和协作。 |
多信任域联邦 + 命名空间隔离的多租户 | 不同的网格可以拥有不同的信任域,通过信任域联邦共享密钥和证书。同时在一个网格内部通过命名空间实现租户隔离。 | 适用于需要强隔离但又要求跨组织或跨业务线协作的环境,如跨国公司或合作伙伴网络。 |
多集群 + 单网格 + 多控制平面 | 多个集群共享一个服务网格,但每个集群拥有自己的控制平面来管理本地服务的配置。 | 适用于需要高可用性和灾难恢复能力的应用,各地区的集群可以独立运行,减少单点故障风险。 |
多集群 + 多网格 + 单控制平面 | 多个集群分布在不同的网格中,但所有网格共享一个中心控制平面。 | 适用于中心化管理的大规模部署,可以减少管理的复杂性,但对控制平面的可用性要求极高。 |
多信任域 + 多网格 + 命名空间隔离的多租户 | 各网格拥有独立的信任域,增强安全性和隔离性。在单个网格内使用命名空间来隔离不同的租户。 | 适用于提供云服务的组织,需要隔离不同客户的数据和服务,同时在不同的法律和合规环境下操作。 |
这些混合模型提供了高度的灵活性和可扩展性,能够满足各种复杂的部署要求。在选择混合模型时,组织需要考虑到管理复杂性、成本、安全要求以及业务需求,以确定最合适的部署策略。通过适当的规划和设计,Istio 的灵活部署模型可以帮助组织构建出既安全又高效的服务网格架构。在大多数场景下,单信任域的多集群 + 单网格 + 多控制平面已足够满足需要。
本文将聚焦多集群 + 多网格 + 多控制平面 + 多信任域的混合部署模型,这是一种相当复杂的场景,如果你可以完成这种场景的部署,那么其他场景也就不在话下了。
网格间的服务要想互相访问,必须了解各自的 FQDN。FQDN 通常由服务名、命名空间和顶级域(如 svc.cluster.local
)组成。在 Istio 的多集群或多网格设置中,可以通过不同的机制(如ServiceEntry
、VirtualService
、Gateway
配置)来控制和管理服务的路由和访问,而不是通过修改 FQDN 来实现。
多集群服务网格中的 FQDN 与单集群并没有什么不同,通常遵循以下格式:
<service-name>.<namespace>.svc.cluster.local
也许你会想到通过 meshID
来区分网格?meshID
主要用于区分和管理在同一环境中或跨环境的多个 Istio 网格,meshID
并不用于直接构造服务的 FQDN。
meshID
的主要作用
在 Istio 多网格环境中,东西向网关(East-West Gateway)起着关键作用,它不仅处理网格间的入口和出口流量,还支持服务的发现和连接。当一个集群需要访问另一个集群中的服务时,它通过这个网关路由到目标服务。
下图展示了跨集群的服务注册发现与路由的过程。
在跨集群的 Istio 网格配置中,服务注册、发现和路由的流程是至关重要的,它们确保了不同集群中的服务可以相互发现并通信。以下是跨集群 Istio 网格中服务注册、发现与路由的基本流程:
在每个 Kubernetes 集群中,当一个服务被部署时,它的信息会被注册到 Kubernetes 的 API Server。这包括服务的名称、标签、选择器、端口等信息。
Istiod,作为控制平面,负责监控 Kubernetes API Server 的状态变化。每当有新的服务被注册或现有服务被更新时,Istiod 会自动检测到这些变化。Istiod 接着提取必要的服务信息并构建内部的服务和端点的配置。
为了使一个集群中的服务能够发现并通信到另一个集群的服务,Istiod 需要将服务端点信息同步到所有相关集群。这通常通过以下两种方式之一实现:
当服务 A 需要与服务 B 通信时,它的 Envoy 代理首先解析服务 B 的名称获取 IP 地址,即服务 B 所在集群的东西向网关的负载均衡器地址。接着该东西向网关将请求路由到目标服务。Envoy 代理可以根据配置的负载均衡策略(如轮询、最少连接数等),选择最佳的服务实例来发送请求。
Istio 提供了丰富的流量管理功能,例如请求路由、故障注入、流量复制等。这些规则在 Istio 的控制平面中定义,并推送到各个 Envoy 代理执行。这样可以在跨集群环境中灵活地控制和优化服务间的通信。
当不同集群中运行的服务需要相互通信时,正确的身份认证和授权是确保服务安全的关键。使用 SPIFFE 可以帮助标识和验证服务的身份,但在多集群环境中需要确保这些身份是唯一且可验证的。
为此,我们将设置 SPIRE 联邦来为多集群的服务分配身份并实现跨集群的身份认证:
spiffe://<trust-domain>/<namespace>/<service>
的唯一标识符。在多集群环境中,可以通过包括集群名称在内的“trust domain”来确保身份的唯一性。例如,可以设置spiffe://foo.com/ns/default/svc/service1
和spiffe://bar.com/ns/default/svc/service1
,以区分不同集群中相同名称的服务。以下是实现 SPIRE 联邦的步骤说明。
每个集群都配置为一个单独的 trust domain。这样,每个集群内的服务都将具有基于其所在 trust domain 的唯一 SPIFFE ID。例如,集群 1 的服务可能拥有 ID spiffe://cluster1/ns/default/svc/service1
,而集群 2 的相同服务则为 spiffe://cluster2/ns/default/svc/service1
。
在 SPIRE 中配置 trust relationships 以允许不同 trust domain 的节点和工作负载相互验证。这涉及到 trust domain 之间交换和接受彼此的 CA 证书或 JWT keys,确保跨集群通信的安全性。
在每个集群中部署 SPIRE Server 和 SPIRE Agent。SPIRE Server 负责管理证书颁发和续签,而 SPIRE Agent 负责将证书和密钥安全地分发给集群内的服务。
服务可以通过 SPIRE 的 Workload API 请求和更新其身份证书。这样,服务即使在不同集群中运行,也能持续验证其身份,并安全地与其他服务通信。我们将配置 Istio 网格中的代理共享 SPIRE Agent 中的 Unix Domain Socket,从而访问 Workload API 来管理证书。
我们将使用 cert-manager 作为 SPIRE 的 UpstreamAuthority,配置 SPIRE 自动轮换服务证书和密钥,增强系统的安全性。通过自动化轮换,即使证书被泄露,攻击者也只能在很短的时间内利用这些证书。
通过这些步骤,你可以建立一个跨集群的、安全的服务身份验证框架,使得各个集群的服务能够安全地识别和通信,从而有效地降低安全风险并简化证书管理。这样的配置不仅增强了安全性,还通过分散的信任域提高了系统的可扩展性和灵活性。
下图展示了 Istio 多集群及 SPIRE 联邦的部署模型。
下面我将演示如何在多集群 Istio 网格中实现无缝地跨集群无缝访问。
cluster-1
和 cluster-2
sidecarInjectorWebhook
挂载 SPIFFE UDS 的 workload-socket
,并启用 DNS 代理我们部署的各组件版本如下:
我将所有命令及步骤说明保存在 Github 上:rootsongjc/istio-multi-cluster,你可以按照该项目中的说明操作。下面是对各主要步骤的说明。
打开 Google Cloud Shell 或本地终端,并确保你已经安装了 gcloud
CLI。使用以下命令创建两个集群:
gcloud container clusters create cluster-1 --zone us-central1-a --num-nodes 3
gcloud container clusters create cluster-2 --zone us-central1-b --num-nodes 3
使用 cert-manager 作为根 CA 为 istiod 和 SPIRE 颁发证书。
./cert-manager/install-cert-manager.sh
SPIRE 联邦的基本信息如下:
Cluster Alias | Trust Domain |
---|---|
cluster-1 | foo.com |
cluster-2 | bar.com |
注意:信任域不需要与 DNS 名称一致,但需要与 Istio Operator 配置中的信任域相同。
执行下面的命令部署 SPIRE 联邦:
./spire/install-spire.sh
想了解 Istio 中使用 SPIRE 进行身份管理的详情,请参考使用 cert-manager 和 SPIRE 管理 Istio 中的证书。
我们将使用 IstioOperator 来安装 Istio,其中为每个集群配置了:
执行下面的命令安装 Istio:
istio/install-istio.sh
为了验证多集群安装的正确性,我们将在两个集群中分别部署不同版本的 helloworld
应用,然后在 cluster-1
中访问 helloworld
服务,以测试以下跨集群访问场景:
执行下面的命令在两个集群中部署 helloworld
应用:
./example/deploy-helloword.sh
部署完成 helloworld
应用后,从 cluster-1
的 sleep
pod 访问 hellowrold
服务:
kubectl exec --context=cluster-1 -n sleep deployment/sleep -c sleep \
-- sh -c "while :; do curl -sS helloworld.helloworld:5000/hello; sleep 1; done"
下图展示的是该场景下的部署架构及流量路由路径。
从请求结果既有 helloworld-v1
又有 helloworld-v2
的响应来看,说明跨集群的服务冗余生效了。
验证 DNS
此时,因为 helloworld
服务既存在于本地又在远程集群中,若你在 cluster-1
中查询 helloworld
服务的 DNS 名称:
kubectl exec -it deploy/sleep --context=cluster-1 -n sleep -- nslookup helloworld.helloworld.svc.cluster.local
你将得到 cluster-1
集群中的 helloworld
服务的 ClusterIP。
验证流量路由
接下来我们将通过查看 Envoy 代理配置来验证跨集群的流量路由路径。
在 cluster-1
中查看 helloworld
服务的端点:
istioctl proxy-config endpoints deployment/sleep.sleep --context=cluster-1 --cluster "outbound|5000||helloworld.helloworld.svc.cluster.local"
你将得到类似下面的输出:
ENDPOINT STATUS OUTLIER CHECK CLUSTER
10.76.3.22:5000 HEALTHY OK outbound|5000||helloworld.helloworld.svc.cluster.local
34.136.67.85:15443 HEALTHY OK outbound|5000||helloworld.helloworld.svc.cluster.local
这两个端点,一个是 cluster-1
中的 helloworld
服务的端点,另一个是 cluster-2
的 istio-eastwestgateway
服务的负载均衡器地址。Istio 将为跨集群的 TLS 连接设置 SNI,在 cluster-2
中将通过 SNI 区分目标服务。
执行下面的命令,在 cluster-2
中查询前面 SNI 的端点:
istioctl proxy-config endpoints deploy/istio-eastwestgateway.istio-system --context=cluster-2 --cluster "outbound_.5000_._.helloworld.helloworld.svc.cluster.local"
你将得到类似下面的结果:
ENDPOINT STATUS OUTLIER CHECK CLUSTER
10.88.2.4:5000 HEALTHY OK outbound_.5000_._.helloworld.helloworld.svc.cluster.local
这个端点就是 helloworld
服务在 cluster-2
集群中的端点。
通过以上步骤,你应该了解了跨集群冗余服务的流量路径。接下来我们将删除 cluster-1
中的 helloworld
服务,不需要对 Istio 做任何配置,就可以自动实现故障转移。
执行下面的命令将 cluster-1
中的 helloworld
副本数量缩容为 0:
kubectl -n helloworld scale deploy helloworld-v1 --context=cluster-1 --replicas 0
再次从 cluster-1
中访问 helloworld
服务:
kubectl exec --context=cluster-1 -n sleep deployment/sleep -c sleep \
-- sh -c "while :; do curl -sS helloworld.helloworld:5000/hello; sleep 1; done"
依然可以获得来自 helloworld-v2
的响应。
现在,直接删除 cluster-1
中的 helloworld
服务:
kubectl delete service helloworld -n helloworld --context=cluster-1
依然可以获得来自 helloworld-v2
的响应,这说明跨集群的故障转移生效了。
下图展示了该场景下的流量路径。
验证 DNS
此时,因为 helloworld
服务既存在于本地又在远程集群中,若你在 cluster-1
中查询 helloworld
服务的 DNS 名称:
kubectl exec -it deploy/sleep --context=cluster-1 -n sleep -- nslookup helloworld.helloworld.svc.cluster.local
你将得到 cluster-2
集群中东西向网关的地址和 15443 端口。
通过入口网关访问远程集群中的服务,是最传统的跨集群访问方式,下图展示了该场景下的流量路径。
执行下面的命令在 cluster-2
中创建 Gateway 和 VirtualService:
kubectl apply --context=cluster-2 \
-f ./examples/helloworld-gateway.yaml -n helloworld
获取 cluster-2
中的入口网关地址:
GATEWAY_URL=$(kubectl -n istio-ingress --context=cluster-2 get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
执行下面的验证可以通过远程入口网关访问服务:
kubectl exec --context="${CTX_CLUSTER1}" -n sleep deployment/sleep -c sleep \
-- sh -c "while :; do curl -s http://$GATEWAY_URL/hello; sleep 1; done"
你将得到来自 helloworld-v2
的响应。
执行下面的命令获取 cluster-1
集群中 sleep
pod 中的证书:
istioctl proxy-config secret deployment/sleep -o json --context=cluster-1| jq -r '.dynamicActiveSecrets[0].secret.tlsCertificate.certificateChain.inlineBytes' | base64 --decode > chain.pem
split -p "-----BEGIN CERTIFICATE-----" chain.pem cert-
openssl x509 -noout -text -in cert-ab
openssl x509 -noout -text -in cert-aa
如果在输出的消息中看到下面的字段,说明身份分配正确:
Subject: C=US, O=SPIFFE
URI:spiffe://foo.com/ns/sample/sa/sleep
查看 SPIRE 中的身份信息:
kubectl --context=cluster-1 exec -i -t -n spire spire-server-0 -c spire-server \
-- ./bin/spire-server entry show -socketPath /run/spire/sockets/server.sock --spiffeID spiffe://foo.com/ns/sleep/sa/sleep
你将看到类似下面的输出:
Found 1 entry
Entry ID : 9b09080d-3b67-44c2-a5b8-63c42ee03a3a
SPIFFE ID : spiffe://foo.com/ns/sleep/sa/sleep
Parent ID : spiffe://foo.com/k8s-workload-registrar/cluster-1/node/gke-cluster-1-default-pool-18d66649-z1lm
Revision : 1
X509-SVID TTL : default
JWT-SVID TTL : default
Selector : k8s:node-name:gke-cluster-1-default-pool-18d66649-z1lm
Selector : k8s:ns:sleep
Selector : k8s:pod-uid:6800aca8-7627-4a30-ba30-5f9bdb5acdb2
FederatesWith : bar.com
DNS name : sleep-86bfc4d596-rgdkf
DNS name : sleep.sleep.svc
对于生产环境,建议使用统一网关,通过 Tier-2 架构,在 Tier-1 边缘网关配置全局的流量路由,该边缘网关将把转写的 Istio 配置下发给 Tier-2 集群中的各个入口网关。
下图展示了使用 TSB 部署的 Tier2 架构的 Istio 服务网格,其中使用 SPIRE 联邦。
我们将这四个 Kubernetes 集群分为 Tier1 集群(tier1
)和 Tier2 集群(cp-cluster-1
、cp-cluster-2
和 cp-cluster-3
)。在 T1 中安装 Edge Gateway,而在 T2 中安装 bookinfo 和 httpbin 应用程序。每个集群将拥有独立的信任域,所有这些集群将构成 SPIRE 联邦。
下图展示了用户通过入口网关访问 bookinfo 和 httpbin 服务的流量路由。
你需要在 Istio 之上创建一个适用于多集群的逻辑抽象层,关于 TSB 中的统一网关的详细信息,请参考 TSB 文档.
本文详细介绍了在 Istio 多集群网格环境中实现服务身份验证、DNS 解析和跨集群流量管理的关键技术和方法。通过精确配置 Istio 和 SPIRE 联邦,我们不仅增强了系统的安全性,还提高了服务间通信的效率和可靠性。遵循这些步骤,你将能够构建一个强大的、可扩展的多集群服务网格,满足现代应用的复杂需求。
最后更新于 2024/12/05