Docker container can't find path reference - node.js

I'm attempting to run a node.js server with a React frontend using a Docker container on my local Synology NAS. I was able to get the node.js server functioning using this guide.
I then attempted to add the React front end, however I'm getting this error:
ReferenceError: path is not defined ... at /app/lib/app.js:7
app.use(express.static(path.join(__dirname, 'client/build)));
I'm able to run the server locally, so it seems that this would be an issue related to Docker, but I'm not quite sure where to look to resolve the issue.
For reference, the Dockerfile I'm using:
# test using the latest node container
FROM node:latest AS teststep
WORKDIR /app
COPY package.json .
COPY package-lock.json .
COPY lin ./lib
COPY test ./test
RUN npm ci --development
# test
RUN npm test
# build production packages with the latest node container
FROM node:latest AS buildstep
# Copy in package.json, install
# and build all node modules
WORKDIR /app
COPY package.json .
COPY package-lock.json .
RUN npm ci --production
# This is our runtime container that will end up
# running on the device.
FROM node:alpine
WORKDIR /app
# Copy our node_modules into our deployable container context.
COPY --from=buildstep /app/node_modules node_modules
COPY lib ./lib
# Launch our App.
CMD ["node", "lib/app.js"]
App.js:
const express = require('express')
const app = express()
const path = require('path');
const port = 3000
app.use(express.static(path.join(__dirname, 'client/build')));
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname, 'client/build', 'index.html'));
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`))

The problem was fixed by resolving the "lin" typo, then deleting the existing container and executing the run.sh script.

Related

What is the command needed for an API backend Dockerfile?

I am new to creating Dockerfiles and cannot figure out what command to use to start up the API backend application. I know that backend applications don't use Angular and that the command to start it is not "CMD ng serve --host 0.0.0.0".
I am attaching the code of the backend Dockerfile and also providing the errors that I am getting when trying to run the container in Docker Desktop below.
I have looked at Docker documentation and Node commands but cannot figure out what command to use to make the API backend run. What am I doing wrong?
Code:
# using Node v10
FROM node:10
# Create app directory
WORKDIR /usr/src/lafs
# 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
# Bundle app source
COPY . .
# Expose port 3000 outside container
EXPOSE 3000
# Command used to start application
CMD ng serve --host 0.0.0.0
Errors that I am receiving in Docker Desktop:
/bin/sh: 1: ng: not found
From your original screenshot, it looks like you've got a server directory. Assuming that's where your Express app lives, try something like this
FROM node:16 # 12 and older are EOL, 14 is in maintenance
WORKDIR /usr/src/lafs
EXPOSE 3000 # assuming this is your server port
COPY server/package*.json . # copy package.json and package-lock.json
RUN npm ci --only=production # install dependencies
COPY server . # copy source code
CMD ["npm", "start"] # start the Express server

Error: connect ECONNREFUSED 0.0.0.0:8000 when hitting Docker containerised Node.js app endpoint

I'm just starting with Docker and dough I succeed in creating an image and a container from it
I'm not succeeding in connecting to the container's port with postman and I get Error: connect ECONNREFUSED 0.0.0.0:8000.
In my server.js file I have:
const app = require('./api/src/app');
const port = process.env.PORT || 3000; // PORT is set to 5000
app.listen(port, () => {
console.log('App executing to port ', port);
});
in my index.js I have :
const express = require('express');
const router = express.Router();
router.get('/api', (req, res) => {
res.status(200).send({
success: 'true',
message: 'Welcome to fixit',
version: '1.0.0',
});
});
module.exports = router;
so if I run my app with either npm start or nodemon server.js the localhost:3000/api endpoint works as expected.
I then build a docker image for my app with the command docker build . -t fixit-server with this Dockerfile:
FROM node:15.14.0
# 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 package.json
COPY package-lock.json package-lock.json
RUN npm install
# If you are building your code for production
# RUN npm ci --only=production
# Bundle app source
COPY . .
EXPOSE 5000
# CMD ["npm", "start"]
CMD npm start
# CMD ["nodemon", "server.js"]
and run the container with the command docker run -d -p 8000:5000 --name fixit-container fixit-server tail -f /dev/null
and listing the containers with docker ps -a shows it running :
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
da0e4ef12402 fixit-server "docker-entrypoint.s…" 9 seconds ago Up 8 seconds 0.0.0.0:8000->5000/tcp fixit-container
but when I hit the endpoint 0.0.0.0:8000/apiI get the ECONNREFUSED error.
I tried both CMD ["npm", "start"]and CMD npm start but I get the error both ways.
Can you what I'm doing wrong?
Update:
#Vincenzo was using docker-machine and to be able to check whether the app was working properly, we needed to execute the following command in the terminal:
docker-machine env
The result was:
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.102:2376"
export DOCKER_CERT_PATH="/Users/vinnytwice/.docker/machine/machines/default"
export DOCKER_MACHINE_NAME="default"
Then based on the DOCKER_HOST value, we hit 192.168.99.102:8000/api and it was working.
I believe the problem is you're never setting the PORT environment variable to 5000.
EXPOSE docker command is a no op. Meaning that it will do nothing but is only for the developer to know that you're exposing the port 5000. You can read it in Docker documentation.
You need to either set an environment variable or pass an environment variable at runtime to the container to specifically tell it that PORT is 5000.
Method 1:
You can change your Dockerfile like below:
FROM node:15.14.0
# 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 package.json
COPY package-lock.json package-lock.json
RUN npm install
# If you are building your code for production
# RUN npm ci --only=production
# Bundle app source
COPY . .
ENV PORT=5000
EXPOSE $PORT
# CMD ["npm", "start"]
CMD npm start
# CMD ["nodemon", "server.js"]
Method 2:
Simply use the following command to run your container:
docker run -d -p 8000:5000 --name fixit-container --env PORT=5000 fixit-server

node js docker is not running on heroku

Node js project in Docker container is not running on Heroku.
Here is the source code.
Docker file
FROM node:14
WORKDIR /home/tor/Desktop/work/docker/speech-analysis/build
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 8080
CMD [ "node", "server.js" ]
server.js
'use strict';
const express = require('express');
const PORT = process.env.port||8080;
const app = express();
app.get('/', (req, res) => {
res.send('Hello World');
});
app.listen(PORT);
console.log("Running on http://:${PORT}");
You don't need to expose anything when having a container for Heroku. It takes care of it automatically. If you are running the same Docker locally, you can do:
docker build -t myapp:latest .
docker run -e PORT=8080 -p 8080:8080 -t myapp:latest
I think that the environment variables are case-sensitive on Linux systems - so you need to change the
const PORT = process.env.port||8080;
... to:
const PORT = process.env.PORT||8080;
... as Heroku sets an environment variable PORT (and not port).
According to this answer you just need to use the port 80 in your expose or inside of nodejs:
app.listen(80)
Heroku at run, will generate a random port and bind it to 80
docker run ... -p 46574:80 ...
So if your nodejs app is running at port 80 inside of container, everything will be fine

Serve static react app with nodejs through docker

I have directories for node and react like this:
Project
+ client
+ admin
+ build
+ index.html
+ server_api
+ server.js
+ Dockerfile
+ docker-compose.yml
docker-compose file
server_api:
container_name: speed_react_server_api
build:
context: ./server_api
dockerfile: Dockerfile
image: speed_react/server_api
ports:
- "5000:5000"
volumes:
- ./server_api:/usr/src/app
My server.js serve static file from react app like this:
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, '../client/admin/', 'build', 'index.html'));
});
But it seems that it is not working.
I do not want to move folder client to server
Can you suggest me solution?
You can rearrange files as needed in your Dockerfile.
Note that this will require removing the volumes: line that (probably) causes your entire Dockerfile to get mostly ignored. Your Docker container won't be usable as a development environment. A host Node is very easy to install and features like live reloading and interactive debugging will work much better with much less configuration and tuning required.
If you move the Dockerfile up to the root directory, it can access both directory trees. Then you can use a multi-stage build to build the client and then copy it into the server. That would roughly look like this:
FROM node:14 AS client
WORKDIR /app
COPY client/package.json client/yarn.lock .
RUN yarn install
COPY client .
RUN yarn build
FROM node:14
WORKDIR /app
COPY server_api/package.json server_api/yarn.lock .
RUN yarn install
COPY server_api .
COPY --from=client /app/admin/build admin # <-----
EXPOSE 5000
CMD ["node", "index.js"]
In your code, change the path to path.resolve(__dirname, 'admin', 'index.html'); it should match the destination inside the container filesystem of the COPY --from=client line.
In your host development environment, you can use a symbolic link to point at the files (on MacOS or Linux)
ln -s ../client/admin/build admin
Finally, you need to change your docker-compose.yml to point at the root directory as the build context, and remove the bind mount (which will have a dangling symlink and not the file content).
server_api:
build: .
image: speed_react/server_api
ports:
- "5000:5000"
Update:
One answer is given by #David, that best to build the Docker image with multi-stage.
The other way is to build the only API and as the client has no dependency and so you can just mount clinet with API.
server_api:
container_name: speed_react_server_api
image: abc
ports:
- "4001:4001"
entrypoint: sh -c "cd /usr/src/app/server_api/; node server.js"
volumes:
- ./server_api/:/usr/src/app/server_api/
- ./client/:/usr/src/app/client/
You did not mention the error but I assume the script not serving the distribution.
But seems like you are missing express-static-files configuration.
To serve static files such as images, CSS files, and JavaScript files, use the >express.static built-in middleware function in Express.
The function signature is:
express.static(root, [options])
Here is the complete script
const express = require('express');
const path = require('path');
const app = express();
const PORT=process.env.PORT | 4001
app.use(express.static('../client/admin/build/dist', {
maxAge: '31557600'
}))
var compress = require('compression');
app.use(compress());
app.use('/', express.static('../client/admin/build/dist/', { redirect: false }));
app.get('*', (req, res) => {
res.sendFile(path.resolve('../client/admin/build/dist/index.html'));
});
app.listen(PORT, () => {
console.log(`Incentive frontend is listening on ${PORT}`);
});

How to update configuration file (.env file) while running docker container

I have created a nodejs app and built docker image as per this link
Dockerizing a Node.js web app
But, I am also using configuration file (.env file) where I can maintain all the environment variables and access them with process.env.<Variable_name>.
My server.js file looks like this.
'use strict';
const express = require('express');
require('dotenv').config()
// Constants
const PORT = process.env.PORT | 8080;
const HOST = process.env.HOST;
// App
const app = express();
app.get('/', (req, res) => {
res.send('Hello world\n');
});
app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);
And My .env file is this.
HOST=10.20.30.40
PORT=8080
I can change my IP address and port to anything with out changing any code in server.js. As similar as this, I want update the .env when I am building it as a docker image.
This is my Dockerfile
FROM node:carbon
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
COPY .env .
EXPOSE 8080
CMD [ "npm", "start" ]
I know I can update the .env file while building image by giving --build-args. But every time if need to make change in .env, I have to rebuild the image and deploy it. So, I want to update the .env file while running the image or container.
In the below command is there any way to give some arguments so it will update the .env file in the docker.
docker run -p 49160:8080 -d <your username>/node-web-app
You can add a directory to the docker using -v
docker run -v /Path/To/The/Env/file:/env-file-directory -p ...
Then target the file in the linked directory inside of your docker using the name /env-file-directory

Resources