摘要:该方案最为关键的技术是istio-eastwestgateway 东西向网关,它解决了网格跨网络访问的难题。

本文分享自华为云社区《基于开源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,设置alias

alias 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.gz
CCE 集群的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 certs
pushd 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-cacerts
make -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.pem

k2 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=network1
kubectl --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中

4.访问测试,以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的集群,从而忽略了地域负载均衡。

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

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐