写点什么

非扁平网络场景下,基于开源 istio 治理 CCE 多集群

  • 2024-08-16
    广东
  • 本文字数:7244 字

    阅读完需:约 24 分钟

本文分享自华为云社区《基于开源istio治理CCE多集群--非扁平网络场景》,作者:可以交个朋友。

一 背景

鉴于扁平网络模式下对网络存在严格的要求,需要所有集群处于同一个扁平网络,Pod IP 地址互通且不重叠,Service 网段也不能冲突。这些网络需求在实际环境中可能难以满足,限制了该模式的应用场景。istio 支持了更加灵活的多集群网络方案,即非扁平网络模型。


二 方案简介

采用多网络多控制面方案,除了扁平网络多控制面网格的方案中使用的技术外(dns 解析,跨集群 apiserver 的访问),该方案最为关键的技术是 istio-eastwestgateway 东西向网关。东西向网关解决了网格跨网络访问的难题,在客户端的工作负载发起请求时,针对其他集群的工作负载,数据流量首先会被转发到目标所在集群的入口网关,然后由网关将请求转发到工作负载容器。


若需实现该方案,需要在扁平网络的基础上再回答如下关键问题:

  • 问题一、同一个负载多集群部署,如何判断各个 endpoint 在哪个 network 内?在安装 istio 时可以在 operator 配置中指定网络环境,比如 A 集群 istio 的 network=networkA,B 集群 istio 的 network=networkB,那么单网格多控制面配置后,各自集群的 pod 会默认被注入对应的 env。A、B 集群的 istiod 在聚合多集群负载实例的 endpoint 信息时,也会基于 pod 的特定 env 或者 lable 字段中的 network 标识获取网络信息。也可以通过为 istiod 系统 namespace 设置topology.istio.io/network为该网格标识 network,则该集群注入的 pod 默认增加topology.istio.io/networklabel。


  • 问题二、跨集群的流量是如何被识别并发送到东西向网关?在为所有 sidecar 生成 EDS 配置时,根据客户端代理和各个服务端实例的 network 标识(如问题一中的 topology.istio.io/networklabel 或ISTIO_MATE_NETWORK env),如果标识不同则认为客户端和服务端跨网络,自动将对应后端的 Endpoint 地址转换成其所在网络入口的东西向网关地址。大致的过程大致为:1) istiod 将服务的所有 Endpoint 都聚合起来,然后对所有的 Endpoint 都做一次遍历,根据调用方及 Endpoint 的网络标签topology.istio.io/network,决定是否进行 Endpoint 地址转换。2) 如果 Endpoint 与调用者在同一 Network 内,则其地址保持不变;如果 Endpoint 与调用者不在同一 Network 内,则将其地址转换为所在集群的入口网关地址。这种在非扁平网络多集群模型中核心技术被称为Split Horizon EDS

上图中 1.94.58.222 为 istio-eastwestgateway 东西向网关的 service externalIP 地址


  • 问题三、东西向网关又是如何将数据报文转发给目标服务的?Istio 东西向网关在工作时使用基于 SNI 的路由,指定了在 TLS 握手时要连接的主机名(对应后端的服务名)。SNI 协议是为了支持同一个 IP 地址的多个域名。东西向网关端口 15443 预设了 SNI 感知的 Listener。当 cluster1 中客户端发出的流量被拦截到 Sidecar 后,Sidecar 会将其转换为 mTLS 流量,并带上 SNI 信息转发出去,在流量到达 cluster2 的 istio-eastwest-gateway 15443 端口后,gateway 会提取 SNI 信息,分析出实际的目的服务,最终转发给 cluster2 中的相关 pod。


三 跨网络多控制面网格环境搭建

3.1 CCE 集群准备

  1. cluster1 位于北京四 region,cluster2 位于上海一 region。不在一个网络平面,所以不用考虑 Pod 网段 Service 网段是否冲突等问题,组网复杂性降低。



2.需要注意: istio 控制面组件 istioD 会 ist-watch 每个 kubernetes 集群的 pod 、endpoint、service 等资源信息,因此每个集群需要给 apiserver 绑定公网地址,以便各自集群中的 istioD 组件能够访问 remote 集群的 apiserver。

3.将每个集群的 kubeconfig 文件拷贝保存到相关文件中,istiod 访问 remote 集群需要借助该 kubeconfig 文件生成相关 secret。istiod 会根据相关标签(istio/multiCluster: ‘true’)探测到该 secret,然后使用该 secret 访问到 remote 集群的 kube-apiserver


4.为了方便使用 kubectl,设置 aliasalias k1='kubectl --kubeconfig=/root/.kube/cluster1.yaml'alias k2='kubectl --kubeconfig=/root/.kube/cluster2.yaml'


3.2 CA 根证书准备

在 istio 多集群通信中,每个集群都会有一个 istio 控制面,其中包含一个 CA 服务,该 CA 服务会自动为每个集群中的 Istio Sidecar 生成证书和密钥,并用于为 Pod 之间的 mTLS 加密认证提供证书签名。这些证书和密钥是由 Istio CA 服务签发的,以确保通信的安全性和可靠性。跨集群的 mTL 要求共享一个 root CA,各集群本地的 CA(citadel)间的 CA 证书,需要由 root CA 签发



  1. 下载 istio 安装包wget https://github.com/istio/istio/releases/download/1.22.3/istio-1.22.3-linux-amd64.tar.gzCCE 集群的 kubernetes 版本为 1.28,可使用 1.22 版本的 istio,kubernetes 和 istio 的对应关系 可参照:https://istio.io/latest/zh/docs/releases/supported-releases/#support-status-of-istio-releases

  2. 在 Istio 安装包的顶层目录下,创建一个目录来存放证书和密钥mkdir -p certspushd certs


3.生成根证书和密钥:make -f ../tools/certs/Makefile.selfsigned.mk root-ca

将会生成以下文件:○ root-cert.pem:生成的根证书○ root-key.pem:生成的根密钥○ root-ca.conf:生成根证书的 openssl 配置○ root-cert.csr:为根证书生成的 CSR


4.对于每个集群,为 Istio CA 生成一个中间证书和密钥make -f ../tools/certs/Makefile.selfsigned.mk cluster1-cacertsmake -f ../tools/certs/Makefile.selfsigned.mk cluster2-cacerts


运行以上命令,将会在名为 cluster1、cluster2 的目录下生成以下文件:○ ca-cert.pem:生成的中间证书○ ca-key.pem:生成的中间密钥○ cert-chain.pem:istiod 使用的生成的证书链○ root-cert.pem:根证书


5.每个集群中,创建一个私密 cacerts,包括所有输入文件 ca-cert.pem, ca-key.pem,root-cert.pem 和 cert-chain.pem

k1 create namespace istio-system k1 create secret generic cacerts -n istio-system \ --from-file=cluster1/ca-cert.pem \ --from-file=cluster1/ca-key.pem \ --from-file=cluster1/root-cert.pem \ --from-file=cluster1/cert-chain.pemk2 create namespace istio-system k2 create secret generic cacerts -n istio-system \ --from-file=cluster1/ca-cert.pem \ --from-file=cluster1/ca-key.pem \ --from-file=cluster1/root-cert.pem \ --from-file=cluster1/cert-chain.pem



6.证书创建完毕,返回 istio 安装目录



3.3 为各个集群设置缺省网络

为了向 Istio 提供集群或者网络上下文,每个集群都有自己的 ClusterID(集群标签)及对应的 Network(网络标签),可以通过给 Istio 系统命名空间添加topology.istio.io/network标签,标识 Network 创建命名空间 istio-system 之后,我们需要设置集群的网络

kubectl --kubeconfig=/root/.kube/cluster1.yaml  label  ns istio-system  topology.istio.io/network=network1kubectl --kubeconfig=/root/.kube/cluster2.yaml  label  ns istio-system  topology.istio.io/network=network2
复制代码



3.4 注册远端集群的服务发现

istiod 需要负责连接所有集群的 kube-apiserver,并且 List-Watch 获取每个集群的 Service、Endpoint、Pod、等。对于集群内 istiod 通过 Pod 内置的 token 连接所在集群的 Kube-apiserver,自动将该集群的服务发现数据(如 service、Endpoint 等)接入 istio 控制面至于如何获取 remote 集群中的 k8s 资源,istio 约定的方式是 : 需要用户主动提供远端集群的访问凭证 kubconfig 文件,然后将该访问凭证存于 Secret,该 Secret 的标签需设置为istio/multiCluster: true,istiod 可以通过该 secret 提供的访问凭据与 remote 集群建立连接,监听 remote 集群的所有服务和相关资源的变化。


  1. 在 cluster1 中安装 remote 集群的 secret,该 secret 提供 cluster2 的 kube-apiserver 的访问权限。

istioctl create-remote-secret \ --kubeconfig=/root/.kube/cluster2.yaml \ --name=cluster2 | \ kubectl apply -f - --kubeconfig=/root/.kube/cluster1.yaml

2.在 cluster2 中安装 remote 集群的 secret,该 secret 提供 cluster1 的 kube-apiserver 的访问权限。

istioctl create-remote-secret \ --kubeconfig=/root/.kube/cluster1.yaml \ --name=cluster1 | \ kubectl apply -f - --kubeconfig=/root/.kube/cluster2.yaml

3.查看 secret



3.5 将 istio 分别安装在各个集群中

因为是多 istio 控制面部署,所以每个集群都需要安装 istio。本文档采用 istioctl 方式进行安装,安装 API 参数配置参考: https://istio.io/latest/zh/docs/reference/config/istio.operator.v1alpha1/


  1. cluster1 集群 istio 安装配置,将该配置文件保存在当前目录下 命名为 xxx.yaml

apiVersion: install.istio.io/v1alpha1 kind: IstioOperator spec: hub: swr.cn-north-4.myhuaweicloud.com/hjmtest #配置istio安装的镜像仓库 meshConfig: accessLogFile: /dev/stdout #开启访问日志 values: global: meshID: mesh1 multiCluster: clusterName: cluster1 network: network1 # 集群的网络标识

istioctl install --kubeconfig=/root/.kube/cluster1.yaml -f cluster1.yaml

2.cluster2 集群 istio 安装配置

apiVersion: install.istio.io/v1alpha1 kind: IstioOperator spec: hub: swr.cn-north-4.myhuaweicloud.com/hjmtest #配置istio安装的镜像仓库 meshConfig: accessLogFile: /dev/stdout #开启访问日志 values: global: meshID: mesh1 #和cluster1 同属于一个mesh multiCluster: clusterName: cluster2 #需要区别cluster1 network: network2 # 集群的网络标识,非扁平网络,区别cluster1中的network1

istioctl install --kubeconfig=/root/.kube/cluster2.yaml -f cluster2.yaml

3.查看 istio 控制面和南北向网关数据面实例是否就绪



3.6 为各个集群安装 istio 东西向网关

为了解决非扁平网络的访问限制,可以通过配置东西向网关来转发跨集群的访问流量。这种方案依赖 Split Horizon EDS ,自动重写 Remote 集群的 Endpoint 地址为网关地址

  1. 在 cluster1 、cluster2 集群中安装专用的 东西向网关。samples/multicluster/gen-eastwest-gateway.sh --network network1 | istioctl --kubeconfig=/root/.kube/cluster1.yaml install -y -f -

samples/multicluster/gen-eastwest-gateway.sh --network network2 | istioctl --kubeconfig=/root/.kube/cluster2.yaml install -y -f -

可以看到东西向网关实例已就绪

备注: gen-eastwest-gateway.sh 东西向网关安装脚本位于 istio 安装目录下,注意安装位置。

2.由于东西向网关创建的是 LoadBalancer 类型的 service,需要云厂商提供 ELB。IstioOperatorAPI 配置文件在进行网关数据面定制时并未提供 service annotation api 的声明。使用的默认的配置文件安装时,存在 ELB 无法自动创建的情况,可以前往 CCE 控制台手动进行设置。

由于绑定了公网 EIP,生产环境可能需要添加额外的访问限制(即:通过防火墙规则)来防止外部攻击。

3.开放 cluster1 、cluster2 中的服务因为集群位于不同的网络中,所以我们需要在两个集群东西向网关上开放所有服务(*.local)。 虽然此网关在互联网上是公开的,但它背后的服务只能被拥有可信 mTLS 证书、工作负载 ID 的服务访问, 就像它们处于同一网络一样。

kubectl --kubeconfig=/root/.kube/cluster1.yaml apply -n istio-system -f samples/multicluster/expose-services.yaml kubectl --kubeconfig=/root/.kube/cluster2.yaml apply -n istio-system -f samples/multicluster/expose-services.yaml

expose-services.yaml 详细内容如下:

apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: cross-network-gateway spec: selector: istio: eastwestgateway servers: - port: number: 15443 name: tls protocol: TLS tls: mode: AUTO_PASSTHROUGH hosts: - "*.local"

4.关于 mTLS 和 AUTO_PASSTHROUGH 说明大部分场景下 Istio Ingress Gateway 需要配套指定服务的 VIrtualService,用来指定 Ingress 流量的后端服务。但在多网络模式下,该入口网关需要作为本数据平面所有服务的流量入口,也就是说所有服务共享单个 Ingress Gateway(单个 IP 地址),这里其实是利用了 TLS 中的 SNI(Server Name Indication).传统的入口网关承载的是南北向流量,这里的入口网关属于网格内部流量,承载的是东西向流量。设置AUTO_PASSTHROUGH,可以允许服务无需配置 VirtualService,直接使用 TLS 中的 SNI 值来表示 Upstream,服务相关的 service/subset/port 都可以编码到 SNI 内容中


四 验证多集群跨网络流量治理

验证内容主要包括跨集群的服务访问,跨集群的灰度发布。

4.1 跨集群服务访问

  1. 部署客户端和服务端

####客户端 k1 apply -f samples/sleep/sleep.yaml -n sample k2 apply -f samples/sleep/sleep.yaml -n sample ####服务端两个版本,cluster1 部署v1 cluster2 部署v2 k1 apply -f samples/helloworld/helloworld.yaml -l version=v1 -n sample k1 apply -f samples/helloworld/helloworld.yaml -l service=helloworld -n sample ### k2 apply -f samples/helloworld/helloworld.yaml -l version=v2 -n sample k2 apply -f samples/helloworld/helloworld.yaml -l service=helloworld -n sample



2.分别从 cluster1、cluster2 的客户端发起请求进行访问测试



3.查看客户端、服务端、东西向网关的访问日志,发现数据流路径 cluster1 中客户端边车容器日志

cluster2 中的东西向网关日志:


cluster2 中服务端边车容器日志:



4.2 跨集群灰度发布

在多控制面模型中,Istio 控制面目前只监听主集群(也就是 istio 所在集群)的 VirtualService、DestinationRule、Gateway 等 Istio API 对象,因此 对于多控制面模型来说,相同的 Istio 配置需要被复制下发到多个集群中,否则不同集群的 Sidecar 订阅到的 xDS 配置可能会存在严重的不一致,导致不同集群的服务访问行为不一致。


  1. 针对服务端配置 virtualservice 路由规则

apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: helloworld namespace: sample spec: hosts: - helloworld http: - route: - destination: host: helloworld subset: v2 weight: 20 - destination: host: helloworld subset: v1 weight: 80

以上规则表示,当有流量访问域名为 helloworld 的服务时,客户端服务的 sidecar 容器 istio-proxy 会将 20%的流量路由到 v2 版本的 helloworld 负载上,将 80%的流量路由到 v1 版本的 helloworld 负载上。

2.针对服务端配置 destinationrule 目标规则

apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: helloworld namespace: sample spec: host: helloworld subsets: - name: v2 labels: version: v2 - name: v1 labels: version: v1

以上规则表示,将域名为 helloworld 的服务创建两个 subset 子集。负载实例满足标签为 version: v2 将被定义到 subset v2 中,负载实例满足标签为 version: v1 将被定义到 subset v1 中,一般和 virtualservice 结合使用。

3.将上述 istio CRD 规则应用于 cluster1、cluster2 中


访问测试,以 cluster2 集群中的 sleep 服务作为客户端访问 helloworld 服务端



4.3 地域负载均衡

  1. istio 的流量治理一般都是通过 virtualservice、destinationrule 、envoyfilter 等来实现,其中地域故障转移是通过 destinationrule 配置实现的。因为在 destinationrule 中可以配置 outerlineDecetion 进行异常点检测,只有检测到异常后,才会进行故障转移

  2. 详细配置如下

apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: helloworld namespace: sample spec: host: helloworld.sample.svc.cluster.local subsets: - name: v2 labels: version: v2 - name: v1 labels: version: v1 trafficPolicy: connectionPool: http: maxRequestsPerConnection: 1 loadBalancer: simple: ROUND_ROBIN localityLbSetting: #开启地域负载均衡 enabled: true failover: #配置故障转移策略,failover主要控制Region等上层位置的切换 - from: cn-north-4 to: cn-east-3 outlierDetection: #异常点检测 consecutive5xxErrors: 1 interval: 1s baseEjectionTime: 1m

以上治理策略表示:○ 异常点检测:当某个客户端访问 helloworld 服务时,客户端对应的 envoy 会根据本次访问 HTTP 状态码对转发的服务端进行故障检测,故障检测条件为当发生 1 次 5xx 错误时实例就会被隔离 1m。○ 故障隔离:当指定 region 的所有后端实例均不正常,触发故障转移到下一个地域,确保了超出地区边界的故障转移将具有可预测的行为。如果位于 cn-north-4 region 的实例异常,流量就会发往 cn-east-3 region 的实例

3.访问测试,分别从 cluster1、cluster2 进行访问,查看流量分发结果




五 注意事项

在跨网络多控制面的网格实践中,发现一些有趣的现象,做下记录声明。

1.跨集群访问时,只有注入了 istio-proxy 的实例才能接受到跨网络的流量,同时访问方式也仅支持通过 serviceName:port 的形式,不能通过 podIP 或者 clusterIP 进行访问,即使该 pod 注入了边车容器。


2.在做灰度发布访问测试时,发现一些问题。如果仅在 cluster2 中下发 virtualservice、destinationrule 流量治理规则,在进行跨集群灰度发布时,访问会报错,报错内容为 TLS_error:|33554536:system library:OPENSSL_internal:Connection reset by peer,如果在远端集群重复下发 virtualservice、destinationrule 流量治理规则,则不会报错客户端访问报错信息:

客户端 sidecar 容器访问日志: 其中119.3.173.42为东西向网关的 externalIP

东西向网关访问日志: 其中192.168.5.21 为东西向网关 pod 实例 ip 地址,119.3.122.8为客户端集群容器出网的 ip


3.如果存在灰度发布和地域负载均衡的使用情况,virtualservice 一般作用在不同的 cluster 上,destinationrule 作用在同个 cluster 的不同 endpoint 上,所以流量会先按照那个灰度发布的规则派发流量到不同版本的相同服务上,然后再考虑地域负载均衡。这样很容易引起误解。如果不同 region 的 cluster 上部署了不同版本的服务,从其中一个 region 的客户端进行访问,流量会安装灰度规则分发到不同 region 的集群,从而忽略了地域负载均衡。


点击关注,第一时间了解华为云新鲜技术~

用户头像

提供全面深入的云计算技术干货 2020-07-14 加入

生于云,长于云,让开发者成为决定性力量

评论

发布
暂无评论
非扁平网络场景下,基于开源istio治理CCE多集群_开源_华为云开发者联盟_InfoQ写作社区