How can I combine three docker images running on different ports to a single docker image

69 views Asked by At

I am trying to create a Docker setup with multiple services including Django, Next.js, and Nginx, where Nginx serves as a reverse proxy for both Django (backend) and Next.js (frontend) applications. I want it to work in such a way that these 3 images are combined into one image for deployment. I have tried to used the multi-staged docker build, but i can't get the configuration right.

PROJECT FILES:

./schoolduo-backend/Dockerfile.prod:

###########
# BUILDER #
###########

# pull official base image
FROM python:3.11.4-slim-buster as builder

# set work directory
WORKDIR /usr/src/schoolduo-backend

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# install system dependencies
RUN apt-get update && \
    apt-get install -y --no-install-recommends gcc

# lint
RUN pip install --upgrade pip
RUN pip install flake8
COPY . /usr/src/schoolduo-backend/
RUN flake8 --exclude env --ignore=E501,F401 .

# install python dependencies
COPY ./requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /usr/src/schoolduo-backend/wheels -r requirements.txt


#########
# FINAL #
#########

# pull official base image
FROM python:3.11.4-slim-buster

# create directory for the app user
RUN mkdir -p /home/app

# create the app user
RUN addgroup --system app && adduser --system --group app

# create the appropriate directories
ENV HOME=/home/app
ENV APP_HOME=/home/app/web
RUN mkdir $APP_HOME
RUN mkdir $APP_HOME/staticfiles
RUN mkdir $APP_HOME/mediafiles
WORKDIR $APP_HOME

# install dependencies
RUN apt-get update && apt-get install -y --no-install-recommends netcat
COPY --from=builder /usr/src/schoolduo-backend/wheels /wheels
COPY --from=builder /usr/src/schoolduo-backend/requirements.txt .
RUN pip install --upgrade pip
RUN pip install --no-cache /wheels/*

# copy entrypoint.prod.sh
COPY ./entrypoint.prod.sh .
RUN sed -i 's/\r$//g'  $APP_HOME/entrypoint.prod.sh
RUN chmod +x  $APP_HOME/entrypoint.prod.sh

# copy project
COPY . $APP_HOME

# chown all the files to the app user
RUN chown -R app:app $APP_HOME

# change to the app user
USER app

# run entrypoint.prod.sh
ENTRYPOINT ["/home/app/web/entrypoint.prod.sh"]

./schoolduo-frontend/Dockerfile.prod:

FROM node:18-alpine as base

RUN apk add --no-cache g++ make py3-pip libc6-compat

WORKDIR /schoolduo-frontend

COPY package*.json ./
RUN npm install

COPY . .

EXPOSE 3000

RUN npm run build

CMD npm run dev

./nginx/Dockerfile:

FROM nginx:1.25

RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d


./nginx/nginx.conf:

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=STATIC:10m inactive=7d use_temp_path=off;

upstream nextjs {
  server frontend:3000;
}

upstream api {
  server backend:8000;
}

server {
  listen 80 default_server;

  server_name _;

  server_tokens off;

  gzip on;
  gzip_proxied any;
  gzip_comp_level 4;
  gzip_types text/css application/javascript image/svg+xml;

  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection 'upgrade';
  proxy_set_header Host $host;
  proxy_cache_bypass $http_upgrade;

  location /_next/static {
    proxy_cache STATIC;
    proxy_pass http://nextjs;

    # For testing cache - remove before deploying to production
    add_header X-Cache-Status $upstream_cache_status;
  }

  location /static {
    proxy_cache STATIC;
    proxy_ignore_headers Cache-Control;
    proxy_cache_valid 60m;
    proxy_pass http://nextjs;

    # For testing cache - remove before deploying to production
    # add_header X-Cache-Status $upstream_cache_status;
  }

  location / {
    proxy_pass http://nextjs;
  }

  location ^~ /api/ {
    proxy_pass http://api;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;
    proxy_redirect off;
    client_max_body_size 100M;
  }

  location /static/ {
        alias /home/app/web/staticfiles/;
  }

  location /media/ {
      alias /home/app/web/mediafiles/;
  }

}

docker-compose.yml:

version: '3.8'

services:
  backend:
    image: schoolduo-backend
    build:
      context: ./schoolduo-backend
      dockerfile: Dockerfile.prod
    command: gunicorn schoolduo.wsgi:application --bind 0.0.0.0:8000
    volumes:
      - ./schoolduo-backend/:/usr/src/schoolduo-backend/
      - static_volume:/home/app/web/staticfiles
      - media_volume:/home/app/web/mediafiles
    ports:
      - 8000:8000
    expose:
      - 8000
    env_file:
      - ./.env.prod

  frontend:
    image: schoolduo-frontend
    build:
      context: ./schoolduo-frontend
      dockerfile: Dockerfile.prod
    volumes:
      - ./schoolduo-frontend/:/schoolduo-frontend/
    ports:
      - 3000:3000
    env_file:
      - ./.env.prod
    depends_on:
      - backend

  nginx:
    build: 
      context: ./nginx
    volumes:
      - static_volume:/home/app/web/staticfiles
      - media_volume:/home/app/web/mediafiles
    ports:
      - 80:80
    depends_on:
      - backend
      - frontend
    

volumes:
  schoolduo-backend:
  schoolduo-frontend:
  static_volume:
  media_volume:

When I build the container using the docker-compose.yml, i get 3 different images schoolduo-backend, schoolduo-frontend and schoolduo-web-ngnix with each running at ports 8000, 3000, 80 respectively, but the port 80 is what i need, which i assume it depends on the backend & frontend services, my challenge is how do i combine these 3 images into a single docker image for deployment purposes, because ngnix seems to not work when the image is ran independently, as it shows 502 error

I have tried to a multi-staged build, by combining each dockerfile content into one dockerfile but i don't think i fully understand how to configure it properly

0

There are 0 answers