How to build optimized docker images for production

Customization post worker Images are a difficult task. You have a lot to learn, many things can go wrong. Ask Matthew McConaughey interstellar,

“Anything that can go wrong will go wrong.”
– Murphy, a smart dude

you want to hurry feedback loop Between pushing code to your GitHub repository and deploying it to a production environment. you don’t want to send gigabyte size Docker images across the network. Sending huge pictures will take your money and time.

They can also cause stress if they are poorly monitored. What if a large package breaks in or old images start accumulating?

Be sure to monitor your containers to watch for spikes for disk usage and don’t be shy with Docker system prune Permission. This will clear out any old pictures you have.

In this article I want to explain how to make your docker images smaller, smaller enough cut construction time And reduce image size,

Finally, you’ll learn how to use small base images, such as alpine distro, to reduce the initial size of your images. but also how to use multi-stage manufacturing process To exclude unnecessary files.

Here’s a quick agenda of what you’ll learn:

  • Why create customized Docker images?
  • How to create customized docker images?
  • Using Base Ubuntu Images
  • Using base alpine images
  • Excluding build tools using multi-stage build
  • CI/CD Pipeline Optimization

Why create customized Docker images?

If you have large docker images the build time is long. It also affects the time it takes to send the images CI/CD Pipeline, container storeand cloud providers.

Let’s say you have a huge, 1GB image. Every time you push a change and trigger a build, it will put a huge strain on your network and make your CI/CD pipeline sluggish. All of this costs you time, resources and money. Sending data over the network is not cheap in the cloud. Cloud Providers Preferred AWS You are billed for every byte you send.

Also an important point to consider is security. Keeping images small reduces the attack vector for potential attacks. Smaller images equate to smaller attack surface, as they have fewer installed packages.

These are all reasons why you would want Docker images in production, with only the essentials installed.

How to create customized docker images?

You have different approaches to reduce the size of Docker images. You usually don’t need any build tools when thinking about production environments. You can run apps without them, so there’s no need to add them at all.

how to create docker images

The multi-stage build process lets you use intermediate images when building your app. You can build the code in an image and install the dependencies, then copy the final version of your app to a blank image without any build tools.

Using small base images is another important factor. Alpine Linux is a small distribution that contains only the essential requirements to run your app.

Using Base Ubuntu Images

It’s natural to use the native Ubuntu image when you get started with Docker. You install the necessary packages and modules to run your app.

Here is a sample Dockerfile for Ubuntu. Let’s see how this affects the build time.

# Take a useful base image

FROM ubuntu:18.04

I

# Install build tools

RUN install build-tools

I

# Set a work directory

WORKDIR /app

I

# Copy the app files to the work directory

COPY . .

I

# Build the app

RUN build app-executable

I

# Run the app

CMD ["./app"]

you drive build Command to build docker image.

docker build -f Dockerfile -t ubuntu .

It usually takes a while, no matter how fast your connection is. The final size of the image is large. after running docker images you will see:

[output]
ubuntu latest fce3547cf981 X seconds ago 1GB

I don’t know about you, but anything close to 1GB for a native Docker image is a bit too much for my taste. This may vary depending on the build tool and runtime you are using.

Using base alpine images

The easiest way to optimize your Docker images is to use smaller base images. Alpine Distro is an amazing lightweight image tool that you can use to reduce the size of your images and create time. let me show you.

If you edit the Dockerfile a bit to use Alpine, you can immediately see a huge improvement.

# Take a tiny Alpine image

FROM alpine:3.8

I

# Install build tools

RUN install build-tools

I

# Set a work directory

WORKDIR /app

I

# Copy the app files to the work directory

COPY . .

I

# Build the app

RUN build app-executable

I

# Run the app

CMD ["./app"]

only by changing FROM Line You can specify a smaller base image. Run the build once again.

docker build -f Dockerfile -t alpine .

Care to guess the size?

docker images

This varies depending on the runtime, but only reaches about 250MB.

[output]
nodejs 10-alpine d7c77d094be1 X seconds ago 71MB
golang 1.10-alpine3.8 ba0866875114 X seconds ago 258MB

This is a huge improvement. By changing just one line you can reduce the size to over 800MB. Let’s take this a step further and use multi-level builds.

Excluding build tools using multi-stage build

Images you want to run in production should not contain any build tools. The same logic applies to redundant dependencies. Remove everything you don’t need from the image. Only add the essentials to make it run.

This is simple to do with a multi-stage build. The logic behind this is to build the executable into an image that contains the build tools. Then move it to a blank image with no unnecessary dependencies.

Edit the Dockerfile again, but this time add a second FROM statement. You can create two images in one Dockerfile. The first will act as the build, while the second will be used to run the executable.

# Take a useful base image

FROM ubuntu:18.04 AS build

I

# Install build tools

RUN install build-tools

I

# Set a build directory

WORKDIR /build

I

# Copy the app files to the work directory

COPY . .

I

# Build the app

RUN build app-executable

I

####################

I

# Take a tiny Alpine image

FROM alpine:3.8

I

# Copy the executable from the first image to the second image

COPY --from=build /build .

I

# Run the app

CMD ["./app"]

Draw the image once again.

docker build -f Dockerfile -t multistage .

Care to guess the size now?

docker images

You will see two pictures using a multi-stage build.

[output]
multistage    golang 82fc005abc40    X seconds ago    11.3MB
multistage    nodejs 4aadd10d2711    X seconds ago    61.9MB
<none>        <none> d7855c8f8280    X seconds ago    1GB

<none> image is created with FROM ubuntu:18.04 Permission. It acts as an intermediary to build and compile the application, whereas the multistage image in this context is the final image that contains only the executable.

from an early 1GBbelow image size 11MBMulti-stage builds are awesome! reduce size 900MB It’s a matter of pride. This size difference will definitely improve your production environment in the long run.

CI/CD Pipeline Optimization

Why is this all important? You don’t want to move billions of bytes around the network. Docker images should be as small as possible because you want deployment to be fast. By following these simple steps, you’ll have an optimized CI/CD pipeline in no time.

Feather semitext We follow these guidelines and use multi-stage builds in our build pipeline. You improve developer productivity when you cut down on the time between builds. This is the ultimate goal. Keeping everyone happy and enjoying what they do. we also use our own product For monitoring our Kubernetes cluster and CI/CD pipeline. It’s lovely to have insight into each container in the cluster and how they behave when the CI/CD pipeline updates them.

In the end it is all about time and money. You don’t want to waste time creating and sending images between clusters and cloud providers. This costs money. Yes, transfer fees are a thing. You have to pay for the throughput you create in the network.

If you have a roughly gigabyte-sized image to push every time you trigger the build, that will add up. It won’t be pretty, it’s a fact. There are clusters in all regions of Sematext. Hear it from someone who has had this headache before. Data transfer charges come to bite you!

Why should you start using multi-stage builds?

Lastly, keep it simple. Install only the bare necessities in your docker images. Images suitable for production do not require any build tools to run the application. Therefore, there is no need to connect them at all. Use multi-stage builds and put them out whenever you can.

You can implement it in any language. Even interpreted languages ​​like Node.js and Python. You use intermediate images to build the code and install dependencies. Until then you don’t copy the final version of your application to a blank image with a small base like Alpine. Once you start using multi-stage builds, you’ll have a hard time stopping. This is great!

I had a lot of fun writing this article, hope you enjoy it! If you have any questions or just want to chat about DevOps, get in touch with me on Twitter @adnanrahic or on our heads Website And use live chat.

,

Sematext. About this
Sematext, a global products and services company, runs Sematext Cloud – an infrastructure and application performance monitoring and log management solution that provides businesses full-stack visibility by exposing logs, metrics and events through a single cloud or on-premises solution. does. Sematext also provides consulting, training, and production support for Elasticsearch, ELK/Elastic Stack, and Apache Solr. visit for more information sematext.com,

About the Author
Adnan Rahi is a JavaScript developer at heart. Startup founder, author and educator with a passion for DevOps and Serverless. Lately focusing on product development and developer advocacy.

Leave a Comment