node server not accessible in docker container

0

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 react-server on port 3000 and my node-server on port 3030 locally it works, but when pushed to aws, I cant access my /dbtest route. I double checked and in my security group I have these inbound rules: Enter image description here As you can see it should be accepting traffic on port 3030, but when I try to make a http get request to http://jermasearch.com/dbtest and http://jermasearch.com:3030/dbtest it just returns the jermasearch.com home page html content.

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:

  • docker build -t myapp .
  • docker run -p 80:80 -p 3030:3030 myapp

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}`);
});

1 Answer
0

From the following link (Section Configuration for Docker platforms (without Docker Compose) -> expand Docker platform Configuration - without Docker Compose -> expand Dockerrun.aws.json v1 -> Ports):

You can specify multiple container ports, but Elastic Beanstalk uses only the first port. It uses this port to connect your container to the host's reverse proxy and route requests from the public internet. If you're using a Dockerfile, the first ContainerPort value should match the first entry in the Dockerfile's EXPOSE list.

So you can't expose multiple ports externally from your Docker container. Instead what you can do is use a reverse proxy like nginx that will expose a single port and will internally map traffic by path the a different localhost port.

profile pictureAWS
EXPERT
answered a month ago