In this Cloud Native Short Take from RX-M, Ron Petty takes us through the process of slimming down Docker images using multi-stage builds. He explains that dependencies are what typically make images large and sets out to see if he can slim down the image using a multi-stage build. This video would is a valuable resource for anyone working with Docker images and looking to optimize their size and performance.
Video Transcript / Summary
In this RX-M cloud native short take we look at the RX-M Image Overview module. The goal is to slim down images. It starts with Docker, which is already installed, so in step two we create a Go application. We package that app in a container image with a Dockerfile and we will review the size of the file and talk about why it is the way it is. Then we’re going to see if we can slim it down through this notion of multi-stage builds.
In this case we build a go application but iIt doesn’t really matter what the application is. It is really about the dependencies–that’s typically what makes images large. The Go code is simple; it uses the built-in print function to print “Hello World” to stdout.
Next we create the Dockerfile. To run a Go program you need Go so our FROM instruction will use a golang base image. We set the WORKDIR to /go/src/hello and tell the COPY`” instruction to put our source code, in the hello.go file, in that directory. Following that, the RUN instruction builds up our Go module, required by default in Go 1.16 and above. The CMD instruction sets our default command to run our code.
Now for those who don’t know Go, when we run our image with docker container run it’s going to compile hello.go–that means there’s a compiler somewhere–and then it’s gonna actually execute that executable that the compiler created.
We call docker image build to build the image. what this does is upload our source code and our Dockerfile to the Docker daemon which reads the Dockerfile, downloads the golang base image, and creates a new image by putting our code in it and setting up the default command to run the code.
Running it with docker container run works but looking at the size of the image reveals that it is pretty large for a simple hello world: 862 meg! Of course that’s not our source code, it’s the go standard library and compiler related tools in the base image.
This is where multi-stage builds come in! If we refactor our Dockerfile to be a multi-stage build we can pull out a compiled artifact from stage one and make a new image where we already have a pre-built artifact. This technique is language specific and our demo illustrates how you would do it with Go in Docker.
In a multi-stage build you make an alias for the build stage(s). By adding the keyword AS after the image argument of your FROM instruction, like this: FROM golang:1.16 AS build-env a referenceable alias is created that can be called in subsequent stages.
We still have put our source code in it and still have to create a module but we need to build a static executable. In go we can use the build command, supply it information via the tags argument to not use external dependencies. In this case the network package instead of using the libc version we’re going to use the go version so we can statically compile it and output it as an executable called hello. And that is stage one.
In our final stage we use the keyword “scratch” in our FROM instruction, making a new image that has nothing in it. The secret sauce is right here: we COPY from that alias we created in our earlier build stage–our executable. We place it, using the same name, in the root directory. Lastly we set the default command using ENTRYPOINT pointing at our binary in the root directory.
In summary: step one, build a static image, step two run it. This time we build the image and tag it “v2”. The build looks similar but there is reference to this first bit of building called the first stage ultimately generating an executable and then copying it over into a new image. When we run this v2 image we see that it works more importantly how big is it? In this case a lot smaller! Image v1 was 800 meg and v2 is 1.22 meg! That is the power of multi-stage builds!
Multi-stage image builds is just one of the things you will learn when you attend one of our Docker courses! You can go to the RX-M custom courseware builder, find the “Image Overview” module under the “Containers” category and add it to a custom course along with a number of other Containers and Kubernetes modules.