Wiring Scala App Docker Image With MongoDB
In the previous blogpost “Dockerizing Scala App”, I described my process of dockerizing a Scala app. In this post, I am going to continue the story by telling how I wired the app with a MongoDB.
The Growth Engine app that I was dockerizing in the previous post depends in its functionality on MongoDB. Therefore, to make it function properly in a Docker container, I had to wire the two, meaning that I needed to find a way to allow the two containers to communicate between each other.
It was achieved by constructing an appropriate docker-compose.yml file which is simply a set of instructions for docker container(s) how to configure them on startup.
The success docker-compose.yml file looks like this:
version: '3services:
growthengine:
image: growthengine:latest
networks:
- network mongodb:
image: mongo:4.0.5
ports:
- "27017:27017"
volumes:
- growth-engine:/data/db
- ./config:/docker-entrypoint-initdb.d
environment:
- MONGO_INITDB_ROOT_USERNAME=myusername
- MONGO_INITDB_ROOT_PASSWORD=mypassword
- MONGO_INITDB_DATABASE=growthEngine
networks:
- networknetworks:
network:
driver: bridgevolumes:
growth-engine:
First, I needed to specify the version of the docker-compose.yml file.
version: '3'
Apparently, Docker went through the process of evolution of docker-compose.yml rules and we need to specify which version we are using so Docker can read the commands properly.
Second, I needed to announce two services that I wanted to wire and configure them.
Let’s have a look at the configurations of the two services:
version: '3services:
growthengine:
image: growthengine:latest
networks:
- networkmongodb:
image: mongo:4.0.5
ports:
- "27017:27017"
volumes:
- growth-engine:/data/db
- ./config:/docker-entrypoint-initdb.d
environment:
- MONGO_INITDB_ROOT_USERNAME=rootusername
- MONGO_INITDB_ROOT_PASSWORD=rootpassword
- MONGO_INITDB_DATABASE=growthEngine
networks:
- networknetworks:
network:
driver: bridgevolumes:
growth-engine:
‘image:’ specifies the name of the docker image that we will use to run containers. If you recall, I created the ‘growthengine’ image in the previous blog post with the tag ‘latest’, so it resides in my local hub with tag ‘growthengine:latest’ and I could see it by running
docker images
command in my console. ‘mongo’ is an image that I do not have locally, but it is present at the Docker hub. If docker cannot find the specified image in the local hub, it looks for it in the Docker hub by its name and tag. I wanted to use a particular version of MongoDB in my app, so I specified it with a tag ‘4.0.5’ in the image name.
Both services are being wired with each other through a network which is being specified in a service’s subfield ‘networks:’. These networks have to be announced in a separate field which also holds the name ‘networks:’ where you also need to provide the connection driver (in my case it was ‘bridge’).
Each network could get any name you like, and to keep things simple, I used the name ‘network’ for my network:
networks:
network:
driver: bridge
which I used later in both of the services:
growthengine:
...networks:
- network
...mongodb:
....networks:
- network
...
The mongodb service required extra configuration. Most of it is being explained on the official mongo Docker Hub page, but still I will shortly explain what they were used for in my context.
‘ports:’ allow to map the 27017th container’s port to 27017th local port, so I could test the app locally by knocking into the 27017th port locally and connect to the 271017th port in the container.
ports:
- "27017:27017"
‘volumes:’ is a bit complicated subject, but keeping things simple, it is just a way to store data in the container.
volumes:
- growth-engine:/data/db
- ./config:/docker-entrypoint-initdb.d
In the first case ‘- growth-engine:/data/db’, we create a ‘named volume’. It basically means that we want to store data safely in a volume called ‘growth-engine’. This volume we need to define first to be able to use it, and I do it later in the end of the docker-compose.yml file with
volumes:
growth-engine:
Volume allows us to delete containers and images, but to keep the data from them, ensuring that it is safe from different accidents.
‘/data/db’ in ‘growth-engine:/data/db’ means that on the startup of the mongodb container, we want to take the data from the volume ‘growth-engine’, and place it in the ‘data/db’ directory.
In the ‘volumes:’ section we have another configuration which has a bit different purpose:
- ./config:/docker-entrypoint-initdb.d
It means, that we want to copy the content of ./config folder (which path is relative to the docker-compose.yml file) to /docker-entrypoint-initdb.d. (which is a directory in container)
/docker-entrypoint-initdb.d. — is the place to put scripts that should be run on the startup of the container. Since I wanted to create a new user in the db, before being able to execute commands from its name in the app, I added the following init.js file in it:
db.createUser({user: 'myuser', pwd: 'mypassword', roles: [{role: 'readWrite', db: 'growthEngine'}]})
The last part of the mongodb service configuration is the environment variables:
environment:
- MONGO_INITDB_ROOT_USERNAME=rootusername
- MONGO_INITDB_ROOT_PASSWORD=rootpassword
- MONGO_INITDB_DATABASE=growthEngine
where we define the startup configurations of the mongoDB. Here you can read more detailed about its application and purpose.
The app and mongodb are wired, and by running ‘docker-compose up’ in my console, I could see starting up of the containers for my app, mongoDB, creation of a user in the mongoDB, and communication between the app and the database.
In the next blog, I will tell the story how I scheduled the app’s execution with the Cron utility.