Docker has been making a lot of noise in the last couple of years. It piqued my interest recently when I came across a tweet saying that Oracle had recently uploaded an Oracle Linux 7 image to the Docker repository. Containers are nothing new in the Unix space but they are relatively new in Linux. Docker was built to take advantage of Linux Containers.
I asked on Twitter if anyone had successfully installed and run the Oracle Database inside of a Linux Container using Docker. I got a couple of responses that it has been done. I looked around for a blog post or documentation on how to do it and couldn’t find one so, here it is. This is also my first use of Linux Containers and Docker so feel free to comment if there are things that I missed or could do more efficiently.
Note: These instructions presume the user is familiar with VirtualBox and at least an intermediate user of Linux.
Create a small VM. Oracle Linux 7 doesn’t need a lot of horsepower to run. However, Oracle Database will run better with more resources. My VM has 4GB of memory and 25GB of dynamically added disk space. Add a second disk of the same size that will be used for the Docker images. Make sure to include a network adapter that will be able to access the internet.
Start the VM. Since there is no OS on the VM and no mounted ISO, you should be presented with a screen asking which ISO you want to use as a start-up disk.
Press enter when the install screen comes up.
Choose the language.
There should only be one exclamation mark on the screen for the installation destination. Click on the exclamation mark to choose the disk layout. Select the first disk and click Done.
Click Begin Installation.
The Oracle Linux 7 install is completely different from previous installs. The tasks have become much more parallelized. You may notice that the installation has already begun at this point. While the OS installs, click Root Password.
Enter a password for root and click Done.
The OS installation will now finish.
Once the install is done, click Reboot.
Install additional packages that will be needed in the VM to support running Docker.
yum -y --enablerepo=ol7_addons install docker btrfs-progs vim bzip2 kernel-uek-devel-$(uname -r) tigervnc-server xterm xorg-x11-xauth
Add the Guest Additions ISO to the VM by clicking the Devices menu on the VM and clicking Insert Guest Additions CD image.
Mount the Guest Additions ISO and install.
mount /dev/cdrom /mnt /mnt/VBoxLinuxAdditions.run
Format the second disk in the VM with btrfs. This will be used for the docker images.
Query the UUID of /dev/sdb. This will be used in the next step.
Create the Docker mount config file /etc/systemd/system/var-lib-docker.mount. Change the UUID to match that of /dev/sdb from the previous step.
[Unit] Description = Docker Image Store [Mount] What = UUID=<UUID from previous step> Where = /var/lib/docker Type = btrfs [Install] WantedBy = multi-user.target
Enable the Docker mount.
systemctl enable var-lib-docker.mount
Create the Docker configuration file.
echo 'OPTIONS=-s btrfs' > /etc/sysconfig/docker
Copy the Docker service to systemd to allow you to start and stop the service with systemctl.
cp /usr/lib/systemd/system/docker.service /etc/systemd/system/docker.service
Enable and then start Docker.
systemctl enable docker systemctl start docker
Pull down the Oracle Linux Docker image. This will require a connection to the internet.
docker pull oraclelinux
View the images currently stored on the VM. This may include more than one since the Oracle Linux Docker image is versioned.
Install the necessary packages in a container to support the database. The docker run command will create a new container from the image and start it. It will only run as long as the command takes to complete. After it completes. The container will still exist but will not be running.
MYCOMMAND='yum -y --enablerepo=ol7_addons install oracle-rdbms-server-12cR1-preinstall vim bzip2 xorg-x11-xauth xeyes xhost' docker run --name oraol7 oraclelinux $MYCOMMAND
List the container that was just created. Running the docker ps command without the -a parameter will only show the running containers. Since the container we created is no longer running, it would not show up with only the docker ps command.
docker ps -a
Commit the container just created to a new image. The new image will be a copy of the container but can also be used to create new containers.
docker commit oraol7 oraol7
Now you can see an additional image available in the Docker images. Notice the virtual size of the image just created is significantly larger than the original oraclelinux images.
The container has been committed to an image so the changes made to the original image have been saved. You can now delete the container.
docker rm oraol7
Notice the container no longer exists.
docker ps -a
Load the Oracle DB12c binaries onto the VM or mount through a shared folder. The files in this case are in the /mnt directory.
Start vncserver on the host VM. Connect to the vnc session on your PC or directly on the VM.
Create a container from the oraol7 image and run it in interactive mode with a bash shell. Give the container a hostname of db12c. Create two directory mappings between the host and the container; one for the DB12c binaries and the other to forward the X11 traffic.
docker run -it --name oraol7 -h db12c -v /mnt:/mnt -v /tmp/.X11-unix:/tmp/.X11-unix oraol7 /bin/bash
You are now at a prompt inside of the running container.
[root@db12c /]$ hostname db12c
Confirm that an X11 application started inside of the container appears in the VNC session.
export DISPLAY=:1.0 xeyes
Give the rest of the users in the container OS permission to use X11 applications. This is necessary because we need to launch the database installer as oracle. By default, only root has permissions to launch X11 applications.
Create a directory in the container for the DB12c install and change the ownership.
mkdir /u01 chown oracle:oinstall /u01
Change user to oracle and start the DB12c installer. The installer application will appear in the VNC session.
su - oracle export DISPLAY=:1.0 /mnt/database/runInstaller
When the install is complete, the database should be running inside of the container.
[oracle@db12c /]$ ps -ef | grep [p]mon oracle 23 1 0 20:04 ? 00:00:00 ora_pmon_orcl
Shut down the database.
While connected to the container and still the oracle user, create two scripts in the /home/oracle directory called start.sh and shut.sh.
#!/bin/bash export ORAENV_ASK=NO export ORACLE_SID=orcl . oraenv echo $ORACLE_HOME lsnrctl start sqlplus / as sysdba << EOF select status from v\$instance; startup select status from v\$instance; exit; EOF while test $(pgrep ora_pmon_orcl); do sleep 5 done
#!/bin/bash export ORAENV_ASK=NO export ORACLE_SID=orcl . oraenv echo $ORACLE_HOME lsnrctl stop sqlplus / as sysdba << EOF select status from v\$instance; shutdown immediate; select status from v\$instance; exit; EOF
Exit from the container and commit the container to an image.
docker ps -a docker commit oraol7 oraol7
The oraol7 image virtual size is now close to 12GB.
Remove the oraol7 container. This is a necessary step because the next step creates a new container with different settings.
docker rm oraol7
Create a new container from the saved oraol7 image. This container will run in the background and start up the database instance. As long as the database instance is running, the container will continue to be active.
docker run -it -d --name oraol7 -h db12c -u oracle oraol7 /home/oracle/start.sh
List the containers. Notice there is one container that has a status of Up.
The database is running inside of the oraol7 container. You can now connect to that running container in a separate interactive session.
docker exec -it oraol7 /bin/bash
While connected to the oraol7 container, try connecting to the database to confirm it is running.
. oraenv ORACLE_SID =  ? orcl sqlplus system/welcome1@localhost/orcl
Containers can be stopped and started. Stop the oraol7 container.
docker stop oraol7
Now try starting it again.
docker start oraol7
You may run into an error having to do with systemctl when trying to start an existing container. This is a bug. The workaround is to stop the systemd scope listed in the error message.
systemctl stop docker-ebe362b4a4cd81fbf7fa5fef4870a32a423f8586ff187679bebb7e9c77c8f3de.scope
Then try starting the container again.
docker start oraol7
When you want to stop the database and the container it is running in, you could simply kill the container. However, killing the container would cause the database to be in an inconsistent state. The container can be closed cleanly with a consistent database using the shut.sh script. When the instance is stopped, the original session which ran the /home/oracle/start.sh will stop automatically.
docker exec -it oraol7 /home/oracle/shut.sh
Keep in mind that any changes that have been made are currently in the container, not the original image. In order to preserve those changes, you would need to commit the container to an image once again. To continue to use the same image name without overwriting the current image as we did previously, you can use a tag.
docker commit oraol7 oraol7:load1
Now you have a container with a database that you can start and stop but it doesn’t do you a lot of good if you can only access it from within the container so we need to make the database (more specifically the listener) accessible from outside the container.
Remove the existing container and start a new one based on the last saved image but this time expose port 1521 on the container to the host OS.
docker rm oraol7 docker run -it -d --name oraol7 -h db12c -u oracle -p 1521 oraol7:load1 /home/oracle/start.sh
Find the IP address of the running container.
[root@localhost ~]# docker inspect oraol7 | grep [I]PAddress "IPAddress": "172.17.0.19",
Connect to the container and add the IP address of the container to the listener.ora.
docker exec -it oraol7 /bin/bash vim /u01/app/oracle/product/12.1.0/dbhome_1/network/admin/listener.ora
LISTENER = (DESCRIPTION_LIST = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = 172.17.0.19)(PORT = 1521)) (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521)) ) )
Restart the database listener.
. oraenv ORACLE_SID =  ? orcl lsnrctl stop; lsnrctl start
You may need to re-register the database service to the listener.
sqlplus / as sysdba alter system register;
Back out on the host OS, I can now connect to the database through port 1521. In this case, I downloaded the Oracle Instant Client and connected to the IP address of the container using EZ Connect.