React app is not loading from docker image in local - node.js

My Docker file
# FROM node:16.14.2
FROM node:alpine
ENV NODE_ENV=production
WORKDIR /app
COPY ["package.json", "package-lock.json", "./"]
RUN npm install
COPY . .
CMD [ "npm", "start"]
Command to run image: docker run -it -d -p 4001:4001 react-app:test2
Project structure
project structure
Output after docker run
result after docker run

Based on this context, a possible mistake for me is basically that you do not copy the rest of the source code correctly.
Try to be more consistent in the Dockerfile, also have a look at the multistage Docker build (within the same file) to optimise the image.
Anyway, your file should be something like:
FROM node:16-alpine
ENV NODE_ENV=production
WORKDIR /app
COPY ["package.json", "package-lock.json", "./"]
RUN npm install
COPY . ./
CMD [ "npm", "start"]

Based on the code in the repo, I managed to spot the following problem.It's neither the Dockerfile, nor the code itslef. It throws some warnings though.
Implicitly, the application is supposed to be running on port 3000, if it is not chnaged manually at some point (in this project there are only default settings). Thus the application starts correclty on port 3000, However you expose 4001:4001. On this port nothing is running according to this Dockerfile.
Try using port 3000 instead and it should work just fine:
docker run -it -d -p 3000:3000 <image-name>:<image-tag>

Related

NPM not found when using npm run start command within shell script from a docker container

I am not sure what I may be doing wrong but I have the following script.sh file sitting at the root of my project:
script.sh
#!/bin/sh
npm run start
envsubst '\$PORT' < /etc/nginx/conf.d/configfile.template > /etc/nginx/conf.d/default.conf
nginx -g 'daemon off;'
Then I referenced the above script in my Dockerfile as shown below:
Dockerfile
# Build environment
FROM node:16.14.2
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install --only=production
COPY . ./
# server environment
FROM nginx:alpine
COPY nginx.conf /etc/nginx/conf.d/configfile.template
ENV HOST 0.0.0.0
ENV NODE_ENV production
EXPOSE 8080
COPY script.sh /
RUN chmod +x /script.sh
ENTRYPOINT ["/script.sh"]
After building the Docker image successfully, I attempted to run it as a container but all I keep getting back is the following error:
/script.sh: line 2: npm: not found
I expect that the script should be able to pick up the already installed npm from the environment.
What can I do differently to make this work?
You're trying to run two separate programs, so run them in two separate containers.
# Dockerfile.app
FROM node:16.14.2
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install --only=production
COPY . ./
ENV HOST 0.0.0.0
ENV NODE_ENV production
EXPOSE 8080
CMD npm run start
# Dockerfile.nginx
FROM nginx:alpine
COPY nginx.conf /etc/nginx/conf.d/configfile.template
You might use a system like Docker Compose to run the two parts together:
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.app
nginx:
build:
context: .
dockerfile: nginx
ports:
- 8080:80
Running docker-compose up -d will start both containers together. In your Nginx configuration, make sure to proxy_pass http://app:8080, using the Compose service name and the port number the service is listening on, to forward requests to the other container.
(The Nginx Dockerfile looks short, but it's correct. The Docker Hub nginx image already knows how to run the envsubst line from your script in its own entrypoint script and it has a correct default command already.)
There's two basic problems in the setup you show in the question, both related to trying to run two programs in the same container. The first is that you can't merge images, having a second FROM line makes Docker start over from the new base image. (So your final image contains only Nginx, not Node or your built application, hence the npm not found error.) The second you'll run into is that your script will start your application, but not start the Nginx proxy until after the application exits. There are some common workarounds to this (like using a background process) but it essentially results in one process or the other being unmonitored by Docker, so your application could potentially fail and Docker wouldn't notice it to be able to restart it.

ConnectionError in Docker with Nodejs (Hapi.js) and Prisma

I would like to put my Nodejs app into a docker. When deploying it via npm run build and start I can send requests to it.
But when creating a docker image I getting problems:
First I have an EXPOSE 8080 in my Dockerfile. Then I am running docker run -p=3000:8080 --env-file .env my-docker-file. After that I am getting the info that the server is running on http://localhost:3000.
I know localhost:3000 ist just in the docker file. But at least the docker is running.
When I use the command http localhost:3000 (or the browser) I am getting http: error: ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) while doing a GET request to URL: http://localhost:3000/.
Does someone have an idea what's going wrong??? I have no clue.
tanks to all hints that directs me into the right direction.
My Dockerfile:
## this is the stage one , also know as the build step
FROM node:12.17.0-alpine as builder
WORKDIR /app
COPY package*.json ./
COPY prisma ./prisma/
COPY tsconfig.json .
COPY src ./src/
COPY tests ./tests/
RUN npm install
RUN npx prisma generate
COPY . .
RUN npm run build
## this is stage two , where the app actually runs
FROM node:12.17.0-alpine
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/dist ./dist
EXPOSE 8080
CMD npm start
If you use a Dockerfile, first you better to build your image.
FROM node:12.17.0-alpine as builder
WORKDIR /app
COPY . .
RUN npm install
RUN npx prisma generate
RUN npm run build
## this is stage two , where the app actually runs
FROM node:12.17.0-alpine
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/dist ./dist
EXPOSE 8080
CMD ["npm","start"]
From the location where your Dockerfile located:
docker build -t your-image-name .
docker run -p 3000:8080 --env-file .env your-image-name
Did you check the IP address?
When i first deploy my Node project to Docker, i couldn't access it too, because my Node project was listening for localhost requests. But if you don't specify your network as host, your Docker container will have some other IP address in your subnet.
I've changed my Node projects listening IP address to 0.0.0.0 and after that i could connect to my Node project running in a Docker container.

Passing environment variables in Dockerfile not working when I use 2 baseImages

To be brief, I want to build a container in docker with a web project which configuration is modifiable depending on a parameter that is passed to it later when running the image in Docker.
This project tries to read a file call "environment.json" with the custom properties.
My Dockerfile is this:
# NodeJS
FROM node:13.12.0-alpine as build
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY dist /app/dist
COPY define-env.js /app
COPY environments.json /app
# I have changed this with a lot of possible solutions
# that I have found in Internet, I tried everything.
# ARG APP_ENV
# ENV ENVIRONMENT $APP_ENV
RUN node define-env.js ${APP_ENV}
# NGINX
FROM nginx:stable-alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
What I am doing is copying both: built basic web project, a node script responsible of writing the correct environment and a JSON file with all the possible configuration environments.
The Node script is this:
#!/usr/bin/env node
// we will need file system module to write the values
var fs = require("fs");
// this script is going to receive one parameter (see DockerFile)
var option = process.argv[2];
// taking all the possible configurations
var json = require("./environments.json");
// taking the chosen option (by default dev)
var environmentCfg = json[option] || json.dev;
// writing... It is important to do this task sync,
// because we need to be sure is finished before any step
fs.writeFileSync("./dist/environment.json", JSON.stringify(environmentCfg));
And the environments.json is something like this:
{
"dev": {
"title": "This is the dev environment",
"anotherAttribute": "Hello dev, I was configured with Docker!"
},
"uat": {
"title": "This is the uat environment",
"anotherAttribute": "Hello uat, I was configured with Docker!"
},
"prod": {
"title": "This is the prod environment",
"anotherAttribute": "Hello prod, I was configured with Docker!"
}
}
I do not know how to pass the variable when I run my Docker image, I am trying this:
docker build . -t docker-env
and then, once I have created my image I try to run it using this command:
docker run -e APP_ENV=uat -it --rm -p 1337:80 docker-env
When I go to the project I see the "dev" configuration always.
I have checked removing from DockerFile the NGINX configuration and I can see its working fine when I put the parameters.
I think something weird (or I do not know) is happening when I change the baseImage from Node to Nginx.
EDIT: as #babakabadkheir suggested in the comments, I have tried this following approach, but it is still failing:
FROM hoosin/alpine-nginx-nodejs:latest
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY dist /app/dist
COPY define-env.js /app
COPY environments.json /app
RUN node define-env.js ${APP_ENV}
RUN mv ./dist/* /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
If I take a look to the mounted image shell I can see it has put the environment.json as well:
EDIT 2:
I have modified my Dockerfile like this but it is still not working:
# I am taking a base image with NodeJS and Nginx
FROM hoosin/alpine-nginx-nodejs:latest
# Telling Docker I like to work in the app workdir
WORKDIR /app
# Copying all files I need
COPY dist /app/dist
COPY define-env.js /app
COPY environments.json /app
# My environment variable for runtime purposes (dev by devfault)
ENV APP_ENV dev
# This instruction I think is failing, but when I swap it with
# CMD instead RUN and put a console.log works without troubles
RUN node define-env.js
# Once I have all the files I move it to the Nginx workspace
RUN mv ./dist /usr/share/nginx/html
# Setup Nginx server
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Now my Node script looks like:
#!/usr/bin/env node
var fs = require("fs");
// NOTE: this line is already taken via environment
var option = process.env.APP_ENV;
var json = require("./environments.json");
var environmentCfg = json[option] || json.dev;
fs.writeFileSync("./dist/environment.json", JSON.stringify(environmentCfg));
LAST EDIT!
Finally I have it!
the solution was put all my needs in the CMD Docker instruction:
CMD node define-env && mv ./dist/* /usr/share/nginx/html && nginx -g 'daemon off;'
You made two mistakes: mixed ENV with ARGS and a syntax error.
When you define an ARG you can pass a value when build the image, if not specified the default value is used; then read the value on Dockerfile without the braces:
(Note: I used slightly different docker images)
Dockerfile
FROM node:12-alpine as build
# Note: I specified a default value
ARG APP_ENV=dev
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY dist /app/dist
COPY define-env.js /app
COPY environments.json /app
# Note $APP_ENV and NOT ${APP_ENV} without braces
RUN node define-env.js $APP_ENV
# NGINX
FROM nginx:1.19
COPY --from=build /app/dist /usr/share/nginx/html
# Not necessary to specify port and CMD
Now for build the image you must specify the APP_ENV argument value
Build
docker build --build-arg APP_ENV=uat -t docker-uat .
Run
docker run -d -p 8888:80 docker-uat
;TL,DR
Your request is to specify the configuration at runtime, this problem has two solutions:
Use ENV var when running container
Use ARGS when building container
First solution has a drawback of security but the advantage of distributing a single image: you copy the entire file from host to container and when running the container you passing the correct ENV value.
Second solution is slightly more complex do not has the security problem but you must build separate images each for each config.
The second solution is the already written answer, for the first solution you must simply read the env variable at runtime
Dockerfile
# NodeJS
FROM node:12-alpine as build
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY dist /app/dist
COPY environments.json /app/dist
# NGINX
FROM nginx:1.19
# Define the environment variable
ENV APP_ENV=dev
COPY --from=build /app/dist /usr/share/nginx/html
Build
docker build -t docker-generic .
Run
docker run -d -p 8888:80 -e APP_ENV=uat docker-generic
Code
# On your nodejs code you simply read the ENV value
// ....
var env = process.env.APP_ENV
References
Dockerfile ARG
Docker build ARG
Dockerfile ENV
Docker run ENV
The solution for this it is to put all the things that are modifiable at runtime in the CMD instruction.
As I have experienced, RUN only works first time during build time, but CMD does not.
So I have put in my DockerFile:
CMD node define-env && mv ./dist/* /usr/share/nginx/html && nginx -g 'daemon off;'
I run my script, I move all the files and then serve my application.
I hope help other developers with this, because for me has been a pain.
TL;DR
In summary, the Dockerfile should be something like this:
# I am taking a base image with NodeJS and Nginx
FROM hoosin/alpine-nginx-nodejs:latest
# Telling Docker I like to work in the app workdir
WORKDIR /app
# Copying all files I need
COPY dist /app/dist
COPY define-env.js /app
COPY environments.json /app
# My environment variable for runtime purposes (dev by devfault)
ENV APP_ENV dev
# Setup Nginx server
EXPOSE 80
CMD node define-env && mv ./dist/* /usr/share/nginx/html && nginx -g 'daemon off;'

404 when serving react application in docker container

I've create a react application with create-react-app and have build a docker image with the following docker file.
FROM node:alpine AS builder
WORKDIR /app
RUN npm install
COPY . .
RUN npm run build
FROM node:alpine
WORKDIR /app
COPY --from=builder /app/build .
RUN npm install -g serve
EXPOSE 80
CMD serve -p 80 -s build
When running the container and accessing port 80 on localhost I'm met with "404 the requested path could not be found". The container is run with the command `docker run -p 80:80 "image name" and the output is "Accepting connections at http://localhost:80" What could be the reasons for the 404 and what can i do to fix it?
Looking at the documentation of serve... You are copying /app/build from the builder container in to /app on the new container and then calling serve with a folder name of build, which does not exist. (-s doesn't take a parameter`)

Passing NODE_ENV to docker to run package.json scripts

This is my dockerfile :
FROM node:6-onbuild
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app/
RUN npm install
COPY . /usr/src/app
ENV PORT 80
EXPOSE ${PORT}
CMD [ "npm","run", "start" ]
and in package.json I do have this :
"scripts": {
"start": "node start.js",
"stagestart": "NODE_ENV=content-staging node start.js"
}
the start script is for production, now I want a way to run the staging script in dockerfile. is there a way to read NODE_ENV inside dockerfile, so I can have one dockerfile which handle staging and production.
Here is two possible implementation.
FYI: you don't need to mention NODE_ENV in package.json if you
already set NODE_ENV at the system level or set NODE_ENV during build time or runtime in docker.
Here Dockerfile as same but I used to alpine base image
FROM node:alpine
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app/
RUN npm install
COPY . /usr/src/app
ENV PORT 3000
ARG DOCKER_ENV
ENV NODE_ENV=${DOCKER_ENV}
RUN if [ "$DOCKER_ENV" = "stag" ] ; then echo your NODE_ENV for stage is $NODE_ENV; \
else echo your NODE_ENV for dev is $NODE_ENV; \
fi
EXPOSE ${PORT}
CMD [ "npm","run", "start" ]
when you build this Dockerfile with this command
docker build --build-arg DOCKER_ENV=stag -t test-node .
You will see at layer
---> Running in a6231eca4d0b your NODE_ENV for stage is stag
When you run this docker container and run this command your output will be
/usr/src/app # echo $NODE_ENV
stag
Simplest Approch same image and but set environment variable at run time
Your Dockerfile
FROM node:alpine
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app/
RUN npm install
COPY . /usr/src/app
ENV PORT 3000
EXPOSE ${PORT}
CMD [ "npm","run", "start" ]
Run this docker image with this command
docker build -t test-node .
docker run --name test -e NODE_ENV=content-staging -p 3000:3000 --rm -it test-node ash
So when you run this command at container you will see
/usr/src/app # echo $NODE_ENV
content-staging
So this is how you can start your node application with NODE_ENV without setting environment variable at package.json. So if your nodejs configuration is based on NODE_ENV it should pick configuration according to NODE_ENV .
You can use the ENV instruction to get the environment variable as an environment variable inside container. Have an entry point script that injects the available environment variable (perhaps something as simple as sed) in place of a placeholder variable name that is in your package.json file. Then start your node application. Obviously this will require you to make a few changes to your Dockerfile in regards to entrypoint script etc.
That is how I have achieved such things in the past.

Resources