
:guide-id: deploy-keycloak-kubernetes
:guide-title: Deploy {project_name} for HA with the {project_name} Operator
:guide-summary: Building block for a {project_name} deployment
:guide-priority: 999
:guide-tile-visible: false
:version: 26.2.2

include::../attributes.adoc[]

[[deploy-keycloak-kubernetes]]
= Deploy {project_name} for HA with the {project_name} Operator

ifeval::["" == "true"]
WARNING: This {section} is describing a feature which is currently in preview.
ifeval::["" == ""]
Please provide your feedback while we’re continuing to work on this.
endif::[]
ifeval::["" != ""]
Please provide your feedback by link:[joining this discussion] while we’re continuing to work on this.
endif::[]
endif::[]


This guide describes advanced {project_name} configurations for Kubernetes which are load tested and will recover from single Pod failures.

These instructions are intended for use with the setup described in the link:{links_high-availability_concepts-multi-site_url}[{links_high-availability_concepts-multi-site_name}] {section}.
Use it together with the other building blocks outlined in the link:{links_high-availability_bblocks-multi-site_url}[{links_high-availability_bblocks-multi-site_name}] {section}.

== Prerequisites

* OpenShift or Kubernetes cluster running.
* Understanding of a link:{links_operator_basic-deployment_url}[{links_operator_basic-deployment_name}] of {project_name} with the {project_name} Operator.
* Aurora AWS database deployed using the link:{links_high-availability_deploy-aurora-multi-az_url}[{links_high-availability_deploy-aurora-multi-az_name}] {section}.
* {jdgserver_name} server deployed using the link:{links_high-availability_deploy-infinispan-kubernetes-crossdc_url}[{links_high-availability_deploy-infinispan-kubernetes-crossdc_name}] {section}.
* Running {project_name} with OpenJDK 21, which is the default for the containers distributed for {project_name}, as this enabled virtual threads for the JGroups communication.

== Procedure

. Determine the sizing of the deployment using the link:{links_high-availability_concepts-memory-and-cpu-sizing_url}[{links_high-availability_concepts-memory-and-cpu-sizing_name}] {section}.

. Install the {project_name} Operator as described in the link:{links_operator_installation_url}[{links_operator_installation_name}] {section}.

. Notice the configuration file below contains options relevant for connecting to the Aurora database from link:{links_high-availability_deploy-aurora-multi-az_url}#connecting-aurora-to-keycloak[{links_high-availability_deploy-aurora-multi-az_name}]

. Notice the configuration file below options relevant for connecting to the {jdgserver_name} server from link:{links_high-availability_deploy-infinispan-kubernetes-crossdc_url}#connecting-infinispan-to-keycloak[{links_high-availability_deploy-infinispan-kubernetes-crossdc_name}]

. Build a custom {project_name} image which is link:{links_server_db_url}#preparing-keycloak-for-amazon-aurora-postgresql[prepared for usage with the Amazon Aurora PostgreSQL database].

. Deploy the {project_name} CR with the following values with the resource requests and limits calculated in the first step:
+
[source,yaml]
----
include::examples/generated/keycloak.yaml[tag=keycloak]
----
<1> The database connection pool initial, max and min size should be identical to allow statement caching for the database.
Adjust this number to meet the needs of your system.
As most requests will not touch the database due to the {project_name} embedded cache, this change can server several hundreds of requests per second.
See the link:{links_high-availability_concepts-database-connections_url}[{links_high-availability_concepts-database-connections_name}] {section} for details.
<2> Specify the URL to your custom {project_name} image. If your image is optimized, set the `startOptimized` flag to `true`.
<3> Enable additional features for multi-site support like the loadbalancer probe `/lb-check`.
<4> XA transactions are not supported by the https://github.com/awslabs/aws-advanced-jdbc-wrapper/releases/[Amazon Web Services JDBC Driver].
<5> To be able to analyze the system under load, enable the metrics endpoint.
The disadvantage of the setting is that the metrics will be available at the external {project_name} endpoint, so you must add a filter so that the endpoint is not available from the outside.
Use a reverse proxy in front of {project_name} to filter out those URLs.

== Verifying the deployment

Confirm that the {project_name} deployment is ready.

[source,bash]
----
kubectl wait --for=condition=Ready keycloaks.k8s.keycloak.org/keycloak
kubectl wait --for=condition=RollingUpdate=False keycloaks.k8s.keycloak.org/keycloak
----

== Optional: Load shedding

To enable load shedding, limit the number of queued requests.

.Load shedding with max queued http requests
[source,yaml,indent=0]
----
spec:
  additionalOptions:
include::examples/generated/keycloak.yaml[tag=keycloak-queue-size]
----

All exceeding requests are served with an HTTP 503.

You might consider limiting the value for `http-pool-max-threads` further because multiple concurrent threads will lead to throttling by Kubernetes once the requested CPU limit is reached.

See the link:{links_high-availability_concepts-threads_url}[{links_high-availability_concepts-threads_name}] {section} about load shedding for details.

== Optional: Disable sticky sessions

When running on OpenShift and the default passthrough Ingress setup as provided by the {project_name} Operator, the load balancing done by HAProxy is done by using sticky sessions based on the IP address of the source.
When running load tests, or when having a reverse proxy in front of HAProxy, you might want to disable this setup to avoid receiving all requests on a single {project_name} Pod.

Add the following supplementary configuration under the `spec` in the {project_name} Custom Resource to disable sticky sessions.

[source,yaml,subs="attributes+"]
----
spec:
  ingress:
    enabled: true
    annotations:
      # When running load tests, disable sticky sessions on the OpenShift HAProxy router
      # to avoid receiving all requests on a single {project_name} Pod.
      haproxy.router.openshift.io/balance: roundrobin
      haproxy.router.openshift.io/disable_cookies: 'true'
----


