Sean Scott is an Oracle ACE with over 25 years experience IN Oracle technologies

Oracle 19c on Docker: Install Oracle via RPM (Part I)

Oracle 19c on Docker: Install Oracle via RPM (Part I)

This is the first in a multi-part series on building Docker images for Oracle 19c. In this first part I show how to create a Dockerfile to build images that use the Oracle 19c RPM instead of the “traditional” method where the Oracle Home is unzipped into a target directory. I’ll also demonstrate how to apply a Release Update (RU) to the Oracle Home during the image build to produce a native Oracle 19.7 or 19.8 database.

In the next installment I cover some of the other unique requirements for running Docker images that use the RPM-based installation. In the third part I describe a method to merge multiple images together, creating containers with a low-version Oracle home and database (11g, 12c, 18c) running alongside a preconfigured 19c database home, ready for testing and practicing upgrades to Oracle 19c!

Building images that take advantage of the RPM installer requires a departure from the builds offered in Oracle’s Docker repository. Installing and creating a database is handled differently. Database creation, for example, is addressed in an init script installed as part of the RPM itself, so there’s no need for createDB.sh. The installation response files and template response file aren’t needed, nor is a script to install the database binaries (at least the way I do it).

Let’s dig in, shall we?

For my builds I manage the environment in the Dockerfile a little differently. I prefer a clean environment for containers so I take advantage of Docker’s ARG to set values within the build instead of persisting them in the container’s environment. Using ARG also allows users to pass values when running a container to provide more flexibility and customization in the resulting environment.

FROM oraclelinux:7-slim as base
MAINTAINER Sean Scott <sean.scott@viscosityna.com>

# Set ARGs, keep the environment clean
ARG ORACLE_BASE=/opt/oracle
ARG ORACLE_HOME=/opt/oracle/product/19c/dbhome_1
ARG INSTALL_DIR=/opt/install
ARG ORACLE_SID=ORCLCDB

# ORACLE_DOCKER_INSTALL=true is required for the 19c RPM on Docker
ENV ORACLE_BASE=$ORACLE_BASE \
    ORACLE_HOME=$ORACLE_HOME \
    ORACLE_SID=$ORACLE_SID \
    ORACLE_VERSION=19c \
    PDB_NAME=ORCLPDB \
    PDB_COUNT=1 \
    ORACLE_DOCKER_INSTALL=true \
    SQLPATH=/home/oracle \
    SETUP_DB=setupDB.sh \
    RUN_FILE=runOracle.sh \
    CONFIG_FILE=oracledb_ORCLCDB-19c.conf \
    INIT_FILE=oracledb_ORCLCDB-19c \
    CHECK_DB_STATUS=checkDBStatus.sh

ENV PATH=$ORACLE_HOME/bin:$ORACLE_HOME/OPatch/:/usr/sbin:$PATH \
    CLASSPATH=$ORACLE_HOME/jlib:$ORACLE_HOME/rdbms/jlib \
    LD_LIBRARY_PATH=$ORACLE_HOME/lib:/usr/lib \
    TNS_ADMIN=$ORACLE_HOME/network/admin

If you’re not already familiar with multi-stage builds, the FROM / as syntax tells Docker to create an intermediate image called base that will be used in subsequent steps. Creating a base image provides greater freedom to manipulate things later and also helps reduce image size. (An Oracle 19.7 image, built without multiple stages, is around 13GB. Using multiple stages it’s 9.3GB. Oracle 19.8 is 16.3GB and 9.5GB respectively.)

Note that I’m using ARG to set a default value for ORACLE_BASE, ORACLE_HOME and ORACLE_SID, then using these in the ENV setup. When building an image I can pass a custom value for any of these variables using the -build-arg option.

The RPM installation expects some values including ORACLE_VERSION and ORACLE_DOCKER_INSTALL. Customizing the database means I also need to manipulate the configuration and initialization files set in CONFIG_FILE and INIT_FILE.

For those worried that all these extra lines will add layers to the image, don’t fret. They do create steps but not layers and add nothing to the size of the image itself.

Next I’m copying files to the ORACLE_BASE:

COPY $CHECK_DB_STATUS $SETUP_DB $RUN_FILE $ORACLE_BASE/

There’s nothing remarkable happening here, besides a much smaller file count!

The next step does nearly all of the OS configuration. I also add some packages I like:

# Build base image with 19c preinstall and Things I Like To Have (epel, git, less, rlwrap, strace, vi), all optional.
# file-5.11, openssl, sudo are necessary (file-5.11 = prereq for 19c RPM, sudo for startup via init.d)
RUN yum -y update; yum -y install oracle-database-preinstall-19c oracle-epel-release-el7 file-5.11-36.el7.x86_64 git less openssl strace sudo vi which && \
    # Create directories, replace OPatch, own things, permissions
    mkdir -p  && \
    chown -R oracle:oinstall $ORACLE_BASE $INSTALL_DIR && \
    chmod ug+x $ORACLE_BASE/*.sh && \
    sync && \
    yum -y install rlwrap && \
    # Create the entrypoint:
    ln -s $ORACLE_BASE/scripts /docker-entrypoint-initdb.d && \
    # Manage the oracle user:
    echo oracle:oracle | chpasswd && \
    # Let oracle run rpm config:
    echo "oracle ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/oracle && \
    chmod 0440 /etc/sudoers.d/oracle && \
    yum clean all && \
    rm -fr $INSTALL_DIR /tmp/* /var/cache/yum

The 19c preinstallation RPM adds the oracle user and groups, after which I create directories and set ownership.

The most interesting thing is probably adding oracle to the sudoers list. With an RPM installation the configuration and initialization is performed by the root user. Giving oracle unlimited sudo privileges is simpler than narrowing things to specific commands and it’s convenient for containers I use in my personal environment.

The preceding stage in this multi-stage build created the base image. The next stage installs Oracle, updates OPatch, and applies a Release Update. As before, it begins with a FROM and sets some ARG values:

FROM base as builder

ARG DBRPM=oracle-database-ee-19c-1.0-1.x86_64.rpm
ARG DBRU=p31281355_190000_Linux-x86-64.zip
ARG DBRU_ID=31281355
ARG OPATCH=p6880880_190000_Linux-x86-64.zip
ARG INSTALL_DIR=/opt/install

The RPM and patch files are copied to the image:

COPY --chown=oracle:oinstall $DBRPM $DBRU $OPATCH $INSTALL_DIR/

Next, root installs Oracle from the RPM and sets ownership and permissions for two files:

USER root
RUN yum -y localinstall $INSTALL_DIR/oracle-database-ee-19c-1.0-1.x86_64.rpm && \
    # Make the config file editable by oracle:
    chown root:oinstall /etc/sysconfig/$CONFIG_FILE /etc/init.d/$INIT_FILE && \
    chmod 664 /etc/sysconfig/$CONFIG_FILE

As noted above, the configuration and initialization files are special files needed for this installation method. The configuration file is just that—configuration information used during database creation. The initialization file is used by the Linux init process to start the database and listener.

With the database software installed we can proceed to update OPatch and apply the Release Update:

USER oracle
RUN unzip -oq -d $ORACLE_HOME $INSTALL_DIR/$OPATCH && \
    unzip -oq -d $INSTALL_DIR $INSTALL_DIR/$DBRU && \
    # Apply the RU
    $ORACLE_HOME/OPatch/opatch apply -silent $INSTALL_DIR/$DBRU_ID && \
    rm -fr $INSTALL_DIR/*

It’s not technically necessary to remove the contents of the installation directory but old habits, as they say!

This concludes the second stage. At this point the build has created two images, base and builder, that will be combined to create the final image. As you might expect, this begins by starting with the base image and setting some environment variables:

FROM base

ENV ORACLE_BASE=$ORACLE_BASE \
    ORACLE_HOME=$ORACLE_HOME \
    ORACLE_SID=$ORACLE_SID \
    ORACLE_VERSION=19c \
    PDB_NAME=ORCLPDB \
    PDB_COUNT=1 \
    ORACLE_DOCKER_INSTALL=true \
    INSTALL_TMP=Docker_Database.dbc \
    SETUP_DB=setupDB.sh \
    RUN_FILE=runOracle.sh \
    CONFIG_FILE=oracledb_ORCLCDB-19c.conf \
    INIT_FILE=oracledb_ORCLCDB-19c \
    CHECK_DB_STATUS=checkDBStatus.sh

ENV PATH=$ORACLE_HOME/bin:$ORACLE_HOME/OPatch/:/usr/sbin:$PATH \
    CLASSPATH=$ORACLE_HOME/jlib:$ORACLE_HOME/rdbms/jlib \
    LD_LIBRARY_PATH=$ORACLE_HOME/lib:/usr/lib \
    TNS_ADMIN=$ORACLE_HOME/network/admin

This is followed by copying the ORACLE_BASE directory from the builder image and setting its ownership to oracle:oinstall, the /etc directory that includes the init and config files added by the RPM installation, and copying a database configuration template into the DBCA template directory under the ORACLE_HOME:

USER oracle
COPY --chown=oracle:oinstall --from=builder $ORACLE_BASE $ORACLE_BASE
COPY --from=builder /etc /etc
COPY --chown=oracle:oinstall $INSTALL_TMP $ORACLE_HOME/assistants/dbca/templates/

This template replaces the response file you may be familiar with from running DBCA “normally.” More on that in the second part of this blog.

The final section should be familiar and remains effectively the same as the builds provided in the Oracle Docker repo:

WORKDIR /home/oracle

VOLUME ["$ORACLE_BASE/oradata"]
EXPOSE 1521 5500
HEALTHCHECK --interval=1m --start-period=5m \
   CMD "$ORACLE_BASE/$CHECK_DB_STATUS" >/dev/null || exit 1

# Define default command to start Oracle Database.
CMD exec $ORACLE_BASE/$RUN_FILE

This sets the working directory, exposes container resources (ports and mount point), initiates the health check, and executes the run file.

In the next installment I cover the remaining files and tweaks needed to run an RPM build. All files for this build are available in my GitHub repository.

Oracle 19c on Docker: Install Oracle via RPM (Part II)

Oracle 19c on Docker: Install Oracle via RPM (Part II)

What's Your Downtime Opportunity?

What's Your Downtime Opportunity?