Set maximum CPU and Memory requests on K8s (#1959)

* Set maximum CPU and Memory requests on K8s
This commit is contained in:
Jociele Padilha
2022-07-28 14:18:27 +02:00
committed by GitHub
parent 1c80ac0acd
commit b41daf4f76
14 changed files with 188 additions and 20 deletions
+33
View File
@@ -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
}
+73 -6
View File
@@ -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 {