Dockerfile — Kick it up a notch!

Lior Dux
5 min readMar 18, 2024

--

the concept behind dockerfile

Introduction

docker layers

Layers, layers, layers! “Rome was not built in a day” — The same goes for a docker container planning, which is built layer after layer after layer.

Only this time we wish to keep the build time minimal, with security in mind. So today we’ll sharpen our skills and knowledge (which is also built in layers.. 😄) and take the multistage dockerfile to the next stage.

In the previous episodes of…

If you follow my writing, you probably encountered my “C Programming — All Over Again” article, where I talk about setting up a template for future c project I’ll have, which uses Dockerfile in various ways to achieve both the development environment as well as the final artifact runtime environment. Recently I’ve learned about new images designed for the final production stage.

Before we make any changes

If you’re not using Git already, or any other VSC tool, make sure to learn the basic of it and use it! Whether you’re going to change your base layer or add additional stages to your existing Dockerfile, make sure to branch-out ( git checkout --branch feature/dockerfile-layers) or at least make an empty commit ( git commit -m “Restore point before messing with Dockerfile” --allow-empty) as a revert point.

What are we doing?

We’re going to take our existing Dockerfile.ubuntu from our repo and make it better, add stages for a more suitable for production use, with security in mind. Our final image will be light, which will decrease our pulling time more than 100%, consume less resources (memory & cpu), and take less of our storage space.

Our dockerfile

FROM ubuntu:22.04 as base

MAINTAINER lior.dux@develeap.com
LABEL maintainer.description='C Development environment with ceedling, cmock unity, and doxygen'

## Ommitted for readability ...

WORKDIR /project
CMD bash

We are using a Ubuntu LTS (Long Term Support) as our base image, which is great for development. However, we have to keep in mind that containers are not vms (virtual machines), the should be as light as possible and only container the bare essentials to fit out purposes.

Using Operation-System-like images, such as. ubuntu:22.04 have greater room for escalations, are more vulnerable to CVE, they are robust and the build and pulling time is significantly larger than slim-er images. Using such images might be fine for development stage only, like we meant to. However when it comes to production, it is a major issue.

So, how would we go and fix it?

When it’s time to prepare for production, we would generally prefer slim images, such as alpine or bullseye-slim, etc.

Baby Steps

######################################################
### Stage 1 - Base image (development environment) ###
######################################################
FROM ubuntu:22.04 as base

MAINTAINER lior.dux@develeap.com
LABEL maintainer.description='C Development environment with ceedling, cmock unity, and doxygen'

## Ommitted for readability ...

WORKDIR /project
CMD bash

###########################################################
### Stage 2 - Build image (compile, generate artifacts) ###
###########################################################
# Use the image i've already created, just from dockerhub.
# How & why we've build this image?
# Checkout this medium article: https://medium.com/@lior.dux/c-programming-all-over-again-b9984a4736c5
# Github Repository: https://github.com/zMynxx/c-all-over-again
# Dockerhub: https://hub.docker.com/r/druxx/ubuntu-ceedling
FROM druxx/ubuntu-ceedling:22.04-v0.32.0-d76db35 as ase

# Copy the source files, headers, and project decleration from the base stage.
COPY --from=base /project/src /project/src
COPY --from=base /project/project.yml /project/project.yml
WORKDIR /project
CMD ceedling release

##########################################################
### Stage 3 - Production image (serve our application) ###
##########################################################
FROM alpine:3.19.1 as release

# Use COPY to pull artifact from previous build layer
Copy --from=base /project/build/artifacts/release/MyApp.out /bin/MyApp.out

ENTRYPOINT ["/bin/MyApp.out"]

We’ve added 2 additional stages to our dockerfile, named build and release. build will be lighter than base and use only the necessities to compile our code, and generate an artifact, a binary named MyApp.out in our example. release will be our final stage, where we use COPY to pull our artifact from the build stage, into a much lighter runtime environment where our application could just serve.

That’s great, but…

Our multistage Dockerfile is indeed great, but it could (and should!) be better. Production environment are sensitive and more prone to malicious attacks according to our security thread model. Therefore we will use scratch as our final image layer, and make to only include our artifact is included.

##########################################################
### Stage 3 - Production image (serve our application) ###
##########################################################
FROM scratch as release

# Use COPY to pull artifact from previous build layer
Copy --from=base /project/build/artifacts
/release/MyApp.out /bin/MyApp.out

ENTRYPOINT ["/bin/MyApp.out"]

Now our offcial image is so slim even /bin/sh is absent!
*Curios about scratch? learn more about it.

What if our code is interpreted, not compiled?

Not all application are written in programming languages which compile into a artifact such as a binary or a static objects. For example, Python. In such occasions we could checkout Wolfi (shoutout to my personal hero, Viktor Farcic, the persona behind DevOps Toolkit) or Google’s Distroless.
Both ot them offer a ‘disto-less’ solution approach, minimalist runtime environment.

Summary

Everyday you learn something new, and today we have learned about Distroless, Wolfi, Scratch, the importance of layering, and the wonderful benefits we gain from having a suitable multistage Dockerfile.

Wish to look at the code? Make sure to check the following branches

in my Git repository 😄

Wish to use this repository as base? Use it as a template!

Create a repository from template.

--

--

Lior Dux

27 years old DevOps Engineer living in Israel Loves computers, tech, security, automations and embedded.