#Docker: Production #Jboss client image that checks if the #Oracle container is initialized with data

By | April 7, 2017

Everybody that works with big vendor databases like Oracle or DB2 knows that when you have a lot of data and the database is initializing or recovering after a disaster there are dead times when the database isin a state of limbo. It is started but still not in usable state.
Usually the enterprise applications should be decoupled from the DB layer, such that they are able to cope with the DB not being available for short moments and be able to continue to work after the DB is available again.
The above may not be true in the initial initialization stage of the main applications when initialization steps need to extract information from the database to be able to start. In this case sometimes the simple dependency checks done by docker-compose are not enough.

We may have the following minimal setup docker-compose.yml

  

version: '2'

services:
    application:
       build: ./app-server
       ports:
         - "8080:8080"
         - "8443:8443"
         - "8787:8787"
         - "9990:9990"
       links:
         - oracle:oracle
       stdin_open: true
       tty: true

    oracle:
       build: ./db-server
       ports:
         - "1521:1521"
         - "8081:8080"

Where “application” is an Jboss EAP based web application that is using an oracle database 12c container for data storage.

The oracle image is define as the following:


cat Dockerfile

# Use latest sath89/oracle-12c  image as the base
FROM sath89/oracle-12c
ADD init.sql /docker-entrypoint-initdb.d/

Note that we are creating users, tables, procedures and import data using the init.sql script.

The “links” section will make sure that the oracle container is started before the application container but this is not enough. My application needs to wait for Oracle to finish init.sql and that takes time when there are a lot of things to be created and imported.

This imposes the need to define a more complex way for the application container to wait for the oracle container to finish.

My solution was to use the nice Oracle Instant Client and a start-up script with a check to create a Jboss based image that waits for a complex initialization of the oracle database container.

STEP 1: Define the application Dockefile

I start from jboss/base-jdk:8 image.


# 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

The following section will install kits/jboss-eap-7.0.0.zip downloaded from Red-Hat official repository (paid support account needed).



# 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

STEP 3: Install the oracle instant client

Download the Oracle Instant client binary rpms and the dependency library libaio-0.3.109-13.el7.x86_64.rpm.
Then add the following section:


# 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


Note 1: We have to switch context to the root user to be able to install rpms into the image.

Note 2: We have to load the new installed oracle libraries into the kernel.
The content of the scripts/oracle.conf is:


/usr/lib/oracle/12.2/client64/lib/

STEP 4: Copy the start-up script and set it as the entry point
Finally switch back to the jboss user context and copy and set the start-up script



# 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


STEP 5: Final Dockerfile


# 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

# 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



STEP 6: Start-up script

The startApplication.sh will use the test.sql test to check if the database initialization is done.


cat test.sql

select object_name from user_procedures where object_name='PROC12';

Note that creation of the PROC12 procedure is the last action done in the init.sql, so we are certain that after it is created the database is initialized.

The script looks like the following:


cat startApplication.sh 

#!/bin/bash

ORACLE_USER="app_user"
ORACLE_USER_PASSWD="passwd"
ORACLE_HOST="oracle"
ORACLE_PORT="1521"
ORACLE_SID="xe"

function wait_for_oracle_server() {
TEST_VALUE="PROC12"

TEST_VAR=`sqlplus64 -s -l "$ORACLE_USER/$ORACLE_USER_PASSWD@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(Host=$ORACLE_HOST)(Port=$ORACLE_PORT))(CONNECT_DATA=(SID=$ORACLE_SID)))" <<EOF
set pages 0
set head off
set feed off
@test.sql
exit
EOF`

echo $TEST_VAR
array=( $TEST_VAR )
TEST_TOKEN=${array[0]}
echo $TEST_TOKEN

while [  "x_$TEST_TOKEN" != "x_$TEST_VALUE"  ]; do
  echo "=====>Waiting for Oracle Instance to be ready"
  sleep 20
TEST_VAR=`sqlplus64 -s -l "$ORACLE_USER/$ORACLE_USER_PASSWD@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(Host=$ORACLE_HOST)(Port=$ORACLE_PORT))(CONNECT_DATA=(SID=$ORACLE_SID)))" <<EOF
set pages 0
set head off
set feed off
@test.sql
exit
EOF`

echo $TEST_VAR

array=( $TEST_VAR )
TEST_TOKEN=${array[0]}

done

}

echo "=====> Waiting for dependencies to start"
wait_for_oracle_server

echo "Start JBOSS EAP 7 with external address"
rm -rf /opt/jboss/jboss-eap-7.0/standalone/configuration/standalone_xml_history/current
/opt/jboss/jboss-eap-7.0/bin/standalone.sh -b 0.0.0.0 -bmanagement 0.0.0.0 --debug

Note 1: Bash Procedure wait_for_oracle_server will wait in a loop and test using the sqlplus64 client until procedure PROC12 is created.

Note 2: From some weird reason I have to delete “/opt/jboss/jboss-eap-7.0/standalone/configuration/standalone_xml_history/current”. Jboss tries to rename the current history to another directory and fails. It looks like a known issue with Jboss in Docker

The end result is a Jboss EAP 7.0.0 + Oracle Instant Client image that is able to wait before starting for the Oracle database container to end the initialization.

Advertisements

One thought on “#Docker: Production #Jboss client image that checks if the #Oracle container is initialized with data

  1. Pingback: #Docker: Custom #JBoss EAP docker image with CLI applied patches and CLI configurations – blog.voina.org