Build a Multi-Stage Docker Image For React App To Load Runtime Environment Settings
By: Kattula Uma Srinivas
Tags: Reactjs, Image, Docker, Environment Variables, Nginx

In the previous article, ReactJS runtime configurations are explained for the server deployments which also provides a solution to fetch and load environment variables as application properties. The main essence of loading configurations during runtime is to avoid the rebuilding process of the application every time and to optimize the size of the image through a multistage docker build process.

In this article, a Multi-stage docker build mechanism is used to optimize built image size for a quicker rollback in CI/CD release process and scaling cost reduction when deployed in any container services.

Prerequisites

· Previous article

· Docker [Multi-Stage Docker build]

· NGINX

Step-1:

Create an “nginx.conf” file in “nginx” directory of the application root folder with appropriate configurations as below

server {

listen 80;



location / {

root /usr/share/nginx/html;

index index.html index.htm;

try_files $uri $uri/ /index.html;

}



error_page 500 502 503 504 /50x.html;

location = /50x.html {

root /usr/share/nginx/html;

}



error_page 400 401 403 404 /40x.html;

location = /40x.html {

root /usr/share/nginx/html;

}

}

Step- 2:

Add “50x.html” and “40x.html” files in “nginx” folder to get rendered on errors with 5xx code and 4XX code respectively

50x.html

<h1>

Internal Server Error

</h1>

40x.html

<h1>

Bad request

</h1>

Step- 3:

Create a Dockerfile in the root of the project directory.

In general, there are two phases to run the React application.

a) Create a build folder — Build Stage-1

b) Serve build folder — Build Stage-2

Build Stage-1

A Node alpine is used as a base image and to generate a build folder from React code.

# ------------------------------------------------------

# Build Phase

# ------------------------------------------------------ FROM node:lts-alpine as builder

WORKDIR /app

COPY . .

RUN npm install

RUN npm run build

Build Stage-2

Nginx is used as a base image to serve the generated build folder that is copied from the builder image.

# ------------------------------------------------------

# NGINX As Web Server

# ------------------------------------------------------ FROM nginx:1.17.0-alpine

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

COPY nginx/50x.html /usr/share/nginx/html

COPY nginx/40x.html /usr/share/nginx/html # Copy runtime-config.sh

COPY ./runtime-config.sh /home/ # Copy generated build artifacts from builder

COPY --from=builder /app/build /usr/share/nginx/html WORKDIR /home

EXPOSE 80 RUN chmod +x env.sh CMD ["/bin/sh", "-c", "/home/runtime-config.sh && nginx -g \"daemon off;\""]

So, the complete Dockerfile will be as below

# ------------------------------------------------------

# Build Phase

# ------------------------------------------------------ FROM node:lts-alpine as builder

WORKDIR /app

COPY . .

RUN npm install

RUN npm run build # ------------------------------------------------------

# NGINX As Web Server

# ------------------------------------------------------ FROM nginx:1.17.0-alpine

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

COPY nginx/nginx.conf /etc/nginx/conf.d

COPY nginx/50x.html /usr/share/nginx/html # Copy runtime-config.sh

COPY ./runtime-config.sh /home/ # Copy generated build artifacts from builder

COPY --from=builder /app/build /usr/share/nginx/html WORKDIR /home

EXPOSE 80 RUN chmod +x env.sh CMD ["/bin/sh", "-c", "/home/runtime-config.sh && nginx -g \"daemon off;\""]

Step- 4:

Change the value of the ‘destfile’ variable in “runtime-config.sh” to:

/usr/share/nginx/html/

Step- 5:

Build an image from this repository using the “docker build” command.

For example:

docker build -t react_webapp_image:1.0.0 .

Step- 6:

Create a container from this image using the docker-compose file or simply by running the docker run command.

docker run –name react_webapp_container -p 80:80 -e service_url=http://0.0.0.0:8000 react_webapp_image:1.0.0

In the next blog, I will be dealing with dangling images and security measures to be taken care of while containerizing applications using docker.