文档首页/ 华为云UCS/ 最佳实践/ 可靠性/ UCS双集群可靠性提升建议
更新时间:2025-08-28 GMT+08:00
分享

UCS双集群可靠性提升建议

应用场景

大企业场景提供多集群多活方案,做小故障域降低逻辑层面故障的风险,提供原有生态的兼容,最大限度降低在业务发布、运维等方面的适配工作量。

通过UCS提供双集群多活容灾,可以确保在任何一个可用区或集群发生故障时,不影响服务整体可用性。

约束限制

  • 您需要拥有至少两个Kubernetes版本为1.21及以上的可用CCE turbo集群(如下文中ucs01与ucs02),并且集群分布在不同的AZ。
  • 当前UCS服务不支持跨region容灾部署。

方案架构简介

容器级容错实施建议

容器级容错旨在通过配置健康检查和自动重启机制,确保容器应用的高可用性和可靠性。应用部署需要遵守以下规范:

项目

描述

说明

应用无状态化

应用必须做无状态和幂等处理。

服务的无状态化是部署的多个服务模块(进程),使其完全对等。也就是部署多个Pod实例,请求到任一实例的处理结果是一样的。这些Pod实例不存储业务的上下文信息,比如session、登录、业务上下文相关的信息。只会根据每次请求携带的数据进行相应的业务处理。

幂等指的是使用相同的参数多次调用相同的API,对后端产生的影响是一致的。

应用副本数

每个应用负载实例数满足业务容量规划和可用性要求。

  • 必须:每个负载的实例数不小于2。
  • 建议:4个实例,每个集群有2个实例。

配合多集群方案,满足生产中应用高可用性要求。为集群级别和可用区(AZ)级别故障域隔离创造条件。

应用健康检查

每个应用必须配置:

  • 启动探针(Startup Probe):适用于启动时间较长的应用,可以避免在应用尚未完全启动时就进行就绪检查。
  • 存活探针(Liveness Probe):用于检测容器是否还在运行,如果失败,Kubernetes会重启容器。
  • 就绪探针(Readiness Probe):用于确定容器是否已经准备好接受流量,如果失败,该容器将不会被认为是“就绪状态”,从而不会接收到服务流量。

配置合理的检查间隔和超时:

  • 设置合理的periodSeconds(检查间隔)和timeoutSeconds(超时时间),以平衡检查频率和系统负载。
  • 对于启动探针,可以设置较长的间隔和超时,以适应应用的启动时间。

容器出现故障或无法正常工作,系统可以自动重启该容器,从而提高应用的可用性和可靠性。

弹性伸缩

业务支持自动扩缩容的能力即配置指标弹性HPA,并且要求:

  • 必须:HPA minReplicas不小于2,HPA maxReplicas大于等于minReplicas。
  • 建议:HPA minReplicas值为4。

业务按需使用资源,最大程度地减少资源浪费。在业务流量突增以及集群级故障时,应用能够自动扩容,保障业务不受损。

优雅停机

应用必须支持优雅停机。

优雅停机是在对应用进程发送停止指令之后,能保证正在执行的业务操作不受影响。应用接收到停止指令之后的步骤应该是,停止接收访问请求,等待已经接收到的请求处理完成,并能成功返回,这时才真正停止应用。

ELB健康检查

弹性负载均衡ELB流量分发正常工作的后端。

必须:应用提供健康检查接口,并在ELB上配置健康检查。

在个别实例异常、节点异常或者整个AZ、整个集群故障时,能快速地隔离故障实例,保证业务访问成功率。

容器镜像

容器镜像体积最大应不超过1G。容器镜像标签使用具体的版本号。

必须:容器镜像标签禁止使用latest。

体积小的镜像有利于分发、快速启动;镜像使用具体的版本号才能做版本控制。

资源配额

资源配额应为资源申请量的两倍数值。

预防应用升级、应用扩容场景时,因资源配额不足导致失败的情况。

节点级容错配置建议

节点级容错是指当某个节点发生故障时,可以将Pod自动重新调度到其他健康节点上。

项目

描述

说明

节点故障自动驱逐

当节点出现异常,变为不可用状态时,容器将在该容忍时间后自动驱逐,默认为300s。默认对所有的容器生效,用户也可以为指定pod进行差异化容忍配置,此时将以Pod配置的容忍时长为准。

无特殊需求建议保持默认配置,容忍时间配置过小可能导致容器在网络抖动等一些短时故障场景下频繁迁移影响业务,容忍时间配置过大可能导致容器在节点故障时长时间无法迁移导致业务受损。

集群节点弹性

节点弹性伸缩,也就是资源层面的弹性伸缩。CA(Cluster AutoScaling)会检查所有Pending状态的Pod,根据用户配置的扩缩容策略,选择出一个最合适的节点池进行扩容。

当集群资源不够时需要CA扩容节点,使得集群有足够资源;而当HPA缩容后集群会有大量空余资源,这时需要CA缩容节点释放资源,才不至于造成浪费。CA的上限应根据业务高峰期的资源需求或者单集群故障来设定,确保有足够的节点应对流量激增。

通过kubectl命令恢复集群级/AZ级故障(可选)

集群关键系统组件出现故障或者集群升级策略不当、升级配置有误、操作人员执行有误等人为因素导致集群整体不可用或者出现AZ站点级别的故障时,UCS提供手动切流的能力。通过创建Remedy对象将MultiClusterIngress流量从故障集群上摘除。

通过Kubectl命令恢复故障步骤如下:

  1. 使用kubectl连接集群联邦,详细操作请参见通过kubectl连接集群联邦
  2. 集群故障后,在执行机上创建并编辑remedy.yaml文件,文件内容如下所示,参数定义请参见表1

    vi remedy.yaml

    示例YAML定义了一个Remedy对象,触发条件为空,表示无条件触发,集群联邦控制器会立即将ucs01上的流量摘除。在集群故障恢复后,删除该Remedy对象,ucs01上的流量会自动恢复,由此保证单集群的故障不会影响服务的可用性。
    apiVersion: remedy.karmada.io/v1alpha1
    kind: Remedy
    metadata:
      name: foo
    spec:
      clusterAffinity:
        clusterNames:
          - ucs01
      actions:
      - TrafficControl
    表1 Remedy参数说明

    参数

    描述

    spec.clusterAffinity.clusterNames

    策略关注的集群名列表。仅在该列表中的集群会执行指定动作,为空时不会执行任何动作。

    spec.decisionMatches

    触发条件列表。当上述集群列表中指定的集群满足任一触发条件时,即会执行指定动作。当列表为空时,表示无条件触发。

    conditionType

    触发条件的类型。当前仅支持ServiceDomainNameResolutionReady类型,即CPD上报的CoreDNS域名解析状态。

    operator

    判断逻辑,仅支持Equal和NotEqual两种值,即等于和不等于。

    conditionStatus

    触发条件的状态。

    actions

    策略要执行的动作,目前仅支持TrafficControl,即流量控制。

  3. 集群故障恢复后,删除该Remedy对象。

    kubectl delete remedy foo

  4. 检查集群ucs01上的流量已自动恢复,手动切流成功。

使用kubectl命令实现UCS高可用部署操作步骤

前置条件:

以下均为示例yaml,请根据实际情况修改参数内容。

实践操作操作步骤:

使用UCS高可用部署,需要进行指定资源下发规则,示例如下yaml:

apiVersion: policy.karmada.io/v1alpha1
kind: ClusterPropagationPolicy
metadata:
  name: karmada-global-policy # 策略名
spec:
  resourceSelectors: # 分发策略关联的资源,支持同时分发多个资源对象
  - apiVersion: apps/v1 # group/version
    kind: Deployment # 资源类型kind
  - apiVersion: apps/v1
    kind: DaemonSet
  - apiVersion: v1
    kind: Service
  - apiVersion: v1
    kind: Secret
  - apiVersion: v1
    kind: ConfigMap
  - apiVersion: v1
    kind: ResourceQuota
  - apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
  - apiVersion: autoscaling/v2beta2
    kind: HorizontalPodAutoscaler
  - apiVersion: autoscaling.cce.io/v2alpha1
    kind: CronHorizontalPodAutoscaler
  priority: 0 # 数值越大,优先级越高
  conflictResolution: Overwrite # conflictResolution声明当正在传播的资源已存在于目标集群中时, 默认为“Abort”,这意味着停止传播以避免意外覆盖。Overwrite则表示强制覆盖。
  placement: # 放置规则即把关联资源分发到哪些集群
    clusterAffinity: # 配置集群亲和性
      clusterNames: # 使用集群名选择集群
      - ucs01 # 集群名:需要修改为环境中实际集群名
      - ucs02 # 集群名:需要修改为环境中实际集群名
    replicaScheduling: # 实例调度策略
      replicaSchedulingType: Divided # 实例拆分
      replicaDivisionPreference: Weighted # 根据权重拆分
      weightPreference: # 权重选项
        staticWeightList: # 静态权重列表: ucs01的权重为1(分配到大约1/2的实例数),ucs02权重为1 (分配到大约1/2的实例数)
        - targetCluster: # 目标集群
            clusterNames:
            - ucs01 # 集群名:需要修改为环境中实际集群名
          weight: 1 # 权重为1
        - targetCluster: # 目标集群
            clusterNames:
             - ucs02 # 集群名:需要修改为环境中实际集群名
          weight: 1 # 权重为1
    clusterTolerations: # 集群容忍,当集群master不健康或不可达时,应用不作驱逐处理
    - key: cluster.karmada.io/not-ready
      operator: Exists
      effect: NoExecute
    - key: cluster.karmada.io/unreachable
      operator: Exists
      effect: NoExecute

若创建了HPA,需要拆分hpa中的最小实例数,可使用如下示例yaml(可选):

以下yaml中clusterNum为集群数量,示例集群数量为2,请根据实际场景配置。

apiVersion: config.karmada.io/v1alpha1
kind: ResourceInterpreterCustomization
metadata:
  name: hpa-min-replica-split-ric
spec:
  customizations:
    replicaResource:
      luaScript: |
        function GetReplicas(obj)
          clusterNum = 2
          replica = obj.spec.minReplicas
          if ( obj.spec.minReplicas == 1 )
          then
             replica = clusterNum
          end
          return replica, nil
        end
    replicaRevision:
      luaScript: |
        function ReviseReplica(obj, desiredReplica)
          obj.spec.minReplicas = desiredReplica
          return obj
        end
  target:
    apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler

创建configmap实例,示例如下yaml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: demo-configmap
  namespace: default       #命名空间,默认为default
data:
  foo: bar

创建deployment实例,示例如下yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
  namespace: default       #命名空间,默认为default
  labels:
     app: demo
spec:
  replicas: 2
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - demo
            topologyKey: kubernetes.io/hostname
      containers:
      - env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
        - name: POD_IP
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: status.podIP
        envFrom:
        - configMapRef:
            name: demo-configmap
        name: demo
        image: nginx    #若使用“开源镜像中心”的镜像,可直接填写镜像名称;若使用“我的镜像”中的镜像,请在SWR中获取具体镜像地址。
        command:
          - /bin/bash
        args:
          - '-c'
          - 'sed -i "s/nginx/podname: $POD_NAME podIP: $POD_IP/g" /usr/share/nginx/html/index.html;nginx "-g" "daemon off;"'
        imagePullPolicy: IfNotPresent
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
          limits:
            cpu: 100m
            memory: 100Mi

创建hpa实例,示例如下yaml:

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: demo-hpa
  namespace: default       #命名空间,默认为default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: demo
  minReplicas: 2
  maxReplicas: 4
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 30
  behavior:
    scaleDown:
      policies:
      - type: Pods
        value: 2
        periodSeconds: 100
      - type: Percent
        value: 10
        periodSeconds: 100
      selectPolicy: Min
      stabilizationWindowSeconds: 300
    scaleUp:
      policies:
      - type: Pods
        value: 2
        periodSeconds: 15
      - type: Percent
        value: 20
        periodSeconds: 15
      selectPolicy: Max
      stabilizationWindowSeconds: 0

创建service实例,示例如下yaml:

apiVersion: v1
kind: Service
metadata:
  name: demo-svc
  namespace: default    #命名空间,默认为default
spec:
  type: ClusterIP
  selector:
    app: demo
  sessionAffinity: None
  ports:
    - name: http
      protocol: TCP
      port: 8080
      targetPort: 8080

创建mci实例,示例如下yaml:

apiVersion: networking.karmada.io/v1alpha1
kind: MultiClusterIngress
metadata:
  name: demo-mci # MCI的名字
  namespace: default       #命名空间,默认为default
  annotations:
    karmada.io/elb.id: xxx # TODO: ELB实例ID
    karmada.io/elb.projectid: xxx #TODO: ELB实例的项目ID
    karmada.io/elb.port: "8080" #TODO: ELB监听端口
    karmada.io/elb.health-check-flag: "on"
    karmada.io/elb.health-check-option.demo-svc: '{"protocol":"TCP"}'
spec:
  ingressClassName: public-elb  # ELB类型,固定值
  rules:
  - host: demo.localdev.me # 对外暴露的域名 TODO:修改实际地址
    http:
      paths:
      - backend:
          service:
            name: demo-svc # 暴露的service名字
            port:
              number: 8080  # 暴露service端口
        path: /
        pathType: Prefix # 前缀匹配

验证双集群高可用业务

用户在执行机上执行如下命令,验证双集群高可用业务:

  • 获取HOSTNAME与ELBIP
    kubectl get mci demo-mci -oyaml

  • 多次访问业务,回显不同PODNAME和PODID,表示实现双集群访问成功
    curl -H "host:demo.localdev.me" http://[ELBIP]:8080/

相关文档