 Hey, I'm Eric Gregory, and I'm a technical content writer at Mirantis today I'm talking hopefully quickly enough about reducing container image size the talk is focused on cloud native beginners But by the end we'll get into some more advanced approaches to tiny workloads So first why does this matter? Smaller images reduce time to build important for CI and time to pull which is important when spinning up a new node in a production environment improved performance and utilization drive sustainability and performance enhancements are particularly impactful on power efficient hardware and Finally minimal base images reduce attack surface and defend against privilege escalation attacks So here we've got four approaches to a simple HTTP server written in go First the default Debian based go 1.21 image with a single-stage build this clocks in at 301 megabytes Next a single-stage build on the Alpine based go image if you're not sure what that means I'll explain in a moment, but this takes us down to 85 megabytes Third is a statically linked binary wrapped in the scratch base image. This gets down to four megabytes Last is a web assembly module that can run on Kubernetes at way under a megabyte. We'll come back to that at the end So to understand the differences, let's review the structure of a container at the bottom is the OS kernel Which is shared with a host machine everything above this point is part of our image That's going to include our application, but also language run times dependencies libraries, etc This middle layer can really add up Now our goal isn't to get the smallest possible image at any cost But to cultivate standard repeatable practices that tend to give us smaller images while respecting other priorities like time and maintainability So as always context is everything Here's the Dockerfile that gave us our Debian based image We're building and running from the same base image and since we're trying to do two jobs with one tool We need a bunch of build time dependencies that we don't need at runtime We've also got a shell curl apt and so on as a rule We don't want to include stuff we don't need the Golang project It's made this easy for us with a Golang image built on a slimmer base image called Alpine Alpine Linux is designed to be a slim foundation for environments like containers And it does so by clearing out everything that doesn't spark joy and leaving a package manager so you can download the stuff You really need The only thing I've changed in this Dockerfile is the base image We can swap out our base image with Golang Alpine and reduce the size by more than two-thirds just that simple step This image uses muscle Lib C rather than g Lib C and that can be an issue in some cases But that's a good reminder that we need to be thinking about what we actually need in the base image So here we have the start of a first-good practice refer the most minimal bit minimal base image that fits your requirements Sometimes you'll need lots of tools and dependencies, especially when containerizing existing applications But try to find the minimum that works where possible try to use consistent base image distros across different applications So you can have consistent base layers across your images When we use the Alpine base image for a dedicated runtime container we can get down to about seven megabytes But Alpine isn't our only choice of base image DistroList tighties up even more and removes things like the shell and package manager There are a number of variants that let you get really granular on things like including or excluding g Lib C or Lib SSL Scratch is an explicitly empty base image good for standalone binaries note that it's really and truly empty So you'll have to account for things like certs and users and time zone info And you won't be able to build inside a scratch base image since there's nothing to build with which brings us to our second practice Multi-stage builds in the top half of this docker file We're using the big Debian base image to build our binary and then in the bottom half copying it over to a new runtime image built from scratch This is most pertinent to compiled languages like go, but even containerized Python that Python apps might use multi-stage builds with tools like pecs For Windows containers Microsoft provides for base images ranging from the 3.4 gig windows image That includes the complete Windows API set down to the 100 odd megabyte nano server image tailored to new applications microservers and apps with minimal dependencies We can take advantage of multi-stage builds here as well for example building from a go-lang nano server image and then running from baseline nano server Finally, let's look forward to an emerging approach web assembly also known as wasm Web assembly is a low-level binary code format enabling small fast and portable workloads Projects like run wassey are making it more and more practical to run web assembly workloads on Kubernetes The K zeros Kubernetes distra makes it easy to experiment with runtime plug-ins Not just for web assembly, but also things like G visor when the cooblet gets a request for a workload with a particular runtime requirement It passes on that information to container D which can then select the appropriate low-level runtime Which might be run C for an ordinary container or in this case wasm time spend for web assembly Once you have the environment up and running building an image for a simple app It doesn't have to be a whole lot more complicated in essence We're compiling the bytecode and then plopping that down in an empty OCI compatible scratch shell of a container Then the web assembly runtime handles the job of running that code against the wasm virtual architecture So practice three is look out for new approaches Finally, if you'd like to check out a detailed tutorial on using web assembly on K zeros or look through the Docker files from this Presentation this QR code will take you to a page where you can find all of that Thank you so much for your time. Keep it tiny. Keep it safe. Have a great coupon