MEAN Application and Docker Compose

Brandon Jones
4 min readNov 12, 2020

Docker compose can be a really powerful tool. Typically, when I’m getting ready to create a K8s cluster, I will create Dockerfiles for all the individual services. While I’m working on this, I will begin adding them to a docker-compose file. It’s an extremely useful method for spinning up multiple containers with a single command. Once all the containers are running and working properly, I’ll know everything is good to go and I’m ready for next steps. Also, if an application doesn’t need to rapidly scale horizontally, then maybe docker compose is all you’ll need. As much as get excited about setting up K8s clusters, they aren’t always necessary. It really just depends on your needs regarding the amount of traffic you’re expecting. Either way, it’s a pretty awesome feeling to spin up multiple containers with a single command, especially when it works properly!

“With great power comes great responsibilityPeter Parker principle

I’m assuming that you already have a MEAN application or application with a similar stack ready to decouple. I will post my application here, so you may use that as a reference as well.

First, the different services need to be separated from each other. Create three folders, one for the frontend, one for the backend, and one for the mongodb. In each of these folders add a Dockerfile. Place all the Angular files in the frontend folder and all the express files in the backend folder. Also, create a docker-compose.yaml file in the root directory.

- frontend/
- Dockerfile
- backend/
- Dockerfile
- mongo/
- Dockerfile
- docker-compose.yaml

Since the express service will be connecting to a local mongo container, make sure to edit the connection for the database. It’s actually super easy to connect to a local mongo container. Check out how I set mine up below.

const dbHost = 'mongodb://mongo:27017/pup'    mongoose.connect(dbHost)
.then(()=>{console.log("Connected to database")})
.catch(()=>{console.log("Connection failed"); });

Let’s get started with creating these Dockerfiles. The contents of mine below should work with any Angular application. You should be able to copy and paste the contents of mine below. The file copies over the package.json file and installs all the dependencies. Then copies all the remaining files. The next RUN command builds the Angular project and uses nginx for a base to run the project.

FROM node:12.16.1-alpine As builder 
WORKDIR /usr/src/app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build --prod
FROM nginx:1.15.8-alpine
COPY --from=builder /usr/src/app/dist/pup/ /usr/share/nginx/html

Next, create the express Dockerfile. This file is very similar to the frontend Dockerfile, but without the build commands. It uses node as a base, then creates a /usr/src/app directory. The package.json files are copied over and installed. Port 4000 is exposed because that’s the port the node server is listening on. Finally, the server is started by running npm run start.

FROM node:latest as node
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app
RUN npm install
COPY . /usr/src/app
EXPOSE 4000
CMD ["npm","run","start"]

The mongodb Dockerfile is a little more complex. The contents of my file can be copied and it should work. Basically, everything needed to run a mongo container is installed, a volume is created, and port 27017 is exposed since this is the default port for mongo. A volume is absolutely necessary if you’re considering running a db in a container, since containers by nature are ephemeral.

FROM debian:jessie-slim
RUN apt-get update && \
apt-get install -y ca-certificates && \
rm -rf /var/lib/apt/lists/*
RUN gpg --keyserver ha.pool.sks-keyservers.net --recv-keys 0C49F3730359A14518585931BC711F9BA15703C6 && \
gpg --export $GPG_KEYS > /etc/apt/trusted.gpg.d/mongodb.gpg
ARG MONGO_PACKAGE=mongodb-org
ARG MONGO_REPO=repo.mongodb.org
ENV MONGO_PACKAGE=${MONGO_PACKAGE} MONGO_REPO=${MONGO_REPO}
ENV MONGO_MAJOR 3.4
ENV MONGO_VERSION 3.4.18
RUN echo "deb http://$MONGO_REPO/apt/debian jessie/${MONGO_PACKAGE%-unstable}/$MONGO_MAJOR main" | tee "/etc/apt/sources.list.d/${MONGO_PACKAGE%-unstable}.list"
RUN echo "/etc/apt/sources.list.d/${MONGO_PACKAGE%-unstable}.list"
RUN apt-get update
RUN apt-get install -y ${MONGO_PACKAGE}=$MONGO_VERSION
VOLUME ["/data/db"]
WORKDIR /data
EXPOSE 27017
CMD ["mongod", "--smallfiles"]

The docker-compose file is what ties all the containers together. This is where all the networking between the containers takes place. Under services, you can see each of the containers that we’ll be running. You can also add configurations, for example restarting the containers if they’re stopped for any reason. You can see this under the express service.

version: '3' 
services:
frontend:
build: front
ports:
- "4200:80"
express:
build: back
restart: always
ports:
- "3000:3000"
links:
- mongo
mongo:
container_name: mongo
build: mongo
ports:
- "27017:27017"
volumes:
- ./mongodbdata:/data/db

Finally, run the docker-compose up command and add the -build flag to build all the docker containers while bringing them up for the first time. This may take a few minutes, but you should see logs showing everything was successful.

docker-compose up --build

That’s all there is to it! At this point, you should be able to go to localhost:4200 and see your application running. If there were any issues, go back and check all the logs generated during the build process. Also, sometimes the first build isn’t successful, but the second one will be. It’s easy for something to go wrong when spinning up several containers. I recommend trying to rebuild the containers by running the docker-compose up -build command again. If that doesn’t work, begin checking the logs to see where the error occurred.

As always, feel free to reach out if any issues do arise during the process.

Automate the planet!

@brandon_jones08

--

--

Brandon Jones

SysAdmin, Devops, Containers, Networks, Automation, Fiddle, Banjo, Pups