Skip to content

Commit

Permalink
Add default CPU limit/request for the operator
Browse files Browse the repository at this point in the history
Closes: keycloak#27432

Signed-off-by: Peter Zaoral <[email protected]>
  • Loading branch information
Pepo48 committed Jun 20, 2024
1 parent fdf4853 commit 49ccd3b
Show file tree
Hide file tree
Showing 6 changed files with 23 additions and 4 deletions.
6 changes: 3 additions & 3 deletions docs/guides/operator/advanced-configuration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ The `unsupported` field of the CR contains highly experimental configuration opt
==== Pod Template

The Pod Template is a raw API representation that is used for the Deployment Template.
This field is a temporary workaround in case no supported field exists at the top level of the CR for your use case.
This field is a temporary workaround in case no supported field exists at the top level of the CR for your use case.

The Operator merges the fields of the provided template with the values generated by the Operator for the specific Deployment.
With this feature, you have access to a high level of customizations. However, no guarantee exists that the Deployment will work as expected.
Expand Down Expand Up @@ -175,7 +175,7 @@ spec:
The Keycloak CR allows specifying the `resources` options for managing compute resources for the {project_name} container.
It provides the ability to request and limit resources independently for the main Keycloak deployment via the Keycloak CR, and for the realm import Job via the Realm Import CR.

When no values are specified, the default `requests` memory is set to `1700MiB`, and the `limits` memory is set to `2GiB`.
When no values are specified, the default `requests` memory is set to `1700MiB`, and the `limits` memory is set to `2GiB`. Similarly, the default `requests` CPU is set to `500m`, and the `limits` CPU is set to `1`.
These values were chosen based on a deeper analysis of {project_name} memory management.

If no values are specified in the Realm Import CR, it falls back to the values specified in the Keycloak CR, or to the defaults as defined above.
Expand Down Expand Up @@ -259,7 +259,7 @@ stringData:
...
------

When running on a Kubernetes or OpenShift environment well-known locations of trusted certificates are included automatically.
When running on a Kubernetes or OpenShift environment well-known locations of trusted certificates are included automatically.
This includes /var/run/secrets/kubernetes.io/serviceaccount/ca.crt and the /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt when present.

</@tmpl.guide>
1 change: 1 addition & 0 deletions operator/src/main/java/org/keycloak/operator/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ interface ResourceRequirements {

interface Resources {
Quantity memory();
Quantity cpu();
}
}
}
15 changes: 14 additions & 1 deletion operator/src/main/java/org/keycloak/operator/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public static Map<String, String> allInstanceLabels(HasMetadata primary) {
public static <T extends HasMetadata> Optional<T> getByName(Class<T> clazz, Function<Keycloak, String> nameFunction, Keycloak primary, Context<Keycloak> context) {
InformerEventSource<T, Keycloak> ies = (InformerEventSource<T, Keycloak>) context
.eventSourceRetriever().getResourceEventSourceFor(clazz);

return ies.get(new ResourceID(nameFunction.apply(primary), primary.getMetadata().getNamespace()));
}

Expand All @@ -92,7 +92,9 @@ public static void addResources(ResourceRequirements resource, Config config, Co
final var requests = Optional.ofNullable(resourcesSpec.getRequests()).orElseGet(HashMap::new);

final var requestsMemory = requests.get("memory");
final var requestsCpu = requests.get("cpu");
final var defaultRequestsMemory = config.keycloak().resources().requests().memory();
final var defaultRequestsCpu = config.keycloak().resources().requests().cpu();

// Validate 'requests' memory
if (requestsMemory != null) {
Expand All @@ -104,9 +106,20 @@ public static void addResources(ResourceRequirements resource, Config config, Co
requests.put("memory", defaultRequestsMemory);
}

// Validate 'requests' cpu
if (requestsCpu != null) {
var specifiedCpuIsLessThanDefault = requestsCpu.getNumericalAmount().intValue() < defaultRequestsCpu.getNumericalAmount().intValue();
if (specifiedCpuIsLessThanDefault) {
Log.debugf("Provided 'requests' cpu ('%s') is less than used default value ('%s'). Use it in your risk, as Keycloak performance might be degraded.", requestsCpu, defaultRequestsCpu);
}
} else {
requests.put("cpu", defaultRequestsCpu);
}

// sets the max boundary when the spec is not present
final var limits = Optional.ofNullable(resourcesSpec.getLimits()).orElseGet(HashMap::new);
limits.putIfAbsent("memory", config.keycloak().resources().limits().memory());
limits.putIfAbsent("cpu", config.keycloak().resources().limits().cpu());

kcContainer.setResources(resourcesSpec);
}
Expand Down
2 changes: 2 additions & 0 deletions operator/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ kc.operator.keycloak.start-optimized=false
kc.operator.keycloak.poll-interval-seconds=60
# Keycloak container default requests/limits resources
kc.operator.keycloak.resources.requests.memory=1700Mi
kc.operator.keycloak.resources.requests.cpu=500m
kc.operator.keycloak.resources.limits.memory=2Gi
kc.operator.keycloak.resources.limits.cpu=1

# https://quarkus.io/guides/deploying-to-kubernetes#environment-variables-from-keyvalue-pairs
quarkus.kubernetes.env.vars.related-image-keycloak=${kc.operator.keycloak.image}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -705,10 +705,12 @@ public void testApplyingResourcesDefaultValues() {
var requests = resources.getRequests();
assertThat(requests).isNotNull();
assertThat(requests.get("memory")).isEqualTo(config.keycloak().resources().requests().memory());
assertThat(requests.get("cpu")).isEqualTo(config.keycloak().resources().requests().cpu());

var limits = resources.getLimits();
assertThat(limits).isNotNull();
assertThat(limits.get("memory")).isEqualTo(config.keycloak().resources().limits().memory());
assertThat(limits.get("cpu")).isEqualTo(config.keycloak().resources().limits().cpu());
}

private void handleFakeImagePullSecretCreation(Keycloak keycloakCR,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ public void testWorkingRealmImport() {
assertThat(job.getSpec().getTemplate().getSpec().getImagePullSecrets().get(0).getName()).isEqualTo("my-empty-secret");

assertResources(container, config.keycloak().resources().requests().memory(), config.keycloak().resources().limits().memory());
assertResources(container, config.keycloak().resources().requests().cpu(), config.keycloak().resources().limits().cpu());

String url =
"https://" + KeycloakServiceDependentResource.getServiceName(kc) + "." + namespace + ":" + KEYCLOAK_HTTPS_PORT + "/realms/count0";
Expand Down

0 comments on commit 49ccd3b

Please sign in to comment.