Cluster Keycloak with MariaDB and LDAP using Docker Standalone - dcm4che/dcm4chee-arc-light GitHub Wiki
s.o.:
- Cluster Keycloak with MariaDB and LDAP using Docker Swarm
- Keycloak - Blog - Keycloak Cluster Setup
- Master to Master Replication between two MariaDB Servers
Note : The whole setup entitles starting two types of Databases. MariaDB shall be the database used for Keycloak backend, whereas Postgres shall be the database used for Archive backend. The LDAP containers of both clusters are also synced which shall be used by archive containers for its configurations and by the Keycloak containers for accessing their User Federation.
-
Create
docker-compose.env
for first and second clusters as follows :ARCHIVE_HOST=<host1-IP-addr> STORAGE_DIR=/storage/fs1 POSTGRES_DB=pacsdb POSTGRES_USER=pacs POSTGRES_PASSWORD=pacs AUTH_SERVER_URL=http://keycloak:8843
Note:
-
AUTH_SERVER_URL
here useskeycloak
in its hostname. On the first node, map this hostkeycloak
to the IP Address of the first node in its/etc/hosts
file. This is done in order to simulate a loadbalancer for two clusters. - Replace
<host1-IP-addr>
with IP address / hostname of first node
-
-
Specify services for first node in a configuration file
docker-compose.yml
(e.g.) :version: "3" services: host1-ldap: image: dcm4che/slapd-dcm4chee:2.6.7-33.0 logging: driver: json-file options: max-size: "10m" ports: - "389:389" env_file: docker-compose.env environment: LDAP_URLS: "ldap://host1-ldap/" LDAP_REPLICATION_HOSTS: "ldap://host1-ldap/ ldap://host2-ldap/" extra_hosts: - "host2-ldap:host2-IP-addr" volumes: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro - /var/local/dcm4chee-arc/ldap1:/var/lib/openldap/openldap-data - /var/local/dcm4chee-arc/slapd1.d:/etc/openldap/slapd.d mariadb: image: mariadb:10.7.3 ports: - "3306:3306" environment: MYSQL_ROOT_PASSWORD: verys3cret MYSQL_DATABASE: keycloak MYSQL_USER: keycloak MYSQL_PASSWORD: keycloak command: - "--log-bin" - "--log-basename=host1" - "--server-id=1" - "--replicate-do-db=keycloak" - "--auto_increment_increment=2" - "--auto_increment_offset=1" volumes: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro - /var/local/dcm4chee-arc/mysql:/var/lib/mysql keycloak: image: dcm4che/keycloak:25.0.5 logging: driver: json-file options: max-size: "10m" ports: - "8843:8843" - "7600:7600" environment: KC_DB: mariadb KC_DB_URL: jdbc:mariadb://mariadb:3306/keycloak?serverTimezone=Europe/Vienna JGROUPS_DISCOVERY_EXTERNAL_IP: host1 JGROUPS_DISCOVERY_PROTOCOL: TCPPING JGROUPS_DISCOVERY_INITIAL_HOSTS: "host2[7600],host1[7600]" KC_HTTPS_PORT: 8843 KC_HOSTNAME: https://host1-IP-addr:8843 KC_HOSTNAME_BACKCHANNEL_DYNAMIC: 'true' KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN_PASSWORD: changeit KC_LOG: file KEYCLOAK_WAIT_FOR: host1-ldap:389 mariadb:3306 depends_on: - host1-ldap - mariadb extra_hosts: - "ldap:host1-IP-addr" - "host1:host1-IP-addr" - "host2:host2-IP-addr" volumes: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro - /var/local/dcm4chee-arc/keycloak:/opt/keycloak/data db: image: dcm4che/postgres-dcm4chee:16.4-33 logging: driver: json-file options: max-size: "10m" ports: - "5432:5432" env_file: docker-compose.env volumes: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro - /var/local/dcm4chee-arc/db:/var/lib/postgresql/data arc: image: dcm4che/dcm4chee-arc-psql:5.33.0-secure logging: driver: json-file options: max-size: "10m" ports: - "8080:8080" - "8443:8443" - "9990:9990" - "9993:9993" - "11112:11112" - "2575:2575" env_file: docker-compose.env extra_hosts: - "ldap:host1-IP-addr" - "host1:host1-IP-addr" environment: LDAP_URL: "ldap://host1-ldap/" WILDFLY_CHOWN: /opt/wildfly/standalone /storage WILDFLY_WAIT_FOR: host1-ldap:389 db:5432 depends_on: - host1-ldap - keycloak - db volumes: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro - /var/local/dcm4chee-arc/wildfly:/opt/wildfly/standalone - /var/local/dcm4chee-arc/storage:/storage
replace host1-IP-addr and host2-IP-addr by the IP Addresses of the docker hosts where each of the two clusters are running, which must be resolvable by your DNS server.
-
Specify services for second node in a configuration file
docker-compose.yml
(e.g.) :version: "3" services: host2-ldap: image: dcm4che/slapd-dcm4chee:2.6.7-33.0 logging: driver: json-file options: max-size: "10m" ports: - "389:389" env_file: docker-compose.env environment: LDAP_URLS: "ldap://host2-ldap/" LDAP_REPLICATION_HOSTS: "ldap://host1-ldap/ ldap://host2-ldap/" SKIP_INIT_CONFIG: "true" extra_hosts: - "host1-ldap:host1-IP-addr" volumes: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro - /var/local/dcm4chee-arc2/ldap2:/var/lib/openldap/openldap-data - /var/local/dcm4chee-arc2/slapd2.d:/etc/openldap/slapd.d mariadb: image: mariadb:10.7.3 ports: - "3306:3306" environment: MYSQL_ROOT_PASSWORD: verys3cret MYSQL_DATABASE: keycloak MYSQL_USER: keycloak MYSQL_PASSWORD: keycloak command: - "--log-bin" - "--log-basename=host2" - "--server-id=2" - "--replicate-do-db=keycloak" - "--auto_increment_increment=2" - "--auto_increment_offset=2" volumes: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro - /var/local/dcm4chee-arc2/mysql:/var/lib/mysql keycloak: image: dcm4che/keycloak:25.0.5 logging: driver: json-file options: max-size: "10m" ports: - "8843:8843" - "7600:7600" env_file: docker-compose.env environment: KC_DB: mariadb KC_DB_URL: jdbc:mariadb://mariadb:3306/keycloak?serverTimezone=Europe/Vienna JGROUPS_DISCOVERY_EXTERNAL_IP: host2 JGROUPS_DISCOVERY_PROTOCOL: TCPPING JGROUPS_DISCOVERY_INITIAL_HOSTS: "host1[7600],host2[7600]" KC_HTTPS_PORT: 8843 KC_HOSTNAME: host2-IP-addr KC_LOG: file KEYCLOAK_WAIT_FOR: host2-ldap:389 mariadb:3306 depends_on: - host2-ldap - mariadb extra_hosts: - "ldap:host2-IP-addr" - "host2:host2-IP-addr" - "host1:host1-IP-addr" volumes: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro - /var/local/dcm4chee-arc/keycloak:/opt/keycloak/data db: image: dcm4che/postgres-dcm4chee:16.4-33 ports: - "5432:5432" env_file: docker-compose.env volumes: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro - /var/local/dcm4chee-arc2/db:/var/lib/postgresql/data arc: image: dcm4che/dcm4chee-arc-psql:5.33.0-secure ports: - "8080:8080" - "8443:8443" - "9990:9990" - "9993:9993" - "8787:8787" - "11112:11112" - "2575:2575" extra_hosts: - "ldap:host2-IP-addr" - "host2:host2-IP-addr" env_file: docker-compose.env environment: LDAP_URL: "ldap://host2-ldap/" WILDFLY_CHOWN: /opt/wildfly/standalone /storage WILDFLY_WAIT_FOR: host2-ldap:389 db:5432 depends_on: - host2-ldap - keycloak - db volumes: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro - /var/local/dcm4chee-arc2/wildfly:/opt/wildfly/standalone - /var/local/dcm4chee-arc2/storage:/storage
Note:
- Replace host1-IP-addr and host2-IP-addr by the IP Addresses of the docker hosts where each of the two clusters are running, which must be resolvable by your DNS server.
- In LDAP container configuration, we have used
SKIP_INIT_CONFIG: "true"
. This is to start LDAP without any configuration
on second host; on enabling replication as explained in following steps, it shall pull the configuration from first LDAP. - KEYCLOAK_ADMIN / KEYCLOAK_ADMIN_PASSWORD are not required in the keycloak container settings on second node, as the user credentials to authenticate Keycloak's master realm are already setup and initialized by keycloak container settings on first node.
-
Start
host1-ldap
andmariadb
containers on first clusterdocker-compose up -d host1-ldap mariadb
-
Start
host2-ldap
andmariadb
containers on second clusterdocker-compose up -d host2-ldap mariadb
-
You may verify on second cluster, by logging in to its LDAP using Apache Directory Studio, that it is started without any configuration in it, whereas on the LDAP of first cluster, it is initialized with default configuration of archive.
-
Prepare replication for ldap on host 1 as follows :
docker exec <host1-ldap-container-name> prepare-replication
replace with the ldap container name on host1
-
Prepare replication for ldap on host 2 as follows :
docker exec <host2-ldap-container-name> prepare-replication
replace with the ldap container name on host2
-
Enable replication first for ldap on host 2, since we have used
SKIP_INIT_CONFIG: "true"
, as follows :docker exec <host2-ldap-container-name> enable-replication
replace with the ldap container name on host2
-
Enable replication for ldap on host 1 as follows :
docker exec <host1-ldap-container-name> enable-replication
replace with the ldap container name on host1
-
You may test replication of LDAP was successful or not, by logging in to LDAP of second cluster using Apache Directory Studio and verify that it has pulled the configuration from LDAP of first cluster. Alternatively, also change value of some attribute on LDAP of first cluster and verify it gets reflected in LDAP of second cluster.
-
Enable Master to Master Replication between two MariaDB Servers. You may skip points 1 and 4 as we have already started the mariadb containers on the two clusters.
-
Once replications of LDAP and MariaDB are successful, start the
keycloak
,db
(postgres) andarc
containers on first cluster
docker-compose up -d keycloak db arc
- Verify that Keycloak on first cluster is completely started and is up and running by verifying in the server logs as :
2023-01-20 08:35:01,084 INFO [org.infinispan.CLUSTER] (keycloak-cache-init) ISPN000094: Received new cluster view for channel ISPN: [608a234f883d-50184|0] (1) [608a234f883d-50184]
2023-01-20 08:35:01,101 INFO [org.infinispan.CLUSTER] (keycloak-cache-init) ISPN000079: Channel `ISPN` local address is `608a234f883d-50184`, physical addresses are `[172.31.0.4:40072]`
2023-01-20 08:35:45,439 INFO [org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory] (main) Node name: 608a234f883d-50184, Site name: null
2023-01-20 08:35:45,543 INFO [org.keycloak.broker.provider.AbstractIdentityProviderMapper] (main) Registering class org.keycloak.broker.provider.mappersync.ConfigSyncEventListener
2023-01-20 08:35:45,551 INFO [org.keycloak.services] (main) KC-SERVICES0050: Initializing master realm
2023-01-20 08:35:49,777 INFO [org.keycloak.services] (main) KC-SERVICES0004: Imported realm dcm4che from file /opt/keycloak/bin/../data/import/dcm4che-realm.json.
2023-01-20 08:35:50,194 INFO [io.quarkus] (main) Keycloak 21.0.0 on JVM (powered by Quarkus 2.13.3.Final) started in 59.278s. Listening on: https://0.0.0.0:8843
2023-01-20 08:35:50,194 INFO [io.quarkus] (main) Profile prod activated.
2023-01-20 08:35:50,194 INFO [io.quarkus] (main) Installed features: [agroal, cdi, hibernate-orm, jdbc-h2, jdbc-mariadb, jdbc-mssql, jdbc-mysql, jdbc-oracle, jdbc-postgresql, keycloak, logging-gelf, narayana-jta, reactive-routes, resteasy, resteasy-jackson, smallrye-context-propagation, smallrye-health, smallrye-metrics, vault, vertx]
2023-01-20 08:35:50,378 INFO [org.keycloak.services] (main) KC-SERVICES0009: Added user 'admin' to realm 'master'
This is done so that, when Keycloak is started on second cluster, it shall find the dcm4che
realm in the clustered
MariaDB database and shall not override the settings of the same.
- Start the
keycloak
,db
(postgres) andarc
containers on second cluster.
docker-compose up -d keycloak db arc
Verify the logs of Keycloak on second cluster as explained in previous point.
2023-01-20 07:37:34,906 INFO [org.infinispan.CLUSTER] (keycloak-cache-init) ISPN000094: Received new cluster view for channel ISPN: [1d1ff27e148f-58712|0] (1) [1d1ff27e148f-58712]
2023-01-20 07:37:34,909 INFO [org.infinispan.CLUSTER] (keycloak-cache-init) ISPN000079: Channel `ISPN` local address is `1d1ff27e148f-58712`, physical addresses are `[172.25.0.5:60822]`
2023-01-20 07:37:35,259 INFO [org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory] (main) Node name: 1d1ff27e148f-58712, Site name: null
2023-01-20 07:37:35,865 INFO [org.keycloak.services] (main) KC-SERVICES0003: Not importing realm dcm4che from file /opt/keycloak/bin/../data/import/dcm4che-realm.json. It already exists.
2023-01-20 07:37:35,878 INFO [org.keycloak.services] (main) KC-SERVICES0003: Not importing realm dcm4che from file /opt/keycloak/bin/../data/import/dcm4che-realm.json. It already exists.
2023-01-20 07:37:36,176 INFO [io.quarkus] (main) Keycloak 21.0.0 on JVM (powered by Quarkus 2.13.3.Final) started in 9.549s. Listening on: https://0.0.0.0:8843
2023-01-20 07:37:36,176 INFO [io.quarkus] (main) Profile prod activated.
2023-01-20 07:37:36,176 INFO [io.quarkus] (main) Installed features: [agroal, cdi, hibernate-orm, jdbc-h2, jdbc-mariadb, jdbc-mssql, jdbc-mysql, jdbc-oracle, jdbc-postgresql, keycloak, logging-gelf, narayana-jta, reactive-routes, resteasy, resteasy-jackson, smallrye-context-propagation, smallrye-health, smallrye-metrics, vault, vertx]
- Once Keycloak and archive containers have started, proceed to Verify OIDC Client for Archive UI in Keycloak
on Keycloak of first cluster.
Valid Redirect URIs
andWeb Origins
shall contain archive specific URLs of both clusters, (e.g.) :
Root URL : https://host1:8443/dcm4chee-arc/ui2
Admin URL : https://host1:8443/dcm4chee-arc/ui2
Valid Redirect URI : http://host1:8080/dcm4chee-arc/ui2/*
https://host1:8443/dcm4chee-arc/ui2/*
http://host2:8080/dcm4chee-arc/ui2/*
https://host2:8443/dcm4chee-arc/ui2/*
Web Origins : http://host1:8080
https://host1:8443
http://host2:8080
https://host2:8443
- Open two browsers (eg. one Firefox normal and one Firefox in Private mode) simultaneously and login to archive UIs of two nodes.
- On Keycloak admin console, verify that the logged-in user has two sessions.