Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFE: [multicluster] make service discovery equal for all clusters by eliminating the extendedServiceReferences attribute #3494

Open
alonsocamaro opened this issue Jul 18, 2024 · 12 comments

Comments

@alonsocamaro
Copy link

alonsocamaro commented Jul 18, 2024

Title

RFE: make service discovery equal for all clusters by eliminating the extendedServiceReferences attribute

Description

At present a service definition behaviour depends on the global configmap, for example using the next:

pools:
    path: /shop
    service: route-b-ocp1
    servicePort: 443
    weight: 50
    alternateBackends:
        service: route-b-ocp2
        weight: 50
    extendedServiceReferences:
        clusterName: ocp3
        namespace: openshift-ingress
        service: route-b-ocp3
        servicePort: 443
        weight: 50

the service discovery in the non-extendedServiceReferences section depends whether the customer is using a CIS HA or Standalone configuration. Furthermore, it is required for DevOps team to know in which clusters CIS is deployed. Also, It is mandatory to specify the cluster name in the extendedServiceReferences section.

This makes service discovery:

  • non-intuitive while the attribute extendedServiceReferences doesn´t add any benefit to the user
  • configuration behaviour depends on the cluster configuration

Instead, the cluster configuration should be kept to the global CM. The cluster configuration should be transparent to the application config by default. From a Service perspective it is irrelevant if its deployed in a cluster which has CIS or not.

Service discovery should be performed in all clusters configured in the global CM, treating all clusters equally. Hopefully this would simplify the code as well.

Actual Problem

extendedServiceReferences doesn´t add any benefit, just makes CIS less intuitive and less user friendly.

CIS placement and Services placement are independent concepts and using extendedServiceReferences we bind these without any advantage to CIS or to the customer.

Solution Proposed

The customer would just specify the following for an A/B use case amongst 3 clusters:

pools:
  - path: /shop
    service: route-b-ocp1
    servicePort: 443
    weight: 50
    alternateBackends:
    - service: route-b-ocp2
      weight: 50
    - service: route-b-ocp3 
       weight: 50

The above example shows that if customers want specific cluster placement this can be done even more easily not using extendedServiceReferences by using different service names. It is optional to know the clusters, depending if the customer wants to do specific placement.

Service discovery would search for these services in all clusters. It is up to the DevOps team where the services are actually deployed.

Additional context

Take into account existing #3493 when considering this

@alonsocamaro
Copy link
Author

Kostas is going to add a better solution

@trinaths
Copy link
Contributor

Created [CONTCNTR-4809] for internal tracking.

@trinaths trinaths added JIRA and removed untriaged no JIRA created labels Jul 27, 2024
@alonsocamaro
Copy link
Author

Kostas is on PTO, pasting his comments next:

He also thinks that the configuration should be more coherent

But he didn´t like my approach because when pushing configs with a CI/CD tools configurations are pushed equally to all clusters and with my approach the service names need to be different. I agree with this.

Instead he recommends an approach as follows

spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  - path: /shop
    multicluster:
    - clusterName: ocp1
      namespace: openshift-ingress
      service: route-b
      servicePort: 443
      weight: 50    
    - clusterName: ocp2
      namespace: openshift-ingress
      service: route-b
      servicePort: 443
      weight: 30
    - clusterName: ocp3
      namespace: openshift-ingress
      service: route-b
      servicePort: 443
      weight: 20

Where clusterName could be also "any":

 pools:
  - path: /shop
    multicluster:
    - clusterName: any
      namespace: openshift-ingress
      service: route-b
      servicePort: 443

But I think that "all" is a better keyword as "any" might suggest that only one cluster is chosen

@avinashchundu9
Copy link

I like the Kostas approach. It's simpler and straight forward for application owner to understand which cluster and service the load balancer is using. Please also implement this in transport server as well.

@alonsocamaro
Copy link
Author

alonsocamaro commented Sep 9, 2024

Another iteration:

  • supporting alternateBackends
  • not adding complexity
  • having a service oriented approach, that is: service is the top level attribute
  • keeps syntax compatibility, just adds a new clustersWeight attribute. See below how this allows transitioning from single cluster to multi-cluster without modifying the manifests.
  • For a given service, the same namespace is used for all clusters. If it is desired to use different namespaces then an additional service entry for that cluster can be created (the service name must be different).
spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  - path: /shop
    service: route-a
    servicePort: 443
    serviceNamespace: a
    clustersWeight: [ cluster1: 50, cluster2: 30, any: 20] 
    alternateBackends:
    - service: route-b1
      servicePort: 443
      serviceNamespace: b1
      clustersWeight: [ cluster1: 50 ]
    - service: route-b2
      servicePort: 443
      serviceNamespace: b2
      clustersWeight: [ any: 30 ]

The effective pool ratio for each service/cluster combination is calculated as:

clusterX weight / total weight

where the clusterX weight is:

  • the weight of the cluster indicated explicitly (ie: cluster2: 30), or
  • the weight indicated by the "any" keyword if present, or
  • 0 if "any" keyword is not present

And total weight is the sum of all cluster weights, following the example above, assuming 4 clusters:

(50+30+20+20)+ (50+0+0+0) + (30+30+30+30) = 290

One more comment about the weight calculation:

Even if no service exists and the cluster matches a service/clusterX entry or "any" the weight is added to it. Just as if a service existed but didn´t have any backend or the backends were marked down by the BIG-IP monitor. In which case a re-balancing should be done.

Details/handling exceptions:

  • When using CIS in multi-cluster mode and the "clusterWeights" attribute is not specified but "weight: X" is specified, the behaviour should be just like "clusterWeights: [ any: X]". No WARNING should be thrown, this allows a clean transition from single cluster to multi-cluster.
  • When using CIS in single cluster mode the "weight" attribute is used. If by mistake, "weight" is missing it should throw a WARNING message and as a fallback evenly split the load evenly across the alternate services of the given route.

Syntax for TransportServer would be the same but not adding alternateBackends for the time being, which could be added later on when there is a customer requesting it.

@alonsocamaro
Copy link
Author

alonsocamaro commented Sep 9, 2024

SUMMARY

  • After review with Kostas, the weight calculation has been redone to make it more explicit. The existing "weight" attribute is used as first tier to distribute the different A/B alternatives. Then, the optional "clusterWeights" can be used to tune each cluster explicitly.
  • keeps syntax compatibility, just adds a new clustersWeight attribute. See below how this allows transitioning from single cluster to multi-cluster without modifying the manifests.
  • supporting alternateBackends
  • not adding complexity
  • having a service oriented approach, that is: service is the top level attribute
  • For a given service, the same namespace is used for all clusters. If it is desired to use different namespaces then an additional service entry for that cluster can be created (the service name must be different).

EXAMPLE: a manifest is worth thousands words

An example meant to show all the syntax of the proposal, rather than a real world scenario.

Note that "weights" have a total of 100 each, just to make calculations easy. Likewise the clusterWeights for each backend also have a total of 100.

spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  - path: /shop
    service: backend-a
    servicePort: 443
    serviceNamespace: a
    weight: 30
    clustersWeight: [ cluster1: 50, cluster2: 30, any: 10] 
    alternateBackends:
    - service: backend-b1
      servicePort: 443
      serviceNamespace: b1
      weight: 60
      clustersWeight: [ cluster1: 100 ]
    - service: backend-b2
      servicePort: 443
      serviceNamespace: b2
      weight: 10

Using this example and assuming 4 clusters defined in CIS:

  • route-a gets 30% of the traffic. This percentage is further split across clusters using the indicated values in clusterWeights.
  • route-b1 gets 60% of the traffic. This percentage is only sent to cluster1 because the other clusters are not specified and the "any" keyword is used either.
  • route-b2 gets 10% of the traffic. This percentage is sent to all clusters (when clusterWeights is not used) assuming an even distribution across all 4 clusters.

Overall the traffic sent to each service backend cluster would be:

- backend-a:
  - cluster1:  50% of 30% =  15% of total
  - cluster2:  30% of 30% =   9% of total
  - cluster3:  10% of 30% =   3% of total
  - cluster4:  10% of 30% =   3% of total
- backend-b1:
  - cluster1: 100% of 60% =  60% of total
  - cluster2:   0% of 60% =   0% of total
  - cluster3:   0% of 60% =   0% of total
  - cluster4:   0% of 60% =   0% of total
- backend-b2:
  - cluster1:  25% of 10% = 2.5% of total
  - cluster2:  25% of 10% = 2.5% of total
  - cluster3:  25% of 10% = 2.5% of total
  - cluster4:  25% of 10% = 2.5% of total

where the clusterX weight is:

  • the weight of the cluster indicated explicitly of the weight for the backend , or
  • the weight indicated by the "any" keyword (if present) of the weight for the backend or
  • 0 if "any" keyword is not present

if the "clusterWeight" attribute is not used it means the percentage for the backend is split across all the clusters defined in CIS.

TransportServer

Syntax for TransportServer would be the same but not adding alternateBackends for the time being, which could be added later on when there is a customer requesting it.

An example for TransportServer without the A/B deployment strategy would be as follows:

apiVersion: "cis.f5.com/v1"
kind: TransportServer
metadata:
   name: transport-server
   labels:
     f5cr: "true"
spec:
  virtualServerAddress: "172.16.3.9"
  virtualServerPort: 8585
  snat: auto
  pools:
    service: backend
    servicePort: 443
    serviceNamespace: myapp
    clustersWeight: [ cluster1: 50, cluster2: 30, any: 10]

Facilitating transition from single cluster to multi-cluster

Note wrt section for backend-b2 above:

    - service: backend-b2
      servicePort: 443
      serviceNamespace: b2
      weight: 10

could be also be written as:

    - service: backend-b2
      servicePort: 443
      serviceNamespace: b2
      weight: 10
      clusterWeights: [ any: 100 ]

Where all clusters of backend-b2 would receive the same weight, regardless of the value indicated

That is: a customer with many manifests defined only using the weight attribute would seamlessly start using multi-cluster without modification.

CIS configuration & Service discovery

It is important that unlike the current behaviour, the proposal above should behave no matter if CIS is configured standalone or in HA mode or in which cluster is deployed.

It is also important to remark that the proposal above, for a given backend, CIS does service discovery in all clusters unless clusterWeight is set to 0 for a cluster.

@vklohiya
Copy link
Contributor

@alonsocamaro , This is how we will be supporting this feature:

Controlling the service discovery in MultiCluster

Case-1: Let's customer want to enable the discovery in specified clusters only without weights.

Virtual Server:


spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  pools:
    - path: /neam
      service: svc-1
      servicePort: 80
      extendedServiceReferences:
        - clusterName: cluster1
        - clusterName: cluster2
        - clusterName: cluster3


Transport server:


apiVersion: "cis.f5.com/v1"
kind: TransportServer
metadata:
   name: transport-server
   labels:
     f5cr: "true"
spec:
  virtualServerAddress: "172.16.3.9"
  virtualServerPort: 8585
  snat: auto
  pools:
    service: backend
    servicePort: 443
    serviceNamespace: myapp
    extendedServiceReferences:
    - clusterName: cluster1
    - clusterName: cluster2

Case-2: Let's say you want to discover service in all the clusters, you define the resources as follows.

Virtual Server:


spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  pools:
    - path: /neam
      service: svc-1
      servicePort: 80
      extendedServiceReferences:
      - clusterName:all


Transport server:


apiVersion: "cis.f5.com/v1"
kind: TransportServer
metadata:
   name: transport-server
   labels:
     f5cr: "true"
spec:
  virtualServerAddress: "172.16.3.9"
  virtualServerPort: 8585
  snat: auto
  pools:
    service: backend
    servicePort: 443
    serviceNamespace: myapp
    extendedServiceReferences:
    - clusterName:all

Case-3: Without extendedServiceReference, In this case we will follow the existing behaviour and do the service discovery in primary and secondary cluster only.

Cluster Traffic Distribution UseCases

Case-4: Let's say you want to distribute the traffic to cluster1 and cluster2 at service level, this is how you define the resources.

Virtual Server:


spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  pools:
    - path: /neam
      service: svc-1
      servicePort: 80
      extendedServiceReferences:
        - clusterName: cluster1
          weight: 20
        - clusterName: cluster2
          weight: 30


Transport server:

apiVersion: "cis.f5.com/v1"
kind: TransportServer
metadata:
   name: transport-server
   labels:
     f5cr: "true"
spec:
  virtualServerAddress: "172.16.3.9"
  virtualServerPort: 8585
  snat: auto
  pools:
    service: backend
    servicePort: 443
    serviceNamespace: myapp
    extendedServiceReferences:
    - clusterName: cluster1
       weight: 20
    - clusterName: cluster2
       weight: 30

Case-5: Let's say customer wants to use alternate backend in multi-cluster scenario, you define the resources as follows.

Virtual Server:


spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  pools:
    - path: /neam
      service: svc-v1
      servicePort: 80
      extendedServiceReferences:
        - clusterName: cluster1
           weight: 20
        - clusterName: cluster2
           weight: 30
        - clusterName: cluster1
           service: svc-v2
           weight: 10
        - clusterName: cluster2
           service: svc-v2
           weight: 10

Transport server:


apiVersion: "cis.f5.com/v1"
kind: TransportServer
metadata:
   name: transport-server
   labels:
     f5cr: "true"
spec:
  virtualServerAddress: "172.16.3.9"
  virtualServerPort: 8585
  snat: auto
  pools:
    service: svc-v1
    servicePort: 443
    serviceNamespace: myapp
    extendedServiceReferences:
    - clusterName: cluster1
       weight: 20
    - clusterName: cluster2
       weight: 30
    - clusterName: cluster1
       service: svc-v2
       weight: 10
    - clusterName: cluster2
       weight: 10
       service: svc-v2

Case-6: Let's say customer have forgot to put weights for some service.

Virtual Server:

spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  pools:
    - path: /neam
      service: svc-1
      servicePort: 80
      extendedServiceReferences:
        - clusterName: cluster1
        - clusterName: cluster2
          weight: 30


Transport server:

apiVersion: "cis.f5.com/v1"
kind: TransportServer
metadata:
   name: transport-server
   labels:
     f5cr: "true"
spec:
  virtualServerAddress: "172.16.3.9"
  virtualServerPort: 8585
  snat: auto
  pools:
    service: backend
    servicePort: 443
    serviceNamespace: myapp
    extendedServiceReferences:
    - clusterName: cluster1
    - clusterName: cluster2
       weight: 20

In this case, we will consider default weight as 100 and traffic will be calculated accordingly. so that cluster-1 will receive 100/120 % traffic and cluster-2 will receive 20/120% traffic.

Note:-

  1. Here extendedServiceReferences will be given priority over the ratio defined at cluster-level in extendedConfigMap.
  2. If no weights are defined for any service, we will consider the ratio defined at cluster-level in extendedConfigMap.
  3. If no cluster ratio is defined in the extendedConfigMap and no weights are defined in any service then traffic will be distributed equally to all the clusters defined by extendedServiceReferences or if extendedServiceReferences is also not defined then to primary and secondary cluster.
  4. AlternateBackend will not be supported in MultiCluster scenario and weight property under the pool will be ignored and will only be applicable in single cluster scenarios.
  5. For transitions from single cluster to multi-cluster, customer needs to adapt to the multi-cluster way for controlling the traffic for alternateBackend use case.
  6. If any weight is updated to zero, that pool-members will be put in disable state, so that existing traffic does not get impacted.
  7. If service/cluster is not found, it will impact overall calculation of traffic distribution and the ratio for this service/cluster will be ignored and remaining services/cluster will distribute traffic as per their ratio defined. for example in below service backend is not found in cluster1 then traffic will be distributed 50-50% to cluster2 and cluster3 backend service.
    service: backend
    servicePort: 443
    serviceNamespace: myapp
    extendedServiceReferences:
    - clusterName: cluster1
       weight: 20
    - clusterName: cluster2
       weight: 20
    - clusterName: cluster3
       weight: 20

@alonsocamaro
Copy link
Author

alonsocamaro commented Sep 11, 2024

If I compare the current two proposals:

The proposal by me and @skenderidis

  • Has simpler syntax, it only requires a single optional attribute: "clusterWeights", for the common case of running multi-cluster in active-active mode.
  • Because of the above, it allows seamless transition from single cluster to multi-cluster. Even customers doing A/B doesn´t require any change.
  • With respect of Case-3, wrt handling of the new behaviour while keeping current behaviour could be done by just creating "cis.f5.com/v2" apiVersion.

On the other hand, the alternate proposal by @vklohiya :

  • Doesn´t support alternate backends in different namespaces. This is a severe limitation to avoid.

@vklohiya
Copy link
Contributor

vklohiya commented Sep 11, 2024

@alonsocamaro , It will support the different namespaces with alternate backend as well. Here is the example for same:

Virtual Server:


spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  pools:
    - path: /neam
      service: svc-v1
      servicePort: 80
      extendedServiceReferences:
        - clusterName: cluster1
           weight: 20
        - clusterName: cluster2
           weight: 30
        - clusterName: cluster1
           service: svc-v2
           namespace: ns2
           weight: 10
        - clusterName: cluster2
           service: svc-v2
           namespace: ns2
           weight: 10

Transport server:


apiVersion: "cis.f5.com/v1"
kind: TransportServer
metadata:
   name: transport-server
   labels:
     f5cr: "true"
spec:
  virtualServerAddress: "172.16.3.9"
  virtualServerPort: 8585
  snat: auto
  pools:
    service: svc-v1
    servicePort: 443
    serviceNamespace: myapp
    extendedServiceReferences:
    - clusterName: cluster1
       weight: 20
    - clusterName: cluster2
       weight: 30
    - clusterName: cluster1
       service: svc-v2
       namespace: ns2
       weight: 10
    - clusterName: cluster2
       weight: 10
       namespace: ns2
       service: svc-v2

Note:- We will still be supporting the properties like servicePort, service, namespace under the extendedServiceReferences. But it will not be the mandatory parameter any more. we are converting them to optional parameters, which will give much flexibility. If these are not defined we will inherit it from the pool level.

It will also avoid the confusion regarding alternateBackend weight distribution and will simplify overall weight distribution under extendedServiceReferences for multi-cluster CIS. And with this approach no new api version is needed for CRDs, which overall reduces the burden of maintaining two api versions for CRDs.

@avinashchundu9
Copy link

avinashchundu9 commented Sep 11, 2024

@vklohiya and @alonsocamaro

Here is my take Starting with ConfigMap:

Here is current configMap configuration

mode: active-active # Mode in a cluster level is least used when weight on each load balancer is available. All 3 (Active-Active, Active-Passive and Ratio can be accomplished with weight.
highAvailabilityCIS:
  primaryEndPoint: http://10.15.247.20:8080/ready # Users are providing kubeconfig. So there is no need for for a separate load balancer to be maintained manually. Use Kubernetes API to check the status.
  probeInterval: 30 # Add this to CIS deployments YAML
  retryInterval: 3 # Add this to CIS deployments YAML
  primaryCluster: # We can treat all clusters equally. Primary and secondary are for CIS not for clusters.
    clusterName: cluster1
    secret: f5-cis/cluster1
  secondaryCluster:
    clusterName: cluster2
    secret: f5-cis/cluster2

Here is new proposal:

Option 1:

highAvailabilityCIS:
  PrimaryCIScluster: cluster1
  PrimaryCISdeployment: f5-cis-deployment
  PrimaryCISNamespace: f5-cis-namespace
  Clusters:
    clusterName: cluster1
    secret: f5-cis/cluster1
    clusterName: cluster2
    secret: f5-cis/cluster2
    clusterName: cluster3
    secret: f5-cis/cluster3

I noticed split brain behavior when CIS having issues communicating with each other which needs to be addressed. This will be a separate discussion.
Primary cluster will be cluster1. There is not need to define secondary cluster as all other CIS pointed to primary will become secondary by default.
CIS from other cluster will use the PrimaryCIScluster, PrimaryCISdeployment and PrimaryCISNamespace details to identify the status.

Option 2:

highAvailabilityCIS:
  primaryEndPointIP: 10.15.247.20
  primaryEndPointPort: 8080
  PrimaryCISdeployment: f5-cis-deployment
  PrimaryCISNamespace: f5-cis-namespace
  PrimaryCISservicetype: NodePort
  PrimaryCIScluster: cluster1
  Clusters:
    clusterName: cluster1
    secret: f5-cis/cluster1
    clusterName: cluster2
    secret: f5-cis/cluster2
    clusterName: cluster3
    secret: f5-cis/cluster3

In this options instead of using the Kubernetes API to check the status. When deploying Primary CIS deploy a service and load balancer that points to Primary CIS and use it for other CIS to connect to Primary.

Now moving on with CRD's:

In below example:

spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
    - path: /neam   
      service: svc-v1 # Defining service at pool level assumes that service name in all cluster has to same name. This case will not work if user wants to use different service names.
      servicePort: 80 # Same applies to port number as.
      extendedServiceReferences: # ExtendedServiceReferences and alternate backends are very confusing with their functionality and they are overlapping. 
        - clusterName: cluster1
           weight: 20
        - clusterName: cluster2
           weight: 30
        - clusterName: cluster1
           service: svc-v2
           namespace: ns2
           weight: 10
        - clusterName: cluster2
           service: svc-v2
           namespace: ns2
           weight: 10

Here is the new proposal:

spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  - path: /neam 
    multicluster:
    - clusterName: cluster1
      namespace: cluster1-ns
      service: route-b
      servicePort: 443
      weight: 50    
    - clusterName: cluster2
      namespace: cluster2-ns
      service: route-b
      servicePort: 443
      weight: 30
    - clusterName: cluster3
      namespace: cluster3-ns
      service: route-b
      servicePort: 443
      weight: 20

If it is same cluster multiple services:

spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  - path: /neam 
    multicluster:
    - clusterName: cluster1
      namespace: cluster1-ns1
      service: route-b
      servicePort: 443
      weight: 50    
    - clusterName: cluster1
      namespace: cluster1-ns2
      service: route-b
      servicePort: 443
      weight: 20

Active-Active:

spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  - path: /neam 
    multicluster:
    - clusterName: cluster1
      namespace: cluster1-ns
      service: route-b
      servicePort: 443
      weight: 50    
    - clusterName: cluster2
      namespace: cluster1-ns
      service: route-b
      servicePort: 443
      weight: 50

Active-Passive:

spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  - path: /neam 
    multicluster:
    - clusterName: cluster1
      namespace: cluster1-ns
      service: route-b
      servicePort: 443
      weight: 100  
    - clusterName: cluster2
      namespace: cluster1-ns
      service: route-b
      servicePort: 443
      weight: 0

Ratio:

spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  - path: /neam 
    multicluster:
    - clusterName: cluster1
      namespace: cluster1-ns
      service: route-b
      servicePort: 443
      weight: 20 
    - clusterName: cluster2
      namespace: cluster1-ns
      service: route-b
      servicePort: 443
      weight: 80

Standalone:

spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  - path: /neam 
    multicluster:
    - clusterName: cluster1
      namespace: cluster1-ns
      service: route-b
      servicePort: 443
      weight: 100

Similar for transport server as well.

@vklohiya
Copy link
Contributor

@avinashchundu9 , Thanks for providing the detailed input. Here are my take on it:

  • I agrees with you that we should tie the cluster weight distribution under single object. Hence we are trying to put this distribution under extendedServiceReference.
  • Looking at requests coming from other customers as well. It looks like that customers wants to use the same service, servicePort and namespace in multiple clusters, Hence I believe it's good to make it implicit and use in all the clusters, unless customer configures the specifics for service in other clusters, using extendedServiceReference.
  • It looks like extendedServiceReference term is not right and creating some confusion. May be should consider renaming it to multiClusterReference, So it makes more sense.
  • ConfigMap related changes are not part of this discussion, Please raise a separate RFE for it.

@vklohiya
Copy link
Contributor

@alonsocamaro , Here is the final proposal from engineering team after the internal discussions.

Controlling the service discovery in MultiCluster

Case-1: Let's say you want to discover service in all the clusters, you define the resources as follows. Here service discovery is implicit for all cluster.

Virtual Server:


spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  pools:
    - path: /neam
      service: svc-1
      servicePort: 80

Transport server:


apiVersion: "cis.f5.com/v1"
kind: TransportServer
metadata:
   name: transport-server
   labels:
     f5cr: "true"
spec:
  virtualServerAddress: "172.16.3.9"
  virtualServerPort: 8585
  snat: auto
  pools:
    service: backend
    servicePort: 443
    serviceNamespace: myapp

Case-2: Let's customer want to enable the discovery in specified clusters only without weights.

Virtual Server:


spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  pools:
    - path: /neam
      service: svc-1
      servicePort: 80
      multiClusterReferences:
        - clusterName: cluster1
        - clusterName: cluster2
        - clusterName: cluster3


Transport server:


apiVersion: "cis.f5.com/v1"
kind: TransportServer
metadata:
   name: transport-server
   labels:
     f5cr: "true"
spec:
  virtualServerAddress: "172.16.3.9"
  virtualServerPort: 8585
  snat: auto
  pools:
    service: backend
    servicePort: 443
    serviceNamespace: myapp
    multiClusterReferences:
    - clusterName: cluster1
    - clusterName: cluster2

Cluster Traffic Distribution UseCases

Case-3: Let's say you want to distribute the traffic to cluster1 and cluster2 at service level, this is how you define the resources.

Virtual Server:


spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  pools:
    - path: /neam
      service: svc-1
      servicePort: 80
      multiClusterReferences:
        - clusterName: cluster1
          weight: 20
        - clusterName: cluster2
          weight: 30


Transport server:

apiVersion: "cis.f5.com/v1"
kind: TransportServer
metadata:
   name: transport-server
   labels:
     f5cr: "true"
spec:
  virtualServerAddress: "172.16.3.9"
  virtualServerPort: 8585
  snat: auto
  pools:
    service: backend
    servicePort: 443
    serviceNamespace: myapp
    multiClusterReferences:
    - clusterName: cluster1
       weight: 20
    - clusterName: cluster2
       weight: 30

Case-4: Let's say customer wants to use alternate backend in multi-cluster scenario, you define the resources as follows.

Virtual Server:


spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  pools:
    - path: /neam
      service: svc-v1
      servicePort: 80
      multiClusterReferences:
        - clusterName: cluster1
           weight: 20
        - clusterName: cluster2
           weight: 30
        - clusterName: cluster1
           service: svc-v2
           weight: 10
        - clusterName: cluster2
           service: svc-v2
           weight: 10

Transport server:


apiVersion: "cis.f5.com/v1"
kind: TransportServer
metadata:
   name: transport-server
   labels:
     f5cr: "true"
spec:
  virtualServerAddress: "172.16.3.9"
  virtualServerPort: 8585
  snat: auto
  pools:
    service: svc-v1
    servicePort: 443
    serviceNamespace: myapp
    multiClusterReferences:
    - clusterName: cluster1
       weight: 20
    - clusterName: cluster2
       weight: 30
    - clusterName: cluster1
       service: svc-v2
       weight: 10
    - clusterName: cluster2
       weight: 10
       service: svc-v2

Case-5: Let's say customer have forgot to put weights for some service.

Virtual Server:

spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  pools:
    - path: /neam
      service: svc-1
      servicePort: 80
      multiClusterReferences:
        - clusterName: cluster1
        - clusterName: cluster2
          weight: 30


Transport server:

apiVersion: "cis.f5.com/v1"
kind: TransportServer
metadata:
   name: transport-server
   labels:
     f5cr: "true"
spec:
  virtualServerAddress: "172.16.3.9"
  virtualServerPort: 8585
  snat: auto
  pools:
    service: backend
    servicePort: 443
    serviceNamespace: myapp
    multiClusterReferences:
    - clusterName: cluster1
    - clusterName: cluster2
       weight: 20

In this case, we will consider default weight as 100 and traffic will be calculated accordingly. so that cluster-1 will receive 100/120 % traffic and cluster-2 will receive 20/120% traffic.

Case-6 Traffic distribution with different namespaces with alternate backend as well. Here is the example for same:

Virtual Server:


spec:
  host: www.demo-mc.com
  virtualServerAddress: "10.1.10.106"
  hostGroup: demo-mc.com
  tlsProfileName: reencrypt-tls
  profileMultiplex: "/Common/oneconnect-32"
  pools:
  pools:
    - path: /neam
      service: svc-v1
      servicePort: 80
      multiClusterReferences:
        - clusterName: cluster1
           weight: 20
        - clusterName: cluster2
           weight: 30
        - clusterName: cluster1
           service: svc-v2
           namespace: ns2
           weight: 10
        - clusterName: cluster2
           service: svc-v2
           namespace: ns2
           weight: 10

Transport server:


apiVersion: "cis.f5.com/v1"
kind: TransportServer
metadata:
   name: transport-server
   labels:
     f5cr: "true"
spec:
  virtualServerAddress: "172.16.3.9"
  virtualServerPort: 8585
  snat: auto
  pools:
    service: svc-v1
    servicePort: 443
    serviceNamespace: myapp
    multiClusterReferences:
    - clusterName: cluster1
       weight: 20
    - clusterName: cluster2
       weight: 30
    - clusterName: cluster1
       service: svc-v2
       namespace: ns2
       weight: 10
    - clusterName: cluster2
       weight: 10
       namespace: ns2
       service: svc-v2

Note:- We will still be supporting the properties like servicePort, service, namespace under the multiClusterReferences. But it will not be the mandatory parameter any more. we are converting them to optional parameters, which will give much flexibility. If these are not defined we will inherit it from the pool level.

  1. Here multiClusterReferences will be given priority over the ratio defined at cluster-level in extendedConfigMap.
  2. If no weights are defined for any service, we will consider the ratio defined at cluster-level in extendedConfigMap.
  3. If no cluster ratio is defined in the extendedConfigMap and no weights are defined in any service then traffic will be distributed equally to all the clusters defined by multiClusterReferences or if multiClusterReferences is also not defined then equally in all the clusters.
  4. AlternateBackend will not be supported in MultiCluster scenario and weight property under the pool will be ignored and will only be applicable in single cluster scenarios.
  5. Alternate backends, will only be supported in single cluster scenario.
  6. For transitions from single cluster to multi-cluster, customer needs to adapt to the multi-cluster way for controlling the traffic for alternateBackend use case.
  7. If any weight is updated to zero, that pool-members will be put in disable state, so that existing traffic does not get impacted.
  8. We won't be supporting traffic distribution with "any" keyword as it creates ambiguity.
  9. If service/cluster is not found, it will impact overall calculation of traffic distribution and the ratio for this service/cluster will be ignored and remaining services/cluster will distribute traffic as per their ratio defined. for example in below service backend is not found in cluster1 then traffic will be distributed 50-50% to cluster2 and cluster3 backend service.
    service: backend
    servicePort: 443
    serviceNamespace: myapp
    multiClusterReferences:
    - clusterName: cluster1
       weight: 20
    - clusterName: cluster2
       weight: 20
    - clusterName: cluster3
       weight: 20

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants