Dockerizing a MEAN-stack application - Part 2
If you haven’t read Part 1, it might be a good idea to at least skim through it, because I’ll refer to the files and folders in this post.
The source code of the project can be downloaded from my GitHub page.
Although I have the Docker image set up for the server, it will throw an error if I start running a container off of it, because
server.ts tries connecting to the database.
I chose to use MongoDB as it can be seen from the source code.
So I went to Docker Hub and searched for the official Mongo image to get some instructions on how to set up the container.
I used the official
mongo image, so I didn’t create any dockerfile for the database. Instead, I set up a
docker-compose.yml file to start both the server and the database with just one command.
docker-compose is used for development, but again, this application will never reach real production and will never have real users. My goal here was to simulate a possible stack of the production environment.
At this point, it looked something like this:
version: '3' services: db: image: mongo:latest container_name: mongo-movie-app-container volumes: - mongo-data:/data/db networks: - api-network node-api: build: context: . args: - mongodb_container - app_env image: node-express-movie-api container_name: node-movie-api-container env_file: .env networks: - api-network depends_on: - db volumes: mongo-data: networks: api-network:
The db service
I initially created two services.
db service is for the MongoDB database. The
image is the latest official Mongo image (
mongo:latest, available from Docker Hub). I was happy to use the image created by the guys at MongoDB, they probably know what they are doing.
I like logging container messages, so I decided to give names to the containers. Docker will name by default, but I prefer using my custom names. In this case, terminal logs related to this container will be displayed as
The next part is important. I created a named volume called
mongo-data, where the saved movies are stored and fetched from.
volumes section inside the
db service has the named volume (
mongo-data) first, this refers to the host (my computer). The second segment of the volume definition is the path to the database inside the MongoDB container (
/data/db), separated by a colon (
The named volume also needs to be declared in a separate
volumes section at the
This way the data will be persistent. If I run the app now and save some movies to the database, and then stop the containers and run them again tomorrow, the saved movies will be available. This is really cool, because I don’t have to install MongoDB on my computer, and I can still use the database.
One more thing left from the
db service, and this is the
networks. I wanted the database and the server containers to communicate with each other, so I set up a common network (
Containers connected to the same network open up their ports to each other. This way I don’t have to declare the port 27017 in my
docker-compose.yml and the database won’t be available from outside, only through my server. I can call the route endpoints directly, and Docker will know how to forward the request from the server container to the database.
networks needs to be declared at the
The node-api service
The structure for the server container is slightly different from the database. I created a separate dockerfile (
Dockerfile, see Part 1) for this image and some extra configurations are needed in
build configuration contains two definitions.
context: . refers to
Dockerfile, which is in the same folder as
docker-compose.yml, hence the
. in the configuration. It means that the image will be built based on
Dockerfile, which is the default file name for a dockerfile. I also displayed
args as an array, these are the build-time arguments I refer to in
Dockerfile (see Part 1).
container_name define the name of the image and container, respectively.
A few words on .env
I have an
env_file definition here, which refers to a
.env file created in the same (root) folder. The
.env file contains all environment variables that are necessary to run the application.
It looks like this:
# .env in the root folder MONGO_URI=mongodb://mongo-movie-app-container:27017/db-production PORT=80 API_KEY=XXXXXXXX
docker-compose starts the containers, it will find the environment variable definitions in the
.env file, and makes them available for the server inside the Docker container.
server.ts refers to the
MONGO_URI is used by Mongoose to set up the connection and
PORT is used by
app.listen to set up the server port.
API_KEY is the open movie database API key, with which I can fetch data from the database.
Two important things here.
MONGO_URI contains the name of the MongoDB container. This is the exact same name I defined in
docker-compose.yml for the database container.
So there’s no
With this, Docker will know which container to connect to, and this will obviously be the container called
mongo-movie-app-container. The connection to the database still occurs on port 27017, and Docker will manage this by itself.
db-production are the values for the
app_env build time arguments from
Dockerfile. Please refer to Part 1, I’ll show the dockerfile I created for the server there.
PORT is equal to
80. This is because the only port exposed to the world will be the default port, which is 80. This will be managed by the client-side container and the nginx server (coming in Part 3).
Back to node-api
node-api service will also be connected to the
api-network, as it was discussed above.
depends_on configuration means that the server container depends on the database. The database should start first and then the server, otherwise we’ll get a MongoDB connection error.
Start the app
The backend of the application is now ready and can be started with the
docker-compose up command. We need to navigate to the root folder in the terminal, where
docker-compose.yml is located.
Once the app has started, the endpoints can be tested with Postman or curl. We are now able to perform the CRUD-operations on the Mongo database.
This is the end of Part 2 of the dockerizing the movie app series. One piece, the client side, is still missing from the puzzle, and this will be covered in the last part.
Thanks for reading and see you next time.