In a previous blog post, I've described how we can use Docker multi-stages build feature to generate thin images. In this article, we're going to push the limit a bit further by combining
multi-stage build and Google’s
distroless base images! This help us to build runtime images that are amazingly more thinner and smaller.
Why should I care?!
Many of us have been running containers in production for years now, and it's just working fine! someone would ask: why bothering myself with it, and struggling to minimize the size of my images?! Well, here are some benefits of keeping your image size as small as possible:
- Security: From a security perspective, you can reduce the attack surface by removing unnecessary components from your image. Less code/less programs in the container means less attack surface. And, the base OS can be more secure.
- Time and cost: Bandwidth and storage are typically expensive. Because the size is so much smaller, it’s much quicker to download the image from a Docker registry and therefore it can be distributed to different machines much quicker. Think when you're running a cluster with a dozens of containers!
- Effective tracking: Smaller images improves the signal to noise of scanners (e.g. CVE). Keep in mind that everything in your image is another thing whose provenance needs to be tracked and which needs to be scanned for known vulnerabilities! The more libraries and binaries you have installed on your image, the greater the odds that your system will be susceptible to vulnerabilities discovered tomorrow.
Distroless docker images
multi-stage builds in Docker CE 17.05 (EE 17.06), in which the first-stage build produces an executable artifact, that is added to the runtime image in the second-stage build. You might be surprised to know that a docker container can function with just a binary in it. That means multi-stage builds works perfectly when we're using binaries, written in Go for example with scratch images, but what about many other languages that needs some runtime like Java?
Someone could suggest using
Alpine, Well that a valid and recommended suggestion! But Alpline is basically a Linux kernel (with an unofficial port of grsecurity patch), the musl C library, BusyBox, LibreSSL, and OpenRC! Alpine also uses its own package manager called apk-tools which can be used to install your needed runtime, the JRE in our case! But here's the question: Will you ever need that package manager again? Probably not, and then it just stays there, being a huge security risk.
This is where Google’s
distroless images come in. "Distroless" images contain only your application and its runtime dependencies. They don’t contain any programs like shells and package managers usually found in a Linux distribution.
We'll use the same
Dockerfile used in my previous article, changing just the
deployment environment image, and using the “distroless” images in our case:
FROM maven:3.5-jdk-8 AS build COPY src /usr/src/app/src COPY pom.xml /usr/src/app RUN mvn -f /usr/src/app/pom.xml clean package FROM gcr.io/distroless/java COPY --from=build /usr/src/app/target/flighttracker-1.0.0-SNAPSHOT.jar /usr/app/flighttracker-1.0.0-SNAPSHOT.jar EXPOSE 8080 ENTRYPOINT ["java","-jar","/usr/app/flighttracker-1.0.0-SNAPSHOT.jar"]
with this small change, our Docker image is even impressively smaller!