An 800 MB image on a depot link REL-118

Open2 versionsContainers · Medium · Tune · about 35 min ·Linux + Docker

Lab machine

A private Linux machine with Docker Engine. Starting takes about 30 seconds. Sessions last up to 60 minutes.
Dmitri Vos opened REL-118 at 14:00task

Pulling the Waybill image to 24 depots takes eleven minutes on the depot links, and every release waits for it. The budget is 200 MB.

The release image works, and the integration test passes. It also ships the compiler toolchain, the package cache, and .git to every depot.

"Under 200 MB, runs as non-root, and the integration test still passes. That is the whole list." (Dmitri)

A multi-stage build compiles in one image and copies only the result into a clean one.

Your task

Rework the Dockerfile so docker build -t waybill:candidate . produces an image under 200 MB that runs as a non-root user, contains no toolchain, cache, or Git history, and passes tests/integration.sh waybill:candidate.

On the machine

  • Dockerfile, .dockerignore
  • docker history waybill:2.6.0, docker image ls
  • tests/integration.sh <image>

Timeline

2.4Image is 310 MB.
2.6.0Image is 812 MB: the toolchain and Git history ended up inside.
TueRelease waits 11 minutes for image pulls on depot links.
14:00REL-118: platform sets a 200 MB budget and a non-root rule.

Done when

  1. The runtime image is under 200 MB and carries no toolchain, cache, or Git history.
  2. tests/integration.sh passes against it.
  3. It runs as a non-root user.

Hints

Hint 1

`docker history` lists each step with the size it added.

Hint 2

`COPY . .` sends everything in the directory. What in it does the running service actually need?

Hint 3

A second `FROM` starts a clean stage, and `COPY --from=build` takes only the paths you name. Copying the whole build directory brings everything back.

Hint 4

The node base image already has a `node` user. Put `USER node` in the runtime stage.

Show the solution

`docker history` shows which layers carry the weight: the toolchain, the npm cache, and Git history. Build in one stage and ship from another: the build stage copies the sources and runs `node tools/build.mjs`, and the runtime stage starts again from the Node base image and copies only `/src/dist` into `/app`, with `USER node`. If the Dockerfile already has two stages, check what the runtime stage copies; `COPY --from=build /src /app` brings everything across. Add `.git` and `.cache` to `.dockerignore`. Rebuild and run `tests/integration.sh waybill:candidate`.