Distroless Container Images

Having smaller docker image size is always beneficial, because of smaller vector attack and from performance perspective. There are several approaches to make it happen:

The first two option are pretty self explanatory for me, but the third one makes me confused what does it mean “distroless”. Concept of “distroless” comes from Google, it doesn’t mean that we put there some magic, it still need to base on something. In this case something is called Debian, but it wasn’t build on current Debian image, but was build from scratch by taking only necessary packages to run your app. That’s why we have dedicated container images for different apps like nodejs, golang, java, python etc. Having different kind of dedicated “distroless” images encourage you to use concept of multi stage build. On how it’s build is described by Bazel, it’s pretty complicated at the first look and I also don’t have enough expertise to show all the use-cases presented in “distroless”. So I will concentrate to prepare dedicated “distroless” image for popular network tool called netcat.

  1. First let’s clone the official repo:
$ git clone git@github.com:GoogleContainerTools/distroless.git
  1. Download the right version of bazel tool:
$ cat .bazelversion
5.1.1
$ curl -fLO https://releases.bazel.build/5.1.1/release/bazel-5.1.1-<OS>-<ARCH>
  1. Find out what are the build targets:
$ ./bazel query 'attr("tags", "'amd64'", "//...")'
Starting local Bazel server and connecting to it...
//base:base_amd64_debian10_test
//base:base_amd64_debian10_test.image
//base:base_amd64_debian11_test
//base:base_amd64_debian11_test.image
//base:base_release_amd64_debian10_test
//base:base_release_amd64_debian10_test.image
//base:base_release_amd64_debian11_test
//base:base_release_amd64_debian11_test.image
...
  1. Let’s say that we’re going to stick with the base image, which is right for ie. golang apps. We need to add some packages for netcat to:
$ git diff
...
diff --git a/base/base.bzl b/base/base.bzl
index 3c3ff6a..6b7e353 100644
--- a/base/base.bzl
+++ b/base/base.bzl
@@ -59,6 +59,9 @@ def distro_components(distro):
                     deb_file(arch, distro, "libc6"),
                     deb_file(arch, distro, "libssl1.1"),
                     deb_file(arch, distro, "openssl"),
+                    deb_file(arch, distro, "libbsd0"),
+                    deb_file(arch, distro, "netcat-openbsd"),
+                    deb_file(arch, distro, "libmd0"),
                 ],
             )
  1. It doesn’t mean that we can build a new image, because new provided packages need to have their checksums. Inside the repo, there is a dedicated tool for that debian_package_manager/:
$ git diff
...
diff --git a/debian_packages.yaml b/debian_packages.yaml
index f6e1661..9b5d081 100644
--- a/debian_packages.yaml
+++ b/debian_packages.yaml
@@ -39,6 +39,9 @@
     - "libgcc-s1"
     - "libgomp1"
     - "libstdc++6"
+    - "libbsd0"
+    - "libmd0"
+    - "netcat-openbsd"
...

$ cd debian_package_manager
$ make generate
$ cd ../
$ debian_package_manager/generate
Processing packages: "s390x" "debian11"
Processing packages: "ppc64le" "debian11"
Processing packages: "arm64" "debian11"
Processing packages: "ppc64le" "debian10"
Processing packages: "amd64" "debian10"
Processing packages: "arm" "debian10"
Processing packages: "arm64" "debian10"
Processing packages: "s390x" "debian10"
Processing packages: "arm" "debian11"
Processing packages: "amd64" "debian11"
Writing archives file: "debian_archives.bzl"
Writing version file: "debian_versions.bzl"
Writing snapshots file: "debian_snapshots.yaml"
  1. Now we can build the new image to produce container image tarball:
$ bazel build //base:base_amd64_debian11_test
  1. Then we can import in into local docker and try to use it:
$ docker import bazel-bin/base/base_root_amd64_debian11-layer.tar distroless-netdata-debian:latest
$ docker images | head -n 2
REPOSITORY                                                         TAG                            IMAGE ID       CREATED         SIZE
distroless-netdata-debian                                          latest                         37829fb2f750   5 minutes ago   18.2MB
$ docker run --entrypoint /bin/nc.openbsd distroless-netdata-debian:latest --rm -ti
/bin/nc.openbsd: invalid option -- '-'
usage: nc [-46CDdFhklNnrStUuvZz] [-I length] [-i interval] [-M ttl]
	  [-m minttl] [-O length] [-P proxy_username] [-p source_port]
	  [-q seconds] [-s sourceaddr] [-T keyword] [-V rtable] [-W recvlimit]
	  [-w timeout] [-X proxy_protocol] [-x proxy_address[:port]]
	  [destination] [port]

So we finished with pretty small (18.2MB) dedicated “distroless” image for netcat, it’s not production ready, but show the powerful combination of bazel and “distroless”.

comments powered by Disqus

powered by Hugo and Noteworthy theme