Reputation: 1980
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
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