Skip to main content

Command Palette

Search for a command to run...

Multistage builds in docker and distroless images

Updated
β€’3 min read
Multistage builds in docker and distroless images
M

πŸš€ DevOps & Cloud Engineer | ☁️ AWS | 🐳 Docker | ☸️ Kubernetes | βš™οΈ Jenkins | πŸ€– Ansible | πŸ› οΈ Terraform | πŸ”„ CI/CD | 🎯 ArgoCD | πŸ“ˆ Driving Automation and Cloud Scalibility

Multistage Builds in Docker

A simple docker consists of instructions as shown below:

  • A Dockerfile consists of instructions such as create a base image, create a Working directory, Copy the dependencies and application code in /app directory and then execute the application code.
FROM ubuntu:latest

# Set environment variables
ENV PYTHONUNBUFFERED=1

# Install dependencies
RUN apt-get update \
    && apt-get install -y python3-pip python3-dev

# Set working directory
WORKDIR /app

# Copy project files
COPY . /app/

# Install Python dependencies
RUN pip3 install -r requirements.txt

# Expose the application port
EXPOSE 8000

# Run the application
CMD ["python3", "manage.py", "runserver", "0.0.0.0:8000"]
  • But If observed a python application only needs Python runtime Environment and application binary file to run the application.

  • It do not require ubuntu base image, commands for building the application etc to run the python application.

The Docker image created from above Dockerfile is of 1.5 GB size.

Hence a Dockerfile can be written in stages depending upon application in order to have docker image of less size.

A simple 2 stage docker file is written below :

# Stage 1: Build stage
FROM ubuntu:latest AS builder

# Set environment variables
ENV PYTHONUNBUFFERED=1

# Install dependencies
RUN apt-get update \
    && apt-get install -y python3-pip python3-dev

# Set working directory
WORKDIR /app

# Copy project files
COPY . /app/

# Install Python dependencies
RUN pip3 install -r requirements.txt

# Stage 2: Final stage
# Set working directory
WORKDIR /app

# Copy only the necessary files from the build stage
COPY --from=builder /app /app

# Expose the application port
EXPOSE 8000

# Run the application
CMD ["python3", "manage.py", "runserver", "0.0.0.0:8000"]
  • In stage1 , only those commands are written which are required to build the application and in stage2 the binary from stage1 is copied and then provided the python runtime environment using CMD command line to run the application.

The Docker image created from above Dockerfile written in 2 stages has size of 250 MB which is 1/6th of previous docker image.

Hence there is a significant reduction in image size and a lot of space is left in the system disc.

Distro less Images:

  • Distro less image is a very minimalistic image (Light weight docker image) that will hardly have any packages.

  • These images have only runtime environment.

  • In distro less image, basic shell commands like pwd, cd,ls,find,curl,wget etc won't work because it is designed just to run the application.

lets Execute a Golang application using concepts of Multistage docker builds and Distro less image.

A Golang calculator application is a statically typed application. It don't even required runtime environment to run the application.

First create the docker image using Dockerfile without multi stages:

###########################################
# BASE IMAGE
###########################################

FROM ubuntu AS build

RUN apt-get update && apt-get install -y golang-go

ENV GO111MODULE=off

COPY . .

RUN CGO_ENABLED=0 go build -o /app .

ENTRYPOINT ["/app"]

The docker image created from Dockerfile without multi stage and no distro less image is shown below:

let's create a docker image using concept of Multistage docker build and Distro less image:

###########################################
# BASE IMAGE
###########################################

FROM ubuntu AS build

RUN apt-get update && apt-get install -y golang-go

ENV GO111MODULE=off

COPY . .

RUN CGO_ENABLED=0 go build -o /app .

############################################
# HERE STARTS THE MAGIC OF MULTI STAGE BUILD
############################################

FROM scratch

# Copy the compiled binary from the build stage
COPY --from=build /app /app

# Set the entrypoint for the container to run the binary
ENTRYPOINT ["/app"]

The docker image created using Multistage build and Distro less image is of below size:

The image size is being reduced by almost 800% (from 861 MB to 1.83 MB).

  • The biggest advantage from Distro less Images is "Security". Because of Distro less Images , application is not exposed to any "OS" related vulnerabilities and security threats.