Best Practices for Writing a Dockerfile

Optimize your Docker Image by following these best practices from day one.

Kasun Rajapakse
Bits and Pieces

--

Photo by frank mckenna on Unsplash

Since its inception, Docker has revolutionized the way we use Containers. This is mainly due to the simplicity Docker brings in. It allows anyone to use it without dealing with any advanced topics related to Containers.

If you are new to Docker, think of it as you can choose a template (Base image) and define your instructions (Docker file commands) to put your code inside and run it.

Although Docker’s simplicity helps to get the job done, optimizing it comes with experience and usually takes time.

Since I have been working with Docker for a long time, I decided to share some of my experience with you on best practices to develop better Containers from Day 1.

1. Identify Cacheable Units ➱

Are you aware that each RUN command includes in Dockerfile affects the cacheable layer?

Using multiple RUN commands for installing packages will affect the performance and efficiency of the build process. Using one RUN command to apply all dependency packages will help create a single cacheable unit without creating multiple.

RUN apt-get update && apt-get install -y \
aufs-tools \
automake \
build-essential \
curl \
dpkg-sig \
libcap-dev \
libsqlite3-dev \
mercurial \
reprepro \
ruby1.9.1 \
ruby1.9.1-dev \
s3cmd=1.1.*

2. Reduce Image Size ✂

The image size plays an essential role in creating a good Dockerfile. Using smaller images will result in faster deployments and less attack surface.

Remove Unnecessary Dependencies 🗑

Avoid installing any unnecessary tools, such as debugging tools in your image. If the package manager uses to install any recommended packages, automatically use the package manager flags, and avoid installing dependencies that are not needed.

RUN apt-get update && apt-get -y install --no-install-recommends

Tip: Share your reusable components between projects using Bit (Github).

Bit makes it simple to share, document, and reuse independent components between projects. Use it to maximize code reuse, keep a consistent design, collaborate as a team, speed delivery, and build apps that scale.

Bit supports Node, TypeScript, React, Vue, Angular, and more.

Exploring components shared on Bit.dev

3. Image Maintainability 🛠

Choosing the right base image for the application is essential.

Use the Official Docker Image 📝

Using the official Docker image reduces the burden of carrying on unnecessary dependencies, making the image larger. There are 3 main advantages of using the official image:

  • This allows us to use an image created based on the best practices.
  • Reliability of the image and its maintenance.
  • Better trust and security.
# pull official base image
FROM node:13.12.0-alpine

# set working directory
WORKDIR /app

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

Use Specific Tags 📌

When choosing the base image, it’s recommended to use a specific tag. Avoid using the latest tag for the image. The latest tag can have breaking changes over time.

# pull official base image
FROM node:13.12.0-alpine

Use Minimal Flavors

Using minimal flavors of the image reduces the footprint of the image. This helps to deploy the applications faster and more securely.

Figure — 1

As we can see from the above image using minimal flavors of the image has a smaller footprint. Most images come with the alpine flavor. Alpine is a very light waited image with a base image of 2MB. By using an alpine based image we can reduce the image size significantly.

4. Reproducibility

Build from the source in a consistent environment

When building an application with Docker, it’s better to build the application in a managed environment for consistency. We have to avoid building applications in the local environment and submit them to the registry.

Otherwise, the packages you might have installed in your local environment could affect the image’s consistency. I hope you don’t want to be in that place since it jeopardizes one of Docker’s core benefits of consistent execution across different environments.

Use Multi Staged Builds remove Build Dependencies

Using multi-staged is the recommended way of deploying applications.

This eliminates the use of build dependencies in the running Container.

We can build the application using a unique build image that has the dev dependencies and move the compiled binaries to a separate Container image for it to run.

In above Dockerfile has two separate stages. Stage 0 use to build the node application from the source node image, and Stage 1 is used to copy the build binaries from the build image to the webserver(Nginx) image that ultimately serves the application.

Conclusion

That’s all I’ve planned for this article. If you are new to Docker, I recommend applying some of these best practices when you build your first image. It will help you to understand them further and allow you to use Docker smartly from Day 1.

Finally, if you have come across any other practices, please comment below. Thanks for reading !!

Learn More

--

--

Anything related to Azure, AWS, GCP, Containers & Kubernetes. Technology enthusiastic, Learner, Blogger