Example: Creating a Simple DXP Cluster

A fast, easy way to learn DXP clustering is to set up a two node DXP cluster environment on one machine using Docker containers. This example creates two DXP server containers along with server containers for a database, search engine, and file store.

Server TypeImplementationContainer Name
DatabaseMariaDBsome-mariadb
File StoreDBStoresome-mariadb
Search EngineElasticsearchelasticsearch
DXP ServerTomcatdxp-1
DXP ServerTomcatdxp-2
Warning

This example is for learning purposes and is not suitable for production use cases. For production environments, you should include an HTTP server for load balancing requests to the DXP servers, use separate database servers for read only and read-write operations, and consider clustering and load balancing database servers, file store servers, and search engine servers. See Clustering for High Availability for more information.

Here are the main steps:

  1. Start a Database Server
  2. Start a File Store Server
  3. Start a Search Engine Server
  4. Configure the Search Engine for Each Node
  5. Start the DXP Cluster
  6. Test the DXP Cluster

Start a Database Server

A DXP cluster requires a data source that’s accessible to all of the DXP cluster nodes. The data source can be a JNDI data source or a direct connection to a database server or a database server cluster. See the compatibility matrix for the database servers your DXP version supports.

Create the database server and DXP database:

  1. Start a MariaDB Docker container.

    docker run --name some-mariadb -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mariadb:10.2
    
  2. In a shell on the container, create the DXP database.

    Sign in to the database server.

    docker exec -it some-mariadb bash -c "/usr/bin/mysql -uroot -pmy-secret-pw"
    

    Create a database for DXP.

    create database dxp_db default character set utf8mb4 collate utf8mb4_unicode_ci;
    

    End your database session.

    quit
    

    End your bash session.

    exit
    

Your database server is ready for DXP. See Database Configuration for Cluster Nodes for more information.

Start a File Store Server

A DXP cluster requires a File Store accessible to all of the DXP cluster nodes. For convenience, this example uses a DBStore File Store configured on the DXP database. The database server already started in this example includes the File Store. See Configuring File Storage for information on configuring all File Store types.

Start a Search Engine Server

A DXP cluster requires a search engine (running as a separate process) accessible to all of the DXP cluster nodes. See Installing a Search Engine for more information.

Create and configure an Elasticsearch server:

  1. Set a local folder for storing an Elasticsearch server’s data volume. For example,

    mkdir -p elasticsearch/es_data_volume
    
  2. Start an Elasticsearch container named elasticsearch.

    docker run -it --name elasticsearch -p 9200:9200 -p 9300:9300 -e cluster.name=LiferayElasticsearchCluster -e ES_JAVA_OPTS="-Xms512m -Xmx512m" -v $(pwd)/elasticsearch/es_data_volume:/var/lib/elasticsearch/data elasticsearch:6.8.7
    
    Note

    If the container reports max virtual memory areas vm.max_map_count [xxxxx] is too low, increase to at least [xxxxxx], then set vm.max_map_count to a sufficient value using a command like this one: sudo sysctl -w vm.max_map_count=[xxxxxx]. Then start the container.

  3. Install the required Elasticsearch plugins.

    docker exec -it elasticsearch bash -c '/usr/share/elasticsearch/bin/elasticsearch-plugin install analysis-icu && /usr/share/elasticsearch/bin/elasticsearch-plugin install analysis-kuromoji && /usr/share/elasticsearch/bin/elasticsearch-plugin install analysis-smartcn && /usr/share/elasticsearch/bin/elasticsearch-plugin install analysis-stempel'
    

Your search engine is ready to manage search indexes.

Configure the Search Engine Server For Each Node

Use Configuration Files to configure Elasticsearch for each DXP node:

  1. Create the Configuration Files locations.

    mkdir -p dxp-1/files/osgi/configs dxp-2/files/osgi/configs
    
  2. Configure Elasticsearch for the dxp-1 server node.

    cat <<EOT >> dxp-1/files/osgi/configs/com.liferay.portal.search.elasticsearch6.configuration.ElasticsearchConfiguration.config
    operationMode="REMOTE"
    transportAddresses="elasticsearch:9300"
    clusterName="LiferayElasticsearchCluster"
    EOT
    
  3. Configure Elasticsearch for the dxp-2 server node.

    cat <<EOT >> dxp-2/files/osgi/configs/com.liferay.portal.search.elasticsearch6.configuration.ElasticsearchConfiguration.config
    operationMode="REMOTE"
    transportAddresses="elasticsearch:9300"
    clusterName="LiferayElasticsearchCluster"
    EOT
    

You’ll make these configuration files accessible to the cluster nodes via bind mounts on the DXP server containers.

Note

The docker run --add-host elasticsearch:[ip] ... commands used later for the DXP servers add /etc/hosts/ entries that map the name elasticsearch to the Elasticsearch server host IP address.

Start the DXP Cluster

  1. Get the container IP addresses for the elasticsearch and some-mariadb containers by executing the docker network inspect bridge command. The bridge network is the default network.

    Important

    In the docker run commands that follow, replace [IP address] with the elasticsearch and some-mariadb container IP addresses.

  2. Start dxp-1.

    Command expanded for readability:

    docker run -it \
       --add-host elasticsearch:[IP address] \
       --add-host some-mariadb:[IP address] \
       -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_JNDI_PERIOD_NAME="" \
       -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_DRIVER_UPPERCASEC_LASS_UPPERCASEN_AME=org.mariadb.jdbc.Driver \
       -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_URL="jdbc:mariadb://some-mariadb:3306/dxp_db?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false" \
       -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_USERNAME=root \
       -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_PASSWORD=my-secret-pw \
       -e LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_ENABLED=true \
       -e LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_CHANNEL_PERIOD_LOGIC_PERIOD_NAME_PERIOD_CONTROL=control-channel-logic-name-1 \
       -e LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_CHANNEL_PERIOD_LOGIC_PERIOD_NAME_PERIOD_TRANSPORT_PERIOD_NUMBER0=transport-channel-logic-name-1 \
       -e LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_AUTODETECT_PERIOD_ADDRESS=some-mariadb:3306 \
       -e LIFERAY_WEB_PERIOD_SERVER_PERIOD_DISPLAY_PERIOD_NODE=true \
       -e LIFERAY_DL_PERIOD_STORE_PERIOD_IMPL=com.liferay.portal.store.db.DBStore \
       --name dxp-1 \
       -p 11311:11311 \
       -p 8009:8009 \
       -p 8080:8080 \
       -v $(pwd)/dxp-1:/mnt/liferay \
       liferay/portal:7.4.3.112-ga112
    

    Command condensed to one line:

    docker run -it --name dxp-1 --add-host elasticsearch:[IP address] --add-host some-mariadb:[IP address] -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_JNDI_PERIOD_NAME="" -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_DRIVER_UPPERCASEC_LASS_UPPERCASEN_AME=org.mariadb.jdbc.Driver -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_URL="jdbc:mariadb://some-mariadb:3306/dxp_db?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false" -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_USERNAME=root -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_PASSWORD=my-secret-pw -e LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_ENABLED=true -e LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_CHANNEL_PERIOD_LOGIC_PERIOD_NAME_PERIOD_CONTROL=control-channel-logic-name-1 -e LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_CHANNEL_PERIOD_LOGIC_PERIOD_NAME_PERIOD_TRANSPORT_PERIOD_NUMBER0=transport-channel-logic-name-1 -e LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_AUTODETECT_PERIOD_ADDRESS=some-mariadb:3306 -e LIFERAY_WEB_PERIOD_SERVER_PERIOD_DISPLAY_PERIOD_NODE=true -e LIFERAY_DL_PERIOD_STORE_PERIOD_IMPL=com.liferay.portal.store.db.DBStore --name dxp-1 -p 11311:11311 -p 8009:8009 -p 8080:8080 -v $(pwd)/dxp-1:/mnt/liferay liferay/portal:7.4.3.112-ga112
    
  3. Start dxp-2.

    Command expanded for readability:

    docker run -it \
       --add-host elasticsearch:[IP address] \
       --add-host some-mariadb:[IP address] \
       -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_JNDI_PERIOD_NAME="" \
       -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_DRIVER_UPPERCASEC_LASS_UPPERCASEN_AME=org.mariadb.jdbc.Driver \
       -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_URL="jdbc:mariadb://some-mariadb:3306/dxp_db?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false" \
       -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_USERNAME=root \
       -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_PASSWORD=my-secret-pw \
       -e LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_ENABLED=true \
       -e LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_CHANNEL_PERIOD_LOGIC_PERIOD_NAME_PERIOD_CONTROL=control-channel-logic-name-2 \
       -e LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_CHANNEL_PERIOD_LOGIC_PERIOD_NAME_PERIOD_TRANSPORT_PERIOD_NUMBER0=transport-channel-logic-name-2 \
       -e LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_AUTODETECT_PERIOD_ADDRESS=some-mariadb:3306 \
       -e LIFERAY_WEB_PERIOD_SERVER_PERIOD_DISPLAY_PERIOD_NODE=true \
       -e LIFERAY_DL_PERIOD_STORE_PERIOD_IMPL=com.liferay.portal.store.db.DBStore \
       --name dxp-2 \
       -p 11312:11311 \
       -p 9009:8009 \
       -p 9080:8080 \
       -v $(pwd)/dxp-2:/mnt/liferay \
       liferay/portal:7.4.3.112-ga112
    

    Command condensed to one line:

    docker run -it --add-host elasticsearch:[IP address] --add-host some-mariadb:[IP address] -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_JNDI_PERIOD_NAME="" -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_DRIVER_UPPERCASEC_LASS_UPPERCASEN_AME=org.mariadb.jdbc.Driver -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_URL="jdbc:mariadb://some-mariadb:3306/dxp_db?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false" -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_USERNAME=root -e LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_PASSWORD=my-secret-pw -e LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_ENABLED=true -e LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_CHANNEL_PERIOD_LOGIC_PERIOD_NAME_PERIOD_CONTROL=control-channel-logic-name-2 -e LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_CHANNEL_PERIOD_LOGIC_PERIOD_NAME_PERIOD_TRANSPORT_PERIOD_NUMBER0=transport-channel-logic-name-2 -e LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_AUTODETECT_PERIOD_ADDRESS=some-mariadb:3306 -e LIFERAY_WEB_PERIOD_SERVER_PERIOD_DISPLAY_PERIOD_NODE=true -e LIFERAY_DL_PERIOD_STORE_PERIOD_IMPL=com.liferay.portal.store.db.DBStore --name dxp-2 -p 11312:11311 -p 9009:8009 -p 9080:8080 -v $(pwd)/dxp-2:/mnt/liferay liferay/portal:7.4.3.112-ga112
    

The --add-host [domain]:[IP address] options add /etc/hosts file entries that map the domain names to the IP addresses. This enables configurations (such as environment variables, portal properties, and .config files) to refer to the servers by domain name.

The -e [variable]=[value] options set the DXP container environment variables. See Appendix A: Environment Settings for more information.

The DXP cluster node containers have these unique settings:

Configurationdxp-1dxp-2
AJP port mapping8009:80099009:8009
HTTP port mapping8080:80809080:8080
OSGi container port mapping11311:1131111312:11311
Bind mount$(pwd)/dxp-1:/mnt/liferay$(pwd)/dxp-2:/mnt/liferay
Cluster Link control channel logic namecontrol-channel-logic-name-1control-channel-logic-name-2
Cluster Link transport channel logic nametransport-channel-logic-name-1transport-channel-logic-name-2

Visit the DXP Nodes

The DXP cluster nodes are available at the following URLs:

  • DXP-1: http://localhost:8080
  • DXP-2: http://localhost:9080

The figure below shows the cluster node home pages.

DXP cluster nodes.

Each node’s container ID and port (Node: [id]:[port]) appear at the bottom of each page. The LIFERAY_WEB_PERIOD_SERVER_PERIOD_DISPLAY_PERIOD_NODE=true environment setting enabled this display feature. You can find a container’s ID using the docker container ls command.

Index the Content into the Search Engine

  1. Navigate to Control PanelConfigurationSearch.

  2. In the Index Actions tab, click Reindex all search indexes and Reindex all spell check indexes.

Content indexes into the search engine. See Search Overview for more information.

Test the DXP Cluster

Test data synchronization between the nodes:

  1. Add content to one of the cluster nodes. For example, add a new Widget Page called New Stuff and add the Language Selector widget to it.

  2. Refresh the UI on the other cluster node.

Both nodes show the same new content.

Content is synchronized between the cluster nodes.

Congratulations on creating a working DXP cluster!

What’s Next

Configure your database for your DXP cluster.

Appendix A: Environment Settings

The example DXP server containers use these settings.

ConfigurationDescription
LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_JNDI_PERIOD_NAME=Data source JNDI name
LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_DRIVER_UPPERCASEC_LASS\
_UPPERCASEN_AME=\
org.mariadb.jdbc.Driver
Database driver class
LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_URL=\
jdbc:mariadb://some-mariadb:3306/dxp_db?\
useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false
Data source URL
LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_USERNAME=\
root
Database admin user name
LIFERAY_JDBC_PERIOD_DEFAULT_PERIOD_PASSWORD=\
my-secret-pw
Database admin user password
LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_ENABLED=\
true
Enables Cluster Link
LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_CHANNEL_PERIOD_LOGIC_PERIOD_NAME\
_PERIOD_CONTROL=\
control-channel-logic-name-2
The cluster node’s unique control channel name
LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_CHANNEL_PERIOD_LOGIC_PERIOD_NAME\
_PERIOD_TRANSPORT_PERIOD_NUMBER0=\
transport-channel-logic-name-2
The cluster node’s unique transport channel name
LIFERAY_CLUSTER_PERIOD_LINK_PERIOD_AUTODETECT_PERIOD_ADDRESS=\
some-mariadb:3306
Known address to ping to get cluster node addresses
LIFERAY_WEB_PERIOD_SERVER_PERIOD_DISPLAY_PERIOD_NODE=\
true
Displays the server address and web server port
LIFERAY_DL_PERIOD_STORE_PERIOD_IMPL=\
com.liferay.portal.store.db.DBStore
File Store (Document Library Store) class

See the Env/Portal Property definitions for more information.

Capabilities

Product

Contact Us

Connect

Powered by Liferay
© 2024 Liferay Inc. All Rights Reserved • Privacy Policy