TT Lab
Get started
Learn Learning paths Courses

Building Images

Cutting It Down With Multi-Stage Builds

Continue in TT Lab

This lab runs on a real VM

This box is not a Pod but a virtual machine launched by KubeVirt. A separate Linux kernel runs in it, systemd actually manages services, and docker is a real Docker engine, not an imitation. A container started with docker run becomes a real process, and both docker exec and docker logs work as usual.

This lab used to run inside a Pod. That box had dropped every kernel capability, so the step that starts a container was blocked, and you learned by working around it and unpacking image archives by hand. The workaround is no longer needed.

There are two things to know.

Goal

You move only the output made in the builder stage into the final image, and confirm by size numbers the process of making the same result as a much smaller image.

Why it matters

Image size is not a matter of appearance but of deployment speed and rollback speed. Every time a node scales out, every time you roll back, you download that size again. And the biggest lever for reducing size is not compression or cleanup but "never creating, in that stage, what must not be in the final image". Because layers are not rewound, if you violate this principle, the size will not come back no matter how much you delete later.

Steps

  1. Create /root/build3 and make two stages in /root/build3/Dockerfile. The first stage uses python:3.12-alpine, is named builder, and, in /out/app.txt, writes built-in-builder. The second stage is alpine:3.20. Build it as labhub/ms:v1.
  2. Change the final stage so that it copies only the builder's /out/app.txt to /app/app.txt, and build it as labhub/ms:v2. When you run it and read the file, built-in-builder must appear.
  3. In /root/build3/fat.Dockerfile, write a version that produces the same result in a single stage (with python:3.12-alpine as the base) and build it as labhub/ms:fat.
  4. Build only the first stage to create the labhub/ms:builder image. It must contain /out/app.txt.
  5. In the final stage of the Dockerfile, add a line that, from the busybox:1.36 image, copies /bin/busybox directly to /app/busybox, and build it as labhub/ms:v3.
  6. Run labhub/ms:v3 and confirm that there is no python3. The image size must be under 60 MB.
  7. Create /root/build3/scratch.Dockerfile that puts, on top of FROM scratch, only the file from busybox:1.36 at /bin/busybox, and specify ENTRYPOINT in array form so that it prints the string it receives as an argument. Build it as labhub/ms:scratch, and when you give scratch-works as an argument, that string must be printed.
  8. In /root/build3/size.md, write the four lines below with the actual values.
fat_mb=<labhub/ms:fat 크기(MB)>
slim_mb=<labhub/ms:v3 크기(MB)>
scratch_mb=<labhub/ms:scratch 크기(MB)>
reduction_pct=<(fat_mb - scratch_mb) / fat_mb * 100 의 정수부>

Notes

Create two stages

Create /root/build3 and make two stages in /root/build3/Dockerfile. The first stage uses python:3.12-alpine, is named builder, and, in /out/app.txt, writes built-in-builder. The second stage is alpine:3.20. Build it as labhub/ms:v1.

Write FROM twice and give the first stage a name. You will refer to the name later in COPY.

Bring only the output

Change the final stage so that it copies only the builder's /out/app.txt to /app/app.txt, and build it as labhub/ms:v2. When you run it and read the file, built-in-builder must appear.

Pick and copy only the file made in the builder stage. If you copy the whole stage, there is no point in using multi-stage.

Compare the size with a single stage

In /root/build3/fat.Dockerfile, write a version that produces the same result in a single stage (with python:3.12-alpine as the base) and build it as labhub/ms:fat.

Compare it with an image that leaves the same result as it is on a heavy base. It should shrink by 30% or more.

Build only the intermediate stage

Build only the first stage to create the labhub/ms:builder image. It must contain /out/app.txt.

When debugging, you can extract only the builder stage as a separate image. There is an option that stops before reaching the final stage.

Copy directly from another image

In the final stage of the Dockerfile, add a line that, from the busybox:1.36 image, copies /bin/busybox directly to /app/busybox, and build it as labhub/ms:v3.

--from does not accept only stage names. You can also give an image reference as it is.

Do not leave build tools behind

Run labhub/ms:v3 and confirm that there is no python3. The image size must be under 60 MB.

Actually run it and confirm that the runtime that was used only for the build has disappeared from the final stage.

Put it on an empty base

Create /root/build3/scratch.Dockerfile that puts, on top of FROM scratch, only the file from busybox:1.36 at /bin/busybox, and specify ENTRYPOINT in array form so that it prints the string it receives as an argument. Build it as labhub/ms:scratch, and when you give scratch-works as an argument, that string must be printed.

scratch has no shell, so ENTRYPOINT must be in array form. You have to choose a statically linked binary.

Size report

In /root/build3/size.md, write the four lines below with the actual values.

Measure the actual sizes of the three images, write them down and calculate the reduction rate. 1 MB = 1048576 bytes.