Before we start building our docker ecosystem, let’s talk a little bit about a very important aspect of using docker. Networking… You must to know how your APP can connect to database container (mongo) when connecting using http://localhost:27017, and why this will not work when running APP from with in container.
Based on where your APP will be launched, the connection string will have to look differently. So, you need to edit the APP src code connection string. Because they will work in one or the other setup, but not in both. If you run APP from your host machine (not from docker container) you will refer to localhost. See the mongoUrlLocal
mongodb connection string variable construction below:
...
/* somewhere in server.js */
// use when starting application locally
let mongoUrlLocal = "mongodb://root:root@localhost:27017";
// use when starting application as docker container
let mongoUrlDocker = "mongodb://root:root@mongodb";
...
// use when starting application locally
let mongoUrlLocal = "mongodb://root:root@localhost:27017";
// use when starting application as docker container
let mongoUrlDocker = "mongodb://root:root@mongodb";
// pass these options to mongo client connect request to avoid DeprecationWarning for current Server Discovery and Monitoring engine
let mongoClientOptions = { useNewUrlParser: true, useUnifiedTopology: true };
// "user-account" in demo with docker. "my-db" in demo with docker-compose
let databaseName = "my-db";
app.post('/update-profile', function (req, res) {
let userObj = req.body;
MongoClient.connect(mongoUrlDocker, mongoClientOptions, function (err, client){
...
});
...
If you run APP files from with in container (Dockerfile, docker-compose would build a image from APP files) then you use container –name as DNS to access those services. See the mongoUrlDocker
above. If you wonder why does it refer to mongodb
, the answer is simple, it uses container --name
which are mapped with in docker network to containers IP and PORTS. This is a docker magic.
What would be all you need to understand before you continue with building this doceriezed environment.
Pull images
docker pull mongo
docker pull mongo-express
Read more anout those images and available options at:
https://hub.docker.com/_/mongo-express
https://hub.docker.com/_/mongo
Create network
You can run docker in network using cmd run command. However, we will not use docker networking for our Dockerfile and docker-compose.yaml, docker always create default network. Just so you know, you can create your own networks:
docker network create mongo-network
If you need to get docker machine IP address on any OS read this How to get the IP of docker machine on Windows / WSL / Linux?
Run containers
Run mongodb
The default port for mongo is 27017. Now let’s map ports and assign those containers to the created network.
docker run -d \
-p 27017:27017 \
-e MONGO_INITDB_ROOT_USERNAME=root \
-e MONGO_INITDB_ROOT_PASSWORD=root \
--net mongo-network \
--name mongodb \
mongo
Run mongo-express
docker run -d \
-p 8081:8081 \
-e ME_CONFIG_MONGODB_ENABLE_ADMIN=true \
-e ME_CONFIG_MONGODB_ADMINUSERNAME=root \
-e ME_CONFIG_MONGODB_ADMINPASSWORD=root \
-e ME_CONFIG_MONGODB_SERVER=mongodb \
--net mongo-network \
--name mongo-express \
mongo-express
Download APP files
cd
<your-custom-dir>
to the directory of your choice.
Clone app from github: mongodb-express-js-app-basic-setup.
git clone https://github.com/DevWL/mongodb-express-js-app-basic-setup.git .
Note the .
at the end. This will copy project file to the current directory without creating an additional wrapper directory.
Run demo app from host machine using CLI
You can run the app on your host machine without containerizing it. Just be sure to use the right connection string in your application src. You would use let mongoUrlLocal = "mongodb://root:root@localhost:27017";
Remember to place the mongoUrlLocal
in every database call.
Lets run our simple JS app. To do that, cd
to the app
directory of the project. You need to run npm install
and npm run start
or run node server.js
. Now your app should to be accessible on port 3000 of localhost machine. In Windows host terminal type Start-Process "http://localhost:3000"
on Linux type xdg-open "http://localhost:3000"
both will open the default browser at http://localhost:3000/.
Adding Dockerfile
If you pulled the GitHub project, this file is already there. I will expose it content in case you would not want to clone the above repository.
FROM node:13-alpine
ENV MONGO_DB_USERNAME=root \
MONGO_DB_PWD=root
RUN mkdir -p /home/app
COPY ./app /home/app
# set default dir so that next commands executes in /home/app dir
WORKDIR /home/app
# will execute npm install in /home/app because of WORKDIR
RUN npm install
# no need for /home/app/server.js because of WORKDIR
CMD ["node", "server.js"]
Now you can build docker image. Navigate to directory with Dockerfile
and run:
docker build -t my-app:1.0 .
Now you would need to run
all container. Usually you would do that using docker-compose.yaml file running docker compose
up
with -d
flags.
Note! For educational purpose only. You can also run containers using docker run
instead. Copy & run the fallowing commands:
docker rm -f mongodb && docker run -d -p 27017:27017 -e MONGO_INITDB_ROOT_USERNAME=root -e MONGO_INITDB_ROOT_PASSWORD=root --net mongo-network --name mongodb mongo
docker rm -f mongo-express && docker run -d -p 8080:8081 -e ME_CONFIG_MONGODB_ENABLE_ADMIN=true -e ME_CONFIG_MONGODB_ADMINUSERNAME=root -e ME_CONFIG_MONGODB_ADMINPASSWORD=root -e ME_CONFIG_MONGODB_SERVER=mongodb --net mongo-network --name mongo-express mongo-express
docker rm -f nxapp && docker build -t nxapp . && docker run -p 3000:3000 -di --name nxapp --net mongo-network nxapp
Now run docker ps
. You should have three containers running like in the example below:
Let’s clean up our containers, so they do not conflict with the containers ports when we run docker compose up instead. Run:
docker rm -f mongo-express && docker rm -f mongodb && docker rm -f nxapp
Creating dockercompose.yaml from the above commands
Since docker will automatically generate a default network for our app, we do not need to specify –net in our dockercompose file.
version: '3.9'
services:
# nxapp: # use this if running app from the presisted image instead of building it from git src + Dockerfile. Also be sure to presist database data in the image and import it in Dockerfile.
# image: devwl/nxapp:1.0 # <dockerhub-username>/<remote-imgname:tag>
# ports:
# - 3000:3000
nxapp:
build: . # Download src from GitHub, cd do dir where Dockerfile is placed. Build the image.
ports:
- 3000:3000
mongodb:
image: mongo
ports:
- 27017:27017
environment:
- MONGO_INITDB_ROOT_USERNAME=root
- MONGO_INITDB_ROOT_PASSWORD=root
volumes:
- mongo-data:/data/db
mongo-express:
image: mongo-express
restart: always # fixes MongoNetworkError when mongodb is not ready when mongo-express starts
ports:
- 8080:8081
environment:
- ME_CONFIG_MONGODB_ADMINUSERNAME=root
- ME_CONFIG_MONGODB_ADMINPASSWORD=root
- ME_CONFIG_MONGODB_SERVER=mongodb
volumes:
mongo-data:
driver: local
Download Project from GitHub
List all containers with docker ps
-a
. Kill all running containers with docker kill -f X Y Z
. Repalce the XYZ letter with the container ID/tag name. Now cd
to project folder and run:
docker compose build && docker compose up -d
Now mongodb and mongo express should now be working and ready to talk with each other. You should be able to access them in your host browser at http://localhost:8080/
If you connect to http://localhost:3000 and see a blank page after which docker container stops . It is more likely that you did not change the connection string in server.js file. NOTE that you need to edit this file in each instance if MongoClient.connect(<chang-param> , ...
in our case that would be in two lines. If you change only one, you will get white screen. And if you pick container logs you will see that your APP can not connect to mongo (ECONNREFUSED).
Persist data in docker – Add Volumes
We already added Volume in our docker file, but you should get familiar with how docker persist and map data from with containers. You can define a path where the direcotry where the voulme is presisted, but if you do not do that your data will be pressited in docker default directory.
Where docker persist data by default?
Data will be persisted in different directory based on OS system. Docker by default would persist data in a VM environment.
Docker volume path on Windows 10 + WSL2:
ls \\wsl$\docker-desktop-data\data\docker\volumes
Docker volume path on Windows 10 + HyperV:
Your volume directory would be at /var/lib/docker/volumes/blog_postgres-data/_data
, and /var/lib/docker
usually mounted in C:\Users\Public\Documents\Hyper-V\Virtual hard disks
.
Docker volume path on Linux (Ubuntu)
ls /var/lib/docker/volumes
Docker volume path on Mac OS
Login to VM on MAC: screen ~/Library/Containers/com.docker.docker/Data/vms/0/
and then $ ls /var/lib/docker/volumes
You can also place your app file with a newly created monster.
Closing project
To shout down your docker container run
docker compose down
Creating private docker repository on AWS
https://stackoverflow.com/questions/51011552/mongodb-on-with-docker-failed-to-connect-to-server-localhost27017-on-first-c
https://stackoverflow.com/questions/72942336/containerized-app-can-not-access-mongo-database-econnrefused