[69] Templating Dockerfiles using ARG & docker-compose

Story Time

Skip to the implementation section below if you don’t want to read the full story 😉

I recently learned that we can use a single Dockerfile to build multiple variants of Docker images. This is a really useful feature because we need not maintain multiple Dockerfiles with one or 2 numbers changed (mainly, the tags).

One of my projects at work uses Microsoft SQL Server (MSSQL) as its database. And I wrote a Dockerfile a while back that builds a Docker Image for MSSQL 2017. And when Microsoft released MSSQL 2019, I duplicated that Dockerfile and altered just one number – that is, the tag of the Dockerfile’s FROM command from mssql:2017 to mssql:2019. To do this, I had to duplicate the whole folder with the MSSQL 2017-related scripts and change just one number in the 2019’s Dockerfile.

When I first wrote it, it felt very weird because this approach looked extremely redundant. So, I kept looking for things called “templating tools” and I came to know about Helm Charts and the like. But the “problem” with Helm Charts is that it’s probably not meant for building Docker Images. It’s supposed to be used for deploying Docker Images.

And after some more research, I saw the answer lying in plain sight. I could’ve simply used the ARG command in Dockerfile to pass the variable as an argument to the Dockerfile. That’s what I ended up doing.

Now I just have one Dockerfile. And one docker-compose-build.yml file to pass the arguments and stuff. It is to be noted that docker-compose is also supposed to be used only for deploying Docker Images. But I used that instead of writing a shell script (with a for loop and stuff) because I wanted to keep everything Docker-ry. Now I just need to run a single-line command to get my Docker Images built!

Old implementation

The old implementation had separate Dockerfiles for each version of the source image (MSSQL Server in my case)

Dockerfile-custom-mssql-2017

FROM mcr.microsoft.com/mssql/server:2017-latest
RUN <do something>
...

Dockerfile-custom-mssql-2019

FROM mcr.microsoft.com/mssql/server:2019-latest
RUN <do same thing>
...

Dockerfile-custom-mssql-2022

FROM mcr.microsoft.com/mssql/server:2022-latest
RUN <do same thing>
...

New implementation

The new implementation has just one Dockerfile which accepts the source image’s tag as an argument (ARG)

ARG MSSQL_VERSION
FROM mcr.microsoft.com/mssql/server:${MSSQL_VERSION}
RUN <do the same thing>
...

And then I wrote a new docker-compose-build.yml which had instructions for the 3 MSSQL versions I needed:

version: "3.9"
services:
    custom-mssql-2017-db:
        image: anushibin007/custom-mssql:2017
        build:
            context: .
            args:
                - MSSQL_VERSION=2017-latest
    custom-mssql-2019-db:
        image: anushibin007/custom-mssql:2019
        build:
            context: .
            args:
                - MSSQL_VERSION=2019-latest
    custom-mssql-2022-db:
        image: anushibin007/custom-mssql:2022
        build:
            context: .
            args:
                - MSSQL_VERSION=2022-latest

Now, I just need to run a build using the docker-compose-build.yml file. The below command will build all the 3 images that are mentioned in the compose file.

docker compose -f docker-compose-build.yaml build

And if I want to push all the 3 images to the Docker registry, I can run the below command.

docker compose -f docker-compose-build.yaml push

Neat!

Leave a comment