reactjsnode.jsdockernginxamazon-ec2

node server not accessible in docker container on aws


I made a react web app which gets built with a dockerfile and supervisord.conf and deployed to an aws container with a load balancer. My react app runs on localhost:3000, and is accessibly at http://www.jermasearch.com/

I want to add a database connection to my react app, which needs a client secret env var to make the database connection/query. In order to safely include this secret var, I'm trying to make a simple small backend node server which runs alongside my react app.

When I run my repo locally with the commands npm start for the react-app, and node server.js for my node-server, they both launch fine. And I can make a postman request to http://localhost:3030/dbtest which returns the expected correct text response: Hello from the Node.js server!.

If I build my docker image and run it with these commands:

note how I specify port 3030:3030 here in the above line, maybe Im missing this from aws somehow?

The /dbtest route also works if I make a postman request to the same url.

But when I push my code to my aws ec2 container and try to make a request to the production url http://jermasearch.com:3030/dbtest or http://jermasearch.com:3030/dbtest, it returns the html content of the react app page which is not correct:

<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <link rel="icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta name="description" content="Web site created using create-react-app" />
    <link rel="apple-touch-icon" href="/logo192.png" />
    <link rel="manifest" href="/manifest.json" />
    <title>React App</title>
    <script defer="defer" src="/static/js/main.9daeaa7a.js"></script>
    <link href="/static/css/main.f855e6bc.css" rel="stylesheet">
</head>

<body><noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
</body>

</html>

All my files are here: https://github.com/MartinBarker/aws-react-docker-ghactions

Currently my dockerfile copies the built react-app and server.js content, then exposes ports and runs a final command:

# Specify a base image
FROM node:18-alpine as build

# Set the working directory
WORKDIR /app

# Copy package.json and package-lock.json
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy the rest of the application files
COPY ./ ./

# Build the React application
RUN npm run build

# Use a multi-stage build to keep the final image small
FROM nginx:alpine

# Install supervisor and Node.js
RUN apk add --no-cache supervisor nodejs npm

# Set the working directory
WORKDIR /app

# Copy nginx configuration
COPY nginx.conf /etc/nginx/nginx.conf

# Copy the built application from the previous stage
COPY --from=build /app/build /usr/share/nginx/html

# Copy the source files for the Node server and React app
COPY server.js ./server.js
COPY package*.json ./
COPY src/ ./src/
COPY public/ ./public/

# Copy node_modules from the build stage
COPY --from=build /app/node_modules ./node_modules

# Install production dependencies (this may be optional if all dependencies are already installed in the build stage)
RUN npm install --only=production

# Copy the supervisord configuration
COPY supervisord.conf /etc/supervisord.conf

# Expose ports
EXPOSE 80 3000 3030

# Command to run supervisord
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]

At the end of the dockerfile it runs mu supervisord.conf file runs npm start and node server.js as well as points output to some log files:

[supervisord]
nodaemon=true

[program:nginx]
command=nginx -g 'daemon off;'
autostart=true
autorestart=true
stdout_logfile=/var/log/nginx.log
stderr_logfile=/var/log/nginx_err.log

[program:node-server]
command=node server.js
autostart=true
autorestart=true
stdout_logfile=/var/log/node_server.log
stderr_logfile=/var/log/node_server_err.log

[program:react-app]
command=npm start
directory=/app
autostart=true
autorestart=true
stdout_logfile=/var/log/react_app.log
stderr_logfile=/var/log/react_app_err.log

I checked my aws logs and both react-app and node-server seem to have started up fine:

2024-07-03 09:04:58,002 CRIT Supervisor is running as root.  Privileges were not dropped because no user is specified in the config file.  If you intend to run as root, you can set user=root in the config file to avoid this message.
2024-07-03 09:04:58,006 INFO supervisord started with pid 1
2024-07-03 09:04:59,009 INFO spawned: 'nginx' with pid 7
2024-07-03 09:04:59,014 INFO spawned: 'node-server' with pid 8
2024-07-03 09:04:59,018 INFO spawned: 'react-app' with pid 9
2024-07-03 09:05:00,859 INFO success: nginx entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-07-03 09:05:00,860 INFO success: node-server entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-07-03 09:05:00,860 INFO success: react-app entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-07-03 09:11:51,682 WARN received SIGTERM indicating exit request
2024-07-03 09:11:51,683 INFO waiting for nginx to die
2024-07-03 09:11:53,719 INFO stopped: nginx (exit status 0)

I ran an nmap on my url and it seems like port 3030 should be open, how do I make a request to it in production? Is my nginx configuration somehow not allowing for port access?

$ nmap www.jermasearch.com
Warning: Nmap may not work correctly on Windows Subsystem for Linux.
For best performance and accuracy, use the native Windows build from https://nmap.org/download.html#windows.
Starting Nmap 7.80 ( https://nmap.org ) at 2024-07-03 02:30 PDT
Problem binding to interface , errno: 92
socket_bindtodevice: Protocol not available
Problem binding to interface , errno: 92
socket_bindtodevice: Protocol not available
NSOCK ERROR [0.6100s] mksock_bind_device(): Setting of SO_BINDTODEVICE failed (IOD #1): Protocol not available (92)
NSOCK ERROR [0.6110s] mksock_bind_device(): Setting of SO_BINDTODEVICE failed (IOD #2): Protocol not available (92)
Problem binding to interface , errno: 92
socket_bindtodevice: Protocol not available
...
Problem binding to interface , errno: 92
socket_bindtodevice: Protocol not available
Nmap scan report for www.jermasearch.com (54.177.88.67)
Host is up (0.16s latency).
Other addresses for www.jermasearch.com (not scanned): 54.176.90.55
rDNS record for 54.177.88.67: ec2-54-177-88-67.us-west-1.compute.amazonaws.com
Not shown: 998 filtered ports
PORT     STATE SERVICE
80/tcp   open  http
3030/tcp open  arepa-cas

Nmap done: 1 IP address (1 host up) scanned in 14.93 seconds

I even tried to make sure that my nginx.conf file allowed for the :3030 port:

worker_processes 1;

events {
  worker_connections 1024;
}

http {
  include mime.types;
  default_type application/octet-stream;

  sendfile on;
  keepalive_timeout 65;

  server {
    listen 80;

    location /internal-api {
        proxy_pass http://localhost:3030/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location / {
      add_header 'Access-Control-Allow-Origin' '*';
      add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
      add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';

      root /usr/share/nginx/html;
      index index.html;
      try_files $uri $uri/ /index.html;
    }
  }
}

Which my server.js runs on:

const express = require('express');
const cors = require('cors');
const app = express();
const port = 3030;

console.log('server.js staring')

// Enable CORS for all routes
app.use(cors());

app.get('/dbtest', (req, res) => {
  res.send('Hello from the Node.js server!');
});

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});


Solution

  • Nmap indicates you have 2 ports open, 80 and 3030:

    Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-02 12:04 PDT
    Nmap scan report for www.jermasearch.com (54.176.90.55)
    Host is up (0.011s latency).
    Other addresses for www.jermasearch.com (not scanned): 54.177.88.67
    rDNS record for 54.176.90.55: ec2-54-176-90-55.us-west-1.compute.amazonaws.com
    Not shown: 998 filtered tcp ports (no-response)
    PORT     STATE SERVICE
    80/tcp   open  http
    3030/tcp open  arepa-cas
    
    Nmap done: 1 IP address (1 host up) scanned in 10.46 seconds
    

    But the fetch data button is only accessing port 80, and not 3030. When accessing port 3030, it seems to be displaying the same output.

    Based on your docker file, port 80, 3000, and 3030 are supposed to be open. Your supervisor config runs CRA(not the build) on port 3000, your backend on port 3030, and nginx(80).

    Nginx is on port 80, but both port 80 and 3030 display nginx output. Your nginx config also declares your backend should be accessed by /internal-data/, but a 502 Bad Gateway error is returned indicating that the server is down.

    Your logs also indicate that the node-server process and react-app process shut down shortly after init. Even though they start, there seems to be an error.

    The react-app returns an error code of 127 meaning script not found. This may be due to how you didn't install node_modules and are trying to use react-scripts. This process is also redundant because of nginx serving built files.

    The node-server app exits with code 1, which gives little information. I suspect this may be with how nginx is also on port 3030.

    Overall this means there's an error with nginx taking port 3030, the node server not being able to start, and possibly your API accessing an incorrect API.