Nowadays when we talk about microservices we often assume “microservices running in containers”. The build tools plugins like sbt-native-packager or dockerfile-maven are commonly used and creating Docker images is a standard build steps. In this article I’d like to describe how to use those tools to produce the Docker images with meaningful tags.

Imagine you have some microservices. Well, a lot of. Maybe hundreds. And you as a DevOps want to know which version of the code is currently running on each environment. Is this serious bug in the auth service already fixed on the test env? It can be hard to answer this question if you don’t know exactly what version of code is deployed on each environment.

If microservices run in Docker containers the Docker image tagging can be handy. But tags are useful only if they are meaningful. For example, timestamp as a tag informs only when this image was created, but tells nothing about the code inside. A good starting point is using application version as the tag. Actually, it’s the default setting in sbt-native-packager and dockerfile-maven plugin — widely used plugins to containerize Java and Scala applications.

This tagging scheme works great for stable releases, but what about software under development? Let’s assume developers work on version 1.1.0, so during active development the application is versioned as 1.1.0-SNAPSHOT. So — the Docker images are always tagged as 1.1.0-SNAPSHOT. It causes some problems:

as mentioned already — we don’t know what code is deployed on development environment

if the docker image tag doesn’t change — it won’t be pulled from docker registry if it’s already present on the server

if you use orchestration system like DC/OS — the application definition won’t change since the docker image definition is exactly the same as in the previous version, so the new image is even not deployed!

Let’s start looking for the solution to solve these problems and to manage docker tags in an automatic way. Our goal is to have docker tags meaningful:

applications running on development environment tagged with (shortened) commit hash,

applications running on staging/production environment tagged with git tags.

We at SoftwareMill focus on JVM-based applications, so let’s narrow our research to maven and sbt.

maven

There is a great maven plugin which gives us what we want. Let’s use it’s functionality based on git describe command. The docker tag is in format <git_tag>-<number_of_commits_since_tag_creation>-g<commit_hash_abbrev> (for example: softwaremill/application:1.0.0-23-ge3a927e ).

You may wonder what is the “g” prefix. According to documentation:

The “g” prefix stands for “git” and is used to allow describing the version of a software depending on the SCM the software is managed with. This is useful in an environment where people may use different SCMs.

The plugin configuration looks like this:

Let me explain the configuration.

gitDescribe — enable versioning based on git describe

— enable versioning based on tags — true — use annotated tags as well as lightweight tags

— use annotated tags as well as lightweight tags abbrev — how many characters from commit hash should be used (0–40)

— how many characters from commit hash should be used (0–40) forceLongFormat — use long format also for tagged commits (the number of commits since the last tag is 0)

The configuration of dockerfile-maven:

git.commit.id.describe property is set by maven-git-commit-id-plugin.

So far — so good. But can we have exactly the same schema with sbt? It turned out it’s possible with sbt-git plugin and some configuration.

sbt-git

With sbt-git plugin it is possible to tag Docker images in exactly the same way as with maven plugin described above. Let’s start with adding this plugin to the project/plugins.sbt file (together with sbt-native-packager):

Then configure sbt-git and sbt-native-packager plugins in build.sbt :

The most important part — we’re changing the default Docker image naming schema:

dockerAlias := DockerAlias(None, dockerUsername.value, (packageName in Docker).value, git.gitDescribedVersion.value)

The image tagging schema is now exactly the same as with git-commit-id maven plugin.

There is one major difference between maven and sbt — when there is no git tag in the git repository, git.gitDescribedVersion.value is empty. A good workaround is to create a dummy git tag like 0.0.0 on initial commit.

And even shell?

Usually, you will build and publish the Docker image using a CI pipeline (let’s assume Jenkins). But what if you want to do something more with the produced image, for example, deploy it within a sh step? How to get the Docker image tag?

As you probably already noticed, both plugins use the git describe command underneath — so if you want to get the Docker image tag in the same format — you can add a groovy method to your Jenkinsfile:

Wrap up

Working with a heterogeneous system having multiple microservices and build tools can be challenging, having things unified and using common patterns can make life a little bit easier :).