Today, I was working with an application that uses Oracle as the database. We decided dockerize the application to make it easy for fellow developers to work with the beast. We found a working Oracle docker image by sath89. Oracle 12c Docker image is close to 5.7GB on disk so we are not talking about lightweight containers here :). Once image was dowloaded, running image was as easy as running the following command.
$ docker run -d -p 8080:8080 -p 1521:1521 sath89/oracle-12c
This will start the Oracle database in a container. On my machine, it takes close to 7-8 minutes for the database to become up and running. If you want to keep running container in the background then you just have to run container once and then use it.
We wanted to bootstrap the entire application with a single command using Docker Compose. Also, in the dev mode, we wanted to create database schema using Hibernate’s schema generation utilities. The problem is that Docker Compose does not wait for the Oracle container to become ready before starting application container. For example, let’s suppose we have two services —
web service depends on the
db service as shown in the
docker-compose.yml shown below.
version: '3' services: db: image: sath89/oracle-12c web: image: com.example/myapp environment: SPRING_JPA_HIBERNATE_DDL-AUTO: create SPRING_PROFILES_ACTIVE: docker-oracle depends_on: - db
Now, when you start the application using
docker-compose up then Docker compose does not ensure that
db service is ready before
web service is started. This means your application will fail to start. The
depends_on only means that
db service should be started first but compose does not ensure
db service is full ready for connections. This cause issues in scenario where we need to have strict control.
wait-it-for to the rescue
wait-for-it is a simple bash utility to test and wait for the availability of TCP host and port. The need for
wait-for-it arises when you want to make sure a container is up and running before another container. It took me time to figure out how to use it with Oracle so I am writing it down for fellow developers who might also need to use
wait-for-it in future.
To use it, first copy the wait-for-it.sh in your project directory.
Next, you will update
docker-compose.yml as shown below.
version: '3' services: db: image: sath89/oracle-12c web: image: com.example/myapp environment: SPRING_JPA_HIBERNATE_DDL-AUTO: create SPRING_PROFILES_ACTIVE: docker-oracle entrypoint: ["./wait-for-it.sh","db:8080","--timeout=0","--strict", "--", "java", "-jar","app.jar" ] depends_on: - db
docker-compose.yml we added
entrypoint instruction. The
entrypoint instruction let you identity which executable should run when container is started from image.
As you can see in the
entrypoint value, we are using
wait-for-it to wait for indefinite time for port 8080 in the db container to become accessible. The Oracle docker image exposes management web console at port 8080. Once db container is available we start the application. You should read wait-for-it documentation to understand its document in case you are unclear about meaning of the options.
You will see logs like the following in compose shell.
web_1 | wait-for-it.sh: waiting for db:8080 without a timeout db_1 | Copying database files db_1 | 1% complete db_1 | 3% complete db_1 | 11% complete db_1 | 18% complete db_1 | 37% complete db_1 | Creating and starting Oracle instance db_1 | 40% complete db_1 | 45% complete db_1 | 50% complete db_1 | 55% complete db_1 | 56% complete db_1 | 60% complete db_1 | 62% complete db_1 | Completing Database Creation db_1 | 66% complete db_1 | 70% complete db_1 | 73% complete db_1 | 85% complete db_1 | 96% complete db_1 | 100% complete db_1 | Look at the log file "/u01/app/oracle/cfgtoollogs/dbca/xe/xe.log" for further details. db_1 | Configuring Apex console db_1 | Database initialized. Please visit http://#containeer:8080/em http://#containeer:8080/apex for extra configuration if needed db_1 | Starting web management console db_1 | db_1 | PL/SQL procedure successfully completed. db_1 | db_1 | Starting import from '/docker-entrypoint-initdb.d': db_1 | found file /docker-entrypoint-initdb.d//docker-entrypoint-initdb.d/* db_1 | [IMPORT] /entrypoint.sh: ignoring /docker-entrypoint-initdb.d/* db_1 | db_1 | Import finished db_1 | db_1 | Database ready to use. Enjoy! 😉 web_1 | wait-for-it.sh: db:8080 is available after 432 seconds
That’s it for this blog. I hope this will be useful to you.