Update : 04 February 2020
Instead of relying on having server.xml being pass as Docker build image step, I added a few environment variables for tomcat ports and use those environment variables to configured server.xml
I got my base image from https://github.com/Dani3lSun/docker-db-apex-dev. His APEX docker image is for someone who wants to tried out APEX without having to configured and installed Oracle Database, APEX, ORDS, Tomcat.
Thank you Daniel Hochelitner.
I cloned his repo https://github.com/Dani3lSun/docker-db-apex-dev and added my own flavor so that I can have my APEX instances running in a docker containers.
/var/lib/docker/docker-images/zeusAPEX
-rwxr-xr-x 1 root root 8215 Dec 5 14:51 README.md
-rwxr-xr-x 1 root root 1085 Dec 5 14:51 LICENSE
drwxr-xr-x 35 root root 28672 Dec 6 09:41 apexImagesForOrds
-rwxr–r– 1 root root 41 Dec 6 10:44 build.sh
drwxr-xr-x 7 root root 93 Dec 9 11:54 zeusImages
drwxr-xr-x 2 root root 4096 Dec 9 14:28 scripts
drwxr-xr-x 2 root root 4096 Dec 10 09:43 files
-rwxr-xr-x 1 root root 748 Dec 10 09:43 Dockerfile
tomcat-users.xml (using the password being passed from Dockerfile)
<tomcat-users>
<role rolename="manager-gui"/>
<role rolename="manager-script"/>
<role rolename="manager-jmx"/>
<role rolename="admin-gui"/>
<role rolename="admin-script"/>
<user username="admin" password="#PASSWORD#" roles="manager-gui,manager-script,manager-jmx,admin-gui,admin-script"/>
</tomcat-users>
tomcat.service
<?xml version='1.0' encoding='utf-8'?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<tomcat-users>
<role rolename="manager-gui"/>
<role rolename="manager-script"/>
<role rolename="manager-jmx"/>
<role rolename="admin-gui"/>
<role rolename="admin-script"/>
<user username="admin" password="#PASSWORD#" roles="manager-gui,manager-script,manager-jmx,admin-gui,admin-script"/>
</tomcat-users>
[root@ files]# cat tomcat.service
# Systemd unit file for tomcat
[Unit]
Description=Apache Tomcat Web Application Container
After=syslog.target network.target
[Service]
Type=forking
Environment=JAVA_HOME=#JAVA_HOME#
Environment=CATALINA_PID=#TOMCAT_HOME#/temp/tomcat.pid
Environment=CATALINA_HOME=#TOMCAT_HOME#
Environment=CATALINA_BASE=#TOMCAT_HOME#
Environment='CATALINA_OPTS=-Xms256M -Xmx512M -server -XX:+UseParallelGC'
Environment='JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom'
ExecStart=#TOMCAT_HOME#/bin/startup.sh
ExecStop=/bin/kill -15 $MAINPID
User=oracle
Group=oracle
UMask=0007
RestartSec=10
Restart=always
[Install]
WantedBy=multi-user.target
apexImagesForOrds Directory
This directory consist of all the contents froma image directory from APEX installation (typically lived under $ORACLE_HOME/apex/images
zeusImages (custom images)
scripts (all the main installation scripts resides)
files
Stage area for all the required download such as Oracle jdk, ORDS, tomcat
Stage area for ords_params_properties (ORDS configuration), server.xml (tomcat server ports configuration so that you can run multiple APEX docker containers from same docker host), tomcat-user.xml, and tomcat.service
Download jdk(jdk-13.0.1_linux-x64), apache-tomcat(apache-tomcat-8.5.47) and ords(ords-19.2.0.199.1647) and stage them here
Dockerfile
Let’s start with a Dockerfile (the main driver of docker image creation process)
At the end of running all the “install” scripts to build this image, it will exposed the Port 7001 and startup tomcat using “Catalina.sh”
FROM oraclelinux:7.6
MAINTAINER Bo Aye <baye@zeusinc.com>
# environment variables
ENV ORDS_HOME=/u01/ords \
ORACLE_SID=TRAIN \
SERVICE_NAME=TRAIN \
JAVA_HOME=/opt/java \
TOMCAT_HOME=/opt/tomcat \
TIME_ZONE=US/Eastern \
shutdownPort=7000 \
httpPort=7001 \
redirectPort=7002 \
ajpPort=7003
# copy all scripts
ADD scripts /scripts/
# copy all files
ADD files /files/
# copy all apex images
ADD apexImagesForOrds /apexImagesForOrds/
ADD zeusImages /zeusImages/
# image setup via shell script to reduce layers and optimize final disk usage
RUN /scripts/install_main.sh
# apex port
EXPOSE 7001
# Start tomcat at container runtime as oracle user
USER oracle
CMD ["/opt/tomcat/bin/catalina.sh", "run"]
Let’s take a deep dive into scripts directory
-rwxr-xr-x 1 root root 600 Dec 6 10:32 image_setup.sh
-rwxr-xr-x 1 root root 1501 Dec 6 11:19 install_main.sh
-rwxr-xr-x 1 root root 1354 Dec 6 14:17 validations.sh
-rwxr-xr-x 1 root root 1482 Dec 6 14:36 install_java.sh
-rwxr-xr-x 1 root root 1046 Dec 9 11:58 install_ords.sh
-rwxr-xr-x 1 root root 1896 Dec 9 14:06 install_tomcat.sh
-rwxr-xr-x 1 root root 589 Dec 9 14:28 entrypoint.sh
Dockerfile will called install_main.sh first,
install_main.sh (the environment variable are being passed down from Dockerfile
#!/bin/bash
echo "--------------------------------------------------"
echo "Environment Vars.................................."
echo "ORACLE_SID: ${ORACLE_SID}"
echo "SERVICE_NAME: ${SERVICE_NAME}"
echo "PASS: ${PASS}"
echo "ORDS_HOME: ${ORDS_HOME}"
echo "JAVA_HOME: ${JAVA_HOME}"
echo "TOMCAT_HOME: ${TOMCAT_HOME}"
echo "TIME_ZONE: ${TIME_ZONE}"
#
#
echo "--------------------------------------------------"
echo "Validations......................................."
./scripts/validations.sh || exit 1
#
echo "--------------------------------------------------"
echo "Image Setup......................................."
./scripts/image_setup.sh
#
echo "--------------------------------------------------"
echo "Installing JAVA..................................."
./scripts/install_java.sh
#
echo "--------------------------------------------------"
echo "Installing TOMCAT................................."
./scripts/install_tomcat.sh
#
echo "--------------------------------------------------"
echo "Installing ORACLE ORDS............................"
./scripts/install_ords.sh
#
#
echo "--------------------------------------------------"
echo "Cleanup..........................................."
yum clean all
rm -r -f /tmp/*
rm -r -f /files/*
rm -r -f /var/tmp/*
echo "--------------------------------------------------"
echo "DONE.............................................."
validations.sh (To ensure the major component aren’t missing)
#!/bin/bash
echo "......Checking Scripts......"
if [ ! -f /scripts/entrypoint.sh ]; then
echo "/scripts/entrypoint.sh not found!"
exit 1
fi
if [ ! -f /scripts/image_setup.sh ]; then
echo "/scripts/image_setup.sh not found!"
exit 1
fi
if [ ! -f /scripts/install_ords.sh ]; then
echo "/scripts/install_ords.sh not found!"
exit 1
fi
if [ ! -f /scripts/install_tomcat.sh ]; then
echo "/scripts/install_tomcat.sh not found!"
exit 1
fi
#
echo "......Checking Files......"
if [ ! -f /files/ords_params.properties ]; then
echo "/files/ords_params.properties not found!"
exit 1
fi
if [ ! -f /files/tomcat-users.xml ]; then
echo "/files/tomcat-users.xml not found!"
exit 1
fi
if [ ! -f /files/tomcat.service ]; then
echo "/files/tomcat.service not found!"
exit 1
fi
#
echo "......Checking Downloaded Files......"
if ! ls /files/jdk-13.0*.tar.gz 1> /dev/null 2>&1; then
echo "Java not found!"
exit 1
fi
if ! ls /files/apache-tomcat*.tar.gz 1> /dev/null 2>&1; then
echo "Tomcat not found!"
exit 1
fi
if ! ls /files/ords*.zip 1> /dev/null 2>&1; then
echo "ORDS not found!"
exit 1
fi
#
echo "......Validations Done......"
install_java.sh
#!/bin/bash
# create oracle groups
groupadd --gid 54321 oinstall
groupadd --gid 54322 dba
groupadd --gid 54323 oper
# create oracle user
useradd --create-home --gid oinstall --groups oinstall,dba --uid 54321 oracle
# install required OS components
yum install -y perl \
tar \
unzip \
wget \
lsof \
locate
cd /files
tar -xzf jdk-13*.tar.gz
mkdir -p ${JAVA_HOME}
mv jdk-*/* ${JAVA_HOME}/.
echo 'JAVA_HOME='${JAVA_HOME} >> /etc/profile
echo 'PATH=$PATH:$HOME/bin:$JAVA_HOME/bin' >> /etc/profile
echo 'export JAVA_HOME' >> /etc/profile
echo 'export PATH' >> /etc/profile
source /etc/profile
echo "export JAVA_HOME=${JAVA_HOME}" >> /home/oracle/.bash_profile
echo "export JAVA_HOME=${JAVA_HOME}" >> /home/oracle/.bashrc # .bash_profile not executed by docker
echo "export JAVA_HOME=${JAVA_HOME}" >> /root/.bash_profile
echo "export JAVA_HOME=${JAVA_HOME}" >> /root/.bashrc # .bash_profile not executed by docker
echo "export PATH=\$JAVA_HOME/bin:\$PATH" >> /home/oracle/.bash_profile
echo "export PATH=\$JAVA_HOME/bin:\$PATH" >> /home/oracle/.bashrc # .bash_profile not executed by docker
echo "export PATH=\$JAVA_HOME/bin:\$PATH" >> /root/.bash_profile
echo "export PATH=\$JAVA_HOME/bin:\$PATH" >> /root/.bashrc # .bash_profile not executed by docker
install_tomcat.sh
#!/bin/bash
mkdir ${TOMCAT_HOME}
cd /files
tar -xzf apache-tomcat*.tar.gz -C ${TOMCAT_HOME} --strip-components=1
sed -i -E 's:#PASSWORD#:'${PASS}':g' /files/tomcat-users.xml
mv /files/tomcat-users.xml ${TOMCAT_HOME}/conf
# add server.xml(custom ports)
sed -i -E 's/port="8005"/port="'${shutdownPort}'"/' ${TOMCAT_HOME}/conf/server.xml
sed -i -E 's/port="8080"/port="'${httpPort}'"/' ${TOMCAT_HOME}/conf/server.xml
sed -i -E 's/port="8443"/port="'${redirectPort}'"/' ${TOMCAT_HOME}/conf/server.xml
sed -i -E 's/port="8009"/port="'${ajpPort}'"/' ${TOMCAT_HOME}/conf/server.xml
#chown -R tomcat:tomcat ${TOMCAT_HOME}
sed -i -E 's:#JAVA_HOME#:'${JAVA_HOME}':g' /files/tomcat.service
sed -i -E 's:#TOMCAT_HOME#:'${TOMCAT_HOME}':g' /files/tomcat.service
mv /files/tomcat.service /etc/systemd/system/tomcat.service
# create setenv.sh
echo 'export CATALINA_HOME="'${TOMCAT_HOME}'"' > ${TOMCAT_HOME}/bin/setenv.sh
echo 'export JAVA_HOME="'${JAVA_HOME}'"' >> ${TOMCAT_HOME}/bin/setenv.sh
echo 'export CATALINA_OPTS="$CATALINA_OPTS -Xms256m"' >> ${TOMCAT_HOME}/bin/setenv.sh
echo 'export CATALINA_OPTS="$CATALINA_OPTS -Xmx512m"' >> ${TOMCAT_HOME}/bin/setenv.sh
echo 'export CATALINA_OPTS="$CATALINA_OPTS -server"' >> ${TOMCAT_HOME}/bin/setenv.sh
echo 'export CATALINA_OPTS="$CATALINA_OPTS -XX:PermSize=128m"' >> ${TOMCAT_HOME}/bin/setenv.sh
echo 'export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxPermSize=256m"' >> ${TOMCAT_HOME}/bin/setenv.sh
chmod a+x ${TOMCAT_HOME}/bin/setenv.sh
# fix some tomcat settings
echo 'tomcat.util.http.parser.HttpParser.requestTargetAllow=|' >> ${TOMCAT_HOME}/conf/catalina.properties
sed -i -E 's/(allow=").*(")/allow=".*"/g' ${TOMCAT_HOME}/webapps/manager/META-INF/context.xml
sed -i -E 's/(allow=").*(")/allow=".*"/g' ${TOMCAT_HOME}/webapps/host-manager/META-INF/context.xml
# add to path
echo 'export CATALINA_HOME="'${TOMCAT_HOME}'"' > ${TOMCAT_HOME}/.profile
echo 'export JAVA_HOME="'${JAVA_HOME}'"' >> ${TOMCAT_HOME}/.profile
chmod a+x ${TOMCAT_HOME}/.profile
mkdir -p ${TOMCAT_HOME}/webapps/i
mkdir -p ${TOMCAT_HOME}/webapps/ZEUS_APEX
chown -R oracle:dba ${TOMCAT_HOME}
If you are running an ORDS version lower than the one you download, unistall current ords (one time only)
java -jar ords_apptrain.war uninstall
ords_params.properties
db.servicename is leveraging the environment variable ORACLE_SID which is pass down from Dockerfile.
#Wed Dec 20 10:30:00 CET 2017
db.hostname=<Database Host name>
db.password=<APEX_PUBLIC_USER password>
db.port=<Database port>
db.servicename=#SERVICE_NAME#
db.username=APEX_PUBLIC_USER
migrate.apex.rest=false
plsql.gateway.add=true
rest.services.apex.add=true
rest.services.ords.add=true
schema.tablespace.default=APEX
schema.tablespace.temp=TEMP
standalone.http.port=8080
standalone.mode=false
user.apex.listener.password=<APEX_LISTENER password>
user.apex.restpublic.password=<APEX_REST_PUBLIC_USER password>
user.public.password=<APEX_PUBLIC_USER password>
user.tablespace.default=USERS
user.tablespace.temp=TEMP
sys.user=<Database Sys user>
sys.password=<Database Sys password>
server.xml (no longer needed as part of build process)
The only changes you need to make in this file are (If you want to run with default port assign, no need to change the ports, if you want to run multiple Tomcat Docker containers on a single host, you need to change this so that there won’t be any port conflict)
Server Port “Shutdown” (7000)
Connector port and redirectPort for “HTTP/1.1 (7001 and 7002 respectively)
Connector port and redirecPort for “AJP1.3 Connector” (7003 and 7002 respectively)
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Note: A "Server" is not itself a "Container", so you may not
define subcomponents such as "Valves" at this level.
Documentation at /docs/config/server.html
-->
<Server port="7000" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<!-- Security listener. Documentation at /docs/config/listeners.html
<Listener className="org.apache.catalina.security.SecurityListener" />
-->
<!--APR library loader. Documentation at /docs/apr.html -->
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<!-- Global JNDI resources
Documentation at /docs/jndi-resources-howto.html
-->
<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<!-- A "Service" is a collection of one or more "Connectors" that share
a single "Container" Note: A "Service" is not itself a "Container",
so you may not define subcomponents such as "Valves" at this level.
Documentation at /docs/config/service.html
-->
<Service name="Catalina">
<!--The connectors can use a shared executor, you can define one or more named thread pools-->
<!--
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
-->
<!-- A "Connector" represents an endpoint by which requests are received
and responses are returned. Documentation at :
Java HTTP Connector: /docs/config/http.html
Java AJP Connector: /docs/config/ajp.html
APR (HTTP/AJP) Connector: /docs/apr.html
Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
-->
<Connector port="7001" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="7002"
relaxedQueryChars="{,}"/>
<!-- A "Connector" using the shared thread pool-->
<!--
<Connector executor="tomcatThreadPool"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
-->
<!-- Define an SSL/TLS HTTP/1.1 Connector on port 8443
This connector uses the NIO implementation. The default
SSLImplementation will depend on the presence of the APR/native
library and the useOpenSSL attribute of the
AprLifecycleListener.
Either JSSE or OpenSSL style configuration may be used regardless of
the SSLImplementation selected. JSSE style configuration is used below.
-->
<!--
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150" SSLEnabled="true">
<SSLHostConfig>
<Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
type="RSA" />
</SSLHostConfig>
</Connector>
-->
<!-- Define an SSL/TLS HTTP/1.1 Connector on port 8443 with HTTP/2
This connector uses the APR/native implementation which always uses
OpenSSL for TLS.
Either JSSE or OpenSSL style configuration may be used. OpenSSL style
configuration is used below.
-->
<!--
<Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
maxThreads="150" SSLEnabled="true" >
<UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
<SSLHostConfig>
<Certificate certificateKeyFile="conf/localhost-rsa-key.pem"
certificateFile="conf/localhost-rsa-cert.pem"
certificateChainFile="conf/localhost-rsa-chain.pem"
type="RSA" />
</SSLHostConfig>
</Connector>
-->
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="7003" protocol="AJP/1.3" redirectPort="7002" />
<!-- An Engine represents the entry point (within Catalina) that processes
every request. The Engine implementation for Tomcat stand alone
analyzes the HTTP headers included with the request, and passes them
on to the appropriate Host (virtual host).
Documentation at /docs/config/engine.html -->
<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
-->
<Engine name="Catalina" defaultHost="localhost">
<!--For clustering, please take a look at documentation at:
/docs/cluster-howto.html (simple how to)
/docs/config/cluster.html (reference documentation) -->
<!--
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
-->
<!-- Use the LockOutRealm to prevent attempts to guess user passwords
via a brute-force attack -->
<Realm className="org.apache.catalina.realm.LockOutRealm">
<!-- This Realm uses the UserDatabase configured in the global JNDI
resources under the key "UserDatabase". Any edits
that are performed against this UserDatabase are immediately
available for use by the Realm. -->
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<!-- SingleSignOn valve, share authentication between web applications
Documentation at: /docs/config/valve.html -->
<!--
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
-->
<!-- Access log processes all example.
Documentation at: /docs/config/valve.html
Note: The pattern used is equivalent to using pattern="common" -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
install_ords.sh
#!/bin/bash
source /etc/profile
mkdir -p ${ORDS_HOME}
unzip -o /files/ords*.zip -d ${ORDS_HOME}
sed -i -E 's:#SERVICE_NAME#:'${SERVICE_NAME}':g' /files/ords_params.properties
cp -rf /files/ords_params.properties ${ORDS_HOME}/params
cd ${ORDS_HOME}
cd ..
CURR_DIR=`pwd`
cd ${ORDS_HOME}
java -jar ords.war configdir $CURR_DIR
java -jar ords.war install simple
# tune some ORDS default settings
sed -i -E '/<\/properties>/d' defaults.xml
echo '<entry key="jdbc.InitialLimit">6</entry>' >> defaults.xml
echo '<entry key="jdbc.MaxConnectionReuseCount">10000</entry>' >> defaults.xml
echo '<entry key="jdbc.MaxLimit">40</entry>' >> defaults.xml
echo '<entry key="jdbc.MinLimit">6</entry>' >> defaults.xml
echo '</properties>' >> defaults.xml
chmod 777 defaults.xml
chown -R oracle:dba ${ORDS_HOME}
cp -rf ${ORDS_HOME}/ords.war ${TOMCAT_HOME}/webapps/
cp -rf /apexImagesForOrds/. ${TOMCAT_HOME}/webapps/i/.
cp -rf /zeusImages/. ${TOMCAT_HOME}/webapps/ZEUS_APEX/.
build.sh
#!/bin/sh
docker build -t zeus/trainapex:1 .
[root@ files]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
zeus/trainapex 1 2774c48dfe69 3 hours ago 2.36GB
zeus/ecc 3 72803831e104 7 days ago 10.5GB
oraclelinux 7 5f993b1aafe5 4 months ago 235MB
oraclelinux 7.6 5f993b1aafe5 4 months ago 235MB
apexrnd/apexofficeprint latest 3d904d519b0f 8 months ago 1.98GB
docker run -dit –restart always –name trainAPEX192 -p 7001:7001 zeus/trainapex:1
this will start tomcat server as an oracle user
You can then access to APEX via
http://<docker host name>:7001/ords