You need Docker running (set up in lesson 3.1.1), git, curl, and a terminal.
Already have the Learn repository? Reading the size columns
If you cloned it in Application foundations, run git pull inside learn instead of cloning again, then cd labs/bloated-app.
Docker 29 lists two sizes. DISK USAGE is the image unpacked on your machine; CONTENT SIZE is the compressed download a server pulls. Older Docker versions show one SIZE column, the unpacked size. Your numbers can differ a little from these by processor type and Docker version.
Build the naive image
git clone https://github.com/computesphere-samples/learn.git cd learn/labs/bloated-app docker build -t bloated-app .Read the
Dockerfilewhile it builds. Its comments point at each problem.transferring contextis the folder being sent to the builder (lesson 3.4.4): every file in it,README.mdincluded.You should seea line with transferring context: 2.70kB near the top, and the build ending with naming to docker.io/library/bloated-app:latest
Measure it and look inside
docker images bloated-app docker run --rm bloated-app ls docker run --rm bloated-app whoamiThe program itself,
server, is a few megabytes. The rest is the Go toolchain, a full Debian, and your source code, running as root.You should seebloated-app:latest at 1.44GB DISK USAGE, then Dockerfile, README.md, go.mod, main.go and server, then root.
Keep junk out of the build context
Create
.dockerignorewith:.git .env *.mdYou should seea new .dockerignore file next to the Dockerfile.
Write a multi-stage Dockerfile
Create
Dockerfile.slim, keeping the original for comparison:- Stage 1, named
build, fromgolang:1.27. Copygo.modand the.gofiles, then build withCGO_ENABLED=0into/out/server. - Stage 2 from
gcr.io/distroless/static-debian12:nonroot. Copy/out/serverfrom the build stage. - Run as
nonroot:nonroot, and start/server. The app listens on 3000.
If you're stuck
# Stage 1: build with the full Go toolchain FROM golang:1.27 AS build WORKDIR /src COPY go.mod ./ COPY *.go ./ RUN CGO_ENABLED=0 go build -o /out/server . # Stage 2: ship only the binary, on a minimal base, as a non-root user FROM gcr.io/distroless/static-debian12:nonroot COPY --from=build /out/server /server EXPOSE 3000 USER nonroot:nonroot ENTRYPOINT ["/server"]ENTRYPOINTworks likeCMDhere. Distroless images have no shell, so use the JSON-array form of either. WithoutCGO_ENABLED=0, the container fails withexec /server: no such file or directory.You should seea new file, Dockerfile.slim, with two FROM lines.
- Stage 1, named
Build it and measure again
docker build -f Dockerfile.slim -t bloated-app:slim . docker images bloated-app-fpicks the Dockerfile; the.is still the build context. The second build is quick, because Docker already hasgolang:1.27from step 1. Itstransferring contextline is smaller:.dockerignorekeptREADME.mdon your machine.Check yourself
You should seebloated-app:slim at 20.2MB DISK USAGE, beside bloated-app:latest at 1.44GB.
Run it and check it's healthy
docker run -d --name slim -p 3000:3000 bloated-app:slim curl localhost:3000/healthz docker image inspect bloated-app:slim --format '{{.Config.User}}'Same app, same answer, no root.
You should see{"status":"ok"}, then nonroot:nonroot.
Clean up
docker rm -f slim docker rmi bloated-app bloated-app:slimDocker also keeps a build cache, which makes rebuilds fast and takes disk space.
docker builder pruneclears it; that's optional, and the next build is slower for it.You should seeslim printed back, then both tags untagged and deleted.