This is an automated email from the ASF dual-hosted git repository.
houston pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr-operator.git
The following commit(s) were added to refs/heads/main by this push:
new 7932abb Add ability to set pod topologySpreadConstraints (#350)
7932abb is described below
commit 7932abb219fc36bd5fb854f8976b436b6960df74
Author: Houston Putman <[email protected]>
AuthorDate: Wed Oct 27 11:45:53 2021 -0400
Add ability to set pod topologySpreadConstraints (#350)
---
api/v1beta1/common_types.go | 13 +++
api/v1beta1/zz_generated.deepcopy.go | 7 ++
config/crd/bases/solr.apache.org_solrclouds.yaml | 55 +++++++++++
.../solr.apache.org_solrprometheusexporters.yaml | 55 +++++++++++
controllers/controller_utils_test.go | 15 +++
controllers/solrcloud_controller_test.go | 6 ++
.../solrprometheusexporter_controller_test.go | 7 ++
controllers/util/common.go | 6 ++
controllers/util/prometheus_exporter_util.go | 13 ++-
controllers/util/solr_util.go | 13 ++-
helm/solr-operator/Chart.yaml | 9 ++
helm/solr-operator/crds/crds.yaml | 110 +++++++++++++++++++++
helm/solr/README.md | 1 +
helm/solr/templates/_custom_option_helpers.tpl | 4 +
helm/solr/values.yaml | 14 ++-
15 files changed, 323 insertions(+), 5 deletions(-)
diff --git a/api/v1beta1/common_types.go b/api/v1beta1/common_types.go
index 13984f4..4d69afd 100644
--- a/api/v1beta1/common_types.go
+++ b/api/v1beta1/common_types.go
@@ -137,6 +137,19 @@ type PodOptions struct {
// Optional Service Account to run the pod under.
// +optional
ServiceAccountName string `json:"serviceAccountName,omitempty"`
+
+ // Optional PodSpreadTopologyConstraints to use when scheduling pods.
+ // More information here:
https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/
+ //
+ // Note: There is no need to provide a "labelSelector", as the operator
will inject the labels for you if not provided.
+ //
+ // +patchMergeKey=topologyKey
+ // +patchStrategy=merge
+ // +listType=map
+ // +listMapKey=topologyKey
+ // +listMapKey=whenUnsatisfiable
+ // +optional
+ TopologySpreadConstraints []corev1.TopologySpreadConstraint
`json:"topologySpreadConstraints,omitempty"`
}
// ServiceOptions defines custom options for services
diff --git a/api/v1beta1/zz_generated.deepcopy.go
b/api/v1beta1/zz_generated.deepcopy.go
index 9ce6426..01ef438 100644
--- a/api/v1beta1/zz_generated.deepcopy.go
+++ b/api/v1beta1/zz_generated.deepcopy.go
@@ -525,6 +525,13 @@ func (in *PodOptions) DeepCopyInto(out *PodOptions) {
*out = new(int64)
**out = **in
}
+ if in.TopologySpreadConstraints != nil {
+ in, out := &in.TopologySpreadConstraints,
&out.TopologySpreadConstraints
+ *out = make([]v1.TopologySpreadConstraint, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver,
creating a new PodOptions.
diff --git a/config/crd/bases/solr.apache.org_solrclouds.yaml
b/config/crd/bases/solr.apache.org_solrclouds.yaml
index 73d911a..dc363fd 100644
--- a/config/crd/bases/solr.apache.org_solrclouds.yaml
+++ b/config/crd/bases/solr.apache.org_solrclouds.yaml
@@ -3635,6 +3635,61 @@ spec:
type: string
type: object
type: array
+ topologySpreadConstraints:
+ description: "Optional PodSpreadTopologyConstraints to
use when scheduling pods. More information here:
https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/
\n Note: There is no need to provide a \"labelSelector\", as the operator will
inject the labels for you if not provided."
+ items:
+ description: TopologySpreadConstraint specifies how
to spread matching pods among the given topology.
+ properties:
+ labelSelector:
+ description: LabelSelector is used to find
matching pods. Pods that match this label selector are counted to determine the
number of pods in their corresponding topology domain.
+ properties:
+ matchExpressions:
+ description: matchExpressions is a list of
label selector requirements. The requirements are ANDed.
+ items:
+ description: A label selector requirement
is a selector that contains values, a key, and an operator that relates the key
and values.
+ properties:
+ key:
+ description: key is the label key that
the selector applies to.
+ type: string
+ operator:
+ description: operator represents a
key's relationship to a set of values. Valid operators are In, NotIn, Exists
and DoesNotExist.
+ type: string
+ values:
+ description: values is an array of
string values. If the operator is In or NotIn, the values array must be
non-empty. If the operator is Exists or DoesNotExist, the values array must be
empty. This array is replaced during a strategic merge patch.
+ items:
+ type: string
+ type: array
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ matchLabels:
+ additionalProperties:
+ type: string
+ description: matchLabels is a map of
{key,value} pairs. A single {key,value} in the matchLabels map is equivalent to
an element of matchExpressions, whose key field is "key", the operator is "In",
and the values array contains only "value". The requirements are ANDed.
+ type: object
+ type: object
+ maxSkew:
+ description: 'MaxSkew describes the degree to
which pods may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`,
it is the maximum permitted difference between the number of matching pods in
the target topology and the global minimum. For example, in a 3-zone cluster,
MaxSkew is set to 1, and pods with the same labelSelector spread as 1/1/0: |
zone1 | zone2 | zone3 | | P | P | | - if MaxSkew is 1, incoming
pod can only be scheduled [...]
+ format: int32
+ type: integer
+ topologyKey:
+ description: TopologyKey is the key of node
labels. Nodes that have a label with this key and identical values are
considered to be in the same topology. We consider each <key, value> as a
"bucket", and try to put balanced number of pods into each bucket. It's a
required field.
+ type: string
+ whenUnsatisfiable:
+ description: 'WhenUnsatisfiable indicates how to
deal with a pod if it doesn''t satisfy the spread constraint. - DoNotSchedule
(default) tells the scheduler not to schedule it. - ScheduleAnyway tells the
scheduler to schedule the pod in any location, but giving higher precedence
to topologies that would help reduce the skew. A constraint is considered
"Unsatisfiable" for an incoming pod if and only if every possible node
assigment for that pod would viol [...]
+ type: string
+ required:
+ - maxSkew
+ - topologyKey
+ - whenUnsatisfiable
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - topologyKey
+ - whenUnsatisfiable
+ x-kubernetes-list-type: map
volumes:
description: Additional non-data volumes to load into
the default container.
items:
diff --git a/config/crd/bases/solr.apache.org_solrprometheusexporters.yaml
b/config/crd/bases/solr.apache.org_solrprometheusexporters.yaml
index eb1a9e4..8e5b6d4 100644
--- a/config/crd/bases/solr.apache.org_solrprometheusexporters.yaml
+++ b/config/crd/bases/solr.apache.org_solrprometheusexporters.yaml
@@ -2538,6 +2538,61 @@ spec:
type: string
type: object
type: array
+ topologySpreadConstraints:
+ description: "Optional PodSpreadTopologyConstraints to
use when scheduling pods. More information here:
https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/
\n Note: There is no need to provide a \"labelSelector\", as the operator will
inject the labels for you if not provided."
+ items:
+ description: TopologySpreadConstraint specifies how
to spread matching pods among the given topology.
+ properties:
+ labelSelector:
+ description: LabelSelector is used to find
matching pods. Pods that match this label selector are counted to determine the
number of pods in their corresponding topology domain.
+ properties:
+ matchExpressions:
+ description: matchExpressions is a list of
label selector requirements. The requirements are ANDed.
+ items:
+ description: A label selector requirement
is a selector that contains values, a key, and an operator that relates the key
and values.
+ properties:
+ key:
+ description: key is the label key that
the selector applies to.
+ type: string
+ operator:
+ description: operator represents a
key's relationship to a set of values. Valid operators are In, NotIn, Exists
and DoesNotExist.
+ type: string
+ values:
+ description: values is an array of
string values. If the operator is In or NotIn, the values array must be
non-empty. If the operator is Exists or DoesNotExist, the values array must be
empty. This array is replaced during a strategic merge patch.
+ items:
+ type: string
+ type: array
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ matchLabels:
+ additionalProperties:
+ type: string
+ description: matchLabels is a map of
{key,value} pairs. A single {key,value} in the matchLabels map is equivalent to
an element of matchExpressions, whose key field is "key", the operator is "In",
and the values array contains only "value". The requirements are ANDed.
+ type: object
+ type: object
+ maxSkew:
+ description: 'MaxSkew describes the degree to
which pods may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`,
it is the maximum permitted difference between the number of matching pods in
the target topology and the global minimum. For example, in a 3-zone cluster,
MaxSkew is set to 1, and pods with the same labelSelector spread as 1/1/0: |
zone1 | zone2 | zone3 | | P | P | | - if MaxSkew is 1, incoming
pod can only be scheduled [...]
+ format: int32
+ type: integer
+ topologyKey:
+ description: TopologyKey is the key of node
labels. Nodes that have a label with this key and identical values are
considered to be in the same topology. We consider each <key, value> as a
"bucket", and try to put balanced number of pods into each bucket. It's a
required field.
+ type: string
+ whenUnsatisfiable:
+ description: 'WhenUnsatisfiable indicates how to
deal with a pod if it doesn''t satisfy the spread constraint. - DoNotSchedule
(default) tells the scheduler not to schedule it. - ScheduleAnyway tells the
scheduler to schedule the pod in any location, but giving higher precedence
to topologies that would help reduce the skew. A constraint is considered
"Unsatisfiable" for an incoming pod if and only if every possible node
assigment for that pod would viol [...]
+ type: string
+ required:
+ - maxSkew
+ - topologyKey
+ - whenUnsatisfiable
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - topologyKey
+ - whenUnsatisfiable
+ x-kubernetes-list-type: map
volumes:
description: Additional non-data volumes to load into
the default container.
items:
diff --git a/controllers/controller_utils_test.go
b/controllers/controller_utils_test.go
index c0de366..31fc8f0 100644
--- a/controllers/controller_utils_test.go
+++ b/controllers/controller_utils_test.go
@@ -911,4 +911,19 @@ var (
MinSessionTimeout: 6,
QuorumListenOnAllIPs: true,
}
+ testTopologySpreadConstraints = []corev1.TopologySpreadConstraint{
+ {
+ MaxSkew: 3,
+ TopologyKey: "zone",
+ WhenUnsatisfiable: corev1.DoNotSchedule,
+ LabelSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"test": "label"},
+ },
+ },
+ {
+ MaxSkew: 3,
+ TopologyKey: "region",
+ WhenUnsatisfiable: corev1.ScheduleAnyway,
+ },
+ }
)
diff --git a/controllers/solrcloud_controller_test.go
b/controllers/solrcloud_controller_test.go
index 3f07e22..848e913 100644
--- a/controllers/solrcloud_controller_test.go
+++ b/controllers/solrcloud_controller_test.go
@@ -180,6 +180,7 @@ var _ = FDescribe("SolrCloud controller - General", func() {
ImagePullSecrets:
testAdditionalImagePullSecrets,
TerminationGracePeriodSeconds:
&testTerminationGracePeriodSeconds,
ServiceAccountName:
testServiceAccountName,
+ TopologySpreadConstraints:
testTopologySpreadConstraints,
},
StatefulSetOptions:
&solrv1beta1.StatefulSetOptions{
Annotations:
testSSAnnotations,
@@ -244,6 +245,11 @@ var _ = FDescribe("SolrCloud controller - General", func()
{
Expect(statefulSet.Spec.Template.Spec.ImagePullSecrets).To(ConsistOf(append(testAdditionalImagePullSecrets,
corev1.LocalObjectReference{Name: testImagePullSecretName})), "Incorrect
imagePullSecrets")
Expect(statefulSet.Spec.Template.Spec.TerminationGracePeriodSeconds).To(Equal(&testTerminationGracePeriodSeconds),
"Incorrect terminationGracePeriodSeconds")
Expect(statefulSet.Spec.Template.Spec.ServiceAccountName).To(Equal(testServiceAccountName),
"Incorrect serviceAccountName")
+
Expect(statefulSet.Spec.Template.Spec.TopologySpreadConstraints).To(HaveLen(len(testTopologySpreadConstraints)),
"Wrong number of topologySpreadConstraints")
+
Expect(statefulSet.Spec.Template.Spec.TopologySpreadConstraints[0]).To(Equal(testTopologySpreadConstraints[0]),
"Wrong first topologySpreadConstraint")
+ expectedSecondTopologyConstraint :=
testTopologySpreadConstraints[1].DeepCopy()
+ expectedSecondTopologyConstraint.LabelSelector =
statefulSet.Spec.Selector
+
Expect(statefulSet.Spec.Template.Spec.TopologySpreadConstraints[1]).To(Equal(*expectedSecondTopologyConstraint),
"Wrong second topologySpreadConstraint")
// Check the update strategy
Expect(statefulSet.Spec.UpdateStrategy.Type).To(Equal(appsv1.RollingUpdateStatefulSetStrategyType),
"Incorrect statefulset update strategy")
diff --git a/controllers/solrprometheusexporter_controller_test.go
b/controllers/solrprometheusexporter_controller_test.go
index ce279cf..7a6ae59 100644
--- a/controllers/solrprometheusexporter_controller_test.go
+++ b/controllers/solrprometheusexporter_controller_test.go
@@ -160,6 +160,7 @@ var _ = FDescribe("SolrPrometheusExporter controller -
General", func() {
TerminationGracePeriodSeconds:
&testTerminationGracePeriodSeconds,
ServiceAccountName:
testServiceAccountName,
Lifecycle:
testLifecycle,
+ TopologySpreadConstraints:
testTopologySpreadConstraints,
},
DeploymentOptions:
&solrv1beta1.DeploymentOptions{
Annotations:
testDeploymentAnnotations,
@@ -232,6 +233,12 @@ var _ = FDescribe("SolrPrometheusExporter controller -
General", func() {
Expect(*deployment.Spec.Template.Spec.SecurityContext).To(Equal(testPodSecurityContext),
"Incorrect Pod securityContext")
Expect(deployment.Spec.Template.Spec.Containers[0].Lifecycle).To(Equal(testLifecycle),
"Incorrect Container lifecycle")
+
Expect(deployment.Spec.Template.Spec.TopologySpreadConstraints).To(HaveLen(len(testTopologySpreadConstraints)),
"Wrong number of topologySpreadConstraints")
+
Expect(deployment.Spec.Template.Spec.TopologySpreadConstraints[0]).To(Equal(testTopologySpreadConstraints[0]),
"Wrong first topologySpreadConstraint")
+ expectedSecondTopologyConstraint :=
testTopologySpreadConstraints[1].DeepCopy()
+ expectedSecondTopologyConstraint.LabelSelector =
deployment.Spec.Selector
+
Expect(deployment.Spec.Template.Spec.TopologySpreadConstraints[1]).To(Equal(*expectedSecondTopologyConstraint),
"Wrong second topologySpreadConstraint")
+
// Volumes
Expect(deployment.Spec.Template.Spec.Containers[0].VolumeMounts).To(HaveLen(len(extraVolumes)+1),
"Container has wrong number of volumeMounts")
Expect(deployment.Spec.Template.Spec.Volumes).To(HaveLen(len(extraVolumes)+1),
"Pod has wrong number of volumes")
diff --git a/controllers/util/common.go b/controllers/util/common.go
index fdd8ac2..fd81e30 100644
--- a/controllers/util/common.go
+++ b/controllers/util/common.go
@@ -483,6 +483,12 @@ func CopyPodTemplates(from, to *corev1.PodTemplateSpec,
basePath string, logger
to.Spec.ServiceAccountName = from.Spec.ServiceAccountName
}
+ if !DeepEqualWithNils(to.Spec.TopologySpreadConstraints,
from.Spec.TopologySpreadConstraints) {
+ requireUpdate = true
+ logger.Info("Update required because field changed", "field",
basePath+"Spec.TopologySpreadConstraints", "from",
to.Spec.TopologySpreadConstraints, "to", from.Spec.TopologySpreadConstraints)
+ to.Spec.TopologySpreadConstraints =
from.Spec.TopologySpreadConstraints
+ }
+
return requireUpdate
}
diff --git a/controllers/util/prometheus_exporter_util.go
b/controllers/util/prometheus_exporter_util.go
index fb92a68..b8722c6 100644
--- a/controllers/util/prometheus_exporter_util.go
+++ b/controllers/util/prometheus_exporter_util.go
@@ -70,7 +70,7 @@ func
GenerateSolrPrometheusExporterDeployment(solrPrometheusExporter *solr.SolrP
annotations = customDeploymentOptions.Annotations
}
- customPodOptions :=
solrPrometheusExporter.Spec.CustomKubeOptions.PodOptions
+ customPodOptions :=
solrPrometheusExporter.Spec.CustomKubeOptions.PodOptions.DeepCopy()
if nil != customPodOptions {
podLabels = MergeLabelsOrAnnotations(podLabels,
customPodOptions.Labels)
podAnnotations = customPodOptions.Annotations
@@ -323,6 +323,17 @@ func
GenerateSolrPrometheusExporterDeployment(solrPrometheusExporter *solr.SolrP
if customPodOptions.Lifecycle != nil {
metricsContainer.Lifecycle = customPodOptions.Lifecycle
}
+
+ if len(customPodOptions.TopologySpreadConstraints) > 0 {
+ deployment.Spec.Template.Spec.TopologySpreadConstraints
= customPodOptions.TopologySpreadConstraints
+
+ // Set the label selector for constraints to the
statefulSet label selector, if none is provided
+ for i := range
deployment.Spec.Template.Spec.TopologySpreadConstraints {
+ if
deployment.Spec.Template.Spec.TopologySpreadConstraints[i].LabelSelector == nil
{
+
deployment.Spec.Template.Spec.TopologySpreadConstraints[i].LabelSelector =
deployment.Spec.Selector.DeepCopy()
+ }
+ }
+ }
}
// Enrich the deployment definition to allow the exporter to make
requests to TLS enabled Solr pods
diff --git a/controllers/util/solr_util.go b/controllers/util/solr_util.go
index 34886e8..9f41e22 100644
--- a/controllers/util/solr_util.go
+++ b/controllers/util/solr_util.go
@@ -98,7 +98,7 @@ func GenerateStatefulSet(solrCloud *solr.SolrCloud,
solrCloudStatus *solr.SolrCl
annotations = MergeLabelsOrAnnotations(annotations,
customSSOptions.Annotations)
}
- customPodOptions := solrCloud.Spec.CustomSolrKubeOptions.PodOptions
+ customPodOptions :=
solrCloud.Spec.CustomSolrKubeOptions.PodOptions.DeepCopy()
var podAnnotations map[string]string
if nil != customPodOptions {
podLabels = MergeLabelsOrAnnotations(podLabels,
customPodOptions.Labels)
@@ -546,6 +546,17 @@ func GenerateStatefulSet(solrCloud *solr.SolrCloud,
solrCloudStatus *solr.SolrCl
if customPodOptions.PriorityClassName != "" {
stateful.Spec.Template.Spec.PriorityClassName =
customPodOptions.PriorityClassName
}
+
+ if len(customPodOptions.TopologySpreadConstraints) > 0 {
+ stateful.Spec.Template.Spec.TopologySpreadConstraints =
customPodOptions.TopologySpreadConstraints
+
+ // Set the label selector for constraints to the
statefulSet label selector, if none is provided
+ for i := range
stateful.Spec.Template.Spec.TopologySpreadConstraints {
+ if
stateful.Spec.Template.Spec.TopologySpreadConstraints[i].LabelSelector == nil {
+
stateful.Spec.Template.Spec.TopologySpreadConstraints[i].LabelSelector =
stateful.Spec.Selector.DeepCopy()
+ }
+ }
+ }
}
// Enrich the StatefulSet config to enable TLS on Solr pods if needed
diff --git a/helm/solr-operator/Chart.yaml b/helm/solr-operator/Chart.yaml
index c5ea9af..c9f87cd 100644
--- a/helm/solr-operator/Chart.yaml
+++ b/helm/solr-operator/Chart.yaml
@@ -132,6 +132,15 @@ annotations:
url: https://github.com/apache/solr-operator/issues/348
- name: Github PR
url: https://github.com/apache/solr-operator/pull/349
+ - kind: added
+ description: Ability to use topologySpreadConstraints for SolrCloud and
SolrPrometheusExporter
+ links:
+ - name: Github Issue
+ url: https://github.com/apache/solr-operator/issues/53
+ - name: Github PR
+ url: https://github.com/apache/solr-operator/pull/350
+ - name: Topology Spread Constraints Documentation
+ url:
https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/
artifacthub.io/images: |
- name: solr-operator
image: apache/solr-operator:v0.5.0-prerelease
diff --git a/helm/solr-operator/crds/crds.yaml
b/helm/solr-operator/crds/crds.yaml
index fa8e34a..43a9551 100644
--- a/helm/solr-operator/crds/crds.yaml
+++ b/helm/solr-operator/crds/crds.yaml
@@ -4769,6 +4769,61 @@ spec:
type: string
type: object
type: array
+ topologySpreadConstraints:
+ description: "Optional PodSpreadTopologyConstraints to
use when scheduling pods. More information here:
https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/
\n Note: There is no need to provide a \"labelSelector\", as the operator will
inject the labels for you if not provided."
+ items:
+ description: TopologySpreadConstraint specifies how
to spread matching pods among the given topology.
+ properties:
+ labelSelector:
+ description: LabelSelector is used to find
matching pods. Pods that match this label selector are counted to determine the
number of pods in their corresponding topology domain.
+ properties:
+ matchExpressions:
+ description: matchExpressions is a list of
label selector requirements. The requirements are ANDed.
+ items:
+ description: A label selector requirement
is a selector that contains values, a key, and an operator that relates the key
and values.
+ properties:
+ key:
+ description: key is the label key that
the selector applies to.
+ type: string
+ operator:
+ description: operator represents a
key's relationship to a set of values. Valid operators are In, NotIn, Exists
and DoesNotExist.
+ type: string
+ values:
+ description: values is an array of
string values. If the operator is In or NotIn, the values array must be
non-empty. If the operator is Exists or DoesNotExist, the values array must be
empty. This array is replaced during a strategic merge patch.
+ items:
+ type: string
+ type: array
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ matchLabels:
+ additionalProperties:
+ type: string
+ description: matchLabels is a map of
{key,value} pairs. A single {key,value} in the matchLabels map is equivalent to
an element of matchExpressions, whose key field is "key", the operator is "In",
and the values array contains only "value". The requirements are ANDed.
+ type: object
+ type: object
+ maxSkew:
+ description: 'MaxSkew describes the degree to
which pods may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`,
it is the maximum permitted difference between the number of matching pods in
the target topology and the global minimum. For example, in a 3-zone cluster,
MaxSkew is set to 1, and pods with the same labelSelector spread as 1/1/0: |
zone1 | zone2 | zone3 | | P | P | | - if MaxSkew is 1, incoming
pod can only be scheduled [...]
+ format: int32
+ type: integer
+ topologyKey:
+ description: TopologyKey is the key of node
labels. Nodes that have a label with this key and identical values are
considered to be in the same topology. We consider each <key, value> as a
"bucket", and try to put balanced number of pods into each bucket. It's a
required field.
+ type: string
+ whenUnsatisfiable:
+ description: 'WhenUnsatisfiable indicates how to
deal with a pod if it doesn''t satisfy the spread constraint. - DoNotSchedule
(default) tells the scheduler not to schedule it. - ScheduleAnyway tells the
scheduler to schedule the pod in any location, but giving higher precedence
to topologies that would help reduce the skew. A constraint is considered
"Unsatisfiable" for an incoming pod if and only if every possible node
assigment for that pod would viol [...]
+ type: string
+ required:
+ - maxSkew
+ - topologyKey
+ - whenUnsatisfiable
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - topologyKey
+ - whenUnsatisfiable
+ x-kubernetes-list-type: map
volumes:
description: Additional non-data volumes to load into
the default container.
items:
@@ -10539,6 +10594,61 @@ spec:
type: string
type: object
type: array
+ topologySpreadConstraints:
+ description: "Optional PodSpreadTopologyConstraints to
use when scheduling pods. More information here:
https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/
\n Note: There is no need to provide a \"labelSelector\", as the operator will
inject the labels for you if not provided."
+ items:
+ description: TopologySpreadConstraint specifies how
to spread matching pods among the given topology.
+ properties:
+ labelSelector:
+ description: LabelSelector is used to find
matching pods. Pods that match this label selector are counted to determine the
number of pods in their corresponding topology domain.
+ properties:
+ matchExpressions:
+ description: matchExpressions is a list of
label selector requirements. The requirements are ANDed.
+ items:
+ description: A label selector requirement
is a selector that contains values, a key, and an operator that relates the key
and values.
+ properties:
+ key:
+ description: key is the label key that
the selector applies to.
+ type: string
+ operator:
+ description: operator represents a
key's relationship to a set of values. Valid operators are In, NotIn, Exists
and DoesNotExist.
+ type: string
+ values:
+ description: values is an array of
string values. If the operator is In or NotIn, the values array must be
non-empty. If the operator is Exists or DoesNotExist, the values array must be
empty. This array is replaced during a strategic merge patch.
+ items:
+ type: string
+ type: array
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ matchLabels:
+ additionalProperties:
+ type: string
+ description: matchLabels is a map of
{key,value} pairs. A single {key,value} in the matchLabels map is equivalent to
an element of matchExpressions, whose key field is "key", the operator is "In",
and the values array contains only "value". The requirements are ANDed.
+ type: object
+ type: object
+ maxSkew:
+ description: 'MaxSkew describes the degree to
which pods may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`,
it is the maximum permitted difference between the number of matching pods in
the target topology and the global minimum. For example, in a 3-zone cluster,
MaxSkew is set to 1, and pods with the same labelSelector spread as 1/1/0: |
zone1 | zone2 | zone3 | | P | P | | - if MaxSkew is 1, incoming
pod can only be scheduled [...]
+ format: int32
+ type: integer
+ topologyKey:
+ description: TopologyKey is the key of node
labels. Nodes that have a label with this key and identical values are
considered to be in the same topology. We consider each <key, value> as a
"bucket", and try to put balanced number of pods into each bucket. It's a
required field.
+ type: string
+ whenUnsatisfiable:
+ description: 'WhenUnsatisfiable indicates how to
deal with a pod if it doesn''t satisfy the spread constraint. - DoNotSchedule
(default) tells the scheduler not to schedule it. - ScheduleAnyway tells the
scheduler to schedule the pod in any location, but giving higher precedence
to topologies that would help reduce the skew. A constraint is considered
"Unsatisfiable" for an incoming pod if and only if every possible node
assigment for that pod would viol [...]
+ type: string
+ required:
+ - maxSkew
+ - topologyKey
+ - whenUnsatisfiable
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - topologyKey
+ - whenUnsatisfiable
+ x-kubernetes-list-type: map
volumes:
description: Additional non-data volumes to load into
the default container.
items:
diff --git a/helm/solr/README.md b/helm/solr/README.md
index b001c2b..4647587 100644
--- a/helm/solr/README.md
+++ b/helm/solr/README.md
@@ -248,6 +248,7 @@ Configure Solr to use a separate TLS certificate for client
auth.
| podOptions.nodeSelector | map[string]string | | Add a node selector for the
Solr pod, to specify where it can be scheduled |
| podOptions.affinity | object | | Add Kubernetes affinity information for
the Solr pod |
| podOptions.tolerations | []object | | Specify a list of Kubernetes
tolerations for the Solr pod |
+| podOptions.topologySpreadConstraints | []object | | Specify a list of
Kubernetes topologySpreadConstraints for the Solr pod. No need to provide a
`labelSelector`, as the Solr Operator will default that for you. More
information can be found in [the
documentation](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/).
|
| podOptions.serviceAccountName | string | | Optional serviceAccount to run
the Solr pods under |
| podOptions.priorityClassName | string | | Optional priorityClassName for the
Solr pod |
| podOptions.sidecarContainers | []object | | An optional list of additional
containers to run along side the Solr in its pod |
diff --git a/helm/solr/templates/_custom_option_helpers.tpl
b/helm/solr/templates/_custom_option_helpers.tpl
index 0e02556..47cb8c8 100644
--- a/helm/solr/templates/_custom_option_helpers.tpl
+++ b/helm/solr/templates/_custom_option_helpers.tpl
@@ -94,6 +94,10 @@ sidecarContainers:
initContainers:
{{- toYaml .Values.podOptions.initContainers | nindent 2 }}
{{ end }}
+{{- if .Values.podOptions.topologySpreadConstraints -}}
+topologySpreadConstraints:
+ {{- toYaml .Values.podOptions.topologySpreadConstraints | nindent 2 }}
+{{ end }}
{{- end -}}
{{/*
diff --git a/helm/solr/values.yaml b/helm/solr/values.yaml
index 773a5a5..f422820 100644
--- a/helm/solr/values.yaml
+++ b/helm/solr/values.yaml
@@ -258,15 +258,23 @@ podOptions:
priorityClassName: ""
envVars: []
- affinity: {}
- tolerations: []
- nodeSelector: {}
podSecurityContext: {}
terminationGracePeriodSeconds: null
# Set Solr service account individually instead of the global
"serviceAccount.name"
serviceAccountName: ""
+ # Manage where the Solr pods are scheduled
+ affinity: {}
+ tolerations: []
+ nodeSelector: {}
+ # Documentation available at
https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/
+ # If a labelSelector is not provided, it will be auto-populated by the Solr
Operator to match the statefulSet labels
+ topologySpreadConstraints: []
+ # - maxSkew: 1
+ # topologyKey: zone
+ # whenUnsatisfiable: DoNotSchedule
+
# Probes for the Solr pods
livenessProbe: {}
readinessProbe: {}