Configuring adminer for Oracle Databases

If you are not familiar with adminer, you can read more about it here. In short, it is a PHP script that allows you to manage various databases via a single interface. We have been successfully using the adminer official docker image from docker hub for a number of different databases. We now have a need to add Oracle as one of those databases to use with adminer. This initially seemed like a simple task. The docker hub page states


“To add support for the other drivers you will need to install the following PHP extensions on top of this image:

oci8 (Oracle)”


I’ve configured Oracle Clients on a number of different machines. I’ve also had my fair share of adding PHP extensions to PHP installations in a past life. I then realized that the adminer image is based off of Alpine. This added some complexity. I was lucky enough that my coworker had started down the rabbit hole so I only needed to do some cleanup to get it over the line.

The Initial Dockerfile

I was able to get a trimmed down Dockerfile going that allowed me to get everything installed. I was also able to get oci8 to compile successfully. The below Dockerfile is where I got stuck:

FROM adminer
USER root

RUN apk --no-cache add libaio libnsl libc6-compat curl && \
  cd /tmp

ENV ORACLE_BASE /usr/local/oracle
ENV LD_LIBRARY_PATH /usr/local/oracle/instantclient_21_7:/lib64
ENV TNS_ADMIN /usr/local/oracle/instantclient_21_7/network/admin
ENV ORACLE_HOME /usr/local/oracle

# # Install Oracle Client and build OCI8 (Oracle Command Interface 8 - PHP extension)
RUN \
## Download and unarchive Instant Client v21
  mkdir /usr/local/oracle && \
  curl -o /tmp/sdk.zip https://download.oracle.com/otn_software/linux/instantclient/217000/instantclient-sdk-linux.x64-21.7.0.0.0dbru.zip && \
  curl -o /tmp/basic_lite.zip https://download.oracle.com/otn_software/linux/instantclient/217000/instantclient-basiclite-linux.x64-21.7.0.0.0dbru.zip && \
  unzip -d /usr/local/oracle /tmp/sdk.zip && \
  unzip -d /usr/local/oracle /tmp/basic_lite.zip

## Build OCI8 with PECL
RUN \
  C_INCLUDE_PATH=/usr/local/oracle/instantclient_21_7/sdk/include/ docker-php-ext-configure oci8 --with-oci8=instantclient,/usr/local/oracle/instantclient_21_7 && docker-php-ext-install oci8

#  Clean up
RUN \
  rm -rf /tmp/*.zip /var/cache/apk/* /tmp/pear/
USER adminer

My coworker was attempting to build this using Oracle 11 so I wanted to use the 21.x libraries to have the latest greatest. In order to cover this a little, you need to have the both the Basic Light Package and the SDK Package for oci8 to compile as per their requirements page. I downloaded the Linux x64 zip files for this build from Oracle’s download page. In addition to these, you’ll need the libraries referenced in the apk add command. In order for a successful compile, we need to make sure we have the SDK’s include directory passed to the docker-php-ext-configure command using the C_INCLUDE_PATH variable. The LD_LIBRARY_PATH statement also covers the Oracle library files as well as the additional libraries that will be installed in the /lib64 directory by apk add.

Dealing With The Last Error

I felt it was important to point out the exact error that I was getting up to this point so that everyone could see it and how to resolve it. With the above Dockerfile, I was able to successfully build my image.

% docker build -t adminer:local_test .
[+] Building 34.7s (10/10) FINISHED                                                                                                                                                   
 => [internal] load build definition from Dockerfile                                                                                                                             0.0s
 => => transferring dockerfile: 1.25kB                                                                                                                                           0.0s
 => [internal] load .dockerignore                                                                                                                                                0.0s
 => => transferring context: 2B                                                                                                                                                  0.0s
 => [internal] load metadata for docker.io/library/adminer:latest                                                                                                                1.1s
 => [auth] library/adminer:pull token for registry-1.docker.io                                                                                                                   0.0s
 => [1/5] FROM docker.io/library/adminer@sha256:ffef52f1cdc49e9877f8e73b62afe358396241fd3d018dd6e55bc865c2d58c87                                                                 5.3s
 => => resolve docker.io/library/adminer@sha256:ffef52f1cdc49e9877f8e73b62afe358396241fd3d018dd6e55bc865c2d58c87                                                                 0.0s
 => => sha256:0cdd6cb15c0d57bb696f4a1a47b41374e78a6a5f1cdd24c45d8b9655b52caa40 1.26kB / 1.26kB                                                                                   0.4s
 => => sha256:ffef52f1cdc49e9877f8e73b62afe358396241fd3d018dd6e55bc865c2d58c87 1.65kB / 1.65kB                                                                                   0.0s
 => => sha256:a600fdbc30cc6501babd12a4b22d75316d5ae384a68b9b4c588bfba33109bbc0 1.72MB / 1.72MB                                                                                   0.6s
 => => sha256:8967c2c42a6bd768b87f60aa77c0eb983d5c4607bdb668c13abf3f4ddb2bab47 3.45kB / 3.45kB                                                                                   0.0s
 => => sha256:75cd6c93316cb33fc0e5264a601cd3a30e3c869aeff375eab6b38679285f40cd 13.88kB / 13.88kB                                                                                 0.0s
 => => sha256:213ec9aee27d8be045c6a92b7eac22c9a64b44558193775a1a7f626352392b49 2.81MB / 2.81MB                                                                                   0.5s
 => => sha256:8a4c40d8aee7b41f415744811ec585376e2f7798b79ac68e04b7127fb847d566 268B / 268B                                                                                       0.6s
 => => extracting sha256:213ec9aee27d8be045c6a92b7eac22c9a64b44558193775a1a7f626352392b49                                                                                        0.2s
 => => sha256:77e67522f4fd69104b389667cdaa7cbac2c77f9de63edb5cfb9ab7e672232186 10.44MB / 10.44MB                                                                                 2.9s
 => => sha256:0d5c09a73378079aa8f6f1444eaadb178bc2440c63cd31a3fc8f0dec79d26919 15.07MB / 15.07MB                                                                                 3.9s
 => => sha256:d181492ef8e98df214123c11ce13997a4838a452a4e9f972b1f4398696d2b4d3 497B / 497B                                                                                       0.6s
 => => sha256:8bb8e21282b9e4f7015c3ab8bc3b58fa5dbc5920796f2ba25ec626afaf801753 2.44kB / 2.44kB                                                                                   0.9s
 => => extracting sha256:a600fdbc30cc6501babd12a4b22d75316d5ae384a68b9b4c588bfba33109bbc0                                                                                        0.2s
 => => extracting sha256:0cdd6cb15c0d57bb696f4a1a47b41374e78a6a5f1cdd24c45d8b9655b52caa40                                                                                        0.0s
 => => extracting sha256:8a4c40d8aee7b41f415744811ec585376e2f7798b79ac68e04b7127fb847d566                                                                                        0.0s
 => => sha256:fdd7ab990e10e983d38a54116fd52979cbba315f3a7776f55ed1d781179e3716 18.62kB / 18.62kB                                                                                 1.1s
 => => sha256:d59940a6c65c6196df1deb4e5985c2af04867b15aaee854127434268ed6c2acc 303B / 303B                                                                                       1.5s
 => => sha256:8af31b44c60c1e4d3276cfca694f285205514c7d5b1728493fc4e514ebad44ab 1.39kB / 1.39kB                                                                                   1.8s
 => => sha256:8f57d1664c2a86dcfe3bffa01915454678248fa87917c62ffaed9feb0e0ba12f 3.87MB / 3.87MB                                                                                   3.2s
 => => extracting sha256:77e67522f4fd69104b389667cdaa7cbac2c77f9de63edb5cfb9ab7e672232186                                                                                        0.1s
 => => sha256:eafa31c9e9994a03878bcd0284eefa41d846e7ceb0b9c9d8d613585897b8d7dc 1.49kB / 1.49kB                                                                                   3.1s
 => => extracting sha256:d181492ef8e98df214123c11ce13997a4838a452a4e9f972b1f4398696d2b4d3                                                                                        0.0s
 => => sha256:d99ebf4176fd240d317e44f69f61480c5a5d9988b1192edb2b2c05c2c86de1de 873.02kB / 873.02kB                                                                               3.3s
 => => sha256:dd175257ae8d3b87ebd540ce478cde35f0051be838c0decfcfdd8f6219375d89 494B / 494B                                                                                       3.4s
 => => extracting sha256:0d5c09a73378079aa8f6f1444eaadb178bc2440c63cd31a3fc8f0dec79d26919                                                                                        0.7s
 => => extracting sha256:8bb8e21282b9e4f7015c3ab8bc3b58fa5dbc5920796f2ba25ec626afaf801753                                                                                        0.0s
 => => extracting sha256:fdd7ab990e10e983d38a54116fd52979cbba315f3a7776f55ed1d781179e3716                                                                                        0.0s
 => => extracting sha256:d59940a6c65c6196df1deb4e5985c2af04867b15aaee854127434268ed6c2acc                                                                                        0.0s
 => => extracting sha256:8af31b44c60c1e4d3276cfca694f285205514c7d5b1728493fc4e514ebad44ab                                                                                        0.0s
 => => extracting sha256:8f57d1664c2a86dcfe3bffa01915454678248fa87917c62ffaed9feb0e0ba12f                                                                                        0.1s
 => => extracting sha256:eafa31c9e9994a03878bcd0284eefa41d846e7ceb0b9c9d8d613585897b8d7dc                                                                                        0.0s
 => => extracting sha256:d99ebf4176fd240d317e44f69f61480c5a5d9988b1192edb2b2c05c2c86de1de                                                                                        0.1s
 => => extracting sha256:dd175257ae8d3b87ebd540ce478cde35f0051be838c0decfcfdd8f6219375d89                                                                                        0.0s
 => [2/5] RUN apk --no-cache add libaio libnsl libc6-compat curl &&   ln -s /lib/libc.so.6 /usr/lib/libresolv.so.2 &&   cd /tmp                                                  1.2s
 => [3/5] RUN   mkdir /usr/local/oracle &&   curl -o /tmp/sdk.zip https://download.oracle.com/otn_software/linux/instantclient/217000/instantclient-sdk-linux.x64-21.7.0.0.0dbr  6.7s 
 => [4/5] RUN   C_INCLUDE_PATH=/usr/local/oracle/instantclient_21_7/sdk/include/ docker-php-ext-configure oci8 --with-oci8=instantclient,/usr/local/oracle/instantclient_21_7   19.3s 
 => [5/5] RUN   rm -rf /tmp/*.zip /var/cache/apk/* /tmp/pear/                                                                                                                    0.2s 
 => exporting to image                                                                                                                                                           0.8s 
 => => exporting layers                                                                                                                                                          0.8s 
 => => writing image sha256:31d6c9350ab7046253da7d3227b718c1715ca20a1b9974aff7f4898b8300f20f                                                                                     0.0s 
 => => naming to docker.io/library/adminer:local_test                         

The problem is that whenever the container starts and PHP attempts to load the oci8 extension, we get an error.

% docker run adminer:local_test       
[Wed Aug 31 15:13:10 2022] PHP Warning:  PHP Startup: Unable to load dynamic library 'oci8.so' (tried: /usr/local/lib/php/extensions/no-debug-non-zts-20190902/oci8.so (Error loading shared library libresolv.so.2: No such file or directory (needed by /usr/local/oracle/instantclient_21_7/libclntsh.so.21.1)), /usr/local/lib/php/extensions/no-debug-non-zts-20190902/oci8.so.so (Error loading shared library /usr/local/lib/php/extensions/no-debug-non-zts-20190902/oci8.so.so: No such file or directory)) in Unknown on line 0
[Wed Aug 31 15:13:10 2022] PHP 7.4.30 Development Server (https://[::]:8080) started

I searched everywhere for this and landed on an interesting Github repo OracleClient_Alpine. This repo uses Alpine Linux as the basis of the container in their Dockerfile and I noticed a number of symbolic links being created. The most notable link is the one for libresolv.so.2! Whenever I checked the output of ldd against the /usr/local/oracle/instantclient_21_7/libclntsh.so.21.1 file, I noticed this was the only thing that couldn’t be found. I adjusted my Dockerfile to now include that symbolic link:

FROM adminer
USER root

RUN apk --no-cache add libaio libnsl libc6-compat curl && \
  ln -s /lib/libc.so.6 /usr/lib/libresolv.so.2 && \
  cd /tmp

ENV ORACLE_BASE /usr/local/oracle
ENV LD_LIBRARY_PATH /usr/local/oracle/instantclient_21_7:/lib64
ENV TNS_ADMIN /usr/local/oracle/instantclient_21_7/network/admin
ENV ORACLE_HOME /usr/local/oracle

# # Install Oracle Client and build OCI8 (Oracle Command Interface 8 - PHP extension)
RUN \
## Download and unarchive Instant Client v11
  mkdir /usr/local/oracle && \
  curl -o /tmp/sdk.zip https://download.oracle.com/otn_software/linux/instantclient/217000/instantclient-sdk-linux.x64-21.7.0.0.0dbru.zip && \
  curl -o /tmp/basic_lite.zip https://download.oracle.com/otn_software/linux/instantclient/217000/instantclient-basiclite-linux.x64-21.7.0.0.0dbru.zip && \
  unzip -d /usr/local/oracle /tmp/sdk.zip && \
  unzip -d /usr/local/oracle /tmp/basic_lite.zip

## Build OCI8 with PECL
RUN \
  C_INCLUDE_PATH=/usr/local/oracle/instantclient_21_7/sdk/include/ docker-php-ext-configure oci8 --with-oci8=instantclient,/usr/local/oracle/instantclient_21_7 && docker-php-ext-install oci8

#  Clean up
RUN \
  rm -rf /tmp/*.zip /var/cache/apk/* /tmp/pear/
USER adminer

I then run another build and start the container

% docker build -t adminer:local_test .
[+] Building 0.6s (10/10) FINISHED                                                                                                                                                    
 => [internal] load build definition from Dockerfile                                                                                                                             0.0s
 => => transferring dockerfile: 1.25kB                                                                                                                                           0.0s
 => [internal] load .dockerignore                                                                                                                                                0.0s
 => => transferring context: 2B                                                                                                                                                  0.0s
 => [internal] load metadata for docker.io/library/adminer:latest                                                                                                                0.5s
 => [auth] library/adminer:pull token for registry-1.docker.io                                                                                                                   0.0s
 => [1/5] FROM docker.io/library/adminer@sha256:ffef52f1cdc49e9877f8e73b62afe358396241fd3d018dd6e55bc865c2d58c87                                                                 0.0s
 => CACHED [2/5] RUN apk --no-cache add libaio libnsl libc6-compat curl &&   ln -s /lib/libc.so.6 /usr/lib/libresolv.so.2 &&   cd /tmp                                           0.0s
 => CACHED [3/5] RUN   mkdir /usr/local/oracle &&   curl -o /tmp/sdk.zip https://download.oracle.com/otn_software/linux/instantclient/217000/instantclient-sdk-linux.x64-21.7.0  0.0s
 => CACHED [4/5] RUN   C_INCLUDE_PATH=/usr/local/oracle/instantclient_21_7/sdk/include/ docker-php-ext-configure oci8 --with-oci8=instantclient,/usr/local/oracle/instantclient  0.0s
 => CACHED [5/5] RUN   rm -rf /tmp/*.zip /var/cache/apk/* /tmp/pear/                                                                                                             0.0s
 => exporting to image                                                                                                                                                           0.0s
 => => exporting layers                                                                                                                                                          0.0s
 => => writing image sha256:31d6c9350ab7046253da7d3227b718c1715ca20a1b9974aff7f4898b8300f20f                                                                                     0.0s
 => => naming to docker.io/library/adminer:local_test                                                                                                                            0.0s
salgatt@MacBook-Pro-Instart adminer % docker run adminer:local_test       
[Wed Aug 31 15:22:25 2022] PHP 7.4.30 Development Server (https://[::]:8080) started

The error is resolved but now let’s confirm it works!

Access an Oracle Database From adminer

After a little trial and error, I found that adminer is expecting to connect to an Oracle database via TNS Name. If you looked closely at my Dockerfile, you’ll see that I set the environment variable TNS_ADMIN to the path of /usr/local/oracle/instantclient_21_7/network/admin. This means that I’ll need to create a tnsnames.ora file to be used for my connection to my Oracle database. For those that aren’t familiar with tnsnames.ora and its syntax, you should read up on them in Oracle’s documentation. For those that just don’t care and want to connect to a database, you can use my sample below:

oracle_rds = (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=oracle-rds.amazon.com)(PORT=1521))(CONNECT_DATA=(SID=ORCL)))

This is a sample entry that I’m using to connect to an AWS Oracle RDS instance over the non-TLS port. I created this tnsnames.ora file in a directory on my machine and then I re-ran the docker image mounting that directory to /usr/local/oracle/instantclient_21_7/network/admin and expose port 8080 to my local machine like below:

% docker run -v /Users/salgatt/tmp/adminer/admin:/usr/local/oracle/instantclient_21_7/network/admin -p 8080:8080 adminer:local_test
[Wed Aug 31 15:31:42 2022] PHP 7.4.30 Development Server (https://[::]:8080) started

From there, I can open a browser and access adminer.

At the adminer login page, the server should be the TNS Name of your Oracle connection. Given the example above, the server name of oracle_rds should be used. Supply the proper credentials and a database on the server and click Login.

From there, you’ll probably want to access the USERS database (assuming this is where your table space is configured to host user databases). An example of my USERS database is below

It looks like everything is working!

As a fun side note, it also looks like you put the full connection string as the database name too.