Time for some investigation…
As we needed Java to run our microservice, we used an image that already contained that. This saved a lot of time at first. We only had to focus on what we wanted to add and how to run the actual microservice. This however led to an image size of over 800 MB.
It should be possible to trim this down…
A quick search on the internet showed that are people who make it a sport in creating images that are as small as possible. It is not just installing packages when you need them, but also removing them if they are not needed anymore and cleaning up installed applications.
Again the easy way would be to just use an image that someone else has created. But how do you make sure that there is no funny stuff installed (eventually it is going to run in production), or a bit too much is cleaned up? You could of course traverse through all the layers of the image or you could take the parts that you like the most and create your own image.
So this is what we did. A good starting point is Alpine Linux with a size of just 5 MB. Next is to install Java on it. So first install a package like wget in order to download Java. Then download and install, clean up some libraries we are never going to use, throw away the downloaded files and remove the package we installed earlier again. Result of this image is almost 190 MB, which is quit reasonable for an image with only Java installed and a whole lot better than the 800 MB we got earlier!
Can we optimise our microservice as well?
The code for the microservice itself is a little over 30 KB, but this does not include the dependencies needed. Looking at these, we quickly see that it is almost 50 MB. So what are we depending upon?!
One of the culprits seems to be a Scala compiler of almost 15 MB. But wait, we do not even need a Scala compiler, so where is this coming from? Since it is a Maven project we can execute a ‘mvn dependency:tree’ and check out what is including this dependency.
Ah, it is coming from Swagger, the API documentation framework/tool… but wait, we do not need that in our microservice.
So a bit of moving dependencies to the right place is needed here, since we do need that somewhere else. And now that we see all dependencies, there are also some that we know are not used in our project either, so let us remove them as well.
If you are not sure if a dependency can be removed, just remove it and compile/test your application. If it breaks, the dependency was needed. It is as simple as that.
Final size was reduced from 50 MB to just over 15 MB. Still a lot, but a whole lot better. That would not bloat our Docker image that much.
By looking at your own code (and especially its dependencies) and at what is the absolute minimum your application needs, you can gain a lot of space. And it is not just space, but also download/transfer time. When you clean out your local Docker images it has to download all layers of the image. One can imagine that it is a lot quicker to download a total of 200 MB compared to 800 MB.