写点什么

基于 istio 实现多集群流量治理

  • 2024-04-11
    广东
  • 本文字数:8403 字

    阅读完需:约 28 分钟

基于istio实现多集群流量治理

本文分享自华为云社区《基于istio实现多集群流量治理》,作者: 可以交个朋友。

一 背景


对多云、混合云等异构基础设施的服务治理是 Istio 重点支持的场景之一。为了提高服务的可用性,避免厂商锁定,企业通常会选择将应用部署在多个地域的多个集群,甚至多云、混合云等多种云环境下,多集群的方案逐步成为企业应用部署的最佳选择。因此越来越多的用户对跨集群的服务治理有着强烈的需求,在此背景下 Istio 作为 ServiceMesh 领域的事实标准,推出了多种多集群管理方案。

二 简介


目前 Istio 支持 4 种多集群模型。


  1. 扁平网络单控制面模型

  2. 扁平网络多控制面模型

  3. 非扁平网络单控制面模型

  4. 非扁平网络多控制面模型


多集群的单控制面模型是指多个集群共用同一套 Istio 控制面,多集群的多控制面模型指每个集群都要独立使用一套 Istio 控制面,无论是单控制面还是多控制面模型,每套 Istio 控制面(istiod)都要连接所有集群的 Kube-apiserver,并且 List-Watch 获取所有集群的Service、Endpoint、Pod 、Node ,并控制面集群内或集群间的服务访问,但是只监听主集群的VirtualService、DestinationRule、Gateway等 Istio API 对象。


根据集群间网络是否扁平,Istio 又对两种控制面模型进行了细分:


  • 扁平网络:多集群容器网络通过 VPN 等技术打通,Pod 跨集群访问直通。

  • 非扁平网络:每个集群的容器网络都相互隔离,跨集群的访问不能直通,必须通过东西向网关


生产环境上在选择 Istio 多集群模型时,当然需要结合自己的实际场景来决定。如果集群之间的网络是扁平的,那么可以选择扁平网络模型,如果集群之间的网络是隔离的,那么可以选择非扁平网络模型。如果集群规模较小,那么可以选择单控制面模型,如果集群规模较大,那么可以选择多控制面模型。


本文档选择非扁平网络多控制面模型来进行安装说明:安装模型如下所示



非扁平网络多控制面模型有如下特点。


  1. 不同的集群不需要在一张大网下,即容器网络不需要三层打通,跨集群的服务访问通过Istio East-West Gateway转发。

  2. 每个 kubernetes 集群的 Pod 地址范围与服务地址范围没有限制,可以与其他集群重叠,不同集群之间互不干扰

  3. 每个 Kubernetes 集群的 Sidecar 仅连接到本集群的 Istio 控制面,通信效率更高。

  4. Istiod 只监听主集群的 Istio 配置,因此 VirtualService、DestinationRule、Gateway 等资源存在冗余复制问题

  5. 同一集群内部服务访问: Pod 之间直接连接;跨集群的服务访问:依赖 DNS 代理解析其他集群的服务域名,由于集群之间的网络相互隔离,所以依赖 Remote 集群的 East-west Gateway中转流量。

三 ClusterMesh 环境搭建


搭建 cluster1 和 cluster2 两个集群,然后每个集群上安装 Istio 控制平面, 且将两者均设置为主集群(primary cluster)。 集群 cluster1 在 network1 网络上,而集群 cluster2 在 network2 网络上。

3.1 前提条件


本次搭建环境信息如下: 使用 Kind 搭建 Kubernetes 集群,Kind 版本为 v0.19.0。 Kubernetes 版本为 1.27.3 ; Istio 版本为 1.20.1。



在搭建 k8s 集群之前确保 Linux 节点已安装 docker kubectl 和 kind。


下载 istioctl 二进制


curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.20.1 TARGET_ARCH=x86_64 sh -


将 istioctl 客户端添加到路径


3.2 Kubernetes 集群安装


cluster1 和 cluster2 集群安装脚本如下


# create-cluster.sh# This script handles the creation of multiple clusters using kind and the# ability to create and configure an insecure container registry.
set -o xtraceset -o errexitset -o nounsetset -o pipefail
# shellcheck source=util.shNUM_CLUSTERS="${NUM_CLUSTERS:-2}"KIND_IMAGE="${KIND_IMAGE:-}"KIND_TAG="${KIND_TAG:-v1.27.3@sha256:3966ac761ae0136263ffdb6cfd4db23ef8a83cba8a463690e98317add2c9ba72}"OS="$(uname)"function create-clusters() { local num_clusters=${1}
local image_arg="" if [[ "${KIND_IMAGE}" ]]; then image_arg="--image=${KIND_IMAGE}" elif [[ "${KIND_TAG}" ]]; then image_arg="--image=kindest/node:${KIND_TAG}" fi for i in $(seq "${num_clusters}"); do kind create cluster --name "cluster${i}" "${image_arg}" fixup-cluster "${i}" echo
done}
function fixup-cluster() { local i=${1} # cluster num
if [ "$OS" != "Darwin" ];then # Set container IP address as kube API endpoint in order for clusters to reach kube API servers in other clusters. local docker_ip docker_ip=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "cluster${i}-control-plane") kubectl config set-cluster "kind-cluster${i}" --server="https://${docker_ip}:6443" fi
# Simplify context name kubectl config rename-context "kind-cluster${i}" "cluster${i}"}echo "Creating ${NUM_CLUSTERS} clusters"create-clusters "${NUM_CLUSTERS}"kubectl config use-context cluster1
echo "Kind CIDR is $(docker network inspect -f '{{$map := index .IPAM.Config 0}}{{index $map "Subnet"}}' kind)"
echo "Complete"
复制代码


以上集群安装的过程中,为了 istiod 能够访问对方集群的apiserver地址,集群kube-apiserver的地址设置为 master 节点的地址。因为是 kind 部署的集群,两个集群的 master 节点本质上都是同个宿主机上的 docker 运行的容器。



确认 cluster1 和 cluster2 是否就绪


3.3 使用 MetalLB 为网关分配 ExternalIP


由于使用的是 kind 部署多集群,istio 南北向网关和东西向网关创建需要创建 LoadBalencer service,均需要使用到 ExternalIP。这里借助 metalLB 实现 LB ip 地址的分发和宣告。查看 kind 搭建集群使用节点子网网段: 172.18.0.0/16采用 metalLB L2 模式进行部署。


cluster1 中的 metalLB 配置清单: metallb-config-1.yaml


### for cluster1##配置IPAddressPool,用于lbip地址的分配。L2模式下,ippool地址和worker节点处于同一子网即可apiVersion: metallb.io/v1beta1kind: IPAddressPoolmetadata:  name: first-pool  namespace: metallb-systemspec:  addresses:    - 172.18.1.230-172.18.1.240---##配置L2Advertisement,用于地址宣告apiVersion: metallb.io/v1beta1kind: L2Advertisementmetadata:  name: first-adv  namespace: metallb-systemspec:  ipAddressPools:     - first-pool
复制代码


cluster2 集群中的 metalLB 配置清单:metallb-config-2.yaml


### for cluster2##配置IPAddressPool,用于lbip地址的分配。L2模式下,ippool地址和worker节点处于同一子网即可apiVersion: metallb.io/v1beta1kind: IPAddressPoolmetadata:  name: second-pool  namespace: metallb-systemspec:  addresses:    - 172.18.1.241-172.18.1.252---##配置L2Advertisement,用于地址宣告apiVersion: metallb.io/v1beta1kind: L2Advertisementmetadata:  name: second-adv  namespace: metallb-systemspec:  ipAddressPools:     - second-pool
复制代码


使用脚本进行安装


#!/usr/bin/env bash
set -o xtraceset -o errexitset -o nounsetset -o pipefail
NUM_CLUSTERS="${NUM_CLUSTERS:-2}"for i in $(seq "${NUM_CLUSTERS}"); do echo "Starting metallb deployment in cluster${i}" kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.10/config/manifests/metallb-native.yaml --context "cluster${i}" kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)" --context "cluster${i}" ## 增加等待时间,如果metallb负载没部署起来,创建IPAddressPool L2Advertisement 会报错 sleep 10 kubectl apply -f ./metallb-config-${i}.yaml --context "cluster${i}" echo "----"done
复制代码


确认 metalLB 部署情况



确认 IPAddressPool 信息:


3.4 集群共享根 CA 配置信任关系


为了支持安全的跨集群 mTLS 通信,多控制面模型要求每个集群的控制面 Istiod 都使用相同的 CA 机构颁发的中间 CA 证书,供 Citatel 签发证书使用,以支持跨集群的 TLS 双向认证。



Istio 东西向网关(跨集群访问)工作时使用基于 SNI 的路由,它根据 TLS 请求的 SNI,自动将其路由到 SNI 对应的 Cluster,因此非扁平网络的跨网络访问要求所有流量都必须经过 TLS 加密。


在集群中插入证书和密钥,脚本如下(需要将该脚本移动到 istio 的安装包目录下):


#!/usr/bin/env bash
set -o xtrace#set -o errexitset -o nounsetset -o pipefailNUM_CLUSTERS="${NUM_CLUSTERS:-2}"##在istio安装包的顶层目录下 创建目录 用来存放证书和密钥mkdir -p certspushd certs
##生成根证书和密钥make -f ../tools/certs/Makefile.selfsigned.mk root-ca
for i in $(seq "${NUM_CLUSTERS}"); do ##对于每个集群,为 Istio CA 生成一个中间证书和密钥 make -f ../tools/certs/Makefile.selfsigned.mk "cluster${i}-cacerts" ##对于每个集群,创建istio-system 命名空间 kubectl create namespace istio-system --context "cluster${i}" ## 对于每个集群,通过给istio系统命名空间打上topology.istio.io/network 标签添加网络标识 kubectl --context="cluster${i}" label namespace istio-system topology.istio.io/network="network${i}" ##对于每个集群,给工作节点node打上地域和可用区标签,便于istio实现地域故障转移、地域负载均衡 kubectl --context="cluster${i}" label node "cluster${i}-control-plane" topology.kubernetes.io/region="region${i}" kubectl --context="cluster${i}" label node "cluster${i}-control-plane" topology.kubernetes.io/zone="zone${i}" #在每个集群中,创建一个私密 cacerts,使用所有输入文件 ca-cert.pem, ca-key.pem,root-cert.pem 和 cert-chain.pem。 kubectl delete secret cacerts -n istio-system --context "cluster${i}" kubectl create secret generic cacerts -n istio-system --context "cluster${i}" \ --from-file="cluster${i}/ca-cert.pem" \ --from-file="cluster${i}/ca-key.pem" \ --from-file="cluster${i}/root-cert.pem" \ --from-file="cluster${i}/cert-chain.pem" echo "----"done
复制代码


执行脚本,将会生成根证书和中间证书等文件



3.5 Istio 服务网格安装


为 cluster1,和 cluster2 集群安装多控制面 istio 网格。


将 cluster1 设置为主集群,在 istio 的安装目录下执行如下命令


cat <<EOF > cluster1.yamlapiVersion: install.istio.io/v1alpha1kind: IstioOperatorspec:  values:    global:      meshID: mesh1      multiCluster:  ##开启多集群配置        clusterName: cluster1 #指定k8s集群名称      network: network1 #指定网络标识      logging:        level: debugEOF
复制代码


将 cluster2 设置为主集群,在 istio 的安装目录下执行如下命令


cat <<EOF > cluster2.yamlapiVersion: install.istio.io/v1alpha1kind: IstioOperatorspec:  values:    global:      meshID: mesh2      multiCluster:  ##开启多集群配置        clusterName: cluster2 #指定k8s集群名称      network: network2 #指定网络标识      logging:        level: debugEOF
复制代码


编写自动化安装脚本


#!/usr/bin/env bash
set -o xtraceset -o errexitset -o nounsetset -o pipefail
OS="$(uname)"NUM_CLUSTERS="${NUM_CLUSTERS:-2}"
for i in $(seq "${NUM_CLUSTERS}"); do
echo "Starting istio deployment in cluster${i}"
istioctl install --force --context="cluster${i}" -f "cluster${i}.yaml"
echo "Generate eastwest gateway in cluster${i}"
## 在每个集群中安装东西向网关。bash samples/multicluster/gen-eastwest-gateway.sh \--mesh "mesh${i}" --cluster "cluster${i}" --network "network${i}" | \istioctl --context="cluster${i}" install -y -f -
echo
done
复制代码


执行脚本,进行 istio 的安装部署



稍等片刻后,等待安装完成



可以发现每个集群中的网关使用的 ExternalIP 信息为配置的 metalLB 设置的 IPPool 中的地址。

3.6 在东西向网关开放服务


因为集群位于不同的网络中,所以我们需要在两个集群东西向网关上开放所有服务(*.local)。 虽然此网关在互联网上是公开的,但它背后的服务只能被拥有可信 mTLS 证书的服务访问, 就像它们处于同一网络一样。执行下面的命令在两个集群中暴露服务:


apiVersion: networking.istio.io/v1beta1kind: Gatewaymetadata:  name: cross-network-gatewayspec:  selector:    istio: eastwestgateway # 专用于东西向流量的网关  servers:    - port:        number: 15443 # 已经声明了        name: tls        protocol: TLS      tls:        mode: AUTO_PASSTHROUGH # 东西向网关工作模式是 TLS AUTO_PASSTHROUGH      hosts:        - "*.local" # 暴露所有的服务
复制代码


分别在每个集群中应用上述 Gateway 配置:kubectl -n istio-system --context=cluster${i} apply -f samples/multicluster/expose-services.yaml


3.7 配置 secret 以便 istiod 访问远程集群 apiserver


每个 k8s 集群中的 istiod 需要 List-Watch 其他集群的 Kube-APIServer,使用 K8s 集群的凭据来创建 Secret 对象,以允许 Istio 访问远程 Kubernetes apiserver。


#!/usr/bin/env bash
set -o xtraceset -o errexitset -o nounsetset -o pipefailOS="$(uname)"NUM_CLUSTERS="${NUM_CLUSTERS:-2}"
for i in $(seq "${NUM_CLUSTERS}"); do for j in $(seq "${NUM_CLUSTERS}"); do if [ "$i" -ne "$j" ] then echo "Enable Endpoint Discovery between cluster${i} and cluster${j}"
if [ "$OS" == "Darwin" ] then # Set container IP address as kube API endpoint in order for clusters to reach kube API servers in other clusters. docker_ip=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "cluster${i}-control-plane") istioctl create-remote-secret \ --context="cluster${i}" \ --server="https://${docker_ip}:6443" \ --name="cluster${i}" | \ kubectl apply --validate=false --context="cluster${j}" -f - else istioctl create-remote-secret \ --context="cluster${i}" \ --name="cluster${i}" | \ kubectl apply --validate=false --context="cluster${j}" -f - fi fi donedone
复制代码


执行以上脚本:remote secret 创建完成。



查看 istiod 日志发现已经监听远程集群了


四 Istio 多集群流量治理实践



每个集群创建 sample 命名空间,并设置 sidecar 自动注入


kubectl create --context=cluster1 namespace samplekubectl create --context=cluster2 namespace sample
kubectl label --context=cluster1 namespace sample \ istio-injection=enabledkubectl label --context=cluster2 namespace sample \ istio-injection=enabled
kubectl apply --context=cluster1 \ -f samples/helloworld/helloworld.yaml \ -l service=helloworld -n samplekubectl apply --context=cluster2 \ -f samples/helloworld/helloworld.yaml \ -l service=helloworld -n sample
复制代码


分别在不同集群部署不同版本的服务


把应用 helloworld-v1 部署到 cluster1:


kubectl apply --context=cluster1 \    -f samples/helloworld/helloworld.yaml \    -l version=v1 -n sample
复制代码


把应用 helloworld-v2 部署到 cluster2:


kubectl apply --context=cluster2 \-f samples/helloworld/helloworld.yaml \-l version=v2 -n sample
复制代码


部署测试客户端


kubectl apply --context=cluster1 \    -f samples/sleep/sleep.yaml -n samplekubectl apply --context=cluster2 \    -f samples/sleep/sleep.yaml -n sample
复制代码


确认负载实例部署成功,并且 sidecar 已经注入


4.1 验证跨集群流量


用 Sleep pod 重复调用服务 HelloWorld。 为了确认负载均衡按预期工作,需要从所有集群调用服务 HelloWorld。


从 cluster1 中的 Sleep pod 发送请求给服务 HelloWorld



从 cluster2 中的 Sleep pod 发送请求给服务 HelloWorld


4.3 验证从网关访问


通过网关访问服务端 Helloworld


创建 virtualservice、gateway 等 istio 资源,配置清单如下


# helloworld-gateway.yamlapiVersion: networking.istio.io/v1beta1kind: Gatewaymetadata:  name: helloworld-gatewayspec:  selector:    istio: ingressgateway # use istio default controller  servers:    - port:        number: 80        name: http        protocol: HTTP      hosts:        - "*"---apiVersion: networking.istio.io/v1beta1kind: VirtualServicemetadata:  name: helloworldspec:  hosts:    - "*"  gateways:    - helloworld-gateway  http:    - match:        - uri:            exact: /hello      route:        - destination:            host: helloworld            port:              number: 5000
复制代码


注意: 两个集群都需要应用该配置


访问效果如下:


4.3 验证地域负载均衡


对流量进行更精细的控制,将 region1 -> zone1 和 region1 -> zone2 两个地区的权重分别为 80% 和 20%,使用 DestinationRule 来配置权重分布


# locality-lb-weight.yamlapiVersion: networking.istio.io/v1beta1kind: DestinationRulemetadata:  name: helloworld  namespace: samplespec:  host: helloworld.sample.svc.cluster.local  trafficPolicy:    connectionPool:      http:        maxRequestsPerConnection: 1    loadBalancer:      simple: ROUND_ROBIN      localityLbSetting:        enabled: true        distribute:          - from: region1/*            to:              "region1/*": 80              "region2/*": 20          - from: region2/*            to:              "region2/*": 80              "region1/*": 20    outlierDetection:      consecutive5xxErrors: 1      interval: 1s      baseEjectionTime: 1m
复制代码


注意: 两个集群都需要应用该配置


从 cluster1 中通过网关发送请求给服务 HelloWorld



从 cluster2 中通过网关发送请求给服务 HelloWorld


4.4 验证地域故障转移


当多个地区/区域部署多个服务实例时,如果某个地区/区域的服务实例不可用,可以将流量转移到其他地区/区域的服务实例上,实现地域故障转移,这样就可以保证服务的高可用性。


# locality-lb-failover.yamlapiVersion: networking.istio.io/v1beta1kind: DestinationRulemetadata:  name: helloworld  namespace: samplespec:  host: helloworld.sample.svc.cluster.local  trafficPolicy:    connectionPool:      http:        maxRequestsPerConnection: 1 # 关闭 HTTP Keep-Alive,强制每个HTTP请求使用一个新连接的策略    loadBalancer:      simple: ROUND_ROBIN      localityLbSetting:  # 地域负载均衡配置,开启异常点检测后,默认开启。        enabled: true             failover:         # 地域故障转移策略          - from: region1              to: region2          - from: region2            to: region1    outlierDetection:      consecutive5xxErrors: 1 # 连续 1 次 5xx 错误      interval: 1s # 检测间隔 1s      baseEjectionTime: 1m # 基础驱逐时间 1m
复制代码


注意: 两个集群都需要应用该配置


从 cluster1 中通过网关发送请求给服务 HelloWorld



模拟故障,手动将 cluster1 集群中 Helloworld V1 版本设置故障



再次访问,故障检测生效,触发故障转移,并验证响应中的 version 始终为 v2,也就是说我们访问的是 region2 的 helloworld 服务,这样就实现了地域故障转移。



故障转移的前提是当前 region 内,所有实例都不可用时,才会转移到到目前 region,否则流量还会发往当前 region 的其他可用实例。

五 备注


参考文献如下:


  1. istio 开源社区(跨网络多主架构的安装说明): https://istio.io/latest/zh/docs/setup/install/multicluster/multi-primary_multi-network/

  2. kind 安装集群脚本参考: https://github.com/cnych/multi-cluster-istio-kind/tree/main/kind-create

  3. 多集群证书管理参考:https://istio.io/latest/zh/docs/tasks/security/cert-management/plugin-ca-cert/


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

发布于: 7 小时前阅读数: 8
用户头像

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

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

评论

发布
暂无评论
基于istio实现多集群流量治理_istio_华为云开发者联盟_InfoQ写作社区