Optimizing Docker Images: From 1.16GB to 22.4MB

Optimizing Docker Images: From 1.16GB to 22.4MB

Author | The Agile Crafter

Planning | Tian Xiaoxu

Docker is a platform for software developers and system administrators to build, run, and share applications with containers. A container is a process running in an isolated environment, operating on its own file system, which is built using a Docker image. The image contains everything needed to run the application (compiled code, dependencies, libraries, etc.). The image is defined using a Dockerfile.

The term dockerization or containerization is often used to define the process of creating Docker containers.

Containers are popular due to the following advantages:

  • Flexibility: Even the most complex applications can be containerized.

  • Lightweight: Containers share the host kernel, making them much more efficient than virtual machines.

  • Portability: Built locally, run anywhere.

  • Loose Coupling: Containers are self-contained; replacing or upgrading one container does not interrupt others.

  • Security: Containers enforce strict limits and isolation on processes without requiring user configuration.

In this article, I will focus on how to optimize Docker images to make them lightweight.

Let’s start with an example where we build a React application and containerize it. After running the npx command and creating the Dockerfile, we end up with the file structure shown in Figure 1.

npx create-react-app app --template typescript

Optimizing Docker Images: From 1.16GB to 22.4MB

Figure 1: File Structure

If we build a basic Dockerfile (as shown below), we end up with a 1.16 GB image:

FROM node:10
WORKDIR /app
COPY app /app
RUN npm install -g webserver.local
RUN npm install && npm run build
EXPOSE 3000
CMD webserver.local -d ./build

Optimizing Docker Images: From 1.16GB to 22.4MB

Figure 2: Initial Size of the Image is 1.16GB

Step One Optimization: Use Lightweight Base Images

There are several images available for download on Docker Hub (the public Docker repository), each with different characteristics and sizes.

Generally, images based on Alpine or BusyBox are much smaller compared to those based on other Linux distributions (like Ubuntu). This is because Alpine images and similar others are optimized to include only the minimal necessary packages. In the image below, you can see the size comparison between Ubuntu, Alpine, Node, and Node based on Alpine images.

Optimizing Docker Images: From 1.16GB to 22.4MB

Figure 3: Different Sizes of Base Images

By modifying the Dockerfile and using Alpine as the base image, our final image size is 330MB:

FROM node:10-alpine
WORKDIR /app
COPY app /app
RUN npm install -g webserver.local
RUN npm install && npm run build
EXPOSE 3000
CMD webserver.local -d ./build

Optimizing Docker Images: From 1.16GB to 22.4MB

Figure 4: After the First Step Optimization, the Image Size is 330MB

Step Two Optimization: Multi-Stage Builds

With multi-stage builds, we can use multiple base images in the Dockerfile and copy compiled products, configuration files, etc., from one stage to another, allowing us to discard unnecessary items.

In this case, what we need to deploy the React application is the compiled code; we do not need the source files, nor the node_modules directory and package.json file, etc.

By modifying the Dockerfile to the following content, we end up with an image size of 91.5MB. Remember, the image from the first stage (lines 1-4) will not be automatically deleted; Docker keeps it in cache, allowing faster builds if the same stages are executed in another build process. So you must manually delete the first stage image.

FROM node:10-alpine AS build
WORKDIR /app
COPY app /app
RUN npm install && npm run build

FROM node:10-alpine
WORKDIR /app
RUN npm install -g webserver.local
COPY --from=build /app/build ./build
EXPOSE 3000
CMD webserver.local -d ./build

Optimizing Docker Images: From 1.16GB to 22.4MB

Figure 5: After the Second Step Optimization, the Image Size is 91.5MB

Now we have a Dockerfile with two stages: in the first stage, we compile the project, and in the second stage, we deploy the application on a web server. However, the Node container is not the best choice for serving web pages (HTML, CSS, and JavaScript files, images, etc.); the best choice is to use a service like Nginx or Apache. In this example, I will use Nginx.

By modifying the Dockerfile to the following content, our final image size is 22.4MB. If we run this container, we can see that the web page works fine without any issues (Figure 7).

FROM node:10-alpine AS build
WORKDIR /app
COPY app /app
RUN npm install && npm run build

FROM nginx:stable-alpine
COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Optimizing Docker Images: From 1.16GB to 22.4MB

Figure 6: After the Third Step Optimization, the Image Size is 22.4MB

Optimizing Docker Images: From 1.16GB to 22.4MB

Figure 7: Final Container Running Result

Reference Links:
https://docs.docker.com/get-started/
https://docs.docker.com/develop/develop-images/multistage-build/
Original Link:
https://medium.com/the-agile-crafter/docker-image-optimization-from-1-16gb-to-22-4mb-53fdb4c53311

InfoQ Reader Group is now online! You can scan the QR code below, add the InfoQ assistant, and reply with the keyword “Join Group” to apply for joining. Reply “Materials” to get the materials package portal. After registering on the InfoQ website, you can receive any one Geek Time course for free! You can chat freely with InfoQ readers, get in touch with editors, and receive valuable technical gifts and participate in exciting activities, come join us!

  • Optimizing Docker Images: From 1.16GB to 22.4MB

    Optimizing Docker Images: From 1.16GB to 22.4MB

    Click to see less bugs 👇

Leave a Comment