Build NodeJS project with Docker
My blog: https://ai-research.dev/build-nodejs-project-with-docker/
First of all, you must have an overview about Docker Compose at https://docs.docker.com/compose/
In the root project, we must create the docker-compose.yml file to define the necessary services for a NodeJS project.
Inside the package.json file, we define 3 new commands for running Docker:
// package.json
{
...
"scripts": {
...
"docker:up": "docker-compose up",
"docker:down": "docker-compose down",
"docker:restart": "docker-compose restart",
...
},
...
}
1. MySQL service
Documentation: https://hub.docker.com/_/mysql
Create a new Dockerfile for MySQL service at docker/services/mysql/ folder.
In the .env file, we also need to define some variables that will be used in the Dockerfile.
- DOCKER_APP_NAME
- DOCKER_MYSQL_PORT
- DOCKER_MYSQL_PORTS
- DOCKER_MYSQL_DB_NAME
- DOCKER_MYSQL_ROOT_PASSWORD
- DOCKER_MYSQL_PASSWORD
- DOCKER_MYSQL_PORT
// Dockerfile
FROM mysql:8
// .env
# Docker
DOCKER_APP_NAME=nodejs-sample
DOCKER_MYSQL_PORT=3307
DOCKER_MYSQL_PORTS=3307:3307
DOCKER_MYSQL_DB_NAME=nodejs-sample
DOCKER_MYSQL_ROOT_PASSWORD=root
DOCKER_MYSQL_USER=root
DOCKER_MYSQL_PASSWORD=root
// docker-compose.yml
...
mysql:
build:
context: .
dockerfile: ./docker/services/mysql/Dockerfile
restart: always
container_name: ${DOCKER_APP_NAME}_mysql
networks:
- net
expose:
- ${DOCKER_MYSQL_PORT}
ports:
- "${DOCKER_MYSQL_PORTS}"
volumes:
- dbdata:/var/lib/mysql/
environment:
- MYSQL_DATABASE=${DOCKER_MYSQL_DB_NAME}
- MYSQL_ROOT_PASSWORD=${DOCKER_MYSQL_ROOT_PASSWORD}
- MYSQL_PASSWORD=${DOCKER_MYSQL_PASSWORD}
- MYSQL_TCP_PORT=${DOCKER_MYSQL_PORT}
- MYSQL_ROOT_HOST=%
command: [
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_unicode_ci',
'--default-authentication-plugin=mysql_native_password'
]
2. Node service
Documentation: https://hub.docker.com/_/node
Add more some variables in the .env file:
- DOCKER_APP_PATH
- DOCKER_NODE_PORTS
Create a new Dockerfile for Node service in the docker/services/node folder.
// .env
DOCKER_APP_PATH=/var/www/nodejs-sample
DOCKER_NODE_PORTS=5555:5555
// Dockerfile
FROM node:14.14.0-alpine
RUN apk update && apk upgrade && apk add --no-cache \
bash \
curl \
gettext && \
npm install -g nodemon && \
npm install -g pm2 && \
npm rebuild bcrypt --build-from-source
// docker-compose.yml
node:
restart: on-failure
depends_on:
- mysql
build:
context: .
dockerfile: ./docker/services/node/Dockerfile
container_name: ${DOCKER_APP_NAME}_node
working_dir: ${DOCKER_APP_PATH}
networks:
- net
ports:
- "${DOCKER_NODE_PORTS}"
entrypoint: sh /bin/node/command.sh
volumes:
- .:${DOCKER_APP_PATH}
- ${DOCKER_APP_PATH}/node_modules
- ./docker/services/node/command-${NODE_ENV}.sh:/bin/node/command.sh
environment:
- DB_HOST=${DOCKER_APP_NAME}_mysql
- DB_USER=${DOCKER_MYSQL_USER}
- DB_PASSWORD=${DOCKER_MYSQL_PASSWORD}
- DB_NAME=${DOCKER_MYSQL_DB_NAME}
- DB_PORT=${DOCKER_MYSQL_PORT}
- NODE_ENV=${NODE_ENV}
- PORT=${PORT}
- TOKEN_SECRET_KEY=${TOKEN_SECRET_KEY}
As you can see, we’ve added some new configs for node service
- entrypoint: sh /bin/node/command.sh It means that the system will run command.sh file in the building process
Where is command.sh from? On the next lines, you can see the config: ./docker/services/node/command-${NODE_ENV}.sh:/bin/node/command.sh
Depends on the running environment, the system will map the appropriate command file. In the docker/services/node folder, we will create two new files.
- command-development.sh for development env.
- command-production.sh for production env.
The difference between them is that while command-development.sh will run the application in the development environment with nodemon, command-production.sh will build & run application in the production environment with pm2-runtime.
// command-development.sh
#!/usr/bin/env bash
yarn install && yarn dev
// command-production.sh
#!/usr/bin/env bash
yarn install && yarn build && cd dist && pm2-runtime start index.js
The docker-compose.yml file for both mysql & node services:
// docker-compose.yml
version: '3'
services:
mysql:
build:
context: .
dockerfile: ./docker/services/mysql/Dockerfile
restart: always
container_name: ${DOCKER_APP_NAME}_mysql
networks:
- net
expose:
- ${DOCKER_MYSQL_PORT}
ports:
- "${DOCKER_MYSQL_PORTS}"
volumes:
- dbdata:/var/lib/mysql/
environment:
- MYSQL_DATABASE=${DOCKER_MYSQL_DB_NAME}
- MYSQL_ROOT_PASSWORD=${DOCKER_MYSQL_ROOT_PASSWORD}
- MYSQL_PASSWORD=${DOCKER_MYSQL_PASSWORD}
- MYSQL_TCP_PORT=${DOCKER_MYSQL_PORT}
- MYSQL_ROOT_HOST=%
command: [
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_unicode_ci',
'--default-authentication-plugin=mysql_native_password'
]
node:
restart: on-failure
depends_on:
- mysql
build:
context: .
dockerfile: ./docker/services/node/Dockerfile
container_name: ${DOCKER_APP_NAME}_node
working_dir: ${DOCKER_APP_PATH}
networks:
- net
ports:
- "${DOCKER_NODE_PORTS}"
entrypoint: sh /bin/node/command.sh
volumes:
- .:${DOCKER_APP_PATH}
- ${DOCKER_APP_PATH}/node_modules
- ./docker/services/node/command-${NODE_ENV}.sh:/bin/node/command.sh
environment:
- DB_HOST=${DOCKER_APP_NAME}_mysql
- DB_USER=${DOCKER_MYSQL_USER}
- DB_PASSWORD=${DOCKER_MYSQL_PASSWORD}
- DB_NAME=${DOCKER_MYSQL_DB_NAME}
- DB_PORT=${DOCKER_MYSQL_PORT}
- NODE_ENV=${NODE_ENV}
- PORT=${PORT}
- TOKEN_SECRET_KEY=${TOKEN_SECRET_KEY}
networks:
net:
driver: bridge
volumes:
dbdata:
driver: local
Running the defined command yarn docker:up to start the Docker. The nodejs application has been built.
Your nodejs app has been working on the port 5555. Open a new terminal tab to see list containers by the command docker container ls
You can see that you’ve had two containers for node & mysql services. Node service has been running at port 5555 & mysql service has been running at port 3307.
Source code: https://github.com/duongduckien/nodejs-sample/tree/feature/docker