In my day job, we've been using Google's Distroless images for some time. The benefits of this are well known: smaller image and attack surface. However, what we didn't expect is to still to have to deal with a significant amount of toil dealing with vulnerability triage (see Snyk output below for distroless/base
). It could be argued our SDLC is a little, clunky and we're not quite ready for scratch
(Graal, Golang, etc.,) images yet so what can we do in the interim? 🤔
That lead me to think about a distroless Alpine Linux image. A quick google shows I'm not the only one as a recent Medium article attests. But as we're mostly dealing with Java to get a working image by pulling library-by-library is somewhat of a faff. Fortunately, APKO to the rescue.
APKO is a tool to create lean images using Alpine with only the stuff we need.
Demo
At time of writing an Alpine 3.15 image was 5.57MB. Pretty small but still includes some things we don't need, such as sh
shell. So with the help of the examples from APKO I came up with:
contents:
repositories:
- https://dl-cdn.alpinelinux.org/alpine/v3.15/main
packages:
- musl
- zlib
accounts:
groups:
- groupname: nonroot
gid: 10000
users:
- username: nonroot
uid: 10000
run-as: nonroot
os-release:
version-id: '3.15' # defaults to 3.16/edge
A few things to note here:
- Using
3.15
repos, rather thanedge
used in the examples, - Adding a few packages:
-
musl
: Needed for pretty much everything, -
zlib
: Required for JRE 17, crashes without it.
-
- Using non-root user.
PS ...> docker run --rm `
-v $pwd/:/app:rw `
-w /app ghcr.io/chainguard-dev/apko:v0.3.3 `
build rootless.yaml alpine:3.15-apko apko.tar
2022/05/10 09:21:44 loading config file: rootless.yaml
2022/05/10 09:21:45 apko (x86_64): building image 'alpine:3.15-apko'
...
PS ...> docker load -i .\apko.tar
Loaded image: alpine:3.15-apko
The result is a very small image:
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine 3.15 0ac33e5f5afa 4 weeks ago 5.57MB
alpine 3.15-apko 5a3ea808f8ed 52 years ago 709kB
That's nearly 1/8th of the size (12.7%) and a grand total of just 2 packages (down from 14):
PS ...> docker scan alpine:3.15-apko
Testing alpine:3.15-apko...
Package manager: apk
Project name: docker-image|alpine
Docker image: alpine:3.15-apko
Platform: linux/amd64
✔ Tested 2 dependencies for known vulnerabilities, no vulnerable paths found.
That's much smaller than roughly equivalent distroless/base
:
REPOSITORY TAG IMAGE ID CREATED SIZE
gcr.io/distroless/base nonroot 555ca12a9222 52 years ago 20.3MB
That's over 28x larger and that does have potential issues:
PS ...> docker scan gcr.io/distroless/base:nonroot
Testing gcr.io/distroless/base:nonroot...
...
Package manager: deb
Project name: docker-image|gcr.io/distroless/base
Docker image: gcr.io/distroless/base:nonroot
Platform: linux/amd64
Tested 6 dependencies for known vulnerabilities, found 11 vulnerabilities.
I've trialled my new image with an existing project via JLink that's heavy on Netty and gRPC the image works great (with a small tweak to exclude grpc-netty-shaded
due to grpc-java#9083).
What about Java?
Ok, so I don't want to use a JLink minimal JRE, what the difference between a Java 17 JRE:
REPOSITORY TAG IMAGE ID CREATED SIZE
gcr.io/distroless/java17-debian11 nonroot 678ed8ce3ba5 52 years ago 231MB
alpine jre17-apko d2302101850c 52 years ago 202MB
29MB less, that's what!
Conclusion
So, we can have a distroless Alpine image, that's even smaller than Google's with a smaller attack surface. 🔥
The maintainers of APKO highlight it's not released yet and liable to change, but it's a great start.
Top comments (1)
Two things:
apko works perfectly as a GCP CloudBuilder.
if you include the package "alpine-release", you get no complaints from the security scanners.