Hi everyone, first post on dev.to, I whish to be more active in the future!
Today I want to explain a small tip I recently fond out to reduce the Docker images size, in my case I was able to reduce the size by half!
Let's assume that you are working with a language like Ruby or Python, even if these languages are not compiled they often need some system libraries to work properly, in particular if you are working with databases (MySQL, SQLite, Postgres) you need to compile the gem or the library for the specific architecture of your machine.
In my case I'm working with Ruby and I'm trying to install the SQLite gem that, unfortunately, requires the build-base
system library.
Here is the project I'm working on:
Faenz Analytics
So I thought to install it, compile the SQLite gem and uninstall everything I do not need anymore, here is my Dockerfile:
FROM ruby:3.0.4-alpine3.15
WORKDIR /faenz-analytics
RUN apk update && \
apk add make && \
apk add build-base && \ # this takes 200Mb of space!!
apk add sqlite-dev
# ...some instructions
RUN bundle install # installing Ruby gems
RUN apk del build-base
# ...some other instructions
easy, right? we installed build-base
, compiled and installed the gems and removed build-base
to reduce the image size.
The problem is that this does not work! The image size is the same as if we did not remove build-base
. Let's discover why.
We wrote three RUN
instructions, each one generates a layer and these layer are put togheter by Docker to create the final image:
- the first
RUN
installs the system libraries - the second
RUN
install the gems/libraries by Ruby or whatever language you are using - the third
RUN
removes the system libraries
since we installed the system libraries in one of the layer they will take some space and will increase the image size, there's no way we can reduce the image size with the next layers.
It's like a sum with no negative numbers. The size can only increase or stay the same from a layer to another.
What? Don't give up, we got this š
Here is the solution: put these operations in the same layer!
FROM ruby:3.0.4-alpine3.15
WORKDIR /faenz-analytics
RUN apk update && \
apk add make && \
apk add sqlite-dev
# ...some instructions
RUN apk add build-base && \
bundle install && \
apk del build-base
# ...some other instructions
grouping the operations in just one RUN
instruction avoids Docker to waste space to store the build-base
in a layer. The layer is generated at the end of the instruction so we are going to install, use the build-base
and prontly remove it so that the layer will not contain it at all.
With this little tip my image size went from 400Mb to 170mb, less than half!
If you want to exagerate with the micro optimizations, you can apply the same solution to apk update
(or apt-get update
for debian/ubuntu images) by deleting the cache created by this command:
RUN apk update && \
add build-base && \
# install other dependencies here...
bundle install && \
apk del build-base && \
rm -rf /var/cache/apk && \
rm -rf tmp/cache
I'm not a Docker expert, so, if you know a better solution or want to share similar tips to reduce the image size please leave a comment, I'm all ears š
Top comments (5)
Hmm, I used to do this too. This is a nice solution for many simpler use cases.
But I personally prefer multi stage builds, makes for much cleaner docker files and more predictable caching.
Ehi thanks for the suggestion, I'm already using multi stage build for my project github.com/a-chris/faenz to generate the frontend assets but I think it is really hard to use to install gems and move them to the final image, there are so many stuff to move
hii
Can u help me solve an error?
what's the error?
dev.to/creatorarno/python-error-no...