In 2 previous posts I wrote about the installation of Docker on Oracle Enterprise Linux and how to create a base Oracle Enterprise Linux image for Docker. For the installation of Oracle XE in a Docker image it is a requirement that you followed most of the steps describes in these posts. Only the creation of the webserver image can be skipped. So the procedure describes in this post assumes that you already have an Oracle Enterprise Linux base image. Many thanks to Alexei Ledenev, whos Docker image I used as an example for the steps described in this post.

1. Download Oracle XE 11.2. The download can be found at: http://www.oracle.com/technetwork/database/database-technologies/express-edition/downloads/index.html. An Oracle account is required.

2. Create a directory which can be used as a place for some installation scripts:

[root@oel6-docker ~]# mkdir –p scripts/oel6-xe

With some scp tool, copy the downloaded zip file to the directory /root/scripts/oel6-xe and unzip the file:

root@oel6-docker oel6-xe]# ls -l
total 308492
-rw-r--r--. 1 root root 315891481 May 2 16:51 oracle-xe-11.2.0-1.0.x86_64.rpm.zip

[root@oel6-docker oel6-xe]# unzip oracle-xe-11.2.0-1.0.x86_64.rpm.zip
Archive: oracle-xe-11.2.0-1.0.x86_64.rpm.zip
creating: Disk1/
creating: Disk1/upgrade/
inflating: Disk1/upgrade/gen_inst.sql
creating: Disk1/response/
inflating: Disk1/response/xe.rsp
inflating: Disk1/oracle-xe-11.2.0-1.0.x86_64.rpm

[root@oel6-docker oel6-xe]# ls -l
total 308496
drwxrwxr-x. 4 root root 4096 Aug 29 2011 Disk1
-rw-r--r--. 1 root root 315891481 May 2 16:51 oracle-xe-11.2.0-1.0.x86_64.rpm.zip

The directory Disk1 contains the installation RPM:
[root@oel6-docker oel6-xe]# ls -l Disk1/
total 309892
-rw-rw-r--. 1 root root 317320273 Aug 29 2011 oracle-xe-11.2.0-1.0.x86_64.rpm
drwxr-xr-x. 2 root root 4096 Aug 29 2011 response
drwxrwxr-x. 2 root root 4096 Aug 29 2011 upgrade

3. Create the Dockerfile with the following content:
[root@oel6-docker oel6-xe]# cat Dockerfile

# Use the OEL6 base image
FROM oel6-base:6.5

# Maintainer of the image
MAINTAINER Rob den Braber

# Install the packages libaio and bc
RUN yum install -y libaio bc

# Copy the RPM file, modified init.ora, initXETemp.ora and the installation response file
# inside the image
ADD Disk1/oracle-xe-11.2.0-1.0.x86_64.rpm /tmp/oracle-xe-11.2.0-1.0.x86_64.rpm
ADD init.ora /tmp/init.ora
ADD initXETemp.ora /tmp/initXETemp.ora
ADD Disk1/response/xe.rsp /tmp/xe.rsp

# Install the Oracle XE RPM
RUN yum localinstall -y /tmp/oracle-xe-11.2.0-1.0.x86_64.rpm

# Delete the Oracle XE RPM
RUN rm -f /tmp/oracle-xe-11.2.0-1.0.x86_64.rpm

# move the files init.ora and initXETemp.ora to the right directory
RUN mv /tmp/init.ora /u01/app/oracle/product/11.2.0/xe/config/scripts
RUN mv /tmp/initXETemp.ora /u01/app/oracle/product/11.2.0/xe/config/scripts

# Configure the database
RUN /etc/init.d/oracle-xe configure responseFile=/tmp/xe.rsp

# Create entries for the database in the profile
RUN echo 'export ORACLE_HOME=/u01/app/oracle/product/11.2.0/xe' >> /etc/profile.d/oracle_profile.sh
RUN echo 'export PATH=$ORACLE_HOME/bin:$PATH' >> /etc/profile.d/oracle_profile.sh
RUN echo 'export ORACLE_SID=XE' >> /etc/profile.d/oracle_profile.sh

# Create ssh keys and change some ssh settings
RUN ssh-keygen -q -N "" -t dsa -f /etc/ssh/ssh_host_dsa_key && ssh-keygen -q -N "" -t rsa -f /etc/ssh/ssh_host_rsa_key && sed -i "s/#UsePrivilegeSeparation.*/UsePrivilegeSeparation no/g" /etc/ssh/sshd_config && sed -i "s/UsePAM.*/UsePAM no/g" /etc/ssh/sshd_config

# Change the root and oracle password to oracle
RUN echo root:oracle | chpasswd
RUN echo oracle:oracle | chpasswd

# Expose ports 22, 1521 and 8080
EXPOSE 22
EXPOSE 1521
EXPOSE 8080

# Change the hostname in the listener.ora file, start Oracle XE and the ssh daemon
CMD sed -i -E "s/HOST = [^)]+/HOST = $HOSTNAME/g" /u01/app/oracle/product/11.2.0/xe/network/admin/listener.ora; \
service oracle-xe start; \
/usr/sbin/sshd -D

The comments in the file should be self explanatory.

4. Since the setting memory_target in the init.ora file does not work inside a container we have to replace this entry in the file for the settings pga_aggregate_target and sga_target. Create the file init.ora with the content:
[root@oel6-docker oel6-xe]# cat init.ora
##############################################################################
# Copyright (c) 1991, 2011, Oracle and/or its affiliates. All rights reserved.
##############################################################################

###########################################
# Cursors and Library Cache
###########################################
open_cursors=300

###########################################
# Database Identification
###########################################
db_name=XE

###########################################
# File Configuration
###########################################
control_files=("/u01/app/oracle/oradata/XE/control.dbf")
DB_RECOVERY_FILE_DEST=/u01/app/oracle/fast_recovery_area
DB_RECOVERY_FILE_DEST_SIZE=10G

###########################################
# Job Queues
###########################################
job_queue_processes=4

###########################################
# Miscellaneous
###########################################
compatible=11.2.0.0.0
diagnostic_dest=/u01/app/oracle
#memory_target=1073741824
pga_aggregate_target=200540160
sga_target=601620480

###########################################
# Sessions
###########################################
sessions=20

###########################################
# Security and Auditing
###########################################
audit_file_dest=/u01/app/oracle/admin/XE/adump
remote_login_passwordfile=EXCLUSIVE

###########################################
# Shared Server
###########################################
dispatchers="(PROTOCOL=TCP) (SERVICE=XEXDB)"
shared_servers=4

###########################################
# System Managed Undo and Rollback Segments
###########################################
undo_management=AUTO
undo_tablespace=UNDOTBS1

5. The same applies for the file initXETemp.ora. This file should have the following content:
[root@oel6-docker oel6-xe]# cat initXETemp.ora
##############################################################################
# Copyright (c) 1991, 2011, Oracle and/or its affiliates. All rights reserved.
##############################################################################

###########################################
# Cursors and Library Cache
###########################################
open_cursors=300

###########################################
# Database Identification
###########################################
db_name=XE

###########################################
# File Configuration
###########################################
control_files=("/u01/app/oracle/oradata/XE/control.dbf")
DB_RECOVERY_FILE_DEST_SIZE=10G
DB_RECOVERY_FILE_DEST=/u01/app/oracle/fast_recovery_area

###########################################
# Job Queues
###########################################

###########################################
# Miscellaneous
###########################################
compatible=11.2.0.0.0
diagnostic_dest=/u01/app/oracle
#memory_target=1073741824
pga_aggregate_target=200540160
sga_target=601620480

###########################################
# Sessions
###########################################
sessions=20

###########################################
# Security and Auditing
###########################################
audit_file_dest=/u01/app/oracle/admin/XE/adump
remote_login_passwordfile=EXCLUSIVE

###########################################
# Shared Server
###########################################
dispatchers="(PROTOCOL=TCP) (SERVICE=XEXDB)"

###########################################
# System Managed Undo and Rollback Segments
###########################################
undo_management=AUTO
undo_tablespace=UNDOTBS1
_no_recovery_through_resetlogs=true

6. Modify the configuration response file xe.rsp located in the Disk1/response directory and change the password settings ORACLE_PASSWORD and ORACLE_CONFIRM_PASSWORD:
[root@oel6-docker oel6-xe]# cat Disk1/response/xe.rsp
###############################################################################
# #
# HTTP port that will be used to access APEX admin page #
# Default : 8080 #
# #
###############################################################################
ORACLE_HTTP_PORT=8080

###############################################################################
# #
# TNS port that will be used to configure listener #
# Default : 1521 #
# #
###############################################################################
ORACLE_LISTENER_PORT=1521

###############################################################################
# #
# Passwords can be supplied for the following two schemas in the #
# starter database: #
# SYS #
# SYSTEM #
# #
###############################################################################
ORACLE_PASSWORD=oracle

###############################################################################
# #
# Passwords can be supplied for the following two schemas in the #
# starter database: #
# SYS #
# SYSTEM #
# #
# ORACLE_CONFIRM_PASSWORD should be same as ORACLE_PASSWORD #
# #
###############################################################################
ORACLE_CONFIRM_PASSWORD=oracle

###############################################################################
# #
# To start/stop listener and database instance up on system boot #
# #
###############################################################################
ORACLE_DBENABLE=y

7. Start the build of the image with the docker build command. Since it contains an installation and a configuration of an Oracle database, this can take some time:
[root@oel6-docker oel6-xe]# docker build -t oel6-xe .
Uploading context 633.2 MB
Uploading context
Step 0 : FROM oel6-base:6.5
---> 5b393d2e7219
Step 1 : MAINTAINER Rob den Braber
---> Running in aea872edbe5d
---> a844eb18cae1
Step 2 : RUN yum install -y libaio bc
---> Running in 096b1c600305
Setting up Install Process
Resolving Dependencies
--> Running transaction check
---> Package bc.x86_64 0:1.06.95-1.el6 will be installed
---> Package libaio.x86_64 0:0.3.107-10.el6 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
Package Arch Version Repository Size
================================================================================
Installing:
bc x86_64 1.06.95-1.el6 public_ol6_latest 109 k
libaio x86_64 0.3.107-10.el6 public_ol6_latest 21 k

Transaction Summary
================================================================================
Install 2 Package(s)

Total download size: 130 k
Installed size: 246 k
Downloading Packages:
--------------------------------------------------------------------------------
Total 1.5 MB/s | 130 kB 00:00
Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction
Installing : libaio-0.3.107-10.el6.x86_64 1/2
Installing : bc-1.06.95-1.el6.x86_64 2/2
Verifying : bc-1.06.95-1.el6.x86_64 1/2
Verifying : libaio-0.3.107-10.el6.x86_64 2/2

Installed:
bc.x86_64 0:1.06.95-1.el6 libaio.x86_64 0:0.3.107-10.el6

Complete!
---> 814180891c22
Step 3 : ADD Disk1/oracle-xe-11.2.0-1.0.x86_64.rpm /tmp/oracle-xe-11.2.0-1.0.x86_64.rpm
---> 043414ca9041
Step 4 : ADD init.ora /tmp/init.ora
---> ccabc3ab4304
Step 5 : ADD initXETemp.ora /tmp/initXETemp.ora
---> 6a6b23555dfb
Step 6 : ADD Disk1/response/xe.rsp /tmp/xe.rsp
---> 8136061af1ad
Step 7 : RUN yum localinstall -y /tmp/oracle-xe-11.2.0-1.0.x86_64.rpm
---> Running in df626715fb90
Setting up Local Package Process
Examining /tmp/oracle-xe-11.2.0-1.0.x86_64.rpm: oracle-xe-11.2.0-1.0.x86_64
Marking /tmp/oracle-xe-11.2.0-1.0.x86_64.rpm to be installed
Resolving Dependencies
--> Running transaction check
---> Package oracle-xe.x86_64 0:11.2.0-1.0 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
Package Arch Version Repository Size
================================================================================
Installing:
oracle-xe x86_64 11.2.0-1.0 /oracle-xe-11.2.0-1.0.x86_64 564 M

Transaction Summary
================================================================================
Install 1 Package(s)

Total size: 564 M
Installed size: 564 M
Downloading Packages:
Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction
Installing : oracle-xe-11.2.0-1.0.x86_64 1/1
cat: /proc/sys/net/ipv4/ip_local_port_range: No such file or directory
cat: /proc/sys/net/ipv4/ip_local_port_range: No such file or directory
/var/tmp/rpm-tmp.qZNbAY: line 305: [: -lt: unary operator expected
/var/tmp/rpm-tmp.qZNbAY: line 311: [: -gt: unary operator expected
Executing post-install steps...
You must run '/etc/init.d/oracle-xe configure' as the root user to configure the database.

Verifying : oracle-xe-11.2.0-1.0.x86_64 1/1

Installed:
oracle-xe.x86_64 0:11.2.0-1.0

Complete!
---> 5845f96b023e
Step 8 : RUN rm -f /tmp/oracle-xe-11.2.0-1.0.x86_64.rpm
---> Running in 69babc231a88
---> 9dccf44903f1
Step 9 : RUN mv /tmp/init.ora /u01/app/oracle/product/11.2.0/xe/config/scripts
---> Running in e396383c133d
---> 6c0a27833f66
Step 10 : RUN mv /tmp/initXETemp.ora /u01/app/oracle/product/11.2.0/xe/config/scripts
---> Running in 05a68cc2b756
---> 6ee188856bad
Step 11 : RUN /etc/init.d/oracle-xe configure responseFile=/tmp/xe.rsp
---> Running in 2dd1e6ddf3b9

Oracle Database 11g Express Edition Configuration
-------------------------------------------------
This will configure on-boot properties of Oracle Database 11g Express
Edition. The following questions will determine whether the database should
be starting upon system boot, the ports it will use, and the passwords that
will be used for database accounts. Press to accept the defaults.
Ctrl-C will abort.

Specify the HTTP port that will be used for Oracle Application Express [8080]:
Specify a port that will be used for the database listener [1521]:
Specify a password to be used for database accounts. Note that the same
password will be used for SYS and SYSTEM. Oracle recommends the use of
different passwords for each database account. This can be done after
initial configuration:
Confirm the password:

Do you want Oracle Database 11g Express Edition to be started on boot (y/n) [y]:
Starting Oracle Net Listener...Done
Configuring database...Done
Starting Oracle Database 11g Express Edition instance...Done
Installation completed successfully.
---> fa43225fb341
Step 12 : RUN echo 'export ORACLE_HOME=/u01/app/oracle/product/11.2.0/xe' >> /etc/profile.d/oracle_profile.sh
---> Running in 10f8e8596174
---> cccb5efb2800
Step 13 : RUN echo 'export PATH=$ORACLE_HOME/bin:$PATH' >> /etc/profile.d/oracle_profile.sh
---> Running in 685d9002f985
---> 3141528c3d08
Step 14 : RUN echo 'export ORACLE_SID=XE' >> /etc/profile.d/oracle_profile.sh
---> Running in 5d1d7bbf5d16
---> 953befd2343d
Step 15 : RUN ssh-keygen -q -N "" -t dsa -f /etc/ssh/ssh_host_dsa_key && ssh-keygen -q -N "" -t rsa -f /etc/ssh/ssh_host_rsa_key && sed -i "s/#UsePrivilegeSeparation.*/UsePrivilegeSeparation no/g" /etc/ssh/sshd_config && sed -i "s/UsePAM.*/UsePAM no/g" /etc/ssh/sshd_config
---> Running in 1b3f17507ba8
---> 1b5bb5c12dd3
Step 16 : RUN echo root:oracle | chpasswd
---> Running in 95a2a5fd391f
---> 0e7275eca423
Step 17 : RUN echo oracle:oracle | chpasswd
---> Running in 3c7a1e7f0785
---> 714e19608386
Step 18 : EXPOSE 22
---> Running in 7be0b68c82ef
---> 9961ff895c0f
Step 19 : EXPOSE 1521
---> Running in c9b5e4b7ad1e
---> f681d600056b
Step 20 : EXPOSE 8080
---> Running in e1956eb9a235
---> 2f585227241f
Step 21 : CMD sed -i -E "s/HOST = [^)]+/HOST = $HOSTNAME/g" /u01/app/oracle/product/11.2.0/xe/network/admin/listener.ora; service oracle-xe start; /usr/sbin/sshd -D
---> Running in 75be66506906
---> 4cd5d389e956
Successfully built 4cd5d389e956
Removing intermediate container aea872edbe5d
Removing intermediate container 736b79bd8417
Removing intermediate container 05a68cc2b756
Removing intermediate container 2dd1e6ddf3b9
Removing intermediate container 685d9002f985
Removing intermediate container 1b3f17507ba8
Removing intermediate container 95a2a5fd391f
Removing intermediate container 10f8e8596174
Removing intermediate container 3c7a1e7f0785
Removing intermediate container 7be0b68c82ef
Removing intermediate container e1956eb9a235
Removing intermediate container 75be66506906
Removing intermediate container 22f1f202a8bc
Removing intermediate container 8d7d2035f920
Removing intermediate container 5d1d7bbf5d16
Removing intermediate container c9b5e4b7ad1e
Removing intermediate container 096b1c600305
Removing intermediate container f003c0914e5d
Removing intermediate container df626715fb90
Removing intermediate container 69babc231a88
Removing intermediate container e396383c133d

8. We are now ready to start our container with the docker run command:
[root@oel6-docker oel6-xe]# docker run -d -p 49160:22 -p 49161:1521 -p 49162:8080 oel6-xe
1d02c22263e98fbb0beadac2daf3dc6109f1c36b6f3f1f8cb449e0e4f5fabbd1

The -p options maps ports to our container. In this case port 41960 is mapped to port 22 (ssh), port 41961 is mapped to port 1521 (Oracle listener) and port 49162 is mapped to port 8080 (Oracle http (APEX)).

9. With ssh we can login to our container:
[root@oel6-docker oel6-xe]# ssh oracle@localhost -p 49160
The authenticity of host '[localhost]:49160 ([::1]:49160)' can't be established.
RSA key fingerprint is 7f:d5:bf:a5:58:d0:85:65:7a:75:cc:23:81:af:fd:88.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[localhost]:49160' (RSA) to the list of known hosts.
oracle@localhost's password:
Last login: Mon May 12 11:17:11 2014 from 172.17.42.1

-bash-4.1$ lsnrctl status

LSNRCTL for Linux: Version 11.2.0.2.0 - Production on 12-MAY-2014 11:18:27

Copyright (c) 1991, 2011, Oracle. All rights reserved.

Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC_FOR_XE)))
STATUS of the LISTENER
------------------------
Alias LISTENER
Version TNSLSNR for Linux: Version 11.2.0.2.0 - Production
Start Date 12-MAY-2014 11:12:27
Uptime 0 days 0 hr. 6 min. 1 sec
Trace Level off
Security ON: Local OS Authentication
SNMP OFF
Default Service XE
Listener Parameter File /u01/app/oracle/product/11.2.0/xe/network/admin/listener.ora
Listener Log File /u01/app/oracle/diag/tnslsnr/1d02c22263e9/listener/alert/log.xml
Listening Endpoints Summary...
(DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC_FOR_XE)))
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=1d02c22263e9)(PORT=1521)))
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=1d02c22263e9)(PORT=8080))(Presentation=HTTP)(Session=RAW))
Services Summary...
Service "PLSExtProc" has 1 instance(s).
Instance "PLSExtProc", status UNKNOWN, has 1 handler(s) for this service...
Service "XE" has 1 instance(s).
Instance "XE", status READY, has 1 handler(s) for this service...
Service "XEXDB" has 1 instance(s).
Instance "XE", status READY, has 1 handler(s) for this service...
The command completed successfully
-bash-4.1$ sqlplus / as sysdba

SQL*Plus: Release 11.2.0.2.0 Production on Mon May 12 11:18:32 2014

Copyright (c) 1982, 2011, Oracle. All rights reserved.

Connected to:
Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production

SQL> select instance_name from v$instance;

INSTANCE_NAME
----------------
XE

SQL> exit
Disconnected from Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
-bash-4.1$ exit
logout
Connection to localhost closed.

The listener in the container is running and so is the database. With an sqlplus client we would be able to connect to this database.

10. With a webbrowser we can logon to APEX. Use the url http://ipaddress of your VM:49162/apex. Use the settings:
Workspace: INTERNAL
Username: ADMIN
Password: oracle

11. If you need more XE databases (APEX environments), just start another container, but use different ports:
[root@oel6-docker oel6-xe]# docker run -d -p 49170:22 -p 49171:1521 -p 49172:8080 oel6-xe
8775e64c532a97e922b018d594581238227787a50b64c1f22e9233a99a00df24[root@oel6-docker oel6-xe]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8775e64c532a oel6-xe:latest "/bin/sh -c 'sed -i 4 seconds ago Up 3 seconds 0.0.0.0:49170->22/tcp, 0.0.0.0:49171->1521/tcp, 0.0.0.0:49172->8080/tcp lonely_wozniak
1d02c22263e9 oel6-xe:latest "/bin/sh -c 'sed -i 20 minutes ago Up 20 minutes 0.0.0.0:49160->22/tcp, 0.0.0.0:49161->1521/tcp, 0.0.0.0:49162->8080/tcp cocky_mcclintock

12. We can also use this image on another computer with even another linux flavor, which runs docker. First save the image to a tar file with the docker save command:
[root@oel6-docker oel6-xe]# docker save --output oel6-xe.tar oel6-xe[root@oel6-docker oel6-xe]# ls -l
total 3362144
drwxrwxr-x. 4 root root 4096 Aug 29 2011 Disk1
-rw-r--r--. 1 root root 1997 May 12 16:55 Dockerfile
-rw-r--r--. 1 root root 1768 May 12 16:50 init.ora
-rw-r--r--. 1 root root 1765 May 2 20:55 initXETemp.ora
-rw-r--r--. 1 root root 3126915584 May 15 06:17 oel6-xe.tar
-rw-r--r--. 1 root root 315891481 May 2 16:51 oracle-xe-11.2.0-1.0.x86_64.rpm.zip

13. Copy the tar file to the other host with for example scp:
[root@oel6-docker oel6-xe]# scp -p oel6-xe.tar robra@docker-ubuntu:
robra@docker-ubuntu's password:
oel6-xe.tar 100% 2982MB 45.2MB/s 01:06

14. My other host runs Ubuntu 14.04 and has docker already installed. Now we can load the image with the docker load command:

robra@docker-ubuntu:~$ lsb_release -d
Description: Ubuntu 14.04 LTS
robra@docker-ubuntu:~$ ls -l
total 3053636
-rw-r--r-- 1 robra robra 3126915584 May 15 06:17 oel6-xe.tar
robra@docker-ubuntu:~$ sudo docker load oel6-xe.tar
robra@docker-ubuntu:~$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE oel6-xe latest 4cd5d389e956 2 days ago 3.116 GB 15.

The last step is to run the image:

robra@docker-ubuntu:~$sudo docker run -d -p 49160:22 -p 49161:1521 -p 49162:8080 oel6-xe 15a9fdf468057d3a04c630c13de8fc45675708102494f0a6d317e782010327f0
robra@docker-ubuntu:~$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 15a9fdf46805 oel6-xe:latest "/bin/sh -c 'sed -i 7 seconds ago Up 6 seconds 0.0.0.0:49160->22/tcp, 0.0.0.0:49161->1521/tcp, 0.0.0.0:49162->8080/tcp grave_bell