§ 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.
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
-
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?
-
Answer the question above to see what happens
Packed in an image
-
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? -
Answer the question above to see what happens
-
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.
MachineStaging
MachineProduction
In the imagebuilt once, the same in both places
Image fennel-app:v43
code: main Python 3.12 12 libraries ImageResizer 7.0Image fennel-app:v43
code: main Python 3.12 12 libraries ImageResizer 7.0Handed in at startdifferent in each place
settings: staging settings: productionKept outside the appoutlives any copy of it
data: test orders data: real ordersIn 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.
each version stays as it was
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.
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.
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.
↑ each VM: a whole operating system · typically gigabytes · boots in tens of seconds to minutes
↑ each container: the app and what it needs · typically tens to hundreds of MB · starts in seconds
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
At the planning meeting, Sam says: “Let’s containerize everything.” What’s the most useful thing Jordan can ask?
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.