Basically, to speeds up building, deploying and also cut costs with storage and network
egress if you're using some cloud provider.
We can achieve minimal Docker images size using base images thats focus on minimalism such Alpine Linux and others strategies like Multi Stage build which is the approach that I use combining with
SCRATCH base image.
Let's play with Docker, even if you don't have it installed in your machine, use the 🐋 online lab it's awesome and gives a real Docker experience through a web browser.
When you sign in into the online lab you can clone the demonstration project and guess what ? There's git binary ready to use in the running instance, so:
git clone -b super-minimalistic-docker-image https://github.com/iamseki/dev-to.git
The main idea is divide Dockerfile into multiple stages passing to following stage just the necessary components to run the image properly. Cool, but how ?
The oficial explanation FROM Docker webpage:
FROMstatements in your Dockerfile. Each
FROMinstruction can use a different base, and each of them begins a new stage of the build.
For this example I continue my previous post example exposing a http server to handle a simple
You can take a look in the full source code in github repo. The Dockerfile was written as follow:
FROM golang:alpine as builder WORKDIR /app COPY . . RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" . FROM scratch WORKDIR /app COPY --from=builder /app/dev-to /usr/bin/ ENTRYPOINT ["dev-to"]
Building and checking the image size:
- if you didn't do yet
docker build -t dev-to .
docker images | grep dev-to -B 1
Voila ! The generated Docker Image has 4.55MB and it's a runnable web server ->
docker run --rm -p 8080:8080 -d dev-to
HTTP GET request in the only route exposed by the server doing:
The response should be a JSON of fake events.
Comment some lines or just rewrite the Dockerfile, for example:
FROM golang:alpine as builder WORKDIR /app COPY . . RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" . RUN mv dev-to /usr/bin/ ENTRYPOINT ["dev-to"]
And we got an image of size 324MB.
This kind of approach is great for static languages like Golang but even with these languages that compiles a raw binary to be executed by the OS it's always a good idea to be careful when writing Dockerfiles to optimizing the CI/CD proccess and cut some costs.
The scratch base Image contains nothing so it's lightweight but you can't debug container from inside, to do so you can download binaries with
RUN command but I encorage you to use the
busybox as base Image, is also lightweight but non-empty so it's possible to debug containers from inside with exec commands such as
docker exec -ti container-name sh.