Search code examples
dockerdockerfilesimple-web-token

problem with adding Nginx base image (any version) to project with Dockerfile


I have a problem with configuring the Nginx base image (any version),

-the application from stage one needs to be copied to the HTTP server and set to be the default launched and displayed as the default (start) page

- operation correctness checking is to be included (HEALTHCHECK)

My Dockerfile:

# Stage 1: Build Stage
FROM node:alpine AS builder

# Install build dependencies
RUN apk add --update curl && \
    rm -rf /var/cache/apk/*

WORKDIR /usr/app

# Copy package.json and install dependencies
COPY ./package.json ./
RUN npm install

# Copy application code
COPY ./index.js ./

# Stage 2: Runtime Stage
FROM nginx:latest

# Remove default nginx website
RUN rm -rf /usr/share/nginx/html/*

# Copy built files from the builder stage
COPY --from=builder /usr/app /usr/share/nginx/html

# Set working directory
WORKDIR /usr/share/nginx/html

# Expose port
EXPOSE 80

# Define version argument
ARG VERSION
ENV VERSION=production.${VERSION:-v1.0}

# Health check configuration
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:80/ || exit 1

# Command to run the application
CMD ["nginx", "-g", "daemon off;"]

My nginx.conf:

server {
    listen 80;
    server_name localhost;

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

    # Health check
    location /healthcheck {
        access_log off;
        return 200 'OK';
    }
}

Rest of code:

const express = require('express');
const os = require('os');

const app = express();
const port = 8084;

app.get('/', (req, res) => {
  let response = `Adres IP serwera: ${getIPAddress()}\n`;
  response += `Nazwa serwera (hostname): ${os.hostname()}\n`;
  response += `Wersja aplikacji: ${process.env.VERSION}\n`;
  res.send(response);
});

app.listen(port, () => {
  console.log(`Aplikacja jest dostępna na porcie ${port}`);
});

function getIPAddress() {
  const interfaces = os.networkInterfaces();
  for (const dev in interfaces) {
    const iface = interfaces[dev].filter(details => details.family === 'IPv4' && !details.internal);
    if (iface.length > 0) return iface[0].address;
  }
  return '0.0.0.0';
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Simple Web App</title>
</head>
<body>
    <h1>Welcome to Simple Web App</h1>
    <p>This is a simple web application.</p>
    <p>Server IP Address: [SERVER_IP_ADDRESS]</p>
    <p>Server Hostname: [SERVER_HOSTNAME]</p>
    <p>Application Version: [APP_VERSION]</p>
</body>
</html>

I use this commands to run image:

docker build --build-arg VERSION=3.0.0 -f Dockerfile10 -t local/base:v10 . -> created successfully

docker run -d -p 8093:8080 --name web13 local/base:v10 -> (health: starting)

curl http://localhost:8093 -> curl: (52) Empty reply from server

docker logs -> The error message "directory index of '/usr/share/nginx/html/' is forbidden" indicates that Nginx is trying to serve the root directory but cannot find an index file to display. ( i have index.html in my directory with rest of files)


Solution

  • There are no HTML files in your image.

    You should be able to verify this by running

    docker run --rm local/base:v10 \
      ls /usr/share/nginx/html
    

    What is in your image? I only see this setup

    WORKDIR /usr/app
    COPY ./package.json ./
    RUN npm install
    COPY ./index.js ./
    
    FROM nginx:latest
    COPY --from=builder /usr/app /usr/share/nginx/html
    

    That is: the only things in the base image's /usr/app directory are package.json, a node_modules directory, and index.js. Those things get copied into the final image. But none of those are HTML files. This means there's not an index.html file in the final image, which results in the permission error you're getting.

    If your local tree has a matching index.html file, then it's enough to COPY it into your build image.

    COPY index.html index.js ./
    #    ^^^^^^^^^^ add?
    

    If you have a single-page application that you build with a tool like Webpack, you need to make sure you run the build step in the base image. In the final image you'd need to copy the built directory and not the base source tree.

    FROM node:alpine AS builder
    WORKDIR /usr/app
    COPY ./package*.json ./
    RUN npm ci
    COPY ./index.js ./
    RUN npm run build  # <-- add
    
    FROM nginx:latest
    COPY --from=builder /usr/app/dist /usr/share/nginx/html
    #                            ^^^^