基于多集群网关实现同城容灾

多集群网关(Multi-cluster Gateways)是ACK One为多云、多集群环境提供的云原生网关,通过托管MSE Ingress并使用Ingress API以管理七层南北向流量。多集群网关支持同城自动容灾和基于Header的灰度发布,可以帮您简化多集群应用的运维和节省成本,结合ACK One GitOps的能力,您可以快速构建同城多活或主备容灾系统(不包含数据容灾)。

容灾概述

目前云上容灾主要分为以下三类:

  • 同城跨AZ容灾

    同城容灾包含多活容灾和主备容灾,同城中心之间物理距离较近,网络延迟低,可防范AZ级别性质的灾难损害,例如火灾、断网或断电等。

  • 跨地域多活容灾

    跨地域多活容灾对于网络延迟相对较高,但可以防范地域性质的灾难损害,例如地震、水灾等。

  • 两地三中心

    两地三中心将同城双中心和跨地域灾备结合起来,兼具两者的优点,适用于应用与数据连续性和可用性要求高的场景。

而从实际情况考虑,同城容灾相比于跨地域容灾,在数据容灾方面更容易实现,所以同城容灾仍然具有非常重要的意义。

功能优势

基于多集群网关的容灾方案与基于DNS流量分发的容灾方案对比优势如下:

  • 基于DNS流量分发的容灾方案需要多个LB IP(每个集群1个),而基于多集群网关的容灾方案在地域级别仅需要1个LB IP,且默认提供同地域多可用区的高可用性。

  • 基于多集群网关的容灾支持七层路由转发能力,而基于DNS流量分发的容灾不支持七层路由转发。

  • 基于DNS流量分发的容灾方案在IP切换时,通常会有客户端缓存而造成服务短暂不可用,而基于多集群网关的容灾方案则可以平滑地将流量Fallback到另一个集群的服务后端。

  • 多集群网关是地域级别的,所以所有操作都仅需在Fleet实例中操作(创建网关、Ingress资源等),无需在每个ACK集群中都安装Ingress Controller和创建Ingress资源,提供地域级全局流量管理能力的同时,还能减少多集群管理成本。

方案架构

ACK One多集群网关通过托管MSE Ingress来实现,结合ACK One GitOps的应用多集群分发能力,可以帮助您快速实现应用的同城容灾系统。本文将通过GitOps部署示例应用到中国(香港)地域的两个不同可用区的ACK集群(Cluster 1、Cluster 2)为例,介绍如何实现同城多活容灾和同城主备容灾。

本文示例应用为Web应用,包含Deployment、Service资源,通过多集群网关实现同城容灾的方案架构如下图所示。image.png

  • 在ACK One Fleet中通过MseIngressConfig资源来创建MSE网关。

  • 在同一个地域的两个不同可用区AZ 1和AZ 2中,分别创建一个ACK集群Cluster 1和Cluster 2。

  • 通过ACK One GitOps将应用分发到已创建的Cluster 1和Cluster 2集群中。

  • 创建多集群网关后,通过设置流量规则可以实现按权重路由流量、根据Header将流量路由到指定集群的能力,当其中一个集群异常时,流量将自动路由到另一个集群中。

前提条件

步骤一:使用GitOps部署应用到多个集群

通过ArgoCD UI部署

  1. 登录ACK One控制台,在左侧导航栏选择舰队 > GitOps

  2. GitOps页面,单击GitOps控制台跳转至GitOps页面。

    说明
    • ACK One舰队的Fleet实例未开启GitOps,请先单击开启GitOps,才能登录GitOps控制台

    • 如需通过公网访问GitOps,请开通公网访问GitOps

  3. 添加应用仓库。

    1. 在ArgoCD UI左侧导航栏选择Settings,然后选择Repositories > + Connect Repo

    2. 在弹出的面板中配置以下信息,然后单击CONNECT添加连接。

      区域

      参数

      参数值

      Choose your connection method

      VIA HTTPS

      CONNECT REPO USING HTTPS

      Type

      git

      Project

      default

      Repository URL

      https://github.com/AliyunContainerService/gitops-demo.git

      Skip server verification

      勾选

      image.png

      连接添加成功后会显示Git的CONNECTION STATUSSuccessful。

      image.png

  4. 创建应用。

    在ArgoCD的Applications页面中,单击+ NEW APP,进行如下参数配置。

    区域

    参数

    说明

    GENERAL

    Application Name

    自定义应用名称。

    Project Name

    default

    SYNC POLICY

    可根据实际情况选择同步策略。取值:

    • Manual:当Git端有变更时,需手动执行同步动作将其部署至目标集群。

    • Automatic:ArgoCD Server每隔3分钟自动检测Git端的变更并将其自动部署至目标集群。

    SYNC OPTIONS

    勾选AUTO-CREATE NAMESPACE

    SOURCE

    Repository URL

    在下拉列表选择已有Git Repo,此处选择上一步已添加的https://github.com/AliyunContainerService/gitops-demo.git

    Revision

    选择Branches:gateway-demo

    Path

    manifests/helm/web-demo

    DESTINATION

    Cluster URL

    选择集群1或集群2的URL。

    也可以单击右侧的URL,切换为根据CLUSTER NAME字段选择。

    Namespace

    本示例使用gateway-demo。表示应用资源(Service、Deployment)将被创建在此Namespace下。

    Helm

    Parameters

    envCluster值设置为cluster-demo-1cluster-demo-2,用于区分请求由哪个集群的后端处理。

通过ArgoCD CLI部署

  1. 在ACK One Fleet实例中开启GitOps。具体操作,请参见在ACK One Fleet实例中开启GitOps

  2. 访问ArgoCD。具体操作,请参见通过ArgoCD CLI方式访问ArgoCD

  3. 创建并发布应用。

    1. 执行以下命令,添加Git Repo。

      argocd repo add https://github.com/AliyunContainerService/gitops-demo.git --name ackone-gitops-demos

      预期输出:

      Repository 'https://github.com/AliyunContainerService/gitops-demo.git' added
    2. 执行以下命令,查看已添加的Git Repo列表。

      argocd repo list

      预期输出:

      TYPE  NAME  REPO                                                       INSECURE  OCI    LFS    CREDS  STATUS      MESSAGE  PROJECT
      git         https://github.com/AliyunContainerService/gitops-demo.git  false     false  false  false  Successful           default
    3. 执行以下命令,查看Clusters列表。

      argocd cluster list

      预期输出:

      SERVER                          NAME                                        VERSION  STATUS      MESSAGE                                                  PROJECT
      https://1.1.XX.XX:6443      c83f3cbc90a****-temp01   1.22+    Successful
      https://2.2.XX.XX:6443      c83f3cbc90a****-temp02   1.22+    Successful
      https://kubernetes.default.svc  in-cluster                                           Unknown     Cluster has no applications and is not being monitored.
    4. 使用Application方式创建并发布Demo应用到目标集群。

      1. 使用以下内容,创建apps-web-demo.yaml文件。

        其中repoURL需替换为您实际的仓库地址。

        展开查看apps-web-demo.yaml文件

        apiVersion: argoproj.io/v1alpha1
        kind: Application
        metadata:
          name: app-demo-cluster1
          namespace: argocd
        spec:
          destination:
            namespace: gateway-demo
            # https://1.1.XX.XX:6443
            server: ${cluster1_url}
          project: default
          source:
            helm:
              releaseName: "web-demo"
              parameters:
              - name: envCluster
                value: cluster-demo-1
              valueFiles:
              - values.yaml
            path: manifests/helm/web-demo
            repoURL: https://github.com/AliyunContainerService/gitops-demo.git
            targetRevision: gateway-demo
          syncPolicy:
            syncOptions:
            - CreateNamespace=true
        ---
        apiVersion: argoproj.io/v1alpha1
        kind: Application
        metadata:
          name: app-demo-cluster1
          namespace: argocd
        spec:
          destination:
            namespace: gateway-demo
            server: ${cluster2_url}
          project: default
          source:
            helm:
              releaseName: "web-demo"
              parameters:
              - name: envCluster
                value: cluster-demo-2
              valueFiles:
              - values.yaml
            path: manifests/helm/web-demo
            repoURL: https://github.com/AliyunContainerService/gitops-demo.git
            targetRevision: gateway-demo
          syncPolicy:
            syncOptions:
            - CreateNamespace=true
      2. 执行以下命令,部署应用。

        kubectl apply -f apps-web-demo.yaml
      3. 执行以下命令,查看应用列表。

        argocd app list

        预期输出:

        # app list
        NAME                      CLUSTER                  NAMESPACE  PROJECT  STATUS  HEALTH   SYNCPOLICY  CONDITIONS  REPO                                                       PATH                     TARGET
        argocd/web-demo-cluster1  https://10.1.XX.XX:6443             default  Synced  Healthy  Auto        <none>      https://github.com/AliyunContainerService/gitops-demo.git  manifests/helm/web-demo  main
        argocd/web-demo-cluster2  https://10.1.XX.XX:6443             default  Synced  Healthy  Auto        <none>      https://github.com/AliyunContainerService/gitops-demo.git  manifests/helm/web-demo  main

步骤二:在Fleet实例中通过kubectl创建多集群网关

通过在ACK One Fleet中创建MseIngressConfig对象来创建一个多集群网关,并将关联集群添加至多集群网关。

  1. 获取ACK One Fleet实例的虚拟交换机ID并记录。具体操作,请参见获取交换机ID

  2. 使用以下内容,创建gateway.yaml文件。

    说明
    • 请将${vsw-id}替换为您上一步获取的交换机ID,${cluster1}${cluster2}替换为您待添加的关联集群ID。

    • 子集群${cluster1}${cluster2}的安全组的入方向需要允许该交换机网段的IP和所有端口通过。

    apiVersion: mse.alibabacloud.com/v1alpha1
    kind: MseIngressConfig
    metadata:
      annotations:
        mse.alibabacloud.com/remote-clusters: ${cluster1},${cluster2}
      name: ackone-gateway-hongkong
    spec:
      common:
        instance:
          replicas: 3
          spec: 2c4g
        network:
          vSwitches:
          - ${vsw-id}
      ingress:
        local:
          ingressClass: mse
      name: mse-ingress

    参数

    说明

    mse.alibabacloud.com/remote-clusters

    表示待添加到多集群网关的集群。此处所填的是已经关联到Fleet实例的集群ID。

    spec.name

    网关实例名称。

    spec.common.instance.spec

    可选,网关实例规格。默认为4c8g

    spec.common.instance.replicas

    可选,网关实例副本数。默认为3个。

    spec.ingress.local.ingressClass

    可选,待监听的ingressClass name,表示监听Fleet实例中所有ingressClass字段设置为mse的Ingress。

  3. 执行以下命令,部署多集群网关。

    kubectl apply -f gateway.yaml
  4. 执行以下命令,验证多集群网关是否创建成功并处于监听状态。

    kubectl get mseingressconfig ackone-gateway-hongkong

    预期输出:

    NAME                      STATUS      AGE
    ackone-gateway-hongkong   Listening   3m15s

    预期输出中网关状态为Listening,表明云原生网关创建成功处于运行状态,并自动监听集群中IngressClass为mse的Ingress资源。

    通过MseIngressConfig创建的多集群网关会按照Pending、Running、Listening的状态依次变化。各状态说明如下:

    • Pending:表明云原生网关正在创建中,需等待3分钟左右。

    • Running:表明云原生网关创建成功,并处于运行状态。

    • Listening:表明云原生网关处于运行状态,并监听集群中Ingress资源。

    • Failed:表明云原生网关处于非法状态,可以查看Status字段中Message来进一步明确原因。

  5. 执行以下命令,确认关联集群是否添加成功。

    kubectl get mseingressconfig ackone-gateway-hongkong -ojsonpath="{.status.remoteClusters}"

    预期输出:

    [{"clusterId":"c7fb82****"},{"clusterId":"cd3007****"}]

    预期输出中已包含指定的Cluster ID,并且无Failed信息,表明关联集群已成功添加至多集群网关。

步骤三:使用Ingress实现同城容灾

多集群网关通过Ingress对多集群流量进行管理,您可以在ACK One Fleet实例中创建Ingress对象来实现同城多活容灾和同城主备容灾。

重要

确认您已经在Fleet实例中创建Namespacegateway-demo,此处创建的Ingress需要和上一步部署的Demo应用中Service的Namespace保持一致为gateway-demo

同城多活容灾

创建Ingress实现同城多活容灾

在ACK One Fleet实例中创建以下Ingress对象,可以实现同城多活容灾。

默认将流量负载均衡到已添加到多集群网关的两个集群的同名服务后端的每个副本中,如果其中的Cluster 1后端异常,网关自动将流量全都路由到Cluster 2。Cluster 1和Cluster 2的副本数比为9:1,所以默认90%流量被路由到Cluster 1, 10%流量被路由到Cluster 2,在Cluster 1后端全部异常后,100%流量自动被路由到Cluster 2。拓扑如下所示:image.png

  1. 使用以下内容,创建ingress-demo.yaml文件。

    以下代码中,通过域名example.com下的/svc1路由规则暴露后端服务service1

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: web-demo
    spec:
      ingressClassName: mse
      rules:
      - host: example.com
        http:
          paths:
          - path: /svc1
            pathType: Exact
            backend:
              service:
                name: service1
                port: 
                  number: 80
  2. 执行以下命令,在ACK One Fleet实例中部署Ingress。

    kubectl apply -f ingress-demo.yaml -n gateway-demo

实现灰度发布验证

在多活容灾场景下,不影响业务的前提下,您可以通过以下方式对应用进行灰度发布验证。

在已有集群中新建应用,仅将Service和Deployment的nameselector与原应用进行区分,之后基于Header在新建的应用上进行验证。

  1. 使用以下内容,在Cluster 1中新建new-app.yaml文件。仅Service和Deployment与原应用有差异,其他均一致。

    apiVersion: v1       
    kind: Service
    metadata:
      name: service1-canary-1
      namespace: gateway-demo
    spec:
      ports:
      - port: 80
        protocol: TCP
        targetPort: 8080
      selector:
        app: web-demo-canary-1
      sessionAffinity: None
      type: ClusterIP
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: web-demo-canary-1
      namespace: gateway-demo
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: web-demo-canary-1
      template:
        metadata:
          labels:
            app: web-demo-canary-1
        spec:
          containers:
            - env:
                - name: ENV_NAME
                  value: cluster-demo-1-canary
              image: 'registry-cn-hangzhou.ack.aliyuncs.com/acs/web-demo:0.6.0'
              imagePullPolicy: Always
              name: web-demo
  2. 执行以下命令,在Cluster 1中部署新的应用。

    kubectl apply -f new-app.yaml
  3. 使用以下内容,创建new-ingress.yaml文件。

    在Fleet实例中创建基于Header的Ingress。通过添加Annotations开启Canary,并指定Header,在请求中使用Header: canary-dest: cluster1来将流量路由到灰度版本进行验证。

    • nginx.ingress.kubernetes.io/canary:设置为"true",表示开启灰度发布能力。

    • nginx.ingress.kubernetes.io/canary-by-header:请求到该集群的header key。

    • nginx.ingress.kubernetes.io/canary-by-header-value:请求到该集群的header value。

      apiVersion: networking.k8s.io/v1
      kind: Ingress
      metadata:
        name: web-demo-canary-1
        namespace: gateway-demo
        annotations:
          nginx.ingress.kubernetes.io/canary: "true"
          nginx.ingress.kubernetes.io/canary-by-header: "canary-dest"
          nginx.ingress.kubernetes.io/canary-by-header-value: "cluster1"
      spec:
        ingressClassName: mse
        rules:
        - host: example.com
          http:
            paths:
            - path: /svc1
              pathType: Exact
              backend:
                service:
                  name: service1-canary-1
                  port: 
                    number: 80
  4. 执行以下命令,在Fleet实例中部署基于Header的Ingress。

    kubectl apply -f new-ingress.yaml

验证同城多活容灾实现效果

修改部署到Cluster 1的副本数为9,Cluster 2的副本数为1,即期望默认流量按照9:1路由到Cluster 1和Cluster 2,并且在Cluster1异常后,自动将所有流量路由到Cluster 2。

执行以下命令,获取多集群网关的对外IP。

kubectl get ingress web-demo -n gateway-demo -ojsonpath="{.status.loadBalancer}"
  • 默认流量按比例路由到两个集群

    执行以下命令,查看流量的路由情况。

    替换以下XX.XX.XX.XX为上一步获取的多集群网关的对外IP。

    for i in {1..100}; do curl -H "host: example.com" XX.XX.XX.XX done

    预期输出:image.png预期输出表明,流量被负载均衡到了Cluster 1和Cluster 2,到Cluster 1和Cluster 2的流量比为9:1。

  • Cluster 1异常后流量全部容灾到Cluster 2

    如果将Cluster 1的Deployment的replica缩为0时,得到的输出结果如下,表明流量已被自动容灾到Cluster 2。image.png

  • 通过Header将请求路由到灰度版本

    执行以下命令,查看流量的路由情况。

    替换以下XX.XX.XX.XX实现灰度发布验证中的应用和Ingress部署完成后查询到的IP。

    for i in {1..100}; do curl -H "host: example.com" -H "canary-dest: cluster1" xx.xx.xx.xx/svc1; sleep 1;  done  

    预期输出:image.png预期输出表明,添加了header:canary-dest: cluster1的请求都被路由到了Cluster 1中的灰度版本。

同城主备容灾

在ACK One Fleet实例中创建以下Ingress对象,可以实现同城主备容灾。

当两个集群后端都正常时,流量仅路由到Cluster 1的服务后端;如果其中一个Cluster 1后端异常,网关会自动将流量路由到Cluster 2。拓扑如下所示:image.png

创建Ingress实现同城主备容灾

  1. 使用以下内容,创建ingress-demo-cluster-one.yaml文件。

    以下Ingress对象的YAML文件中,通过添加两个Annotations mse.ingress.kubernetes.io/service-subsetmse.ingress.kubernetes.io/subset-labels以实现域名example.com下的/service1路由规则暴露后端服务service1。关于MSE Ingress支持的Annotations详情,请参见MSE Ingress支持的Annotation

    • mse.ingress.kubernetes.io/service-subset:服务的子版本名称。建议定义为与目标集群相关且可读性强的值。

    • mse.ingress.kubernetes.io/subset-labels:指定目标集群的ID。

      apiVersion: networking.k8s.io/v1
      kind: Ingress
      metadata:
        annotations:
          mse.ingress.kubernetes.io/service-subset: cluster-demo-1
          mse.ingress.kubernetes.io/subset-labels: |
            topology.istio.io/cluster ${cluster1-id}
        name: web-demo-cluster-one
      spec:
        ingressClassName: mse
        rules:
        - host: example.com
          http:
            paths:
            - path: /service1
              pathType: Exact
              backend:
                service:
                  name: service1
                  port: 
                    number: 80
  2. 执行以下命令,在ACK One Fleet实例中部署Ingress。

    kubectl apply -f ingress-demo-cluster-one.yaml -ngateway-demo

实现集群级别灰度发布

多集群网关支持创建基于Header的Ingress以实现将请求路由到指定集群,基于该能力与创建Ingress实现同城主备容灾中Ingress对象实现的全副本负载均衡的能力,可以实现基于Header的灰度发布。

  1. 创建Ingress实现同城主备容灾中的Ingress。

  2. 在ACK One Fleet实例中创建包含Header相关Annotation的Ingress,实现集群级别的灰度发布。请求的Header与Ingress中的配置相匹配时,请求将被路由给灰度版本的后端。

    1. 使用以下内容,创建ingress-demo-cluster-gray.yaml文件。

      以下Ingress对象的YAML文件中,替换YAML中的${cluster1-id}为目标集群ID。除了mse.ingress.kubernetes.io/service-subsetmse.ingress.kubernetes.io/subset-labels两个Annotation之外,您还需要添加以下Annotation以实现域名example.com下的/service1路由规则暴露后端服务service1

      • nginx.ingress.kubernetes.io/canary:设置为"true",表示开启灰度发布能力。

      • nginx.ingress.kubernetes.io/canary-by-header:表示请求到该集群的header key。

      • nginx.ingress.kubernetes.io/canary-by-header-value:表示请求到该集群的header value。

      apiVersion: networking.k8s.io/v1
      kind: Ingress
      metadata:
        annotations:
          mse.ingress.kubernetes.io/service-subset: cluster-demo-2
          mse.ingress.kubernetes.io/subset-labels: |
            topology.istio.io/cluster ${cluster2-id}
          nginx.ingress.kubernetes.io/canary: "true"
          nginx.ingress.kubernetes.io/canary-by-header: "app-web-demo-version"
          nginx.ingress.kubernetes.io/canary-by-header-value: "gray"
        name: web-demo-cluster-gray
        name: web-demo
      spec:
        ingressClassName: mse
        rules:
        - host: example.com
          http:
            paths:
            - path: /service1
              pathType: Exact
              backend:
                service:
                  name: service1
                  port: 
                    number: 80
    2. 执行以下命令,在ACK One Fleet实例中部署Ingress。

      kubectl apply -f ingress-demo-cluster-gray.yaml -n gateway-demo

验证同城主备容灾实现效果

为便于验证,修改部署到Cluster 1和Cluster 2的副本数都为1,期望默认流量只被路由到Cluster 1,加灰度Header的流量被路由到Cluster 2,当Cluster 1应用异常后,默认流量也被路由到Cluster 2。

执行以下命令,获取多集群网关的对外IP。

kubectl get ingress web-demo -n gateway-demo -ojsonpath="{.status.loadBalancer}"
  • 默认流量路由到Cluster 1

    执行以下命令,查看默认流量是否被路由到Cluster 1。

    替换以下XX.XX.XX.XX为上一步获取的多集群网关的对外IP。

    for i in {1..100}; do curl -H "host: example.com" xx.xx.xx.xx/service1; sleep 1;  done

    预期输出:image.png预期输出表明,默认流量全都被路由到Cluster 1了。

  • 通过header将请求路由到灰度版本

    执行以下命令,查看Header的请求是否被路由到灰度版本。

    替换以下XX.XX.XX.XX为上一步获取的多集群网关的对外IP。

    for i in {1..50}; do curl -H "host: example.com" -H "app-web-demo-version: gray" xx.xx.xx.xx/service1; sleep 1;  done

    预期输出:image.png预期输出表明,添加了Header:app-web-demo-version: gray的请求都被路由到了Cluster 2(灰度版本)。

  • Cluster 1异常后流量被容灾到Cluster 2

    如果将Cluster 1的Deployment的replica缩为0时,得到的输出结果如下,表明流量已被自动容灾到Cluster 2。image.png

相关文档