CHAPTER 1 ■ UNDERSTANDING DOCKER
3
Complex applications may require additional components: more application servers to share the work,
load balancers to distribute HTTP requests among the application servers, and data caches to improve
performance. As the number of components increases, additional servers are needed, as are networks to link
everything together, name servers to aid discovery, and storage arrays to provide data resilience.
Few projects can afford to provide each developer with a complete replica of the production systems.
As a consequence, developers create an approximation of the production systems, typically running all the
components required by an application on a single development workstation and ignoring key infrastructure
such as networks and load balancers.
Working with an approximation of the production system can lead to several different difficulties, all of
which arise because the platform that the developer is using is not consistent with the production systems
into which the application is deployed.
The first difficulty is that differences in the environment can cause the application to behave
unexpectedly when it is deployed. A project developed on Windows but deployed onto Linux servers, for
example, is susceptible to differences in file systems, storage locations, and countless other features.
The second difficulty is that the approximations developers use to represent the production
environment can drift apart. Dependencies on different versions of development tools, NuGet packages,
and even versions of the .NET Core and ASP.NET Core runtimes can occur, leading to code that makes
assumptions that are not valid in production or on other developers’ workstations, which have their own
approximation of production.
The third difficulty is performing the actual deployment. The differences between development
and production systems require two configurations, one of which is difficult to test until the moment
of deployment. I have lost track of the amount of time that I have spent over the years trying to deploy
applications only to find that a single character is missing from a configuration setting or that there is a hard-
coded assumption that the database can be accessed via localhost.
The fourth difficulty is that it can be difficult to ensure that all the servers for an application are
configured consistently. A misconfigured server may cause only periodic problems, especially if a user’s
HTTP requests are distributed to a large group of servers, and identifying the problem and isolating the
cause can be a difficult task, made fraught by having to perform diagnostics on a live production system.
How Does Docker Solve the Consistency Problem?
When you put an ASP.NET Core MVC application in a container—a process known as containerization—you
create an image, which is a template for containers that includes the complete environment in which the
application will exist. Everything that will be used to run the application is part of the image: the .NET Core
runtime, the ASP.NET Core packages, third-party tools, configuration files, and the custom classes and Razor
views that provide the application functionality.
Docker uses the image to create a container, and any container created from the same image will
contain an identical instance of the ASP.NET Core MVC application.
If you adopt Docker for the development phase of your project, the developers will all use a single
image to create and test the application. The development image is still an approximation of the production
system, but it is a more faithful replica and will differ only by including development tools such as a compiler
and a debugger. In all other regards, the development image will have the same contents as the image used
to deploy the application, with the same file system, network topology, NuGet packages, and .NET runtime.
A production image is created when the application is ready to be deployed. This image is similar to
the one used by the developers but omits the development tools and contains compiled versions of the C#
classes. The production image is used to create all of the containers in production, which ensures that all
the instances are configured consistently. And, since the development and production images contain the
same content, there is no need to change configuration files in production because the database connection
strings that worked in development, for example, will work in production without modification.