Day 1: Build a Minimal Service Image and Run It Isolated – The “Hello World” of Production

Lesson 1 60 min

Day 1: The Minimal Service Image

Welcome to the start of our journey. Over the next several weeks, we will build a distributed system from the ground up, moving from a single container to a resilient, multi-service stack.

Today, we establish our foundation. We aren't just running a process; we are defining a boundary.

The Problem: The "It Works on My Machine" Mirage

In professional environments, the most dangerous phrase is "it works on my machine." This usually happens because your local environment has hidden dependencies—a specific version of a library, a global configuration file, or a stray environment variable—that your production server lacks.

When you package an application as a Docker image, you aren't just bundling code; you are defining a deterministic environment. You are freezing the OS-level dependencies so that the binary execution is identical, whether it runs on your laptop or a 64-core server in a data center.

The Concept: The Immutable Execution Unit

The core concept today is Immutability. An image is a read-only template. Once you build it, it does not change. If you need to update the application, you build a new image.

This is the design principle behind systems like Google’s Borg or Netflix’s Titus. By ensuring the image is immutable, you eliminate "configuration drift," where servers slowly become unique, snowflake-like entities that are impossible to replicate when they fail.

The Architecture: Packaging the Producer

Component Architecture

Kernel Container

Our system will eventually consist of multiple services. We start with the producer, a small Python service. In a production system, this component would be responsible for ingesting raw data and serializing it into a common format (like JSON) before handing it off to a message bus.

Here is how we define the boundary in our Dockerfile:

dockerfile
# We start from a slim image to minimize attack surface and download time.
FROM python:3.11-slim

# We set a working directory to avoid polluting the root filesystem.
WORKDIR /app

# We copy only the requirements first to leverage Docker's layer caching.
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Finally, we copy the source code.
COPY . .

# The entrypoint defines the immutable execution behavior.
CMD ["python", "producer.py"]

The Failure Demo: The "Missing Dependency" Trap

To understand why this boundary matters, consider a production outage where a developer updates a system library on a host server, inadvertently breaking every application relying on that library.

In our failure demo, we will simulate this by attempting to run our code without the container's isolated environment. We will manually purge a required library from the host system. You will observe that the application crashes immediately. Then, we will run the same code inside the container. Because the container carries its own "world," it remains blissfully unaware of the damage we inflicted on the host.

Production Stakes: The Cost of State

Flowchart

Build Run Kill

In a hyperscale system, we treat containers as ephemeral. If a container crashes, the orchestrator replaces it. If we store data inside the container’s writable layer, that data vanishes the moment the container restarts. This is how data loss occurs.

We cut a corner today by not yet attaching persistent volumes (we will do that in a later lesson). For now, our container is a "stateless" worker. If it dies, we lose its local memory, but the system remains consistent because the state lives elsewhere.

Forward Momentum

Today, you build an isolated, immutable container. Tomorrow, we will connect this producer to a network, allowing it to talk to other services. You are building the individual bricks before we assemble the cathedral.


Assignment

Your task is to build and run the producer image.

  1. Build: Create the image tagged as producer:v1.

  2. Run: Start the container in detached mode.

  3. Verify: Use docker logs to ensure the producer is emitting JSON transactions.

  4. Break: Attempt to kill the process inside the container using docker kill and observe how the container exits. Restart it and observe that it resumes perfectly.


Solution

Steps:

  1. Navigate to the /producer directory in the repo.

  2. Run docker build -t producer:v1 .

  3. Run docker run -d --name my-producer producer:v1

  4. Run docker logs my-producer to see the output.

  5. Run docker kill my-producer to simulate a crash.

  6. Run docker start my-producer to bring it back.

State Machine

Created Exited
System Design Fundamentals – E-Book

Free download

Free eBook: System Design Fundamentals

Create a free account and download the ebook instantly. Learn the core building blocks — scaling, caching, databases and messaging — the way interviewers expect you to explain them.

Register free & download →

Already a member? Sign in to download · See what’s inside