What You Built in This Module

“Reproducible” is the property that separates an image that runs from an image you can trust, and it is invisible the first time you build — the unpinned image and the locked one both run today. The gap shows up a week later, on a machine that has never seen your code, when the first build cannot be reproduced and the second can. What you can hand off now is an image that builds the same twice, carries no secret it should not, weighs only what it needs to, and stays up answering requests as a service.

“Reproducible” is the property that separates an image that runs from an image you can trust, and it is invisible the first time you build — the unpinned image and the locked one both run today. The gap shows up a week later, on a machine that has never seen your code, when the first build cannot be reproduced and the second can. What you can hand off now is an image that builds the same twice, carries no secret it should not, weighs only what it needs to, and stays up answering requests as a service.

What you completed

A working image came first; the next three lessons broke it to expose what “it runs” leaves out, and the last two turned the hardened image into a service and then a stack:

  • The vocabulary is only real once you have used each piece. Image, layer, container, build time, run time — these confuse people because they are taught as definitions instead of as things you did. Building one and scoring a record is what makes the later distinctions (a digest is not a tag, a layer cache is not a rebuild) something you can reason about instead of memorize.
  • An unpinned install is a different model every time it builds. “Latest” resolves to whatever shipped this morning, so the same Dockerfile a week apart produces two environments and two models — reproducible by accident, which is to say not at all. Installing from the locked graph, and ordering layers so a code edit does not re-run the dependency install, is what turns “it builds” into “it builds the same.”
  • A secret deleted in a later layer is still sitting in the earlier one. Layers are append-only history, so a credential removed by a later RUN is fully recoverable from the image — which is why a leak is rotated, never edited out. Reading configuration from the run-time environment and gating the build context with .dockerignore is what keeps the secret from entering the layers in the first place.
  • “Works on my machine” is a missing-environment failure with a name. A multi-gigabyte image is one nobody can push or pull fast enough, and the size comes from a build toolchain nobody needs at run time. The multi-stage build — compile in a fat builder, ship only artifacts in a thin runtime — is what cuts the image to a fraction of its size with the identical model inside.
  • A server has to hold PID 1 open to stay a service. Changing ENTRYPOINT from a script that scores once and exits to a blocking server is the difference between a batch job and a service; publishing the port, binding 0.0.0.0, and mounting state on a volume is what makes it reachable from off the container and durable across docker rm. None of these is optional once something else has to call you.
  • ready and started are two different claims, and callers depend on the first. One declarative compose.yaml replaces a page of docker run flags, lets services find each other by name instead of localhost, and gates startup on a healthcheck — so a dependent service waits for the backend to be able to answer, not merely to have a process. This is the seam the serving module starts from.

Check your understanding

Work these without scrolling back up. Each one is a new service, not the loan-scoring image the lessons used — the point is to apply the judgment, not recall the case. Try to answer before opening the hint.

  1. You containerize an IoT sensor anomaly scorer and a teammate uses image, layer, and container interchangeably while debugging why a change did not appear after a rebuild. Distinguish the three terms in the context of this build, and explain why a tag pointing at a new digest is different from the cache having actually rebuilt the layer you edited.

    HintThe vocabulary is only real once you have used each piece. Anchor each term to something concrete in the build, then think about what a digest identifies that a tag does not, and what a cached layer means for a change that did not take.
  2. The sensor-scorer Dockerfile installs its dependencies with no version pins, and the image built today scores differently from the one built last week off the same Dockerfile. Explain why an unpinned install is a different environment every time it builds, and the two changes you would make so the image builds the same and a code edit does not re-run the dependency install.

    Hint"Latest" resolves to whatever shipped that morning, so reproducibility happens by accident, which is to say not at all. Think about installing from a locked graph, and how layer ordering decides what the cache can reuse when only your code changes.
  3. A build for the sensor service writes a cloud API token into the image in one RUN step and deletes it in a later one. Someone argues the token is gone because the final image does not show it. Explain why it is still recoverable, what the correct response to the exposure is, and how you would keep the secret out of the layers in the first place.

    HintLayers are append-only history — a later delete does not remove the earlier layer. Think about why the only real fix once a secret enters is rotation, and how reading config from the run-time environment plus a build-context ignore rule prevents entry.
  4. The sensor-scorer image is 4 GB and the deploy pipeline keeps timing out pushing it. Most of the bulk is a compiler toolchain used only to build a wheel. Describe the build structure that ships a fraction of the size with the identical model inside, and what specifically moves to the final stage versus stays behind.

    Hint"Works on my machine" here is a size failure with a name. Think about a multi-stage build — a fat builder that compiles, a thin runtime that carries only the artifacts — and what a run-time image actually needs versus what only the build needed.
  5. The sensor scorer currently runs a script that scores one batch and exits, and a teammate cannot understand why it will not stay reachable as a service. Explain the change that turns a batch job into a long-lived service, and the three things that make it reachable from off the container and durable across a docker rm.

    HintA service has to hold PID 1 open. Think about swapping the entrypoint from score-once-and-exit to a blocking server, and what publishing the port, binding to all interfaces, and mounting state on a volume each buy you.
  6. Your sensor stack has a scorer that depends on a message broker, and on startup the scorer crashes because it connects before the broker can answer. Explain the difference between a dependency having started and being ready, and how one declarative compose file lets the scorer wait for the broker to actually answer rather than merely exist.

    HintStarted and ready are two different claims, and the caller depends on the second. Think about what a healthcheck gate adds over plain start ordering, and how service-name resolution replaces brittle `localhost` wiring.

What it adds up to

The model was the easy part, and making it run identically anywhere was the job. The artifact you can now hand off is not just a model file; it is a self-contained image that builds the same twice, carries no credential it should not, weighs what it needs to and no more, runs as a long-lived service that stays up and answers requests, and composes with the other services an app needs. That is the difference between a model that works in your notebook and one another team can pull, run, and trust without asking you how.

What comes next

The service runs and answers requests, but it trusts whatever arrives and says nothing about how fast it responds. The next module hardens the endpoint you stood up here: it validates the input so a malformed request fails cleanly instead of scoring garbage, states a latency budget and measures against it, adds a batch path for callers scoring many records at once, and handles the model-unavailable case the readiness gate only postponed. The running, composed service you built here is exactly what that hardening pass starts from.

This lesson is part of Pro

The Ship a Machine Learning Product path — every lesson, capstone, and the failure modes free tutorials skip. Sign in if you already have Pro, or unlock it below.

Unlock with Pro Sign in