I am trying to learn Docker and Docker Compose to set up a small example node/express server. I am running Docker toolbox on Windows 10 Home. Below is my project structure
root/
docker-compose.yml
Dockerfile
index.js
package.json
This is my Dockerfile
FROM node:8
WORKDIR /usr/app
COPY . .
RUN npm install --quiet
This is my docker-compose.yml
version: "2"
services:
node:
build: .
working_dir: /usr/app
environment:
- NODE_ENV=production
expose:
- "3000:3000"
command: "node index.js"
This is my package.json
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.16.4"
}
}
and this is my index.js
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => res.send('Hello World!'))
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
I have mixed together various examples found on docs for each of the components. But I couldn't get it to run. When I do docker-compose up I get the following :
$ docker-compose up
Building node
Step 1/5 : FROM node:8
8: Pulling from library/node
61be48634cb9: Pull complete
fa696905a590: Pull complete
b6dd2322bbef: Pull complete
32477089adb4: Pull complete
febe7209ec28: Pull complete
4364cbe57162: Pull complete
ace5c680ff94: Pull complete
4acd6a9b7a48: Pull complete
Digest: sha256:7b65413af120ec5328077775022c78101f103258a1876ec2f83890bce416e896
Status: Downloaded newer image for node:8
---> 82c0936c46c1
Step 2/5 : WORKDIR /usr/app
---> Running in 454ed6735e6e
Removing intermediate container 454ed6735e6e
---> 62d35be2db4c
Step 3/5 : COPY package.json .
---> fdbb25021d83
Step 4/5 : RUN npm install --quiet
---> Running in e535ef0bc18f
npm WARN test#1.0.0 No description
npm WARN test#1.0.0 No repository field.
added 48 packages from 36 contributors and audited 121 packages in 3.179s
found 0 vulnerabilities
Removing intermediate container e535ef0bc18f
---> 1a61e872c386
Step 5/5 : COPY . .
---> 56e765cf1c3c
Successfully built 56e765cf1c3c
Successfully tagged root_node:latest
WARNING: Image for service node was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating root_node_1 ... done
Attaching to root_node_1
node_1 | module.js:550
node_1 | throw err;
node_1 | ^
node_1 |
node_1 | Error: Cannot find module '/usr/app/index.js'
node_1 | at Function.Module._resolveFilename (module.js:548:15)
node_1 | at Function.Module._load (module.js:475:25)
node_1 | at Function.Module.runMain (module.js:694:10)
node_1 | at startup (bootstrap_node.js:204:16)
node_1 | at bootstrap_node.js:625:3
root_node_1 exited with code 1
I did come across my googling that there is some issue with Windows and file permissions but I couldn't find anything more detailed about this.
EDIT 1 : I have removed volume from docker-compose.yml and it works fine when I run with Docker CE for MacOS. The same thing cannot still find index.js in Windows with Docker tools
Would you please try the below.
In your index.js, expose port is 3000, so we will expose the same in the DockerFile.
In docker-compose.yml, 5000 to 3000 mapping of port is done. Where 5000 will be host (Windows) and 3000 client (Linux).
Dockerfile:
FROM node:8
WORKDIR /usr/app
COPY package.json .
RUN npm install --quiet
COPY . .
EXPOSE 3000
CMD [ "npm", "start" ]
docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- "5000:3000"
Related
When I try to deploy a container using docker-compose, I get the following error:
testing |
testing | > test#1.0.0 start
testing | > npm-run-all --parallel start:server
testing |
testing |
testing | ERROR: "start:server" exited with 243.
testing exited with code 1
This only happens on any node:18.4.0 images. I have to use that node version.
My Dockerfile:
FROM node:18.4.0-alpine3.16
WORKDIR /app
COPY ./package.json ./
COPY ./package-lock.json ./
RUN npm install
COPY . /app
EXPOSE 80
CMD npm start
My docker-compose
version: '2'
services:
testing:
container_name: testing
build:
context: .
volumes:
- '.:/app'
ports:
- 80
- 9009:9009
My app (index.js):
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
My package.json
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "npm-run-all --parallel start:server",
"start:server": "nodemon .",
"start:web": "echo web starting"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.1",
"nodemon": "^2.0.18"
},
"devDependencies": {
"npm-run-all": "^4.1.5"
}
}
os: Ubuntu 20.04.4 LTS.
docker-compose: version 1.29.2
docker: Docker version 20.10.12, build 20.10.12-0ubuntu2~20.04.1
Maybe you found the problem, but this can help someone:
I was havin the same issue using node:bullseye image that comes with npm v8.13.0, so I updated it to the latest version (v8.15.1, in my case) an it was solved.
So, to keep using this image with the latest version, i put this in Dockerfile:
RUN npm install -g npm#latest
You have a volume mapping onto the /app path. That hides everything in that path in the image, including the npm packages you install when building your Dockerfile.
Also, your port mappings don't match your app. Your app listens on port 3000, so your should map port 3000 to a host port.
If you use this docker-compose file, it'll work.
version: '2'
services:
testing:
container_name: testing
build:
context: .
ports:
- 3000:3000
Then you can go to http://localhost:3000/ and you'll get your "Hello World!" message.
First of all, In your index.js you exposed port 3000 const port = 3000.
But on docker you exposed 80 and 3009
ports:
- 80
- 9009:9009
A tip - you don't have to COPY ./package.json ., you copied the entire folder into the container.
WORKDIR /app
COPY . /app
RUN npm install
EXPOSE 80
CMD npm start
I am getting a npm ERR! enoent ENOENT: no such file or directory, open '/usr/src/app/package.json' error currently with the below docker setup or a error TS2307: Cannot find module 'Actions' or its corresponding type declarations- i think its a case that the paths are not found in tsconfig.json during the build or i am not COPYing the correct directory/volume as part of the Dockerfile. Have spent multiple days working through different path configs / setups, any help getting this to build would be greatly appreciated.
Would love to see a node / TS / docker / mysql project example if there are any in the community to share - have found it difficult to find opensource projects to compare this to for hints.
...
"paths": {
"Actions/*": [
"Actions/*"
],
}
docker-compose
version: '3.8'
services:
app:
image: app:latest
container_name: balanced-money-backend
build:
context: .
dockerfile: Dockerfile
# TODO investigate uid and gid, how does it get in - from a startup script? Think it needs to be added like user: $UID:$GID if my cmd calls a setup to id on host machine. Needs more investigation.
depends_on:
db:
condition: service_healthy
env_file:
- .env
restart: always
volumes:
- .:/var/www/
command: npm start
ports:
- $NODE_LOCAL_PORT:$NODE_DOCKER_PORT
environment:
- DB_HOST=$MYSQL_HOST
- DB_USERNAME=$MYSQL_USER
- DB_PORT=$MYSQL_DOCKER_PORT
- DB_PASSWORD=$MYSQL_PASSWORD
- DB_DATABASE=$MYSQL_DATABASE
db:
image: mysql:5.7
restart: always
container_name: balanced-money-database
environment:
- MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD
- MYSQL_USER=$MYSQL_USER
- MYSQL_PASSWORD=$MYSQL_PASSWORD
- MYSQL_DATABASE=$MYSQL_DATABASE
ports:
- $MYSQL_LOCAL_PORT:$MYSQL_DOCKER_PORT
volumes:
- db:/var/lib/mysql
healthcheck: # mysql does not start immediatly, app needs to wait for mysql to start, having condition: service_healthy on app and a healthcheck makes sure db has started before app... i think.
test: mysqladmin ping -h 127.0.0.1 -u $$MYSQL_USER --password=$$MYSQL_PASSWORD
timeout: 20s
retries: 10
volumes:
db:
Dockerfile
############### Stage 1 - build the project
# use alpine version of node to keep the image size small as possible
FROM node:16-alpine AS build
# node docs recommend this
WORKDIR /usr/src/app
# docker caches per row as it builds, so copy those files which do not change often to the container first and following builds will not need copy as they are already cached by Docker.
COPY package*.json ./
COPY src tsconfig.json ./
RUN npm install
RUN npm run build
# TODO not sure about the stages - can i have a test / dev stage so test / dev is run in docker too.
############### Stage 2 - run the project
FROM build AS prod
EXPOSE 4000
# from stage 1, i.e. build take the code in the dist / package.json and copy to the container
COPY --from=build /usr/src/app/dist ./dist/
COPY --from=build /usr/src/app/package*.json ./
# npm ci will install exact versions from a package-lock file, and --production will only install dependencies, not dev dependencies.
RUN npm ci --production && npm cache clean --force
# make sure user is not root which could have security consequences.
USER node
CMD ["node", "dist/index.js"]
package.json scripts
"scripts": {
"build": "tsc",
"start": "node ./dist/index.js",
"node": "./dist/index.js",
"dev": "NODE_ENV=development DOTENV_CONFIG_PATH=.env.dev nodemon ts-node src/index.ts",
"format:prettier": "prettier --config .prettierrc 'src/**/*.ts' --write",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"test": "DOTENV_CONFIG_PATH=.env.test NODE_ENV=test jest --runInBand",
"test:coverage": "DOTENV_CONFIG_PATH=.env.test NODE_ENV=test jest --coverage",
},
I am following Node.js's "Intro to Docker" tutorial and, when I run npm start, the project works. When I run docker run (options), the build is generated, but I'll find the error below in the logs. The project is bare-bones, simple, and straight-forward, I'm not sure what I'm missing here. I've gotten a very similar error in production earlier (to Heroku, without Docker), where local runs look good and live deploys get a similar error.
I'm not sure if I'm using something outdated, but I updated npm, docker, and am not sure what else could be.
Any help is appreciated!
Error:
internal/modules/cjs/loader.js:969
throw err;
^
Error: Cannot find module '/usr/src/app/server.js'
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:966:15)
at Function.Module._load (internal/modules/cjs/loader.js:842:27)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
at internal/main/run_main_module.js:17:47 {
code: 'MODULE_NOT_FOUND',
requireStack: []
}
Directory:
package.json:
{
"name": "SampleProject",
"version": "1.0.0",
"description": "Node.js on Docker",
"author": "First Last <first.last#example.com>",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dock": "docker run -p 1234:1234 -d <My>/<Info>"
},
"dependencies": {
"core-util-is": "^1.0.2",
"express": "^4.17.1"
}
}
Dockerfile
# I'm using Node.js -> Import it's image
FROM node:12
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm#5+)
COPY package*.json ./
RUN npm install
# If you are building your code for production
# RUN npm ci --only=production
# Run on port 1234
EXPOSE 1234
CMD [ "node", "server.js" ]
server.js
'use strict';
const express = require('express');
// Constants
const PORT = 1234;
const HOST = '0.0.0.0';
// App
const app = express();
app.get('/', (req, res) => {
res.send('Hello World');
});
app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);
Run:
npm run dock
Note:
I've also cleaned out the project by running the following:
rm -rf node_modules package-lock.json && npm install && npm start
RESOLVED:
Dockerfile
# I'm using Node.js -> Import it's image
FROM node:12
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm#5+)
COPY package*.json ./
RUN npm install
# If you are building your code for production
# RUN npm ci --only=production
# * BEGIN SOLUTION *
# Bundle app source
COPY . .
# * END SOLUTION *
# Run on port 1234
EXPOSE 1234
CMD [ "node", "server.js" ]
I think you are missing the following important part, should be placed after you have RUN npm install:
To bundle your app's source code inside the docker image, use the COPY instruction:
# Bundle app source
COPY . .
And to force the execution of each step in the Dockerfile,
docker build --no-cache
I have angular 8 as client side, server side as node and mysql as bd
server app is up on localhost:3000
node package.json:
"scripts": {
"start": "node src/index.ts",
"test": "echo \"Error: no test specified\" && exit 1",
"watch-changes": "nodemon --watch 'src/**/*' -e ts,tsx --exec ts-node src/index.ts"
},
angular just builds into dist folder and i use one from server side:
angular package.json:
"scripts": {
"ng": "ng",
"start": "ng serve --host 0.0.0.0",
"build": "ng build",
........
index.ts
app.use(express.static(join(__dirname, '../../file-manager/dist/file-manager')));
app.use(json());
app.use(urlencoded({extended: true}));
app.use('/api', new Router().routes);
app.all('/*', function(req, res, next) {
res.sendFile('index.html', { root: join(__dirname, '../../file-manager/dist/file-manager') });
});
app.use((err, req, res, next) => {
if(err) {
res.status(500).json({message: err.human || 'Internal Server Error'});
return next(err);
}
return next();
});
console.log('PORT IS ', port);
app.listen(port);
\file-manager.docker\dev.dockerfile
FROM node:10
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
COPY package.json ./
RUN npm install
COPY . .
EXPOSE 4200
CMD ["npm", "start"]
\server.docker\dev.dockerfile
FROM node:10
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "run", "watch-changes"]
When try docker-compose down and after docker-compose up:
version: '3'
services:
client:
build:
context: ./file-manager
dockerfile: .docker/dev.dockerfile
image: file-manager-client-dev
container_name: file-manager-client-dev
ports:
- "4000:4200"
volumes:
- ./file-manager/src:/usr/src/app/src
server:
build:
context: ./server
dockerfile: .docker/dev.dockerfile
image: file-manager-server-dev
container_name: file-manager-server-dev
ports:
- "8000:3000"
external_links:
- client
depends_on:
- mysql
mysql: # name of the third service
build:
context: ./mysql
dockerfile: .docker/dev.dockerfile
container_name: mysql-container
restart: always
ports:
- "3306:3306"
and enter http://localhost:8000/ I see
{"message":"Internal Server Error"}
on white page of browser
When I 'ng build' and 'npm run watch-changes' separately all works
docker-compose ps
file-manager-client-dev docker-entrypoint.sh npm start Up 0.0.0.0:4000->4200/tcp
file-manager-server-dev docker-entrypoint.sh npm r ... Up 0.0.0.0:8000->3000/tcp
mysql-container docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp, 33060/tcp
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
file-manager-client-dev latest 24d9f5c92c12 17 minutes ago 1.51GB
file-manager-server-dev latest 02b5dd9cac83 25 minutes ago 1.03GB
angular-8-file-manager_mysql latest 4191cb9d3c3f 26 minutes ago 437MB
node 10 a68faf70e589 4 days ago 904MB
node latest 977e59b234b7 4 days ago 908MB
mysql 5 cd3ed0dfff7e 5 days ago 437MB
logs
docker container logs file-manager-server-dev
[nodemon] 1.19.1
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: src/**/*
[nodemon] starting `ts-node src/index.ts`
Error: ENOENT: no such file or directory, stat '/usr/src/file-manager/dist/file-manager/index.html'
Error: ENOENT: no such file or directory, stat '/usr/src/file-manager/dist/file-manager/index.html'
docker tries find index.html in file-manager-server-dev image but need to look for in file-manager-client-dev, how to specify it? Help please
I am trying to mount my working node code from my host into a docker container and run it using nodemon using docker-compose.
But container doesn't seems to be able to find nodemon.
Note: My host machine does not has node or npm installed on it.
Here are the files in the root folder of my project (test). (This is only a rough draft)
Dockerfile
FROM surenderthakran/nodejs:v4
ADD . /test
WORKDIR /test
RUN make install
CMD make run
Makefile
SHELL:=/bin/bash
PWD:=$(shell pwd)
export PATH:= $(PWD)/node_modules/.bin:$(PWD)/bin:$(PATH)
DOCKER:=$(shell grep docker /proc/1/cgroup)
install:
#echo Running make install......
#npm config set unsafe-perm true
#npm install
run:
#echo Running make run......
# Check if we are inside docker container
ifdef DOCKER
#echo We are dockerized!! :D
#nodemon index.js
else
#nodemon index.js
endif
.PHONY: install run
docker-compose.yml
app:
build: .
command: make run
volumes:
- .:/test
environment:
NODE_ENV: dev
ports:
- "17883:17883"
- "17884:17884"
package.json
{
"name": "test",
"version": "1.0.0",
"description": "test",
"main": "index.js",
"dependencies": {
"express": "^4.13.3",
"nodemon": "^1.8.0"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"api",
"nodejs",
"express"
],
"author": "test",
"license": "ISC"
}
index.js
'use strict';
var express = require('express');
I build my image using docker-compose build. It finishes successfully.
But when I try to run it using docker-compose up, I get:
Creating test_app_1...
Attaching to test_app_1
app_1 | Running make run......
app_1 | We are dockerized!! :D
app_1 | /bin/bash: nodemon: command not found
app_1 | make: *** [run] Error 127
test_app_1 exited with code 2
Gracefully stopping... (press Ctrl+C again to force)
Can anyone please advice?
Note: The Dockerfile for my base image surenderthakran/nodejs:v4 can be found here: https://github.com/surenderthakran/dockerfile_nodejs/blob/master/Dockerfile
The issue has been resolved. The issue boiled down to me not having node_modules in the mounted volume.
Basically, while doing docker-compose build the image was build correctly with the actual code being added to the image and creating the node_modules folder by npm install in the project root. But with docker-compose up the code was being mounted in the project root and it was overriding the earlier added code including the newly created node_modules folder.
So as a solution I compromised to install nodejs on my host and do a npm install on my host. So when the code my being mounted I still got my node_modules folder in my project root because it was also getting mounted from my host.
Not a very elegant solution but since it is a development setup I am ready for the compromise. On production I would be setting up using docker build and docker run and won't be using nodemon anyways.
If anyone can suggest me a better solution I will be greatful.
Thanks!!
I believe you should use a preinstall script in your package.json.
So, in the script section, just add script:
"scritpts": {
"preinstall": "npm i nodemon -g",
"start": "nodemon app.js",
}
And you should good to go :)
Pretty late for an answer. But you could use something called as named volumes to mount your node_modules in the docker volumes space. That way it would hide your bind mount.
You need to set the node_modules as a mounted volume in the docker container.
e.g
docker-compose.yml
app:
build: .
command: make run
volumes:
- .:/test
- /test/node_modules
environment:
NODE_ENV: dev
ports:
- "17883:17883"
- "17884:17884"
I've figured out how to do this without a Dockerfile, in case that's useful to anyone...
You can run multiple commands in the docker-compose.yml command line by using sh -c.
my-server:
image: node:18-alpine
build: .
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
working_dir: /usr/src/app
ports:
- "3100:3100"
command: sh -c "npm install -g nodemon && npm install && npm run dev "
environment:
NODE_ENV: development
PORT: 3100