Cluster Keycloak with MariaDB and LDAP using Docker Standalone - amsalama/dcm4chee-arc-light GitHub Wiki

Cluster Keycloak with MariaDB and LDAP using Docker Standalone

s.o.:

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.

  1. Create docker-compose.env for first and second clusters as follows :

    STORAGE_DIR=/storage/fs1
    POSTGRES_DB=pacsdb
    POSTGRES_USER=pacs
    POSTGRES_PASSWORD=pacs
    AUTH_SERVER_URL=http://keycloak:8880/auth
    

    Note the AUTH_SERVER_URL here uses keycloak in its hostname. On the first node, map this host keycloak 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.

  2. Specify services for first node in a configuration file docker-compose.yml (e.g.) :

    version: "3"
    services:
      host1-ldap:
        image: dcm4che/slapd-dcm4chee:2.4.48-22.2
        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:10.0.1
        ports:
          - "8880:8880"
          - "8843:8843"
          - "8990:8990"
          - "8993:8993"
          - "7600:7600"
        environment:
          DB_VENDOR: mysql
          KEYCLOAK_DB_CONNECTION_URL: jdbc:mysql://mariadb:3306/keycloak?serverTimezone=Europe/Vienna
          JGROUPS_DISCOVERY_EXTERNAL_IP: host1
          JGROUPS_DISCOVERY_PROTOCOL: TCPPING
          JGROUPS_DISCOVERY_INITIAL_HOSTS: "host2[7600],host1[7600]"
          HTTP_PORT: 8880
          HTTPS_PORT: 8843
          MANAGEMENT_HTTP_PORT: 8990
          MANAGEMENT_HTTPS_PORT: 8993
          KEYCLOAK_WAIT_FOR: host1-ldap:389 mariadb:3306
          JAVA_OPTS: -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -agentlib:jdwp=transport=dt_socket,address=*:8887,server=y,suspend=n
        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/standalone
      db:
        image: dcm4che/postgres-dcm4chee:12.2-22
        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.22.2-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:
          WILDFLY_CHOWN: /opt/wildfly/standalone /storage
          WILDFLY_WAIT_FOR: host1-ldap:389 db:5432
          JAVA_OPTS: -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -agentlib:jdwp=transport=dt_socket,address=*:8787,server=y,suspend=n
        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.

  3. Specify services for second node in a configuration file docker-compose.yml (e.g.) :

    version: "3"
    services:
      host2-ldap:
        image: dcm4che/slapd-dcm4chee:2.4.48-22.2
        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:10.0.1
        ports:
          - "8880:8880"
          - "8843:8843"
          - "8990:8990"
          - "8993:8993"
        env_file: docker-compose.env
        environment:
          DB_VENDOR: mysql
          KEYCLOAK_DB_CONNECTION_URL: jdbc:mysql://mariadb:3306/keycloak?serverTimezone=Europe/Vienna
          JGROUPS_DISCOVERY_EXTERNAL_IP: host2
          JGROUPS_DISCOVERY_PROTOCOL: TCPPING
          JGROUPS_DISCOVERY_INITIAL_HOSTS: "host1[7600],host2[7600]"
          HTTP_PORT: 8880
          HTTPS_PORT: 8843
          MANAGEMENT_HTTP_PORT: 8990
          MANAGEMENT_HTTPS_PORT: 8993
          VALIDATE_PASSWORD_POLICY: "true"
          KEYCLOAK_WAIT_FOR: host2-ldap:389 mariadb:3306
          JAVA_OPTS: -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -agentlib:jdwp=transport=dt_socket,address=*:8887,server=y,suspend=n
        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-arc2/keycloak:/opt/keycloak/standalone
      db:
        image: dcm4che/postgres-dcm4chee:12.2-22
        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.22.2-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:
          WILDFLY_CHOWN: /opt/wildfly/standalone /storage
          WILDFLY_WAIT_FOR: host2-ldap:389 db:5432
          JAVA_OPTS: -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -agentlib:jdwp=transport=dt_socket,address=*:8787,server=y,suspend=n
        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
    

    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. Note: 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.

  4. Start host1-ldap and mariadb containers on first cluster

    docker-compose up -d host1-ldap mariadb
    
  5. Start host2-ldap and mariadb containers on second cluster

    docker-compose up -d host2-ldap mariadb
    
  6. 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.

  7. 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

  8. 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

  9. 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

  10. 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

  1. 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.

  2. 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.

  3. Once replications of LDAP and MariaDB are successful, start the keycloak, db (postgres) and arc containers on first cluster

docker-compose up -d keycloak db arc
  1. Verify that Keycloak on first cluster is completely started and is up and running by verifying in the server logs as :
2019-11-25 16:49:29,459 INFO  [org.jboss.as.server] (ServerService Thread Pool -- 60) WFLYSRV0010: Deployed "keycloak-server.war" (runtime-name : "keycloak-server.war")
2019-11-25 16:49:29,553 INFO  [org.jboss.as.server] (Controller Boot Thread) WFLYSRV0212: Resuming server
2019-11-25 16:49:29,558 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0062: Http management interface listening on http://172.22.0.4:8990/management and https://172.22.0.4:8993/management
2019-11-25 16:49:29,558 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0053: Admin console listening on http://172.22.0.4:8990 and https://172.22.0.4:8993
2019-11-25 16:49:29,559 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: Keycloak 10.0.1 (WildFly Core 11.1.1.Final) started in 49640ms - Started 686 of 974 services (682 services are lazy, passive or on-demand)

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.

  1. Start the keycloak, db (postgres) and arc containers on second cluster.
docker-compose up -d keycloak db arc

Verify the logs of Keycloak on second cluster as explained in previous point.

  1. In the server logs of Keycloak on both sides, one shall also eventually see that the Keycloaks of the two clusters have discovered each other, as follows :
2019-11-25 16:51:09,920 INFO  [org.infinispan.CLUSTER] (thread-11,ejb,3ff29e403864) ISPN000093: Received new, MERGED cluster view for channel ejb: MergeView::[cd93fdec978e|1] (2) [cd93fdec978e, 3ff29e403864], 2 subgroups: [3ff29e403864|0] (1) [3ff29e403864], [cd93fdec978e|0] (1) [cd93fdec978e]
2019-11-25 16:51:09,923 INFO  [org.infinispan.CLUSTER] (thread-11,ejb,3ff29e403864) ISPN100000: Node cd93fdec978e joined the cluster
2019-11-25 16:51:09,924 INFO  [org.infinispan.CLUSTER] (thread-11,ejb,3ff29e403864) ISPN000093: Received new, MERGED cluster view for channel ejb: MergeView::[cd93fdec978e|1] (2) [cd93fdec978e, 3ff29e403864], 2 subgroups: [3ff29e403864|0] (1) [3ff29e403864], [cd93fdec978e|0] (1) [cd93fdec978e]
2019-11-25 16:51:09,925 INFO  [org.infinispan.CLUSTER] (thread-11,ejb,3ff29e403864) ISPN100000: Node cd93fdec978e joined the cluster
2019-11-25 16:51:09,925 INFO  [org.infinispan.CLUSTER] (thread-11,ejb,3ff29e403864) ISPN000093: Received new, MERGED cluster view for channel ejb: MergeView::[cd93fdec978e|1] (2) [cd93fdec978e, 3ff29e403864], 2 subgroups: [3ff29e403864|0] (1) [3ff29e403864], [cd93fdec978e|0] (1) [cd93fdec978e]
2019-11-25 16:51:09,926 INFO  [org.infinispan.CLUSTER] (thread-11,ejb,3ff29e403864) ISPN100000: Node cd93fdec978e joined the cluster
2019-11-25 16:51:09,926 INFO  [org.infinispan.CLUSTER] (thread-11,ejb,3ff29e403864) ISPN000093: Received new, MERGED cluster view for channel ejb: MergeView::[cd93fdec978e|1] (2) [cd93fdec978e, 3ff29e403864], 2 subgroups: [3ff29e403864|0] (1) [3ff29e403864], [cd93fdec978e|0] (1) [cd93fdec978e]
2019-11-25 16:51:09,926 INFO  [org.infinispan.CLUSTER] (thread-11,ejb,3ff29e403864) ISPN100000: Node cd93fdec978e joined the cluster
2019-11-25 16:51:09,931 INFO  [org.infinispan.CLUSTER] (thread-11,ejb,3ff29e403864) ISPN000093: Received new, MERGED cluster view for channel ejb: MergeView::[cd93fdec978e|1] (2) [cd93fdec978e, 3ff29e403864], 2 subgroups: [3ff29e403864|0] (1) [3ff29e403864], [cd93fdec978e|0] (1) [cd93fdec978e]
2019-11-25 16:51:09,932 INFO  [org.infinispan.CLUSTER] (thread-11,ejb,3ff29e403864) ISPN100000: Node cd93fdec978e joined the cluster 

and

2019-11-25 16:51:09,963 INFO  [org.infinispan.CLUSTER] (thread-13,ejb,cd93fdec978e) ISPN000093: Received new, MERGED cluster view for channel ejb: MergeView::[cd93fdec978e|1] (2) [cd93fdec978e, 3ff29e403864], 2 subgroups: [3ff29e403864|0] (1) [3ff29e403864], [cd93fdec978e|0] (1) [cd93fdec978e]
2019-11-25 16:51:09,964 INFO  [org.infinispan.CLUSTER] (thread-13,ejb,cd93fdec978e) ISPN100000: Node 3ff29e403864 joined the cluster
2019-11-25 16:51:09,965 INFO  [org.infinispan.CLUSTER] (thread-13,ejb,cd93fdec978e) ISPN000093: Received new, MERGED cluster view for channel ejb: MergeView::[cd93fdec978e|1] (2) [cd93fdec978e, 3ff29e403864], 2 subgroups: [3ff29e403864|0] (1) [3ff29e403864], [cd93fdec978e|0] (1) [cd93fdec978e]
2019-11-25 16:51:09,965 INFO  [org.infinispan.CLUSTER] (thread-13,ejb,cd93fdec978e) ISPN100000: Node 3ff29e403864 joined the cluster
2019-11-25 16:51:09,966 INFO  [org.infinispan.CLUSTER] (thread-13,ejb,cd93fdec978e) ISPN000093: Received new, MERGED cluster view for channel ejb: MergeView::[cd93fdec978e|1] (2) [cd93fdec978e, 3ff29e403864], 2 subgroups: [3ff29e403864|0] (1) [3ff29e403864], [cd93fdec978e|0] (1) [cd93fdec978e]
2019-11-25 16:51:09,966 INFO  [org.infinispan.CLUSTER] (thread-13,ejb,cd93fdec978e) ISPN100000: Node 3ff29e403864 joined the cluster
2019-11-25 16:51:09,966 INFO  [org.infinispan.CLUSTER] (thread-13,ejb,cd93fdec978e) ISPN000093: Received new, MERGED cluster view for channel ejb: MergeView::[cd93fdec978e|1] (2) [cd93fdec978e, 3ff29e403864], 2 subgroups: [3ff29e403864|0] (1) [3ff29e403864], [cd93fdec978e|0] (1) [cd93fdec978e]
2019-11-25 16:51:09,966 INFO  [org.infinispan.CLUSTER] (thread-13,ejb,cd93fdec978e) ISPN100000: Node 3ff29e403864 joined the cluster
2019-11-25 16:51:09,967 INFO  [org.infinispan.CLUSTER] (thread-13,ejb,cd93fdec978e) ISPN000093: Received new, MERGED cluster view for channel ejb: MergeView::[cd93fdec978e|1] (2) [cd93fdec978e, 3ff29e403864], 2 subgroups: [3ff29e403864|0] (1) [3ff29e403864], [cd93fdec978e|0] (1) [cd93fdec978e]
2019-11-25 16:51:09,967 INFO  [org.infinispan.CLUSTER] (thread-13,ejb,cd93fdec978e) ISPN100000: Node 3ff29e403864 joined the cluster
  1. Once clustering of Keycloaks has been verified, proceed to Register Archive UI as OIDC Client in Keycloak on Keycloak of first cluster. Note that the Valid Redirect URIs and Web Origins it shall contain URLs of both sides, (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
  1. Login to Archive UI of first cluster and verify that one is able to login. Logout and verify one is able to login on Archive UI of second cluster as well.