Documentation

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. Here you’ll create two DXP server containers along with server containers for a database, search engine, and file store.

Here are the server containers you’ll create:

Server Type

Implementation

Container Name

Database

MariaDB

some-mariadb

File Store

DBStore

some-mariadb

Search Engine

Elasticsearch

elasticsearch

DXP Server

Tomcat

dxp-1

DXP Server

Tomcat

dxp-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. Please read all of the Clustering for High Availability articles 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. Please see the compatibility matrix for the database servers your DXP version supports. Please see Database Configuration for Cluster Nodes for more information.

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 character set utf8;
    

    End your database session.

    quit
    

    End your bash session.

    exit
    

Your database server is ready for DXP.

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. Please 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. Please 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:/usr/share/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

The DXP cluster node containers will have these unique settings:

Configuration

dxp-1

dxp-2

AJP port mapping

8009:8009

9009:8009

HTTP port mapping

8080:8080

9080:8080

OSGi container port mapping

11311:11311

11312:11311

Bind mount

$(pwd)/dxp-1:/mnt/liferay

$(pwd)/dxp-2:/mnt/liferay

Cluster Link control channel logic name

control-channel-logic-name-1

control-channel-logic-name-2

Cluster Link transport channel logic name

transport-channel-logic-name-1

transport-channel-logic-name-2

Start the DXP containers.

  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, you’ll 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.3.2-ga3
    

    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.29-ga29
    
  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.3.2-ga3
    

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

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.

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

Initiate indexing DXP content:

  1. Navigating to Control Panel → Configuration → Search.

  2. In the Index Actions tab, click these options:

    • Reindex all search indexes

    • Reindex all spell check indexes

Content indexes into the search engine. Please 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 uses these settings.

Configuration

Description

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

Please see the Env/Portal Property definitions for more information.