CHAPTER 02Part I · Fennel, year 1

“Works on my machine”: containers

A year on, Fennel has six engineers and a new kind of bug: it happens in production and nowhere else. How packing the app with everything it needs makes that bug show up on a laptop instead, and what it still doesn’t fix.

Read
18 MIN
For
TECHNICAL MANAGERS
Chapter
02 / 15
Widgets
01
Checks
07

§ 01A year later

Cast

Sam
Founding engineer. Built Fennel’s first server by hand and still looks after production.
Jordan
Co-founder, now managing Fennel’s engineers: that’s you, in this story.
Riley
Engineer. Joined Fennel last month.
I didn’t read the last chapter. What happened?
  • Fennel is a grocery-delivery startup. In Chapter 1, its app ran on one server that Sam had set up by hand.
  • One Friday the server’s disk filled up during the dinner rush, and the site was down for two hours.
  • Rebuilding the server from Sam’s notes, with exactly the same code, gave a machine where some features failed. Over the months, things had been installed on the server by hand and never written down. A server whose real setup exists only on the machine itself is called a snowflake server.
  • Machines set up by hand also drift: each one quietly becomes a little different from the others. The check below asks about one of the features that failed.
RETRIEVECheck 01 / 07

In the last chapter you rebuilt Sam’s server from Sam’s notes, with exactly the same code. Why did uploading a product photo fail on the new machine?

That gap, a year on, is on six laptops. Fennel now has six engineers, and each of them set up their own laptop by hand. Riley joined last month and spent the whole first week installing tools from a page on the team wiki. The page was months out of date: Sam’s notes all over again.

Fennel also has a second machine now, called staging, where each release is tried out before customers see it. Production still runs on one rented machine. Both were set up by hand, at different times, by different people.

From Jordan’s seat, none of this looks like a server problem. It shows up in three other places:

  • Onboarding takes a week. A new engineer’s first days go to installing things, not to working on Fennel.
  • Some bugs happen only in production. A customer reports one, an engineer tries the same thing on their laptop, and it works. “Can’t reproduce” turns into a regular line in the weekly update.
  • Releases pass on staging and then fail in production. Staging was supposed to catch exactly these.

Nobody is being careless. This is last chapter’s drift, now on every machine Fennel has. Each laptop, the staging machine and production was set up by hand, and each has quietly become a little different from the others. When the software installed on two machines differs, small incompatibilities appear, and code that passed its tests on one machine can fail on the other. Here’s what one of those bugs looks like up close.

§ 02Same code, two machines

Here is one of those bugs, step by step, on Riley’s laptop and Fennel’s production machine. Scroll through it. Two questions along the way unlock what happens next.

Installed by hand

  1. PREDICTCheck 02 / 07

    Riley’s change passes every test on the laptop, and the code is the same on both machines. What happens when the change ships to production?

  2. Answer the question above to see what happens

Packed in an image

  1. PREDICTCheck 03 / 07

    Riley’s laptop now runs the app from fennel-app:v42. Will the tests that passed on Riley’s laptop before still pass?

  2. Answer the question above to see what happens

  3. Answer the question above to see what happens

§ 03What goes in the image, and what doesn’t

New term · 01 / 04

image

The app’s code packed together with everything it needs to run: the language runtime, the libraries, tools like ImageResizer, and the files the app starts with. Everyone who runs the same image gets exactly the same versions.

You met it in §02: fennel-app:v42.

That also answers Riley’s first week. Instead of following a wiki page to install Python, the libraries and the tools one at a time, a new engineer downloads the image and runs it, and starts with exactly what everyone else has.

New term · 02 / 04

immutable

Can’t be changed once it’s built. To change anything inside an image, even one tool, you build a new image with a new version name.

You saw it in §02: Riley built v43, and v42 stayed as it was.

This is why an image can’t drift the way Sam’s server did. The server drifted because people changed it by hand, one sensible fix at a time. Nobody can change a built image by hand, so the image that passed the tests is exactly the image that ships.

Can a version name ever point to a different image?

Yes, so engineers are careful with them. A version name like v42 is called a tag, and a tag is only a name pointing at an image. Someone can move it to point at a newer image; neither image changes, but “v42” now means something else.

Teams that need certainty never reuse a tag, or they refer to the image by its digest, a fingerprint calculated from the image’s contents, which can’t be moved.

Two things stay out of the image on purpose. Settings that differ from place to place, like the database address and its password, are handed to the app when it starts. That’s why one image can run in staging and in production. And the data the app writes, like orders, is kept outside the app, in Fennel’s database, so replacing the app doesn’t wipe it out.

Fig. 01One image, two places

MachineStaging

MachineProduction

In the imagebuilt once, the same in both places

Image fennel-app:v43

code: main Python 3.12 12 libraries ImageResizer 7.0

Image fennel-app:v43

code: main Python 3.12 12 libraries ImageResizer 7.0

Handed in at startdifferent in each place

settings: staging settings: production

Kept outside the appoutlives any copy of it

data: test orders data: real orders
Fig. 01The image is identical in staging and production. Each place hands the app its own settings when it starts, and keeps its own data outside the app.

In the last chapter you found that the code isn’t the whole app. The image isn’t the whole app either: it’s the code plus what it needs to run, without the settings and data that make each place different. Keeping the data out has a name of its own.

New term · 03 / 04

stateless

Said of an app that keeps nothing it needs inside itself: its data lives outside it, and its settings are handed in at start. You can stop it and start a fresh copy from the image at any time, and nothing is lost.

Fennel’s app is built this way. Apps that can’t be, like the database itself, are Chapter 10.

The two words work as a pair. The image is immutable, so every fresh copy starts out exactly the same. The app is stateless, so throwing a copy away costs nothing. The next section shows what those running copies are.

§ 04Build once, run many: images and containers

An image is a package. It does nothing until a machine runs it. Here is how one image gets from Riley’s laptop to every machine that needs it.

Fig. 02One image, many containers
Machine · Riley’s laptopBuild
build fennel-app:v43
Registry

each version stays as it was

Fig. 02Build the image once and keep it in the registry. Every machine runs containers from that same package, and one image can run as several containers.

Riley builds fennel-app:v43 once and uploads it to a registry, a shared store of images that Fennel’s machines can reach. Engineers call that upload a push. The laptop, the staging machine and production then each download, or pull, the same image from it.

Running an image starts a container. Last chapter called a running app a process; in practice an app is usually one main process, sometimes with a few helper processes it starts itself. A container is that group of processes, started from an image and isolated from the rest of the machine by the machine’s operating system. The processes inside see the files that came from the image, not the machine’s own, and they see each other but none of the machine’s other programs.

New term · 04 / 04

container

One or more processes of an app, started from an image and walled off from the rest of the machine.

Chapter 1’s process, packaged and walled off.

One image can run as several containers, on one machine or on many; in Fig. 02, production runs two.

A running container can write files, and someone can even change things inside it by hand. But containers don’t last. Every release replaces the running containers with new ones, started from the new image, and when a container is replaced, the changes made inside it go with it. Each new container starts fresh, with exactly what the image holds. Because Fennel’s app is stateless, that costs nothing: its orders were never inside the container.

ANALOGYThe usual comparison is to shipping containers. Before them, cargo was loaded piece by piece, and differently at every port. After them, any ship, crane or truck could move the same sealed box without caring what was inside. Engineers call the software version of that promise “build once, run anywhere.”

WHERE THIS ANALOGY BREAKSA shipping container doesn’t care which ship carries it. A software container does: it needs a machine with the right kind of processor, and its settings are handed in when it arrives. Outside services, like the database, may differ too. “Run anywhere” means the inside is the same everywhere, not everything around it.

You’ll hear the name Docker in conversations like these. The next chapter places it.

APPLYCheck 04 / 07

Saturday night, photo uploads break in production. Riley connects to the running container and fixes the problem by hand, inside it. It works. On Tuesday, Fennel releases its next version. What happens to Riley’s fix?

A note on where Riley made the fix

A container can also be given storage that lives outside it, so that files written there outlive the container. Chapter 10 names it and shows what it’s for: data, like orders. Riley made the fix inside the container itself, not in storage like that, so it went with the container.

Even if Riley could have kept it, a fix to the app belongs in a new image, where everyone can see it and every new container gets it. A fix kept anywhere else is drift again.

§ 05Not a small VM

Sam has a one-line summary of all this, and it’s one you’ll hear often: “a container is basically a small VM.” Last chapter’s virtual machine was a whole computer made in software, with its own operating system. Before reading on, decide whether that’s what a container is.

PREDICTCheck 05 / 07

Sam says a container is basically a small VM. Before you read on, which of these is closest to what a container actually is?

The core of the operating system has a name, the kernel: the part that runs the hardware and every process on it. Here is the same machine running three apps two ways.

Fig. 03One machine, two ways
Machine · Running 3 VMs
VMappruntime, librarieswhole OS + kernel
VMappruntime, librarieswhole OS + kernel
VMappruntime, librarieswhole OS + kernel
VM SOFTWARE
HARDWARE: processor, memory, disk

↑ each VM: a whole operating system · typically gigabytes · boots in tens of seconds to minutes

Machine · Running 3 containers
room for more
CONTAINERappruntime, libraries
CONTAINERappruntime, libraries
CONTAINERappruntime, libraries
OS KERNEL · SHARED BY ALLone shared core: lighter, and a thinner wall between containers
HARDWARE

↑ each container: the app and what it needs · typically tens to hundreds of MB · starts in seconds

Fig. 03Each VM carries a whole operating system. Containers share the machine’s one kernel, so each is smaller and quicker to start, and the walls between them are thinner. Sizes and times are typical, not guarantees.

Leaving out the kernel is what makes a container small and quick to start. A VM has to carry a whole operating system and boot it, the way a computer starts up. A container skips both: starting one just starts a process on a kernel that’s already running. An image often does include an operating system’s basic files and tools (engineers will say an image is “based on Debian”, for example), but never its kernel, and nothing boots. So a container is typically tens to hundreds of megabytes and starts in seconds, while a VM is typically gigabytes and takes tens of seconds to minutes to boot. Those are rough orders of magnitude, not measurements; the real numbers depend on the app and the setup.

Without a whole operating system and kernel for every app, the same machine has room for many more apps. How many more depends on the apps themselves.

The price is a thinner wall. Every container on a machine shares one kernel, so a flaw in that kernel is shared by all of them, while each VM has its own kernel. That’s why cloud providers that run many different customers’ containers on the same machines put each customer’s containers inside a lightweight VM of its own; AWS does this for some of its services. Containers are still walled off from each other, though. The wall between containers isn’t missing, it’s just thinner. For a company running only its own code, that trade-off is usually fine.

You also don’t have to choose one or the other. When Fennel rents a machine from a cloud provider, that machine is usually a VM, and Fennel’s containers run inside it. In the cloud, you usually get both: containers, running inside a VM.

ANALOGYA VM is a house: its own foundation, plumbing and wiring. A container is an apartment: its own locked door, sharing the building’s foundation and plumbing. Apartments are cheaper and pack more people into the same land, and a burst main pipe reaches every flat.

WHERE THIS ANALOGY BREAKSThe analogy makes house and apartment a choice, but in the cloud you usually get both: Fennel’s containers run inside a VM, like apartments carved out of one house. And buildings don’t start up, so it can’t show the other difference: a container starts in seconds because, unlike a VM, it has no operating system to boot.

§ 06The conversation

After the photo bug, Sam brings a proposal to the next planning meeting.

“Let’s containerize everything.”

Sam
APPLYCheck 06 / 07

At the planning meeting, Sam says: “Let’s containerize everything.” What’s the most useful thing Jordan can ask?

PREDICTCheck 07 / 07

So ask it. Saturday, 3 a.m.: the production machine running all of Fennel’s containers dies for good. What happens?

So containers settle what runs on each machine. They leave open what happens to the machines themselves:

  • Who notices the machine died?
  • Who starts its containers again, and on which machine?
  • What happens to anything written only on that machine?
  • Once there are many machines, who decides what runs where?

Those jobs have a name. We’ll name it in the next chapter.

In short

  • An image packs the app with what it needs, so every machine runs the same thing, and a version gap shows up on your laptop instead of in production.
  • An image never changes; containers are copies of it, running, and they start fresh each time.
  • Containers share the machine’s operating system core: lighter than VMs, with thinner walls, and they still die with their machine.

Who starts the containers again when a machine dies? A year from now, Fennel runs its containers on eight machines, and that question becomes Sam’s 3 a.m. job.

End of chapter 02

Next: Many containers, many machines

Chapter 03 · 17 min Many containers, many machines: the orchestration problem A year on, Fennel runs four services in containers across about eight machines, and Sam keeps a spreadsheet of what runs where. One week of Sam’s job shows the work that containers leave over, what it costs a manager, and the name for the software that does it. Start chapter 03 →

Course

chapters
15
running
05
in review
02
pending
08
  1. Part IBefore Kubernetes03/03 running

    1. Ch 0117 minOne app, one serverRunning
    2. Ch 0218 min“Works on my machine”containersYou are here
    3. Ch 0317 minMany containers, many machinesthe orchestration problemRunning
  2. Part IIThe core model02/05 running · 02 in review

    1. Ch 0423 minSay what you want, not what to dodesired state and the reconcile loopRunning
    2. Ch 0525 minWho runs wherescheduling, resources and costRunning
    3. Ch 0627 minShipping without downtimerollouts and rollbacksUnder review
    4. Ch 0724 minFinding each otherservices and trafficUnder review
    5. Ch 08The Friday dinner rushautoscalingPending
  3. Part IIIThe hard parts00/04 running

    1. Ch 09Configuration and secretsPending
    2. Ch 10The database problemstatePending
    3. Ch 11Many teams, one clusternamespaces, permissions and quotasPending
    4. Ch 12Seeing insideincidents in a self-healing systemPending
  4. Part IVOwning it00/02 running

    1. Ch 13Teaching the cluster new tricksextensions and the ecosystemPending
    2. Ch 14What all this costsmanaged Kubernetes, platform teams and when not to use itPending
  5. CapstoneMixed practice00/01 running

    1. Ch 15Fennel’s hard weekPending
Fig. 00The whole course: 15 chapters in 4 parts and a capstone. Raised tiles are running; ones marked Under review are written and readable, still being checked; dashed ones are Pending, planned but not written yet.