Deployment and Design

This chapter went from a monolith to a Docker image. Before moving on, we should say what to take from it, and what to leave alone.

What Moved and What Did Not

At every step, the application was the same. HopPress was one deployable unit on bare metal, one deployable unit in a virtual machine, and one deployable unit in a container. What changed was the boundary around it, and what the university had to supply on each side of that boundary.

On bare metal, the boundary was the physical server. The university supplied the machine, the operating system, and every library the application needed, and over time the application became tied to that one machine.

With a virtual machine, the boundary was the virtual machine. Two applications could share hardware, but each still carried a full operating system that someone had to update and watch.

With a container, the boundary is the application and what it needs to run. The university supplies a well-maintained host and nothing else. The developers supply an image that says what the application needs.

The direction is consistent. Each step moved more of the application’s environment into the package that developers hand over, and less of it into something IT has to build by hand on the server.

We stop here. How a Dockerfile is written, how images are layered, how containers are networked: those are implementation details, and they belong to a different course. What we need is the concept of the boundary.

Why a Designer Cares

You might ask why a course on software system design spends a chapter on where software runs. The answer is that the deployment boundary is something the designer decides, whether or not they decide it on purpose.

Look back at the WordPress diagram in the reading on monoliths. Core, plugins, and the theme sit inside one boundary. The database and media storage sit outside it. That was not an accident of hosting. It is a statement about which parts change together, which parts are released together, and which parts can be replaced without touching the others. That is a design decision, and it has consequences the designer must accept:

  • Failure. Everything inside one boundary fails together. Two containers on the same host fail together if the host fails. A designer who puts two things in one unit has decided they can go down together.
  • Change. Everything inside one boundary is released together. If a small fix to one part means redeploying the whole application, that is a cost the design imposed.
  • Capacity. A unit is scaled as a unit. On bare metal, the university guessed a server size for the whole application. That guess is easier or harder depending on what the designer put in the unit.
  • Ownership. The boundary also divides people. In the Docker workflow, developers own the image and operations owns the host. Where the line falls decides who has to understand what.

A monolith makes all of these decisions once, for the whole application. Later chapters look at architectures that draw several boundaries instead of one. The trade-offs above do not go away in those architectures; they multiply.

DevOps

For a long time, the people who wrote software and the people who ran it were different groups, with little coordination between them. Developers finished a version and handed it over. Operations installed it, configured it, kept it running, and got paged when it failed. Each group could reasonably say the other’s problems were not its own.

DevOps is the name for the practice that ended that separation. The idea is that the people who build a system also share responsibility for running it, and that the tools and habits of development, such as version control, automated testing, and repeatable builds, apply to operations as well.

Docker is a good example of what that looks like in practice. The Dockerfile is a developer artifact. It lives in the repository next to the source code, and it describes the running environment of the application. A developer who writes it is doing something that used to be an operations job.

A designer no longer gets to treat deployment as someone else’s department. Deployment choices land on the same people who make the architectural choices, and often in the same repository. A designer needs a working picture of the deployment boundary to understand the consequences of their design decisions.

Containers Are Not Monoliths

I do not want this chapter to leave you with the impression that using Docker means building a monolith.

The two ideas are independent. A monolith is an architecture: the application has one deployment boundary. A container is a package: one way to put one deployable unit somewhere it can run. HopPress and CourseTracker happen to be both, so in this chapter one monolith went into one container. That is a coincidence of the example, not a rule.

A monolith can run without containers. HopPress ran on bare metal and in a virtual machine before it ran in a container, and it was a monolith the whole time.

A system built from several independently deployed components can also use containers. It would use several of them, one per component, and those containers might run on one host or on many. The architecture decides how many units there are. The container only decides how each unit is packaged.

So when a later chapter splits a system into separately deployed pieces, nothing about Docker changes. What changes is the number of boundaries, and the new problem of getting many containers to find each other and work together. There is a large body of tooling to solve that problem, and we will look at some of it in later chapters.