Baris Can Vural

Feb 28, 2020

Deploy brXM authoring and delivery apps separately with Docker-compose or Kubernetes

Typically Bloomreach Experience Manager's authoring (cms) and delivery (site) applications are deployed together on the same application server. However there are use cases where deploying the authoring and delivery applications separately on different application servers is preferable. How to achieve this is described in this documentation page.

Starting from version 13.2.x, brXM comes with docker support. There’s a dedicated “docker.build” Maven profile for building a docker image and “docker.run” profile for running the image locally. 

What if you want to deploy authoring and delivery applications separately, using docker and Kubernetes? How does one go about creating these separate docker images? In this article I’ll go through the project I prepared for this purpose. The project is at https://github.com/bcanvural/brxm-separate-authoring-delivery

 

Check out the project. In the main pom.xml, you’ll notice two additional docker profiles called “docker.authoring” and “docker.delivery”. These profiles are meant to be used like the following:

mvn clean install && mvn -Pdocker.build,docker.authoring
mvn clean install && mvn -Pdocker.build,docker.delivery

We activate both the “docker.build” and “docker.authoring” (or docker.delivery) profiles together.

 

Let’s first check the docker.authoring profile:

      <profile>
            <id>docker.authoring</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>io.fabric8</groupId>
                        <artifactId>docker-maven-plugin</artifactId>
<!--                        <executions>-->
<!--                            <execution>-->
<!--                                <id>Push the docker image</id>-->
<!--                                <phase>initialize</phase>-->
<!--                                <goals>-->
<!--                                    <goal>push</goal>-->
<!--                                </goals>-->
<!--                            </execution>-->
<!--                        </executions>-->
                        <configuration>
                            <images combine.children="override">
                                <image>
                                    <name>bcanvural/${project.artifactId}-authoring:${project.version}</name>
                                    <alias>${project.artifactId}-authoring</alias>
                                    <build>
                                   <dockerFileDir>${project.basedir}/src/main/docker</dockerFileDir>
                                        <assembly>
                                            <mode>dir</mode>
                                            <name>maven/</name>
                                            <descriptor>${project.basedir}/src/main/docker/assembly/distribution-docker.xml</descriptor>
                                        </assembly>
                                    </build>
                                </image>
                            </images>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>

 

  • In the <configuration> section we override the default <configuration> coming from the “docker.build” profile (remember we activate two profiles at the same time!)

  • In the build section we specify the dockerfile directory we want to use. Note that for this “docker.authoring” profile we continue to use the default dockerfile and assembly configuration

  • There’s a commented out section in the configuration which, when commented in, pushes the image to a public image registry. I left it there for convenience.

 

Now let’s take a look at the “docker.delivery” profile which is more interesting:

       <profile>
            <id>docker.delivery</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>io.fabric8</groupId>
                        <artifactId>docker-maven-plugin</artifactId>
<!--                        <executions>-->
<!--                            <execution>-->
<!--                                <id>Push the docker image</id>-->
<!--                                <phase>initialize</phase>-->
<!--                                <goals>-->
<!--                                    <goal>push</goal>-->
<!--                                </goals>-->
<!--                            </execution>-->
<!--                        </executions>-->
                        <configuration>
                            <images combine.children="override">
                                <image>
                                    <name>bcanvural/${project.artifactId}-delivery:${project.version}</name>
                                    <alias>${project.artifactId}-delivery</alias>
                                    <build>
                                        <dockerFileDir>${project.basedir}/src/main/docker/separate-deployment</dockerFileDir>
                                        <assembly>
                                            <mode>dir</mode>
                                            <name>maven/</name>
                                            <descriptor>${project.basedir}/src/main/docker/assembly/distribution-without-cms.xml</descriptor>
                                        </assembly>
                                    </build>
                                </image>
                            </images>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>

 

  • Just like the “docker.authoring” profile we override the default <configuration> from the “docker.build” profile (remember we activate two profiles at the same time!)

  • We override the dockerfile directory location. Check that at src/main/docker/separate-deployment there’s a separate dockerfile for delivery deployment.

  • We use a different assembly configuration here. It is at ${project.basedir}/src/main/docker/assembly/distribution-without-cms.xml

 

Let’s take a look at this assembly file now (distribution-without-cms.xml)

<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
    <id>distribution-without-cms</id>
    <formats>
        <format>tar.gz</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    <componentDescriptors>
        <componentDescriptor>conf-component.xml</componentDescriptor>
        <componentDescriptor>webapps-without-cms-component.xml</componentDescriptor>
        <componentDescriptor>common-lib-component.xml</componentDescriptor>
        <componentDescriptor>shared-lib-component.xml</componentDescriptor>
    </componentDescriptors>
</assembly>

 

 

Now let’s take a look at that file at src/main/assembly/webapps-without-cms-component.xml :

<component xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/component/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/component/1.1.2 http://maven.apache.org/xsd/component-1.1.2.xsd">
    <files>
        <file>
            <source>platform/target/platform.war</source>
            <outputDirectory>webapps</outputDirectory>
            <destName>platform.war</destName>
        </file>
        <file>
            <source>site/webapp/target/site.war</source>
            <outputDirectory>webapps</outputDirectory>
            <destName>site.war</destName>
        </file>
    </files>
    <dependencySets>
        <dependencySet>
            <useProjectArtifact>false</useProjectArtifact>
            <outputDirectory>webapps</outputDirectory>
            <outputFileNameMapping>bpm.war</outputFileNameMapping>
            <scope>provided</scope>
            <includes>
                <include>com.onehippo.cms7:hippo-addon-wpm-camunda:war</include>
            </includes>
        </dependencySet>
    </dependencySets>
</component>

 

 

This is all we need to build separate docker images for authoring and delivery apps. Now if we want to run these images we have to pass a series of environment variables to these images. Otherwise they won’t start properly and fail.

 

Now let’s take a look at the docker-compose.yaml file in docker-compose folder to see how one can use these images. Docker compose configuration looks more compact than kubernetes configuration, which will help us understand easier how to run these docker images.

version: '3.4'
services:
  authoring:
    image: bcanvural/myproject-authoring:0.1.0-SNAPSHOT
    ports:
      - "8081:8080"
    links:
      - db
    environment:
      profile: postgres
      POSTGRES_DB_HOST: db
      POSTGRES_DB_PORT: 5432
      POSTGRES_DB_USER: postgres
      POSTGRES_DB_PASSWORD: mypass
      POSTGRES_DB_NAME: brxmdb
      POSTGRES_WPM_DB_NAME: wpmdb
      POSTGRES_DB_DRIVER: org.postgresql.Driver
      REPO_WORKSPACE_BUNDLE_CACHE: 256
      REPO_VERSIONING_BUNDLE_CACHE: 64
      REPO_BOOTSTRAP: "true"
    depends_on:
      - db
  delivery:
    image: bcanvural/myproject-delivery:0.1.0-SNAPSHOT
    user: root
    ports:
      - "8082:8080"
      - "5005:5005"
    links:
      - db
    environment:
      profile: postgres
      POSTGRES_DB_HOST: db
      POSTGRES_DB_PORT: 5432
      POSTGRES_DB_USER: postgres
      POSTGRES_DB_PASSWORD: mypass
      POSTGRES_DB_NAME: brxmdb
      POSTGRES_WPM_DB_NAME: wpmdb
      POSTGRES_DB_DRIVER: org.postgresql.Driver
      REPO_WORKSPACE_BUNDLE_CACHE: 256
      REPO_VERSIONING_BUNDLE_CACHE: 64
      REPO_BOOTSTRAP: "false"
      JAVA_ENABLE_DEBUG: "true"
    volumes:
      - ./curl_till_200.sh:/brxm/curl_till_200.sh
    depends_on:
      - db
      - authoring
    entrypoint: ["sh", "-c", "/brxm/curl_till_200.sh && /brxm/bin/docker-entrypoint.sh"]
  db:
    image: postgres
    restart: always
    ports:
      - "5432:5432"
    environment:
      POSTGRES_PASSWORD: mypass
      POSTGRES_USER: postgres
    volumes:
      - ./brxmdb.sql:/docker-entrypoint-initdb.d/brxmdb.sql
      - ./wpmdb.sql:/docker-entrypoint-initdb.d/wpmdb.sql


 

  • One can run this docker compose file with “docker-compose up”

  • There are 3 services deployed together on the same network: “authoring”, “delivery” and “db”

  • The image properties for each service should match the image name we used in respective docker build profiles

  • There are two database init scripts: brxmdb.sql and wpmdb.sql being injected into a specific location in the db image. The creation of databases is a prerequisite for deploying brXM.

  • The delivery image should be deployed after authoring is deployed. This is important! To make sure this happens, there’s a bash script called “curl_till_200.sh” injected into the delivery image which makes sure authoring service is up before the delivery image’s tomcat is started.

  • Inspect the environment variables required for each service. Note that for delivery, the REPO_BOOTSTRAP is false!

  • The authoring is reachable at localhost:8081/cms and delivery is available at localhost:8082/site

  • Remote debugging for delivery is enabled at port 5005. This is not enabled ootb in brxm. Check the relevant part in src/main/docker/scripts/tomcat/setenv.sh to achieve that in your own project

 

The configuration for Kubernetes is very similar conceptually. The deployment yamls can be found in Kubernetes folder with names authoring-deployment.yaml and delivery-deployment.yaml respectively. Note that before these can be used the db should be set up using the setup_dbs.sh script.

In this article we covered how to:

  • Build separate docker images for authoring and delivery webapps.

  • Pass the right environment variables to each image.