Tuz
Tuz

Reputation: 1980

reduce docker build time for react static app

I trying to reduce the time take to build a docker image to react app, the react should be static without server rendering.

now it takes around 5-10 minute to create an image and the image size on the local machine is around 1.5GB !!, the issue is that also after the second time of image creation, even I changed smth in the code it doesn't use any cache I am looking for a solution to cut the time the size and here is my docker File after lot of changes

# Producation and dev build 

FROM node:14.2.0-alpine3.10 AS test1
RUN apk update
RUN apk add \
        build-base \
        libtool \
        autoconf \
        automake \
        jq \
        openssh \
        libexecinfo-dev


ADD package.json package-lock.json /app/

# set working directory
WORKDIR /app

# add `/app/node_modules/.bin` to $PATH
ENV PATH /app/node_modules/.bin:$PATH

# install app dependencies
COPY package.json ./
COPY package-lock.json ./

ADD . /app/

RUN rm -rf node_modules

RUN npm install --production

# copy production node_modules aside, to prevent collecting them
RUN cp -R node_modules prod_node_modules

# install ALL node_modules, including 'devDependencies'
RUN npm install
RUN npm install [email protected] -g --silent
RUN npm run build

RUN rm -rf node_modules
RUN cp -R prod_node_modules node_modules
 
#FROM node:13.14.0
FROM test1

# copy app sources
COPY --from=test1 /app/build .
COPY --from=test1 /app/env-config.js .

# serve is what we use to run the web application
RUN npm install -g serve
# remove the sources & other needless stuff
RUN rm -rf ./src
RUN rm -rf ./prod_node_modules

# Add bash
RUN apk add --no-cache bash

CMD ["/bin/bash", "-c", "serve -s build"]

Upvotes: 1

Views: 2963

Answers (1)

David Maze
David Maze

Reputation: 158995

You're hitting two basic dynamics here. The first is that your image contains a pretty large amount of build-time content, including at least some parts of a C toolchain; since your run-time "stage" is built FROM the build stage as is, it brings all of the build toolchain along with it. The second is that each RUN command produces a new Docker layer with differences from the previous layer, so RUN commands only make the container larger. More specifically RUN rm -rf ... makes the image slightly larger and does not result in space savings.

You can use a multi-stage build to improve this. Each FROM line causes docker build to start over from some specified base image, and you can COPY --from=... previous build stages. I'd do this in two stages, a first stage that builds the application and a second stage that runs it.

# Build stage:
FROM node:14.2.0-alpine3.10 AS build

# Install OS-level dependencies (including C toolchain)
RUN apk update \
 && apk add \
        build-base \
        libtool \
        autoconf \
        automake \
        jq \
        openssh \
        libexecinfo-dev

# set working directory
WORKDIR /app

# install app dependencies
# (copy _just_ the package.json here so Docker layer caching works)
COPY package.json package-lock.json ./
RUN npm install

# build the application
COPY . ./
RUN npm run build

# Final stage:
FROM node:14.2.0-alpine3.10

# set working directory
WORKDIR /app

# install dependencies
COPY package.json package-lock.json ./
RUN npm install --production

# get the build tree
COPY --from=build /app/build/ ./build/

# explain how to run the application
ENTRYPOINT ["npx"]
CMD ["serve", "-g", "build"]

Note that when we get to the second stage, we run npm install --production on a clean Node installation; we don't try to shuffle back and forth between dev and prod dependencies. Rather than trying to RUN rm -rf src, we just don't COPY it into the final image.

This also requires making sure you have a .dockerignore file that contains node_modules (which will reduce build times and avoid some potential conflicts; RUN npm install will recreate it in the directory). If you need react-scripts or serve those should be listed in your package.json file.

Upvotes: 3

Related Questions