Setting Up a Keycloak Cluster with JDBC-PING for Caching
Written on
Chapter 1: Introduction to Keycloak Clustering
In this guide, we will walk through the process of establishing a Keycloak Cluster utilizing JDBC-PING for distributed caching. This cluster will consist of two Keycloak instances deployed as Docker containers, with Postgres serving as the database to manage and share cached data across these instances.
To visualize our objective, here’s a project diagram outlining the architecture we aim to implement.
Let's dive in!
Additional Readings: Distributed Caching in Keycloak
Keycloak is an open-source tool designed for identity and access management (IAM). It facilitates the management of user identities, authentication, and authorization for various applications.
Keycloak's current distributed cache system is built on Infinispan, an open-source, distributed, in-memory key-value data store known for its scalability, availability, and fault tolerance.
When Keycloak is initiated in development mode with the "start-dev" command, distributed caching is entirely disabled. Conversely, when launched in production mode with the "start" command, caching becomes active, allowing all Keycloak instances within your network to be identified. By default, the caching system employs a UDP transport layer for instance discovery via IP multicast.
However, there are superior alternatives to UDP for discovery, such as JDBC-PING. This method enables Keycloak to facilitate distributed caching by utilizing a JDBC-compatible database for sharing and storing cached data among multiple instances.
In the following sections, we’ll explore how to set this up.
Section 1.1: Configuring JDBC-PING in Infinispan
As previously mentioned, the distributed cache relies on Infinispan. Let’s examine how to configure it.
To initiate, execute the following command in your terminal. This command will launch a Docker container featuring the Keycloak image in interactive mode, display the contents of the cache-ispn.xml file using the cat command, and subsequently remove the container once it stops running.
docker run -it --rm --entrypoint=cat
quay.io/keycloak/keycloak:22.0.1 opt/keycloak/conf/cache-ispn.xml
You should observe XML content resembling the following.
<!-- XML content goes here -->
To implement JDBC-PING, we need to adjust the Infinispan configuration. Let's create a new file named cache-ispn-jdbc-ping.xml and replicate the content from cache-ispn.xml into it.
Next, we will incorporate the JDBC-PING configuration. In this scenario, we will be utilizing a Postgres database. Insert the highlighted lines within the <jgroups> element and modify the <stack> element to reflect the postgres-jdbc-ping-tcp stack.
<jgroups>
<stack="postgres-jdbc-ping-tcp"/>
</jgroups>
In this configuration, the <jgroups> tag sets up the reliable group communication library for distributed systems. The <stack> tag defines a stack named postgres-jdbc-ping-tcp, which builds upon the TCP stack for node communication.
Inside the <tcp> tag, two further tags configure the JGroups behavior for this stack:
- The <bind> tag specifies the external IP address for the JGroups discovery process. If the JGROUPS_DISCOVERY_EXTERNAL_IP environment variable is defined, it will be utilized; otherwise, it defaults to 127.0.0.1.
- The <jdbc-ping> tag employs a JDBC-based discovery protocol to identify other nodes in the cluster. It outlines the required JDBC connection parameters, including the driver, username, password, and connection URL, alongside SQL commands for initializing the schema and managing ping data.
Section 1.2: Launching the Keycloak Cluster
Let’s create a Docker network named keycloak-net. Execute the following command in your terminal:
docker network create keycloak-net
Next, we will start a Postgres Docker container. The command below initializes a new Docker container in detached mode (-d) with the name postgres.
docker run -d --rm --name postgres
-e POSTGRES_DB=keycloak
-e POSTGRES_USER=keycloak
-e POSTGRES_PASSWORD=password
--network keycloak-net
postgres:15.3
Below are the commands necessary to launch the Keycloak instances. Ensure you're in the directory where the cache-ispn-jdbc-ping.xml file is stored before executing the following commands.
To start the first Keycloak instance, run:
docker run --rm --name keycloak-1 -p 8080:8080
-e KEYCLOAK_ADMIN=admin
-e KEYCLOAK_ADMIN_PASSWORD=admin
-e KC_DB=postgres
-e KC_DB_URL_HOST=postgres
-e KC_DB_URL_DATABASE=keycloak
-e KC_DB_USERNAME=keycloak
-e KC_DB_PASSWORD=password
-e JGROUPS_DISCOVERY_EXTERNAL_IP=keycloak-1
-e KC_CACHE_CONFIG_FILE=cache-ispn-jdbc-ping.xml
-v ${PWD}/cache-ispn-jdbc-ping.xml:/opt/keycloak/conf/cache-ispn-jdbc-ping.xml
--network keycloak-net
quay.io/keycloak/keycloak:22.0.1 start-dev
In a separate terminal, execute the following command to start the second Keycloak instance:
docker run --rm --name keycloak-2 -p 8081:8080
-e KEYCLOAK_ADMIN=admin
-e KEYCLOAK_ADMIN_PASSWORD=admin
-e KC_DB=postgres
-e KC_DB_URL_HOST=postgres
-e KC_DB_URL_DATABASE=keycloak
-e KC_DB_USERNAME=keycloak
-e KC_DB_PASSWORD=password
-e JGROUPS_DISCOVERY_EXTERNAL_IP=keycloak-2
-e KC_CACHE_CONFIG_FILE=cache-ispn-jdbc-ping.xml
-v ${PWD}/cache-ispn-jdbc-ping.xml:/opt/keycloak/conf/cache-ispn-jdbc-ping.xml
--network keycloak-net
quay.io/keycloak/keycloak:22.0.1 start-dev
Notice that the Docker container name is specified for the JGROUPS_DISCOVERY_EXTERNAL_IP environment variable. We also set up a volume connection between the host and the Docker containers, allowing the cache-ispn-jdbc-ping.xml file to be accessible inside the containers.
After starting the instances, you can monitor the logs for messages indicating that the Infinispan cluster is operational.
Section 1.3: Verifying Distributed Caching via Keycloak UI
To check the setup, open two different browsers—such as Chrome and Safari or Chrome and Incognito Chrome.
In one browser, navigate to http://localhost:8080/admin/, and in the other, go to http://localhost:8081/admin/.
Log in using admin for both the username and password. Click on "Sessions" in the left menu, where you should see two active sessions for the admin user.
Checking the JGROUPSPING Table
Next, let’s inspect the JGROUPSPING table in Postgres where the ping data is recorded. Access the Postgres shell by executing the following command:
docker exec -it postgres psql -U keycloak -d keycloak
Once inside the Postgres shell, run this SQL command:
SELECT * FROM JGROUPSPING;
You should be able to view the entries stored in the JGROUPSPING table.
Section 1.4: Shutting Down the Keycloak Cluster
To terminate the Keycloak Cluster, return to the terminals running the Keycloak instances and press Ctrl+C. Then, execute the following commands to stop the Postgres Docker container and remove the keycloak-net network:
docker rm -fv postgres
docker network rm keycloak-net
Conclusion
In summary, we have successfully set up a Keycloak Cluster utilizing JDBC-PING for distributed caching in this tutorial, using Postgres as the database. Keycloak, however, also supports other databases like MySQL, MariaDB, and Microsoft SQL Server.
Additionally, I maintain a Docker image called keycloak-clustered on Docker Hub, providing out-of-the-box clustering for Keycloak instances using JDBC-PING for all supported databases. You can find the code in the keycloak-clustered GitHub repository.
Support and Engagement
If you found this article helpful and would like to show your support, consider the following actions:
- Engage by clapping, highlighting, and replying to my story. I'm happy to answer any questions you may have.
- Share this article on social media.
- Follow me on Medium, LinkedIn, and Twitter.
- Subscribe to my newsletter to stay updated on my latest posts.
This video titled "KEYCLOAK Cluster - Up and Running in Seconds" by Niko Köbler provides a quick overview of setting up a Keycloak cluster efficiently.