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

0
Would love your thoughts, please comment.x
()
x