Reputation: 1774
I'm looking for a solution to have both a development and a production environment in my project using docker, docker-compose and nodejs.
How do I approach this?
Basically what I want is a command to start my docker production environment, and a command to start my development environment (which could use nodemon for example).
Here is my Dockerfile
FROM node:13-alpine
RUN mkdir /app
WORKDIR /app
COPY . /app
RUN npm install
RUN npm run build
EXPOSE 1234
CMD ["npm", "run", "prod"] # <--- Have a possibility to run something like "npm run dev" here instead
docker-compose.yml
version: "3"
services:
findus:
build: .
ports:
- "1234:1234"
links:
- mongo
container_name: myapp
mongo:
image: mongo
restart: always
ports:
- "4444:4444"
package.json
// ...
"scripts": {
"build": "tsc",
"dev": "nodemon source/index.ts",
"prod": "node build/index.js"
},
// ...
Upvotes: 15
Views: 21764
Reputation: 16900
You can make use of entrypoint and pass the command to the docker container. Then you can use docker-compose inharitance to launch compose for the environment that you want and append command to the entrypoint.
Dockerfile :
FROM node:13-alpine
RUN mkdir /app
WORKDIR /app
COPY . /app
RUN npm install
RUN npm run build
EXPOSE 1234
ENTRYPOINT ["npm", "run"]
Main docker-compose.yml :
version: "3"
services:
findus:
build: .
ports:
- "1234:1234"
links:
- mongo
container_name: myapp
mongo:
image: mongo
restart: always
ports:
- "4444:4444"
And then have two docker-compose files to append the command passed to the image entry point. For development - docker-compose.dev.yml :
version: "3"
services:
findus:
command: dev
and docker-compose.prod.yml :
version: "3"
services:
findus:
command: prod
Then to launch dev environment :
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
and for prod environment :
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up
So the command
will be appended to the ENTRYPOINT
instruction.
Also this approach could work with enviroment variables if you wanted to pass the command as environment variable. You can find more information in the docs.
Upvotes: 13
Reputation: 2536
You can create a structure like this:
docker-compose.yml --> docker-compose.dev.yml docker-compose.prod.yml
Where the base configuration resides in docker-compose.yml
, while environment-specific info such as ports or user credentials would be in docker-compose.dev.yml
or docker-compose.prod.yml
And then you can run the dev environment with:
docker-compose \
-f docker-compose.yml \
-f docker-compose.dev.yml \
up -d
Or the prod environment with:
docker-compose \
-f docker-compose.yml \
-f docker-compose.prod.yml \
up -d
Upvotes: 1
Reputation: 1123
One way to do it is to create two "targets" on your Dockerfile like this:
Dockerfile:
FROM node:13-alpine As development
RUN mkdir /app
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM node:13-alpine as production
ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}
WORKDIR /app
COPY package*.json ./
RUN npm install --only=production
COPY . .
COPY --from=development /app/dist ./dist
CMD ["node", "dist/index.js"]
And them on your docker-compose run only the Development version:
version: '3.7'
services:
main:
container_name: main
build:
context: .
target: development
command: npm run dev
...
So on your dev environment, you can run:
docker-compose up
and them in prod you can run docker directly with
docker run --target production
I'm assuming that when you run "npm run build" you are generating a dist/production folder so it's better to run node there instead of running straight at index.js on your root folder.
Upvotes: 2