make minimum limits boundaries configurable (#808)
* make minimum limits boundaries configurable * add e2e test
This commit is contained in:
parent
fddaf0fb73
commit
1f0312a014
|
|
@ -179,6 +179,12 @@ spec:
|
||||||
default_memory_request:
|
default_memory_request:
|
||||||
type: string
|
type: string
|
||||||
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
|
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
|
||||||
|
min_cpu_limit:
|
||||||
|
type: string
|
||||||
|
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
|
||||||
|
min_memory_limit:
|
||||||
|
type: string
|
||||||
|
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
|
||||||
timeouts:
|
timeouts:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
|
|
||||||
|
|
@ -115,13 +115,17 @@ configKubernetes:
|
||||||
# configure resource requests for the Postgres pods
|
# configure resource requests for the Postgres pods
|
||||||
configPostgresPodResources:
|
configPostgresPodResources:
|
||||||
# CPU limits for the postgres containers
|
# CPU limits for the postgres containers
|
||||||
default_cpu_limit: "3"
|
default_cpu_limit: "1"
|
||||||
# cpu request value for the postgres containers
|
# CPU request value for the postgres containers
|
||||||
default_cpu_request: 100m
|
default_cpu_request: 100m
|
||||||
# memory limits for the postgres containers
|
# memory limits for the postgres containers
|
||||||
default_memory_limit: 1Gi
|
default_memory_limit: 500Mi
|
||||||
# memory request value for the postgres containers
|
# memory request value for the postgres containers
|
||||||
default_memory_request: 100Mi
|
default_memory_request: 100Mi
|
||||||
|
# hard CPU minimum required to properly run a Postgres cluster
|
||||||
|
min_cpu_limit: 250m
|
||||||
|
# hard memory minimum required to properly run a Postgres cluster
|
||||||
|
min_memory_limit: 250Mi
|
||||||
|
|
||||||
# timeouts related to some operator actions
|
# timeouts related to some operator actions
|
||||||
configTimeouts:
|
configTimeouts:
|
||||||
|
|
@ -251,7 +255,7 @@ configScalyr:
|
||||||
# CPU rquest value for the Scalyr sidecar
|
# CPU rquest value for the Scalyr sidecar
|
||||||
scalyr_cpu_request: 100m
|
scalyr_cpu_request: 100m
|
||||||
# Memory limit value for the Scalyr sidecar
|
# Memory limit value for the Scalyr sidecar
|
||||||
scalyr_memory_limit: 1Gi
|
scalyr_memory_limit: 500Mi
|
||||||
# Memory request value for the Scalyr sidecar
|
# Memory request value for the Scalyr sidecar
|
||||||
scalyr_memory_request: 50Mi
|
scalyr_memory_request: 50Mi
|
||||||
|
|
||||||
|
|
@ -272,13 +276,13 @@ serviceAccount:
|
||||||
|
|
||||||
priorityClassName: ""
|
priorityClassName: ""
|
||||||
|
|
||||||
resources: {}
|
resources:
|
||||||
# limits:
|
limits:
|
||||||
# cpu: 100m
|
cpu: 500m
|
||||||
# memory: 300Mi
|
memory: 500Mi
|
||||||
# requests:
|
requests:
|
||||||
# cpu: 100m
|
cpu: 100m
|
||||||
# memory: 300Mi
|
memory: 250Mi
|
||||||
|
|
||||||
# Affinity for pod assignment
|
# Affinity for pod assignment
|
||||||
# Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
|
# Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
|
||||||
|
|
|
||||||
|
|
@ -108,13 +108,17 @@ configKubernetes:
|
||||||
# configure resource requests for the Postgres pods
|
# configure resource requests for the Postgres pods
|
||||||
configPostgresPodResources:
|
configPostgresPodResources:
|
||||||
# CPU limits for the postgres containers
|
# CPU limits for the postgres containers
|
||||||
default_cpu_limit: "3"
|
default_cpu_limit: "1"
|
||||||
# cpu request value for the postgres containers
|
# CPU request value for the postgres containers
|
||||||
default_cpu_request: 100m
|
default_cpu_request: 100m
|
||||||
# memory limits for the postgres containers
|
# memory limits for the postgres containers
|
||||||
default_memory_limit: 1Gi
|
default_memory_limit: 500Mi
|
||||||
# memory request value for the postgres containers
|
# memory request value for the postgres containers
|
||||||
default_memory_request: 100Mi
|
default_memory_request: 100Mi
|
||||||
|
# hard CPU minimum required to properly run a Postgres cluster
|
||||||
|
min_cpu_limit: 250m
|
||||||
|
# hard memory minimum required to properly run a Postgres cluster
|
||||||
|
min_memory_limit: 250Mi
|
||||||
|
|
||||||
# timeouts related to some operator actions
|
# timeouts related to some operator actions
|
||||||
configTimeouts:
|
configTimeouts:
|
||||||
|
|
@ -248,13 +252,13 @@ serviceAccount:
|
||||||
|
|
||||||
priorityClassName: ""
|
priorityClassName: ""
|
||||||
|
|
||||||
resources: {}
|
resources:
|
||||||
# limits:
|
limits:
|
||||||
# cpu: 100m
|
cpu: 500m
|
||||||
# memory: 300Mi
|
memory: 500Mi
|
||||||
# requests:
|
requests:
|
||||||
# cpu: 100m
|
cpu: 100m
|
||||||
# memory: 300Mi
|
memory: 250Mi
|
||||||
|
|
||||||
# Affinity for pod assignment
|
# Affinity for pod assignment
|
||||||
# Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
|
# Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
|
||||||
|
|
|
||||||
|
|
@ -318,11 +318,19 @@ CRD-based configuration.
|
||||||
|
|
||||||
* **default_cpu_limit**
|
* **default_cpu_limit**
|
||||||
CPU limits for the Postgres containers, unless overridden by cluster-specific
|
CPU limits for the Postgres containers, unless overridden by cluster-specific
|
||||||
settings. The default is `3`.
|
settings. The default is `1`.
|
||||||
|
|
||||||
* **default_memory_limit**
|
* **default_memory_limit**
|
||||||
memory limits for the Postgres containers, unless overridden by cluster-specific
|
memory limits for the Postgres containers, unless overridden by cluster-specific
|
||||||
settings. The default is `1Gi`.
|
settings. The default is `500Mi`.
|
||||||
|
|
||||||
|
* **min_cpu_limit**
|
||||||
|
hard CPU minimum what we consider to be required to properly run Postgres
|
||||||
|
clusters with Patroni on Kubernetes. The default is `250m`.
|
||||||
|
|
||||||
|
* **min_memory_limit**
|
||||||
|
hard memory minimum what we consider to be required to properly run Postgres
|
||||||
|
clusters with Patroni on Kubernetes. The default is `250Mi`.
|
||||||
|
|
||||||
## Operator timeouts
|
## Operator timeouts
|
||||||
|
|
||||||
|
|
@ -579,4 +587,4 @@ scalyr sidecar. In the CRD-based configuration they are grouped under the
|
||||||
CPU limit value for the Scalyr sidecar. The default is `1`.
|
CPU limit value for the Scalyr sidecar. The default is `1`.
|
||||||
|
|
||||||
* **scalyr_memory_limit**
|
* **scalyr_memory_limit**
|
||||||
Memory limit value for the Scalyr sidecar. The default is `1Gi`.
|
Memory limit value for the Scalyr sidecar. The default is `500Mi`.
|
||||||
|
|
|
||||||
10
docs/user.md
10
docs/user.md
|
|
@ -232,11 +232,11 @@ spec:
|
||||||
memory: 300Mi
|
memory: 300Mi
|
||||||
```
|
```
|
||||||
|
|
||||||
The minimum limit to properly run the `postgresql` resource is `256m` for `cpu`
|
The minimum limits to properly run the `postgresql` resource are configured to
|
||||||
and `256Mi` for `memory`. If a lower value is set in the manifest the operator
|
`250m` for `cpu` and `250Mi` for `memory`. If a lower value is set in the
|
||||||
will cancel ADD or UPDATE events on this resource with an error. If no
|
manifest the operator will raise the limits to the configured minimum values.
|
||||||
resources are defined in the manifest the operator will obtain the configured
|
If no resources are defined in the manifest they will be obtained from the
|
||||||
[default requests](reference/operator_parameters.md#kubernetes-resource-requests).
|
configured [default requests](reference/operator_parameters.md#kubernetes-resource-requests).
|
||||||
|
|
||||||
## Use taints and tolerations for dedicated PostgreSQL nodes
|
## Use taints and tolerations for dedicated PostgreSQL nodes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,57 @@ class EndToEndTestCase(unittest.TestCase):
|
||||||
k8s.create_with_kubectl("manifests/minimal-postgres-manifest.yaml")
|
k8s.create_with_kubectl("manifests/minimal-postgres-manifest.yaml")
|
||||||
k8s.wait_for_pod_start('spilo-role=master')
|
k8s.wait_for_pod_start('spilo-role=master')
|
||||||
|
|
||||||
|
@timeout_decorator.timeout(TEST_TIMEOUT_SEC)
|
||||||
|
def test_min_resource_limits(self):
|
||||||
|
'''
|
||||||
|
Lower resource limits below configured minimum and let operator fix it
|
||||||
|
'''
|
||||||
|
k8s = self.k8s
|
||||||
|
cluster_label = 'version=acid-minimal-cluster'
|
||||||
|
_, failover_targets = k8s.get_pg_nodes(cluster_label)
|
||||||
|
|
||||||
|
# configure minimum boundaries for CPU and memory limits
|
||||||
|
minCPULimit = '250m'
|
||||||
|
minMemoryLimit = '250Mi'
|
||||||
|
patch_min_resource_limits = {
|
||||||
|
"data": {
|
||||||
|
"min_cpu_limit": minCPULimit,
|
||||||
|
"min_memory_limit": minMemoryLimit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
k8s.update_config(patch_min_resource_limits)
|
||||||
|
|
||||||
|
# lower resource limits below minimum
|
||||||
|
pg_patch_resources = {
|
||||||
|
"spec": {
|
||||||
|
"resources": {
|
||||||
|
"requests": {
|
||||||
|
"cpu": "10m",
|
||||||
|
"memory": "50Mi"
|
||||||
|
},
|
||||||
|
"limits": {
|
||||||
|
"cpu": "200m",
|
||||||
|
"memory": "200Mi"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
k8s.api.custom_objects_api.patch_namespaced_custom_object(
|
||||||
|
"acid.zalan.do", "v1", "default", "postgresqls", "acid-minimal-cluster", pg_patch_resources)
|
||||||
|
k8s.wait_for_master_failover(failover_targets)
|
||||||
|
|
||||||
|
pods = k8s.api.core_v1.list_namespaced_pod(
|
||||||
|
'default', label_selector='spilo-role=master,' + cluster_label).items
|
||||||
|
self.assert_master_is_unique()
|
||||||
|
masterPod = pods[0]
|
||||||
|
|
||||||
|
self.assertEqual(masterPod.spec.containers[0].resources.limits['cpu'], minCPULimit,
|
||||||
|
"Expected CPU limit {}, found {}"
|
||||||
|
.format(minCPULimit, masterPod.spec.containers[0].resources.limits['cpu']))
|
||||||
|
self.assertEqual(masterPod.spec.containers[0].resources.limits['memory'], minMemoryLimit,
|
||||||
|
"Expected memory limit {}, found {}"
|
||||||
|
.format(minMemoryLimit, masterPod.spec.containers[0].resources.limits['memory']))
|
||||||
|
|
||||||
@timeout_decorator.timeout(TEST_TIMEOUT_SEC)
|
@timeout_decorator.timeout(TEST_TIMEOUT_SEC)
|
||||||
def test_multi_namespace_support(self):
|
def test_multi_namespace_support(self):
|
||||||
'''
|
'''
|
||||||
|
|
@ -76,10 +127,9 @@ class EndToEndTestCase(unittest.TestCase):
|
||||||
|
|
||||||
@timeout_decorator.timeout(TEST_TIMEOUT_SEC)
|
@timeout_decorator.timeout(TEST_TIMEOUT_SEC)
|
||||||
def test_scaling(self):
|
def test_scaling(self):
|
||||||
"""
|
'''
|
||||||
Scale up from 2 to 3 and back to 2 pods by updating the Postgres manifest at runtime.
|
Scale up from 2 to 3 and back to 2 pods by updating the Postgres manifest at runtime.
|
||||||
"""
|
'''
|
||||||
|
|
||||||
k8s = self.k8s
|
k8s = self.k8s
|
||||||
labels = "version=acid-minimal-cluster"
|
labels = "version=acid-minimal-cluster"
|
||||||
|
|
||||||
|
|
@ -93,9 +143,9 @@ class EndToEndTestCase(unittest.TestCase):
|
||||||
|
|
||||||
@timeout_decorator.timeout(TEST_TIMEOUT_SEC)
|
@timeout_decorator.timeout(TEST_TIMEOUT_SEC)
|
||||||
def test_taint_based_eviction(self):
|
def test_taint_based_eviction(self):
|
||||||
"""
|
'''
|
||||||
Add taint "postgres=:NoExecute" to node with master. This must cause a failover.
|
Add taint "postgres=:NoExecute" to node with master. This must cause a failover.
|
||||||
"""
|
'''
|
||||||
k8s = self.k8s
|
k8s = self.k8s
|
||||||
cluster_label = 'version=acid-minimal-cluster'
|
cluster_label = 'version=acid-minimal-cluster'
|
||||||
|
|
||||||
|
|
@ -145,7 +195,7 @@ class EndToEndTestCase(unittest.TestCase):
|
||||||
|
|
||||||
@timeout_decorator.timeout(TEST_TIMEOUT_SEC)
|
@timeout_decorator.timeout(TEST_TIMEOUT_SEC)
|
||||||
def test_logical_backup_cron_job(self):
|
def test_logical_backup_cron_job(self):
|
||||||
"""
|
'''
|
||||||
Ensure we can (a) create the cron job at user request for a specific PG cluster
|
Ensure we can (a) create the cron job at user request for a specific PG cluster
|
||||||
(b) update the cluster-wide image for the logical backup pod
|
(b) update the cluster-wide image for the logical backup pod
|
||||||
(c) delete the job at user request
|
(c) delete the job at user request
|
||||||
|
|
@ -153,7 +203,7 @@ class EndToEndTestCase(unittest.TestCase):
|
||||||
Limitations:
|
Limitations:
|
||||||
(a) Does not run the actual batch job because there is no S3 mock to upload backups to
|
(a) Does not run the actual batch job because there is no S3 mock to upload backups to
|
||||||
(b) Assumes 'acid-minimal-cluster' exists as defined in setUp
|
(b) Assumes 'acid-minimal-cluster' exists as defined in setUp
|
||||||
"""
|
'''
|
||||||
|
|
||||||
k8s = self.k8s
|
k8s = self.k8s
|
||||||
|
|
||||||
|
|
@ -208,10 +258,10 @@ class EndToEndTestCase(unittest.TestCase):
|
||||||
"Expected 0 logical backup jobs, found {}".format(len(jobs)))
|
"Expected 0 logical backup jobs, found {}".format(len(jobs)))
|
||||||
|
|
||||||
def assert_master_is_unique(self, namespace='default', version="acid-minimal-cluster"):
|
def assert_master_is_unique(self, namespace='default', version="acid-minimal-cluster"):
|
||||||
"""
|
'''
|
||||||
Check that there is a single pod in the k8s cluster with the label "spilo-role=master"
|
Check that there is a single pod in the k8s cluster with the label "spilo-role=master"
|
||||||
To be called manually after operations that affect pods
|
To be called manually after operations that affect pods
|
||||||
"""
|
'''
|
||||||
|
|
||||||
k8s = self.k8s
|
k8s = self.k8s
|
||||||
labels = 'spilo-role=master,version=' + version
|
labels = 'spilo-role=master,version=' + version
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,8 @@ spec:
|
||||||
cpu: 10m
|
cpu: 10m
|
||||||
memory: 100Mi
|
memory: 100Mi
|
||||||
limits:
|
limits:
|
||||||
cpu: 300m
|
cpu: 500m
|
||||||
memory: 300Mi
|
memory: 500Mi
|
||||||
patroni:
|
patroni:
|
||||||
initdb:
|
initdb:
|
||||||
encoding: "UTF8"
|
encoding: "UTF8"
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@ data:
|
||||||
# custom_pod_annotations: "keya:valuea,keyb:valueb"
|
# custom_pod_annotations: "keya:valuea,keyb:valueb"
|
||||||
db_hosted_zone: db.example.com
|
db_hosted_zone: db.example.com
|
||||||
debug_logging: "true"
|
debug_logging: "true"
|
||||||
# default_cpu_limit: "3"
|
# default_cpu_limit: "1"
|
||||||
# default_cpu_request: 100m
|
# default_cpu_request: 100m
|
||||||
# default_memory_limit: 1Gi
|
# default_memory_limit: 500Mi
|
||||||
# default_memory_request: 100Mi
|
# default_memory_request: 100Mi
|
||||||
docker_image: registry.opensource.zalan.do/acid/spilo-cdp-12:1.6-p16
|
docker_image: registry.opensource.zalan.do/acid/spilo-cdp-12:1.6-p16
|
||||||
# enable_admin_role_for_users: "true"
|
# enable_admin_role_for_users: "true"
|
||||||
|
|
@ -48,6 +48,8 @@ data:
|
||||||
# master_pod_move_timeout: 10m
|
# master_pod_move_timeout: 10m
|
||||||
# max_instances: "-1"
|
# max_instances: "-1"
|
||||||
# min_instances: "-1"
|
# min_instances: "-1"
|
||||||
|
# min_cpu_limit: 250m
|
||||||
|
# min_memory_limit: 250Mi
|
||||||
# node_readiness_label: ""
|
# node_readiness_label: ""
|
||||||
# oauth_token_secret_name: postgresql-operator
|
# oauth_token_secret_name: postgresql-operator
|
||||||
# pam_configuration: |
|
# pam_configuration: |
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,12 @@ spec:
|
||||||
default_memory_request:
|
default_memory_request:
|
||||||
type: string
|
type: string
|
||||||
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
|
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
|
||||||
|
min_cpu_limit:
|
||||||
|
type: string
|
||||||
|
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
|
||||||
|
min_memory_limit:
|
||||||
|
type: string
|
||||||
|
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
|
||||||
timeouts:
|
timeouts:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,10 @@ spec:
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
cpu: 500m
|
cpu: 100m
|
||||||
memory: 250Mi
|
memory: 250Mi
|
||||||
limits:
|
limits:
|
||||||
cpu: 2000m
|
cpu: 500m
|
||||||
memory: 500Mi
|
memory: 500Mi
|
||||||
securityContext:
|
securityContext:
|
||||||
runAsUser: 1000
|
runAsUser: 1000
|
||||||
|
|
|
||||||
|
|
@ -54,10 +54,12 @@ configuration:
|
||||||
# toleration: {}
|
# toleration: {}
|
||||||
# watched_namespace: ""
|
# watched_namespace: ""
|
||||||
postgres_pod_resources:
|
postgres_pod_resources:
|
||||||
default_cpu_limit: "3"
|
default_cpu_limit: "1"
|
||||||
default_cpu_request: 100m
|
default_cpu_request: 100m
|
||||||
default_memory_limit: 1Gi
|
default_memory_limit: 500Mi
|
||||||
default_memory_request: 100Mi
|
default_memory_request: 100Mi
|
||||||
|
# min_cpu_limit: 250m
|
||||||
|
# min_memory_limit: 250Mi
|
||||||
timeouts:
|
timeouts:
|
||||||
pod_label_wait_timeout: 10m
|
pod_label_wait_timeout: 10m
|
||||||
pod_deletion_wait_timeout: 10m
|
pod_deletion_wait_timeout: 10m
|
||||||
|
|
@ -115,6 +117,6 @@ configuration:
|
||||||
scalyr_cpu_limit: "1"
|
scalyr_cpu_limit: "1"
|
||||||
scalyr_cpu_request: 100m
|
scalyr_cpu_request: 100m
|
||||||
# scalyr_image: ""
|
# scalyr_image: ""
|
||||||
scalyr_memory_limit: 1Gi
|
scalyr_memory_limit: 500Mi
|
||||||
scalyr_memory_request: 50Mi
|
scalyr_memory_request: 50Mi
|
||||||
# scalyr_server_url: ""
|
# scalyr_server_url: ""
|
||||||
|
|
|
||||||
|
|
@ -810,6 +810,14 @@ var OperatorConfigCRDResourceValidation = apiextv1beta1.CustomResourceValidation
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
|
Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
|
||||||
},
|
},
|
||||||
|
"min_cpu_limit": {
|
||||||
|
Type: "string",
|
||||||
|
Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
|
||||||
|
},
|
||||||
|
"min_memory_limit": {
|
||||||
|
Type: "string",
|
||||||
|
Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"timeouts": {
|
"timeouts": {
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,8 @@ type PostgresPodResourcesDefaults struct {
|
||||||
DefaultMemoryRequest string `json:"default_memory_request,omitempty"`
|
DefaultMemoryRequest string `json:"default_memory_request,omitempty"`
|
||||||
DefaultCPULimit string `json:"default_cpu_limit,omitempty"`
|
DefaultCPULimit string `json:"default_cpu_limit,omitempty"`
|
||||||
DefaultMemoryLimit string `json:"default_memory_limit,omitempty"`
|
DefaultMemoryLimit string `json:"default_memory_limit,omitempty"`
|
||||||
|
MinCPULimit string `json:"min_cpu_limit,omitempty"`
|
||||||
|
MinMemoryLimit string `json:"min_memory_limit,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// OperatorTimeouts defines the timeout of ResourceCheck, PodWait, ReadyWait
|
// OperatorTimeouts defines the timeout of ResourceCheck, PodWait, ReadyWait
|
||||||
|
|
|
||||||
|
|
@ -227,8 +227,8 @@ func (c *Cluster) Create() error {
|
||||||
|
|
||||||
c.setStatus(acidv1.ClusterStatusCreating)
|
c.setStatus(acidv1.ClusterStatusCreating)
|
||||||
|
|
||||||
if err = c.validateResources(&c.Spec); err != nil {
|
if err = c.enforceMinResourceLimits(&c.Spec); err != nil {
|
||||||
return fmt.Errorf("insufficient resource limits specified: %v", err)
|
return fmt.Errorf("could not enforce minimum resource limits: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, role := range []PostgresRole{Master, Replica} {
|
for _, role := range []PostgresRole{Master, Replica} {
|
||||||
|
|
@ -495,38 +495,38 @@ func compareResourcesAssumeFirstNotNil(a *v1.ResourceRequirements, b *v1.Resourc
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) validateResources(spec *acidv1.PostgresSpec) error {
|
func (c *Cluster) enforceMinResourceLimits(spec *acidv1.PostgresSpec) error {
|
||||||
|
|
||||||
// setting limits too low can cause unnecessary evictions / OOM kills
|
|
||||||
const (
|
|
||||||
cpuMinLimit = "256m"
|
|
||||||
memoryMinLimit = "256Mi"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
isSmaller bool
|
isSmaller bool
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// setting limits too low can cause unnecessary evictions / OOM kills
|
||||||
|
minCPULimit := c.OpConfig.MinCPULimit
|
||||||
|
minMemoryLimit := c.OpConfig.MinMemoryLimit
|
||||||
|
|
||||||
cpuLimit := spec.Resources.ResourceLimits.CPU
|
cpuLimit := spec.Resources.ResourceLimits.CPU
|
||||||
if cpuLimit != "" {
|
if cpuLimit != "" {
|
||||||
isSmaller, err = util.IsSmallerQuantity(cpuLimit, cpuMinLimit)
|
isSmaller, err = util.IsSmallerQuantity(cpuLimit, minCPULimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error validating CPU limit: %v", err)
|
return fmt.Errorf("could not compare defined CPU limit %s with configured minimum value %s: %v", cpuLimit, minCPULimit, err)
|
||||||
}
|
}
|
||||||
if isSmaller {
|
if isSmaller {
|
||||||
return fmt.Errorf("defined CPU limit %s is below required minimum %s to properly run postgresql resource", cpuLimit, cpuMinLimit)
|
c.logger.Warningf("defined CPU limit %s is below required minimum %s and will be set to it", cpuLimit, minCPULimit)
|
||||||
|
spec.Resources.ResourceLimits.CPU = minCPULimit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memoryLimit := spec.Resources.ResourceLimits.Memory
|
memoryLimit := spec.Resources.ResourceLimits.Memory
|
||||||
if memoryLimit != "" {
|
if memoryLimit != "" {
|
||||||
isSmaller, err = util.IsSmallerQuantity(memoryLimit, memoryMinLimit)
|
isSmaller, err = util.IsSmallerQuantity(memoryLimit, minMemoryLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error validating memory limit: %v", err)
|
return fmt.Errorf("could not compare defined memory limit %s with configured minimum value %s: %v", memoryLimit, minMemoryLimit, err)
|
||||||
}
|
}
|
||||||
if isSmaller {
|
if isSmaller {
|
||||||
return fmt.Errorf("defined memory limit %s is below required minimum %s to properly run postgresql resource", memoryLimit, memoryMinLimit)
|
c.logger.Warningf("defined memory limit %s is below required minimum %s and will be set to it", memoryLimit, minMemoryLimit)
|
||||||
|
spec.Resources.ResourceLimits.Memory = minMemoryLimit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -543,7 +543,6 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
oldStatus := c.Status
|
|
||||||
c.setStatus(acidv1.ClusterStatusUpdating)
|
c.setStatus(acidv1.ClusterStatusUpdating)
|
||||||
c.setSpec(newSpec)
|
c.setSpec(newSpec)
|
||||||
|
|
||||||
|
|
@ -555,22 +554,6 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err := c.validateResources(&newSpec.Spec); err != nil {
|
|
||||||
err = fmt.Errorf("insufficient resource limits specified: %v", err)
|
|
||||||
|
|
||||||
// cancel update only when (already too low) pod resources were edited
|
|
||||||
// if cluster was successfully running before the update, continue but log a warning
|
|
||||||
isCPULimitSmaller, err2 := util.IsSmallerQuantity(newSpec.Spec.Resources.ResourceLimits.CPU, oldSpec.Spec.Resources.ResourceLimits.CPU)
|
|
||||||
isMemoryLimitSmaller, err3 := util.IsSmallerQuantity(newSpec.Spec.Resources.ResourceLimits.Memory, oldSpec.Spec.Resources.ResourceLimits.Memory)
|
|
||||||
|
|
||||||
if oldStatus.Running() && !isCPULimitSmaller && !isMemoryLimitSmaller && err2 == nil && err3 == nil {
|
|
||||||
c.logger.Warning(err)
|
|
||||||
} else {
|
|
||||||
updateFailed = true
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if oldSpec.Spec.PgVersion != newSpec.Spec.PgVersion { // PG versions comparison
|
if oldSpec.Spec.PgVersion != newSpec.Spec.PgVersion { // PG versions comparison
|
||||||
c.logger.Warningf("postgresql version change(%q -> %q) has no effect", oldSpec.Spec.PgVersion, newSpec.Spec.PgVersion)
|
c.logger.Warningf("postgresql version change(%q -> %q) has no effect", oldSpec.Spec.PgVersion, newSpec.Spec.PgVersion)
|
||||||
//we need that hack to generate statefulset with the old version
|
//we need that hack to generate statefulset with the old version
|
||||||
|
|
@ -616,6 +599,12 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error {
|
||||||
|
|
||||||
// Statefulset
|
// Statefulset
|
||||||
func() {
|
func() {
|
||||||
|
if err := c.enforceMinResourceLimits(&c.Spec); err != nil {
|
||||||
|
c.logger.Errorf("could not sync resources: %v", err)
|
||||||
|
updateFailed = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
oldSs, err := c.generateStatefulSet(&oldSpec.Spec)
|
oldSs, err := c.generateStatefulSet(&oldSpec.Spec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("could not generate old statefulset spec: %v", err)
|
c.logger.Errorf("could not generate old statefulset spec: %v", err)
|
||||||
|
|
@ -623,6 +612,9 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update newSpec to for latter comparison with oldSpec
|
||||||
|
c.enforceMinResourceLimits(&newSpec.Spec)
|
||||||
|
|
||||||
newSs, err := c.generateStatefulSet(&newSpec.Spec)
|
newSs, err := c.generateStatefulSet(&newSpec.Spec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("could not generate new statefulset spec: %v", err)
|
c.logger.Errorf("could not generate new statefulset spec: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
oldStatus := c.Status
|
|
||||||
c.setSpec(newSpec)
|
c.setSpec(newSpec)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -35,16 +34,6 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err = c.validateResources(&c.Spec); err != nil {
|
|
||||||
err = fmt.Errorf("insufficient resource limits specified: %v", err)
|
|
||||||
if oldStatus.Running() {
|
|
||||||
c.logger.Warning(err)
|
|
||||||
err = nil
|
|
||||||
} else {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = c.initUsers(); err != nil {
|
if err = c.initUsers(); err != nil {
|
||||||
err = fmt.Errorf("could not init users: %v", err)
|
err = fmt.Errorf("could not init users: %v", err)
|
||||||
return err
|
return err
|
||||||
|
|
@ -76,6 +65,11 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = c.enforceMinResourceLimits(&c.Spec); err != nil {
|
||||||
|
err = fmt.Errorf("could not enforce minimum resource limits: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
c.logger.Debugf("syncing statefulsets")
|
c.logger.Debugf("syncing statefulsets")
|
||||||
if err = c.syncStatefulSet(); err != nil {
|
if err = c.syncStatefulSet(); err != nil {
|
||||||
if !k8sutil.ResourceAlreadyExists(err) {
|
if !k8sutil.ResourceAlreadyExists(err) {
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,8 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
|
||||||
result.DefaultMemoryRequest = fromCRD.PostgresPodResources.DefaultMemoryRequest
|
result.DefaultMemoryRequest = fromCRD.PostgresPodResources.DefaultMemoryRequest
|
||||||
result.DefaultCPULimit = fromCRD.PostgresPodResources.DefaultCPULimit
|
result.DefaultCPULimit = fromCRD.PostgresPodResources.DefaultCPULimit
|
||||||
result.DefaultMemoryLimit = fromCRD.PostgresPodResources.DefaultMemoryLimit
|
result.DefaultMemoryLimit = fromCRD.PostgresPodResources.DefaultMemoryLimit
|
||||||
|
result.MinCPULimit = fromCRD.PostgresPodResources.MinCPULimit
|
||||||
|
result.MinMemoryLimit = fromCRD.PostgresPodResources.MinMemoryLimit
|
||||||
|
|
||||||
// timeout config
|
// timeout config
|
||||||
result.ResourceCheckInterval = time.Duration(fromCRD.Timeouts.ResourceCheckInterval)
|
result.ResourceCheckInterval = time.Duration(fromCRD.Timeouts.ResourceCheckInterval)
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,10 @@ type Resources struct {
|
||||||
PodToleration map[string]string `name:"toleration" default:""`
|
PodToleration map[string]string `name:"toleration" default:""`
|
||||||
DefaultCPURequest string `name:"default_cpu_request" default:"100m"`
|
DefaultCPURequest string `name:"default_cpu_request" default:"100m"`
|
||||||
DefaultMemoryRequest string `name:"default_memory_request" default:"100Mi"`
|
DefaultMemoryRequest string `name:"default_memory_request" default:"100Mi"`
|
||||||
DefaultCPULimit string `name:"default_cpu_limit" default:"3"`
|
DefaultCPULimit string `name:"default_cpu_limit" default:"1"`
|
||||||
DefaultMemoryLimit string `name:"default_memory_limit" default:"1Gi"`
|
DefaultMemoryLimit string `name:"default_memory_limit" default:"500Mi"`
|
||||||
|
MinCPULimit string `name:"min_cpu_limit" default:"250m"`
|
||||||
|
MinMemoryLimit string `name:"min_memory_limit" default:"250Mi"`
|
||||||
PodEnvironmentConfigMap string `name:"pod_environment_configmap" default:""`
|
PodEnvironmentConfigMap string `name:"pod_environment_configmap" default:""`
|
||||||
NodeReadinessLabel map[string]string `name:"node_readiness_label" default:""`
|
NodeReadinessLabel map[string]string `name:"node_readiness_label" default:""`
|
||||||
MaxInstances int32 `name:"max_instances" default:"-1"`
|
MaxInstances int32 `name:"max_instances" default:"-1"`
|
||||||
|
|
@ -66,7 +68,7 @@ type Scalyr struct {
|
||||||
ScalyrCPURequest string `name:"scalyr_cpu_request" default:"100m"`
|
ScalyrCPURequest string `name:"scalyr_cpu_request" default:"100m"`
|
||||||
ScalyrMemoryRequest string `name:"scalyr_memory_request" default:"50Mi"`
|
ScalyrMemoryRequest string `name:"scalyr_memory_request" default:"50Mi"`
|
||||||
ScalyrCPULimit string `name:"scalyr_cpu_limit" default:"1"`
|
ScalyrCPULimit string `name:"scalyr_cpu_limit" default:"1"`
|
||||||
ScalyrMemoryLimit string `name:"scalyr_memory_limit" default:"1Gi"`
|
ScalyrMemoryLimit string `name:"scalyr_memory_limit" default:"500Mi"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogicalBackup defines configuration for logical backup
|
// LogicalBackup defines configuration for logical backup
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue