From 5761fbda55a5ae1c64c63cddbda74c8db1022837 Mon Sep 17 00:00:00 2001 From: Varga Zsolt Date: Thu, 11 Jul 2019 15:46:39 +0200 Subject: [PATCH] Add Locality Load Balancing support (#258) * Add Locality Load Balancing support --- config/base/crds/istio_v1beta1_istio.yaml | 45 ++++++++++ config/samples/istio_v1beta1_istio.yaml | 12 +++ pkg/apis/istio/v1beta1/istio_types.go | 51 +++++++++++ .../istio/v1beta1/zz_generated.deepcopy.go | 87 +++++++++++++++++++ pkg/resources/common/configmap.go | 20 ++++- pkg/resources/pilot/deployment.go | 7 ++ 6 files changed, 221 insertions(+), 1 deletion(-) diff --git a/config/base/crds/istio_v1beta1_istio.yaml b/config/base/crds/istio_v1beta1_istio.yaml index 0ff118e3e..c884214df 100644 --- a/config/base/crds/istio_v1beta1_istio.yaml +++ b/config/base/crds/istio_v1beta1_istio.yaml @@ -280,6 +280,51 @@ spec: type: object type: array type: object + localityLB: + description: Locality based load balancing distribution or failover + settings. + properties: + distribute: + description: 'Optional: only one of distribute or failover can be + set. Explicitly specify loadbalancing weight across different + zones and geographical locations. Refer to [Locality weighted + load balancing](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/load_balancing/locality_weight) + If empty, the locality weight is set according to the endpoints + number within it.' + items: + properties: + from: + description: Originating locality, '/' separated, e.g. 'region/zone'. + type: string + to: + description: Map of upstream localities to traffic distribution + weights. The sum of all weights should be == 100. Any locality + not assigned a weight will receive no traffic. + type: object + type: object + type: array + enabled: + description: If set to true, locality based load balancing will + be enabled + type: boolean + failover: + description: 'Optional: only failover or distribute can be set. + Explicitly specify the region traffic will land on when endpoints + in local region becomes unhealthy. Should be used together with + OutlierDetection to detect unhealthy endpoints. Note: if no OutlierDetection + specified, this will not take effect.' + items: + properties: + from: + description: Originating region. + type: string + to: + description: Destination region the traffic will fail over + to when endpoints in the 'from' region becomes unhealthy. + type: string + type: object + type: array + type: object meshExpansion: description: If set to true, the pilot and citadel mtls will be exposed on the ingress gateway also the remote istios will be connected through diff --git a/config/samples/istio_v1beta1_istio.yaml b/config/samples/istio_v1beta1_istio.yaml index e6afe8fe7..b0b384654 100644 --- a/config/samples/istio_v1beta1_istio.yaml +++ b/config/samples/istio_v1beta1_istio.yaml @@ -145,3 +145,15 @@ spec: accessToken: secure: true cacertPath: /etc/lightstep/cacert.pem + localityLB: + enabled: false + # distribute: + # - from: "us-central1/*" + # to: + # "us-central1/*": 80 + # "us-central2/*": 20 + # failover: + # - from: us-east + # to: eu-west + # - from: us-west + # to: us-east diff --git a/pkg/apis/istio/v1beta1/istio_types.go b/pkg/apis/istio/v1beta1/istio_types.go index 166547d03..3622c7e2d 100644 --- a/pkg/apis/istio/v1beta1/istio_types.go +++ b/pkg/apis/istio/v1beta1/istio_types.go @@ -305,6 +305,54 @@ type IstioCoreDNS struct { Tolerations []corev1.Toleration `json:"tolerations,omitempty"` } +// Describes how traffic originating in the 'from' zone is +// distributed over a set of 'to' zones. Syntax for specifying a zone is +// {region}/{zone} and terminal wildcards are allowed on any +// segment of the specification. Examples: +// * - matches all localities +// us-west/* - all zones and sub-zones within the us-west region +type LocalityLBDistributeConfiguration struct { + // Originating locality, '/' separated, e.g. 'region/zone'. + From string `json:"from,omitempty"` + // Map of upstream localities to traffic distribution weights. The sum of + // all weights should be == 100. Any locality not assigned a weight will + // receive no traffic. + To map[string]uint32 `json:"to,omitempty"` +} + +// Specify the traffic failover policy across regions. Since zone +// failover is supported by default this only needs to be specified for +// regions when the operator needs to constrain traffic failover so that +// the default behavior of failing over to any endpoint globally does not +// apply. This is useful when failing over traffic across regions would not +// improve service health or may need to be restricted for other reasons +// like regulatory controls. +type LocalityLBFailoverConfiguration struct { + // Originating region. + From string `json:"from,omitempty"` + // Destination region the traffic will fail over to when endpoints in + // the 'from' region becomes unhealthy. + To string `json:"to,omitempty"` +} + +// Locality-weighted load balancing allows administrators to control the +// distribution of traffic to endpoints based on the localities of where the +// traffic originates and where it will terminate. +type LocalityLBConfiguration struct { + // If set to true, locality based load balancing will be enabled + Enabled *bool `json:"enabled,omitempty"` + // Optional: only one of distribute or failover can be set. + // Explicitly specify loadbalancing weight across different zones and geographical locations. + // Refer to [Locality weighted load balancing](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/load_balancing/locality_weight) + // If empty, the locality weight is set according to the endpoints number within it. + Distribute []*LocalityLBDistributeConfiguration `json:"distribute,omitempty"` + // Optional: only failover or distribute can be set. + // Explicitly specify the region traffic will land on when endpoints in local region becomes unhealthy. + // Should be used together with OutlierDetection to detect unhealthy endpoints. + // Note: if no OutlierDetection specified, this will not take effect. + Failover []*LocalityLBFailoverConfiguration `json:"failover,omitempty"` +} + // IstioSpec defines the desired state of Istio type IstioSpec struct { // Contains the intended Istio version @@ -397,6 +445,9 @@ type IstioSpec struct { // Istio CoreDNS provides DNS resolution for services in multi mesh setups IstioCoreDNS IstioCoreDNS `json:"istioCoreDNS,omitempty"` + // Locality based load balancing distribution or failover settings. + LocalityLB *LocalityLBConfiguration `json:"localityLB,omitempty"` + networkName string meshNetworks *MeshNetworks } diff --git a/pkg/apis/istio/v1beta1/zz_generated.deepcopy.go b/pkg/apis/istio/v1beta1/zz_generated.deepcopy.go index ac6ac7081..ab8736bd0 100644 --- a/pkg/apis/istio/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/istio/v1beta1/zz_generated.deepcopy.go @@ -456,6 +456,11 @@ func (in *IstioSpec) DeepCopyInto(out *IstioSpec) { **out = **in } in.IstioCoreDNS.DeepCopyInto(&out.IstioCoreDNS) + if in.LocalityLB != nil { + in, out := &in.LocalityLB, &out.LocalityLB + *out = new(LocalityLBConfiguration) + (*in).DeepCopyInto(*out) + } if in.meshNetworks != nil { in, out := &in.meshNetworks, &out.meshNetworks *out = new(MeshNetworks) @@ -532,6 +537,88 @@ func (in *LightstepConfiguration) DeepCopy() *LightstepConfiguration { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LocalityLBConfiguration) DeepCopyInto(out *LocalityLBConfiguration) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.Distribute != nil { + in, out := &in.Distribute, &out.Distribute + *out = make([]*LocalityLBDistributeConfiguration, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(LocalityLBDistributeConfiguration) + (*in).DeepCopyInto(*out) + } + } + } + if in.Failover != nil { + in, out := &in.Failover, &out.Failover + *out = make([]*LocalityLBFailoverConfiguration, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(LocalityLBFailoverConfiguration) + **out = **in + } + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalityLBConfiguration. +func (in *LocalityLBConfiguration) DeepCopy() *LocalityLBConfiguration { + if in == nil { + return nil + } + out := new(LocalityLBConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LocalityLBDistributeConfiguration) DeepCopyInto(out *LocalityLBDistributeConfiguration) { + *out = *in + if in.To != nil { + in, out := &in.To, &out.To + *out = make(map[string]uint32, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalityLBDistributeConfiguration. +func (in *LocalityLBDistributeConfiguration) DeepCopy() *LocalityLBDistributeConfiguration { + if in == nil { + return nil + } + out := new(LocalityLBDistributeConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LocalityLBFailoverConfiguration) DeepCopyInto(out *LocalityLBFailoverConfiguration) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalityLBFailoverConfiguration. +func (in *LocalityLBFailoverConfiguration) DeepCopy() *LocalityLBFailoverConfiguration { + if in == nil { + return nil + } + out := new(LocalityLBFailoverConfiguration) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MeshNetwork) DeepCopyInto(out *MeshNetwork) { *out = *in diff --git a/pkg/resources/common/configmap.go b/pkg/resources/common/configmap.go index 9a0a3c105..4f438a43d 100644 --- a/pkg/resources/common/configmap.go +++ b/pkg/resources/common/configmap.go @@ -104,7 +104,7 @@ func (r *Reconciler) meshConfig() string { "defaultConfig": defaultConfig, "rootNamespace": "istio-system", "connectTimeout": "10s", - "localityLbSetting": nil, + "localityLbSetting": r.getLocalityLBConfiguration(), } if util.PointerToBool(r.Config.Spec.UseMCP) { @@ -117,6 +117,24 @@ func (r *Reconciler) meshConfig() string { return string(marshaledConfig) } +func (r *Reconciler) getLocalityLBConfiguration() *istiov1beta1.LocalityLBConfiguration { + var localityLbConfiguration *istiov1beta1.LocalityLBConfiguration + + if r.Config.Spec.LocalityLB == nil || !util.PointerToBool(r.Config.Spec.LocalityLB.Enabled) { + return localityLbConfiguration + } + + if r.Config.Spec.LocalityLB != nil { + localityLbConfiguration = r.Config.Spec.LocalityLB.DeepCopy() + localityLbConfiguration.Enabled = nil + if localityLbConfiguration.Distribute != nil && localityLbConfiguration.Failover != nil { + localityLbConfiguration.Failover = nil + } + } + + return localityLbConfiguration +} + func (r *Reconciler) meshNetworks() string { marshaledConfig, _ := yaml.Marshal(r.Config.Spec.GetMeshNetworks()) return string(marshaledConfig) diff --git a/pkg/resources/pilot/deployment.go b/pkg/resources/pilot/deployment.go index 91defc554..60de39bc8 100644 --- a/pkg/resources/pilot/deployment.go +++ b/pkg/resources/pilot/deployment.go @@ -143,6 +143,13 @@ func (r *Reconciler) containers() []apiv1.Container { TerminationMessagePolicy: apiv1.TerminationMessageReadFile, } + if r.Config.Spec.LocalityLB != nil && util.PointerToBool(r.Config.Spec.LocalityLB.Enabled) { + discoveryContainer.Env = append(discoveryContainer.Env, apiv1.EnvVar{ + Name: "PILOT_ENABLE_LOCALITY_LOAD_BALANCING", + Value: "1", + }) + } + containers := []apiv1.Container{ discoveryContainer, }