mirror of
https://github.com/zalando/postgres-operator.git
synced 2026-10-04 03:02:34 +02:00
Set maximum CPU and Memory requests on K8s (#1959)
* Set maximum CPU and Memory requests on K8s
This commit is contained in:
@@ -1471,6 +1471,14 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{
|
||||
Type: "string",
|
||||
Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
|
||||
},
|
||||
"max_cpu_request": {
|
||||
Type: "string",
|
||||
Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
|
||||
},
|
||||
"max_memory_request": {
|
||||
Type: "string",
|
||||
Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
|
||||
},
|
||||
"min_cpu_limit": {
|
||||
Type: "string",
|
||||
Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
|
||||
|
||||
@@ -109,6 +109,8 @@ type PostgresPodResourcesDefaults struct {
|
||||
DefaultMemoryLimit string `json:"default_memory_limit,omitempty"`
|
||||
MinCPULimit string `json:"min_cpu_limit,omitempty"`
|
||||
MinMemoryLimit string `json:"min_memory_limit,omitempty"`
|
||||
MaxCPURequest string `json:"max_cpu_request,omitempty"`
|
||||
MaxMemoryRequest string `json:"max_memory_request,omitempty"`
|
||||
}
|
||||
|
||||
// OperatorTimeouts defines the timeout of ResourceCheck, PodWait, ReadyWait
|
||||
|
||||
@@ -183,6 +183,32 @@ func (c *Cluster) enforceMinResourceLimits(resources *v1.ResourceRequirements) e
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cluster) enforceMaxResourceRequests(resources *v1.ResourceRequirements) error {
|
||||
var (
|
||||
err error
|
||||
)
|
||||
|
||||
cpuRequest := resources.Requests[v1.ResourceCPU]
|
||||
maxCPURequest := c.OpConfig.MaxCPURequest
|
||||
maxCPU, err := util.MinResource(maxCPURequest, cpuRequest.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not compare defined CPU request %s for %q container with configured maximum value %s: %v",
|
||||
cpuRequest.String(), constants.PostgresContainerName, maxCPURequest, err)
|
||||
}
|
||||
resources.Requests[v1.ResourceCPU] = maxCPU
|
||||
|
||||
memoryRequest := resources.Requests[v1.ResourceMemory]
|
||||
maxMemoryRequest := c.OpConfig.MaxMemoryRequest
|
||||
maxMemory, err := util.MinResource(maxMemoryRequest, memoryRequest.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not compare defined memory request %s for %q container with configured maximum value %s: %v",
|
||||
memoryRequest.String(), constants.PostgresContainerName, maxMemoryRequest, err)
|
||||
}
|
||||
resources.Requests[v1.ResourceMemory] = maxMemory
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setMemoryRequestToLimit(resources *v1.ResourceRequirements, containerName string, logger *logrus.Entry) {
|
||||
|
||||
requests := resources.Requests[v1.ResourceMemory]
|
||||
@@ -260,6 +286,13 @@ func (c *Cluster) generateResourceRequirements(
|
||||
setMemoryRequestToLimit(&result, containerName, c.logger)
|
||||
}
|
||||
|
||||
// enforce maximum cpu and memory requests for Postgres containers only
|
||||
if containerName == constants.PostgresContainerName {
|
||||
if err = c.enforceMaxResourceRequests(&result); err != nil {
|
||||
return nil, fmt.Errorf("could not enforce maximum resource requests: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1841,8 +1841,10 @@ func TestSidecars(t *testing.T) {
|
||||
},
|
||||
Resources: config.Resources{
|
||||
DefaultCPURequest: "200m",
|
||||
MaxCPURequest: "300m",
|
||||
DefaultCPULimit: "500m",
|
||||
DefaultMemoryRequest: "0.7Gi",
|
||||
MaxMemoryRequest: "1.0Gi",
|
||||
DefaultMemoryLimit: "1.3Gi",
|
||||
},
|
||||
SidecarImages: map[string]string{
|
||||
@@ -2128,8 +2130,10 @@ func TestGenerateService(t *testing.T) {
|
||||
},
|
||||
Resources: config.Resources{
|
||||
DefaultCPURequest: "200m",
|
||||
MaxCPURequest: "300m",
|
||||
DefaultCPULimit: "500m",
|
||||
DefaultMemoryRequest: "0.7Gi",
|
||||
MaxMemoryRequest: "1.0Gi",
|
||||
DefaultMemoryLimit: "1.3Gi",
|
||||
},
|
||||
SidecarImages: map[string]string{
|
||||
@@ -2415,18 +2419,21 @@ func TestGenerateResourceRequirements(t *testing.T) {
|
||||
roleLabel := "spilo-role"
|
||||
sidecarName := "postgres-exporter"
|
||||
|
||||
// two test cases will call enforceMinResourceLimits which emits 2 events per call
|
||||
// hence bufferSize of 4 is required
|
||||
newEventRecorder := record.NewFakeRecorder(4)
|
||||
// enforceMinResourceLimits will be called 2 twice emitting 4 events (2x cpu, 2x memory raise)
|
||||
// enforceMaxResourceRequests will be called 4 times emitting 6 events (2x cpu, 4x memory cap)
|
||||
// hence event bufferSize of 10 is required
|
||||
newEventRecorder := record.NewFakeRecorder(10)
|
||||
|
||||
configResources := config.Resources{
|
||||
ClusterLabels: map[string]string{"application": "spilo"},
|
||||
ClusterNameLabel: clusterNameLabel,
|
||||
DefaultCPURequest: "100m",
|
||||
DefaultCPULimit: "1",
|
||||
MaxCPURequest: "500m",
|
||||
MinCPULimit: "250m",
|
||||
DefaultMemoryRequest: "100Mi",
|
||||
DefaultMemoryLimit: "500Mi",
|
||||
MinCPULimit: "250m",
|
||||
MaxMemoryRequest: "1Gi",
|
||||
MinMemoryLimit: "250Mi",
|
||||
PodRoleLabel: roleLabel,
|
||||
}
|
||||
@@ -2558,6 +2565,10 @@ func TestGenerateResourceRequirements(t *testing.T) {
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: acidv1.PostgresSpec{
|
||||
Resources: &acidv1.Resources{
|
||||
ResourceRequests: acidv1.ResourceDescription{Memory: "200Mi"},
|
||||
ResourceLimits: acidv1.ResourceDescription{Memory: "300Mi"},
|
||||
},
|
||||
TeamID: "acid",
|
||||
Volume: acidv1.Volume{
|
||||
Size: "1G",
|
||||
@@ -2565,8 +2576,8 @@ func TestGenerateResourceRequirements(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expectedResources: acidv1.Resources{
|
||||
ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "500Mi"},
|
||||
ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "500Mi"},
|
||||
ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "300Mi"},
|
||||
ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "300Mi"},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -2691,6 +2702,62 @@ func TestGenerateResourceRequirements(t *testing.T) {
|
||||
ResourceLimits: acidv1.ResourceDescription{CPU: "100m", Memory: "100Mi"},
|
||||
},
|
||||
},
|
||||
{
|
||||
subTest: "test enforcing max cpu and memory requests",
|
||||
config: config.Config{
|
||||
Resources: configResources,
|
||||
PodManagementPolicy: "ordered_ready",
|
||||
SetMemoryRequestToLimit: false,
|
||||
},
|
||||
pgSpec: acidv1.Postgresql{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: clusterName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: acidv1.PostgresSpec{
|
||||
Resources: &acidv1.Resources{
|
||||
ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "2Gi"},
|
||||
ResourceLimits: acidv1.ResourceDescription{CPU: "2", Memory: "4Gi"},
|
||||
},
|
||||
TeamID: "acid",
|
||||
Volume: acidv1.Volume{
|
||||
Size: "1G",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedResources: acidv1.Resources{
|
||||
ResourceRequests: acidv1.ResourceDescription{CPU: "500m", Memory: "1Gi"},
|
||||
ResourceLimits: acidv1.ResourceDescription{CPU: "2", Memory: "4Gi"},
|
||||
},
|
||||
},
|
||||
{
|
||||
subTest: "test SetMemoryRequestToLimit flag but raise only until max memory request",
|
||||
config: config.Config{
|
||||
Resources: configResources,
|
||||
PodManagementPolicy: "ordered_ready",
|
||||
SetMemoryRequestToLimit: true,
|
||||
},
|
||||
pgSpec: acidv1.Postgresql{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: clusterName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: acidv1.PostgresSpec{
|
||||
Resources: &acidv1.Resources{
|
||||
ResourceRequests: acidv1.ResourceDescription{Memory: "500Mi"},
|
||||
ResourceLimits: acidv1.ResourceDescription{Memory: "2Gi"},
|
||||
},
|
||||
TeamID: "acid",
|
||||
Volume: acidv1.Volume{
|
||||
Size: "1G",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedResources: acidv1.Resources{
|
||||
ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "1Gi"},
|
||||
ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "2Gi"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -129,6 +129,8 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
|
||||
result.DefaultMemoryLimit = util.Coalesce(fromCRD.PostgresPodResources.DefaultMemoryLimit, "500Mi")
|
||||
result.MinCPULimit = util.Coalesce(fromCRD.PostgresPodResources.MinCPULimit, "250m")
|
||||
result.MinMemoryLimit = util.Coalesce(fromCRD.PostgresPodResources.MinMemoryLimit, "250Mi")
|
||||
result.MaxCPURequest = fromCRD.PostgresPodResources.MaxCPURequest
|
||||
result.MaxMemoryRequest = fromCRD.PostgresPodResources.MaxMemoryRequest
|
||||
|
||||
// timeout config
|
||||
result.ResourceCheckInterval = util.CoalesceDuration(time.Duration(fromCRD.Timeouts.ResourceCheckInterval), "3s")
|
||||
|
||||
@@ -54,6 +54,8 @@ type Resources struct {
|
||||
DefaultMemoryLimit string `name:"default_memory_limit" default:"500Mi"`
|
||||
MinCPULimit string `name:"min_cpu_limit" default:"250m"`
|
||||
MinMemoryLimit string `name:"min_memory_limit" default:"250Mi"`
|
||||
MaxCPURequest string `name:"max_cpu_request"`
|
||||
MaxMemoryRequest string `name:"max_memory_request"`
|
||||
PodEnvironmentConfigMap spec.NamespacedName `name:"pod_environment_configmap"`
|
||||
PodEnvironmentSecret string `name:"pod_environment_secret"`
|
||||
NodeReadinessLabel map[string]string `name:"node_readiness_label" default:""`
|
||||
|
||||
@@ -367,3 +367,21 @@ func IsSmallerQuantity(requestStr, limitStr string) (bool, error) {
|
||||
|
||||
return request.Cmp(limit) == -1, nil
|
||||
}
|
||||
|
||||
func MinResource(maxRequestStr, requestStr string) (resource.Quantity, error) {
|
||||
|
||||
isSmaller, err := IsSmallerQuantity(maxRequestStr, requestStr)
|
||||
if isSmaller && err == nil {
|
||||
maxRequest, err := resource.ParseQuantity(maxRequestStr)
|
||||
if err != nil {
|
||||
return maxRequest, err
|
||||
}
|
||||
return maxRequest, nil
|
||||
}
|
||||
|
||||
request, err := resource.ParseQuantity(requestStr)
|
||||
if err != nil {
|
||||
return request, err
|
||||
}
|
||||
return request, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user