#Docker: Custom #JBoss EAP docker image with CLI applied patches and CLI configurations

By | April 11, 2017

In the following I am trying to create a Jboss EAP docker image with up-to-date patches and configurations applied using JBoss CLI.
With this template I can then very easy setup any complex JBoss based application environment with ready defined data sources, connectors , resource adapters etc.
In the following I will describe the steps to create the Dockerfile associated scripts of the image.

STEP 1: Start creation of the Dockerfile from a base image

I will start from the jboss base image with jdk8 available on docker.io


# Use latest jboss/base-jdk:8 image as the base
FROM jboss/base-jdk:8

STEP 2: Add the section to install jboss-EAP 7.0.0 base

The following section will install kits/jboss-eap-7.0.0.zip downloaded from Red-Hat official repository (paid support account needed). Make sure to download the zip archive not the installer.
Place the downloaded kit in a kits directory at the same level with the Dockerfile.
Add the following lines to the Dockerfile


# Set the JBOSS_VERSION env variable
ENV JBOSS_VERSION 7.0.0
ENV JBOSS_HOME /opt/jboss/jboss-eap-7.0

# Copy Jboss kit
COPY kits/jboss-eap-$JBOSS_VERSION.zip $HOME

# Add the JBoss distribution to /opt, and make jboss the owner of the extracted zip content

# Install Jboss
RUN cd /opt/jboss \
    && unzip -qq jboss-eap-$JBOSS_VERSION.zip \
    && rm jboss-eap-$JBOSS_VERSION.zip

# Ensure signals are forwarded to the JVM process correctly for graceful shutdown
ENV LAUNCH_JBOSS_IN_BACKGROUND true

# Expose the ports we're interested in
EXPOSE 8080 8443 8787 9990

The above will set some environment variables like version and home then will copy the jboss-eap kit and install it by unzipping. Note that we also do a clean-up after that.
We set up also an environment variable to “Ensure signals are forwarded to the JVM process correctly for graceful shutdown”.
The last part will expose the ports to the host, where:
– 8080 is the http application port where a deployed application will be listening
– 8443 is the secure https application port where a deployed application will be listening
– 8787 is the application debug port where a deployed application debug will be listening
– 9990 is the http jboss admin console port where jboss administration console will be listening

STEP 3: Prepare to apply the Jboss EAP patches and configurations

Download from the Jboss EAP RedHat repository the latest cumulative patches:
jboss-eap-7.0.2-patch.zip
jboss-eap-7.0.3-patch.zip
jboss-eap-7.0.4-patch.zip
jboss-eap-7.0.5-patch.zip
etc.

Place them in the same kit directory at the same level with the Dockerfile.

For each patch file create a scripts/patch-7.x.x.cli file containing the line:


patch apply /opt/jboss/jboss-eap-7.x.x-patch.zip

To configure the application server environment we are going to use the embed server to create resource adapters, data sources etc.
Create the scripts/setup.cli and scripts/setup.properties which are using the embed-server and CLI code to set up the environment.

A sample scripts/setup.cli defining an Oracle datasource that we will access through hibernate looks like:


set dbConnectionUrl=${db.connection.url}
set dbDsName=${db.ds.name}
set dbUser=${db.user}
set dbPassword=${db.password}
embed-server --server-config=standalone.xml --std-out=echo

# add Oracle datasource
batch
  echo "=============================================================================="
  echo "        ORACLE DATABASE CONFIGURATION                                         "
  echo "=============================================================================="

  data-source add --name=$dbDsName --jndi-name=java:jboss/datasources/$dbDsName --connection-url=$dbConnectionUrl \
                  --driver-class=oracle.jdbc.OracleDriver \
                  --driver-name=OracleJDBCDriver \
                  --transaction-isolation=TRANSACTION_READ_COMMITTED \
                  --user-name=$dbUser \
                  --password=$dbPassword \
                  --valid-connection-checker-class-name=org.jboss.jca.adapters.jdbc.extensions.oracle.OracleValidConnectionChecker \
                  --background-validation=true \
                  --stale-connection-checker-class-name=org.jboss.jca.adapters.jdbc.extensions.oracle.OracleStaleConnectionChecker \
                  --exception-sorter-class-name=org.jboss.jca.adapters.jdbc.extensions.oracle.OracleExceptionSorter \
                  --enabled=true \
                  --use-ccm=true \
                  --jta=true
  /subsystem=datasources/jdbc-driver=OracleJDBCDriver:add(driver-name=OracleJDBCDriver,driver-module-name=oracle.jdbc)
  /subsystem=ee/service=default-bindings:write-attribute(name=datasource, value=java:jboss/datasources/$dbDsName)
  /subsystem=ee/service=default-bindings:write-attribute(name=jms-connection-factory, value=java:/JmsXA)
  /subsystem=ee:write-attribute(name=ear-subdeployments-isolated,value=false)
holdback-batch configure-oracle
batch configure-oracle
run-batch
stop-embedded-server

Content of scripts/setup.properties where variables are defined


db.ds.name=UGAchDS
db.connection.url=jdbc:oracle:thin:@oracle:1521:xe
db.user=ug_ach
db.password=m

STEP 4: Prepare the datasource driver

We define kits/module.zip a binary archive containing the Oracle driver:


# unzip  modules.zip
Archive:  modules.zip
   creating: modules/
   creating: modules/com/
   creating: modules/oracle/
   creating: modules/oracle/jdbc/
   creating: modules/oracle/jdbc/main/
  inflating: modules/oracle/jdbc/main/module.xml
  inflating: modules/oracle/jdbc/main/ojdbc7.jar
   creating: modules/system/layers/base/.overlays/layer-base-jboss-eap-7.0.5.CP/org/hibernate/main/
  inflating: modules/system/layers/base/.overlays/layer-base-jboss-eap-7.0.5.CP/org/hibernate/main/module.xml

Note that under “modules/oracle/jdbc/main/” we have the ojdbc7.jar driver and a module.xml:



<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="oracle.jdbc">
    <resources>
        <resource-root path="ojdbc7.jar"/>
        <!-- Make sure this matches the name of the JAR you are installing -->
    </resources>
    <dependencies>
        <module name="javax.api"/>
        <module name="javax.transaction.api"/>
        <module name="javax.servlet.api" optional="true"/>
    </dependencies>
</module>


Very important note that the hibernate libraries require to have a module.xml under “modules/system/layers/base/.overlays/layer-base-jboss-eap-7.0.5.CP/org/hibernate/main/” that is a relative path given by the latest patch. Do not ask me why is this, I know is stupid but this is how it works.


<?xml version="1.0" encoding="UTF-8"?>
<!-- Represents the Hibernate 5.0.x module-->
<module xmlns="urn:jboss:module:1.3" name="org.hibernate">
    <resources>
        <resource-root path="hibernate-core-5.0.11.Final-redhat-1.jar"/>
        <resource-root path="hibernate-envers-5.0.11.Final-redhat-1.jar"/>
        <resource-root path="hibernate-entitymanager-5.0.11.Final-redhat-1.jar"/>
        <resource-root path="hibernate-java8-5.0.11.Final-redhat-1.jar"/>
    </resources>

    <dependencies>
        <module name="asm.asm"/>
        <module name="com.fasterxml.classmate"/>
        <module name="javax.api"/>
        <module name="javax.annotation.api"/>
        <module name="javax.enterprise.api"/>
        <module name="javax.persistence.api"/>
        <module name="javax.transaction.api"/>
        <module name="javax.validation.api"/>
        <module name="javax.xml.bind.api"/>
        <module name="org.antlr"/>
        <module name="org.dom4j"/>
        <module name="org.javassist"/>
        <module name="org.jboss.as.jpa.spi"/>
        <module name="org.jboss.jandex"/>
        <module name="org.jboss.logging"/>
        <module name="org.jboss.vfs"/>
        <module name="org.hibernate.commons-annotations"/>
        <module name="org.hibernate.infinispan" services="import" optional="true"/>
        <module name="org.hibernate.jipijapa-hibernate5" services="import"/>
        <module name="oracle.jdbc"/>
    </dependencies>
</module>


STEP 5: Prepare the CLI setup script

I created a bash script to apply the patches and the server configurations. Note that several application server restarts are necessary and they are triggered by the script.
The script also waits after each restart for the Jboss server to start, to make sure the CLI interface is ready.

Create the following “setupJboss.sh”


#!/bin/bash

function wait_for_server() {
  until `$JBOSS_HOME/bin/jboss-cli.sh -c ":read-attribute(name=server-state)" 2> /dev/null | grep -q running`; do
    sleep 1
  done
}


echo "Start JBOSS"
$JBOSS_HOME/bin/standalone.sh -c standalone.xml &
echo "=> Waiting for the server to boot"
wait_for_server

echo "Patch Jboss to 7.0.2"
$JBOSS_HOME/bin/jboss-cli.sh --connect --file=/opt/jboss/patch-7.0.2.cli
echo "patch applied"
echo "Restart Jboss"
$JBOSS_HOME/bin/jboss-cli.sh --connect --command=:shutdown
$JBOSS_HOME/bin/standalone.sh -c standalone.xml &
wait_for_server

echo "Patch Jboss to 7.0.3"
$JBOSS_HOME/bin/jboss-cli.sh --connect --file=/opt/jboss/patch-7.0.3.cli
echo "patch applied"
echo "Restart Jboss"
$JBOSS_HOME/bin/jboss-cli.sh --connect --command=:shutdown
$JBOSS_HOME/bin/standalone.sh -c standalone.xml &
wait_for_server

echo "Patch Jboss to 7.0.4"
$JBOSS_HOME/bin/jboss-cli.sh --connect --file=/opt/jboss/patch-7.0.4.cli
echo "patch applied"
echo "Restart Jboss"
$JBOSS_HOME/bin/jboss-cli.sh --connect --command=:shutdown
$JBOSS_HOME/bin/standalone.sh -c standalone.xml &
wait_for_server

echo "Patch Jboss to 7.0.5"
$JBOSS_HOME/bin/jboss-cli.sh --connect --file=/opt/jboss/patch-7.0.5.cli
echo "patch applied"
echo "Restart Jboss"
$JBOSS_HOME/bin/jboss-cli.sh --connect --command=:shutdown
$JBOSS_HOME/bin/standalone.sh -c standalone.xml &
wait_for_server

echo "Clear Jboss patch history"
$JBOSS_HOME/bin/jboss-cli.sh --connect "/core-service=patching:ageout-history"

echo "Apply JBoss configurations"
$JBOSS_HOME/bin/jboss-cli.sh --properties=/opt/jboss/setup.properties --file=/opt/jboss/setup.cli
echo "configuration applied"

echo "Add ApplicationRealm test user, used by external EJB clients"
$JBOSS_HOME/bin/add-user.sh -a test -p testpassword
echo "user test created"

echo "stop JBoss"
$JBOSS_HOME/bin/jboss-cli.sh --connect --command=:shutdown

Note that we have the same block repeated for each patch. If additional patches are released we just have to add a new “patch” block to this script.

STEP 6: Apply the Jboss EAP patches and configurations

Add the following lines to the Dockerfile:


# Copy jboss patches and cli patch scripts
COPY scripts/patch-*.cli /opt/jboss/
COPY kits/jboss-eap-*-patch.zip /opt/jboss/

# Add jboss datasource modules
COPY kits/modules.zip /opt/jboss/jboss-eap-7.0/
RUN cd /opt/jboss/jboss-eap-7.0 \
    && unzip -qq modules.zip \
    && rm modules.zip

# Copy jboss configuration
COPY scripts/setup.cli /opt/jboss/
COPY scripts/setup.properties /opt/jboss/
COPY scripts/setupJboss.sh /opt/jboss/

USER root
RUN chmod +x /opt/jboss/setupJboss.sh
USER jboss

# Start Jboss and apply patches and configurations
RUN /opt/jboss/setupJboss.sh

# Delete applied patch kits and cli files
RUN rm /opt/jboss/jboss-eap-*-patch.zip \
    && rm /opt/jboss/setup.cli \
    && rm /opt/jboss/setupJboss.sh \
    && rm /opt/jboss/patch-*.cli \
    && rm /opt/jboss/setup.properties

STEP 7: The final Dockerfile and conclusions

At this step we add also the Jboss configurations described in Production #Jboss client image that checks if the #Oracle container is initialized with data to have a proper start script.

The final Dockerfile looks like:


# Use latest jboss/base-jdk:8 image as the base
FROM jboss/base-jdk:8

# Set the JBOSS_VERSION env variable
ENV JBOSS_VERSION 7.0.0
ENV JBOSS_HOME /opt/jboss/jboss-eap-7.0

# Copy Jboss kit
COPY kits/jboss-eap-$JBOSS_VERSION.zip $HOME

# Add the JBoss distribution to /opt, and make jboss the owner of the extracted zip content

# Install Jboss
RUN cd /opt/jboss \
    && unzip -qq jboss-eap-$JBOSS_VERSION.zip \
    && rm jboss-eap-$JBOSS_VERSION.zip

# Ensure signals are forwarded to the JVM process correctly for graceful shutdown
ENV LAUNCH_JBOSS_IN_BACKGROUND true

# Expose the ports we're interested in
EXPOSE 8080 8443 8787 9990

# Copy jboss patches and cli patche scripts
COPY scripts/patch-*.cli /opt/jboss/
COPY kits/jboss-eap-*-patch.zip /opt/jboss/

# Add jboss datasource modules
COPY kits/modules.zip /opt/jboss/jboss-eap-7.0/
RUN cd /opt/jboss/jboss-eap-7.0 \
    && unzip -qq modules.zip \
    && rm modules.zip

# Copy jboss configuration
COPY scripts/setup.cli /opt/jboss/
COPY scripts/setup.properties /opt/jboss/
COPY scripts/setupJboss.sh /opt/jboss/

USER root
RUN chmod +x /opt/jboss/setupJboss.sh
USER jboss

# Start Jboss and apply patches and configurations
RUN /opt/jboss/setupJboss.sh

# Delete applied patch kits and cli files
RUN rm /opt/jboss/jboss-eap-*-patch.zip \
    && rm /opt/jboss/setup.cli \
    && rm /opt/jboss/setupJboss.sh \
    && rm /opt/jboss/patch-*.cli \
    && rm /opt/jboss/setup.properties

# Install oracle tester
# User root user to install software
USER root

# Install necessary packages
COPY kits/*.rpm /root/

RUN cd /root && yum -y install oracle-instantclient12.2-basic-12.2.0.1.0-1.x86_64.rpm \
 oracle-instantclient12.2-sqlplus-12.2.0.1.0-1.x86_64.rpm \
 oracle-instantclient12.2-devel-12.2.0.1.0-1.x86_64.rpm \
 libaio-0.3.109-13.el7.x86_64.rpm \
 && yum clean all \
 && rm -f oracle-instantclient12.2* \
 && rm -f libaio-0.3.109-13.el7.x86_64.rpm

COPY scripts/oracle.conf /etc/ld.so.conf.d/oracle.conf
RUN ldconfig


# Switch back to jboss user
USER jboss

# Set the default command to run on boot
# This will test if oracle container is up and then boot JBoss EAP in the standalone mode and bind to all interface
COPY scripts/test.sql /opt/jboss/
COPY scripts/startApplication.sh /opt/jboss/

USER root
RUN chmod +x /opt/jboss/startApplication.sh
USER jboss

CMD /opt/jboss/startApplication.sh


Advertisements