Pilots In Habitats: Basic Unit of Application Deployment
What is the basic unit of application deployment?
Two related trends have changed the answer to this question:
- DevOps
- Containers
For many years, the tasks between engineer and operator were cleanly, if painfully, split:
- Engineer builds and delivers a package of files to deploy and run
- Operator deploys and runs those files in a production operating environment
In the early years, the package of files consisted of a directory with a ream of paper and instructions. Over time that improved to zip files, then proper packaging and installation tools like rpm.
Most recently, with the simplicity Docker (and others such as CoreOS' rkt) brought to container packaging, the preferred unit of deployment has become a container image.
The goal of each step in this evolution has been to simplify two parts:
- Deployment: how easy is it to perform the one-time per release process of deploying it?
- Management: how easy is it to perform the ongoing process of resolving issues?
Container images attempt to simplify the issues further by including all of the dependencies in a single runtime file. Whereas "file copy" included instructions such as, "copy these files to the following directories on the following operating systems with the following prerequisites", and rpms attempted to automate some of that, container images include all of the server dependencies in the right locations; just run it.
However, as we come closer to resolving lower level dependencies via container images, we have become more acutely aware that applications are more than just the process running on one single host with lower-level dependencies. They also have parallel and upstream dependencies: other processes; databases; middleware services; etc.
People often wonder why there were no reported cases of cancer one hundred years ago. "It must be our lifestyle," or "it is pollution and our environment." But the answer is simple: a century ago, life expectancy in the United States was 47, while the median age at cancer diagnosis is 67. Quite simply, few people lived long enough to get cancer! Once life expectancy and health improved, other illnesses had their opportunity.
Similarly, the lower-level issues of per-instance app deployment were so thorny, that higher-level cross-instance deployment coordination simply did not rise to the top of the stack (despite some attention). Now that we are solving those, higher level issues are becoming of concern.
Perhaps, then, the correct question is: given clean packaging of an app instance, what is the proper unit of complete app deployment?
I have been dealing with this in general and at some clients in working on clean, complete and self-managed deployments, as well as exploring the newer tools available to help, specifically the ContainerPilot work of Joyent and the Habitat work of Chef.
On a rather long flight last week, I listened to a podcast interview with Tim Gross of ContainerPilot, and Adam Jacob of Habitat.
In the interview, both Tim and Adam recognize similarities in each other's issues with packaging, deployment and management, and similar solutions.
The primary argument for these solutions is, to my mind, one that requires some clarification.
The primary purpose of these tools is the one we described earlier: having solved the problem of reliable distribution, deployment and maintenance of one instance of an application, we now approach how to solve the distribution, deployment and maintenance of an entire application.
This is something that we could not do before, at least not simply.
Let's take a simple application, a node app with a Web front-end on a static Web server and a MySQL database on the backend.
In the very old days (when, of course, neither node nor MySQL existed, but we will ignore that fact), the deployment would be as follows:
- Engineer packages up the static Web pages as a zip or tar file.
- Engineer packages up node application as zip or tar file.
- Engineer delivers two packages with instructions:
- Expand node app package on server A into the following directory with the following prerequisites
- Expand Web files on Web server B into the following directory with the following prerequisites
- Launch node app using the following command
- Serve up the Web files with the following configurations
- Configure the database to have the following
- Configure the node app to access the database at the following settings
Fortunately, we have come a long way since then, through many iterations. Much of the configuration, unpacking, deployment, prerequisites have been simplified dramatically. It now looks like this:
- Engineer packages up static Web files along with Web server in a container image
- Engineer packages up node application in a container image
- Engineer delivers two images with instructions
- Run node app with the following command line options and environment variables, including information to access the database
- Run Web server app with the following command line options and environment variables
- Configure the database to have the following
The number of steps is cut in half, and the complexity, and therefore opportunities for error, by much more.
Tim and Adam look at the above and say, "this isn't enough!" The basic unit of deployment still shouldn't be individual packages and instructions, however simplified. It should be a single deployable unit.
Entire applications should be single deployable units.
They are looking for a world that looks as follows:
- Engineer packages up everything - static Web files along with Web server, node application, even database, and every other reasonable upstream and downstream dependency - in a series of self-described images.
- Engineers delivers it with instruction: run this one command.
(Actually, they go one step further and say, "I can run this one command myself, who needs separate operators...")
Tim and Adam are arguing that the unit of deployment for an application is the entire application. It is not each container image, however much of an improvement that is.
In a recent application for a client, we did precisely that. The entire application with all of its dependencies should be a single unit of deployment.
To do that, however, the individual units that compose the application - in our example, container images - must be able to know about each other and coordinate, without depending on external management.
The more I think about it, the more I believe that they are correct. It simply was a matter of solving the lower-level packaging issues, raising the bar to the point that we can begin to ask, "is this the best atomic deployment unit?"
Of course, an entirely different question is, are container images the future of higher-level deployment units, or will serverless, a.k.a. FaaS or Framework-as-a-Service, dominate. That is a question for a different day.
Summary
Solving the challenges of deploying a single instance frees us up to attack the problems of deploying an entire application with all of its related parts. DevOps means no longer being dependent on some infrastructure run by some operator to run your app, but being able to self-service.
How good are your deployment methodologies? Do you still "throw it over the fence", or can you manage your apps dynamically? Ask us to help.