make minimum limits boundaries configurable (#808)

* make minimum limits boundaries configurable
* add e2e test
This commit is contained in:
Felix Kunde 2020-02-03 11:43:18 +01:00 committed by GitHub
parent fddaf0fb73
commit 1f0312a014
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 175 additions and 93 deletions

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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`.

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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: |

View File

@ -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:

View File

@ -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

View File

@ -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: ""

View File

@ -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": {

View File

@ -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

View File

@ -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)

View File

@ -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) {

View File

@ -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)

View File

@ -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