Following part 1 of our blog series, here's part 2.
Containers Enable Scale
Right around the time that applications started being broken into microservices, containers became popular. Though these are distinct technologies that are hypothetically unrelated, the rise of containers and the rise of microservices were intertwined.
To understand containers, another history lesson is required. If we go back to the ’90s, the era in which monolithic and SOA-type applications ruled, it is important to understand that each server typically only ran one application.
Every now and again, a monolithic application would grow so large that it had to be broken up because no single server could reasonably run the application. When this occurred, the individual application components would themselves become applications, with each typically operating on its own machine.
Despite all the talk about the importance of breaking up large, monolithic applications, these bare metal application servers were massively underutilized. They were also fragile; if something happened to the server on which an application was executing, it could take several hours to restore the application to service on another server. This step often involved restoring data from backups, which could mean that a day's data might be lost during the outage.
Virtualization and centralized storage arrays solved these problems. Together, they allowed multiple applications to be run on a single server, driving up server utilization. They also allowed applications to be run in high availability, meaning that you could recover from hardware failures in seconds.
The virtual machine model was wasteful. Each virtual machine requires its own complete operating system because every virtual machine is an emulation of an entire computer. In other words, the way that developers convinced multiple applications to securely operate on a single server was by lying to the applications and pretending that each of them had an entire server to itself.
This worked fine when the application components that were being hosted in individual virtual machines were largish SOA-style components. As soon as developers started breaking applications into microservices, however, putting each microservices in its own VM started to seem like overkill. The underlying operating system would often consume several times the resources of the microservices.
Containers are an attempt to solve the high resource consumption of virtual machines. Containers use a number of technologies to carve up a single operating system such that multiple applications can be run on that operating system but still remain completely isolated from one another. This solution provides application security similar to virtual machines while allowing even greater application density.
The downside to containers is that when updates applied to the host operating system require a reboot of that operating system, all containers hosted by that operating system are restarted as well. Containers can't be live migrated/ VMotioned between physical hosts in the same way that virtual machines can. This means that every time the container host is restarted, there must be an outage of that host's containers.
Composability and Immutability
For modern applications, which are typically designed around microservices and cloud computing, composability and immutability aren’t necessarily a big deal. Modern cloud native applications are not designed with the same expectations as traditional monolithic or SOA applications were.
Traditional applications were generally designed with several assumptions in mind. Among them was the idea that all of components of the application would be available all of the time. These applications also generally assumed that their storage would always be available and that each application component had exclusive access to that storage.
Applications based on microservices are generally designed under different assumptions. They are designed with the idea of composability. Modern IT infrastructures can be controlled using Application Program Interfaces (APIs). This means that the underlying infrastructure upon which an application operates—from the creation of virtual machines and containers, to networking and storage resources—can be manipulated programmatically.
Applications can communicate directly with the automation and orchestration software that manages the infrastructure upon which the application operates.
This means that applications of the microservices era can do things that their predecessors could not; specifically, they can grow and shrink the number of instances of an application component to meet demand. If the web server hosting the user interface portion of an application is nearing maximum capacity, the application can cause another web server instance to be created and registered with the load balancer. If the database is a little slow, another instance of it can be spun up as well.
This infrastructure awareness permeated application design. Applications were designed to be able to survive the loss of individual components, and components were designed to cease doing work if they lost access to storage. Microservices-based applications following modern design principles are designed such that the limitations of containers are compensated for.
Infrastructure awareness also meant that application components and/or portions of application storage could be treated as "immutable.” Immutable applications components or storage cannot be changed.
Immutable application components, should they become infected by malware or misconfigured, can simply be restarted to be restored to a known good, working condition. Immutable storage is storage that cannot be changed, so it cannot be deleted or overwritten.
Messages Queues and the Road to Serverless
A monolithic application doesn't have to communicate between pieces of itself. It is one application running as a single block of code with sole control of its storage on a single system. As soon as that application is broken up into pieces, however, communication between each of the application components becomes a concern.
With larger, SOA-style applications, it wasn't uncommon for each component to have its own means of communication using TCP/IP networking. Databases, for example, expose their own methods of communication, and anything that wants to communicate with that database needs to be able to communicate using that database's proprietary connection protocol.
Over time, the concept of message queues was developed. Message queues are a simple means by which individual application components can communicate with one another without having to create communications protocols for each component or build awareness of those protocols into each component. Adopting message queues is often considered the first step in evolving older applications, such as SOA-based ones, over to a more microservices-based approach. At the very least, implementing message queues between components is a great way to start an argument about how microservices and SOA are all just the same concept with varying sizes of services sitting on a spectrum.
As adoption of message busses grew, a new kind of application component became possible.
Despite the name, serverless is anything but. Serverless can be thought of as the ultimate evolution of the “componentization” of applications, and message queues are very important for serverless functions.
SOA broke monolithic applications into a series of smaller applications, where each application performed a cluster of related duties. Microservices then broke these down further, with each microservices performing a single, specific function, often representing a single feature or isolated piece of functionality.
Serverless takes a specific function—a very short piece of code—and performs only that function. Instead of worrying about containers or virtual machines, developers simply place the code for the function they wish to execute into the serverless management interface and set up triggers. And as we have recently shown, developers love using serverless for various reasons:
Image 1: 2018 Survey Report: The State of Securing Cloud Workloads
A trigger might be something like "has a file been uploaded into this folder." Once a file is uploaded, the serverless function is triggered. The serverless function will do something very simple, such as "move this file to that storage location" or "have this file analyzed by the public cloud provider's built-in facial recognition service".
Many serverless functions are triggered by message queue messages. Most serverless functions report success, report failure, or hand data off to another function (or microservices) using a message queue.
Serverless and Hybrid / Multi-cloud
Another important trend that is occurring alongside the adoption of microservices, serverless, and containers is the adoption of a hybrid and a multi-cloud approach to computing. Organizations of all sizes are abandoning the idea of putting all of their IT eggs in a single basket.
Image 2: 2018 Survey Report: The State of Securing Cloud Workloads
Instead of an either/or approach to on-premises and cloud computing, organizations are embracing the use of multiple public cloud providers as well as preserving their on-premises IT capabilities. Each of the technologies discussed above has various roles to play in enabling multi-infrastructure IT.
Serverless functions, for example, help make use of cloud provider-specific capabilities by offering an economic means for applications to interact with a cloud provider's infrastructure. Instead of having to keep a service running 24/7 on the infrastructure of every service provider that an application might be able to use, serverless functions can be employed. Serverless functions are billed per execution of the function. This means that the application component can reside in a cloud provider's infrastructure but only be executed if a relevant trigger is engaged.
Containers, meanwhile, are fantastic for housing microservices in a hybrid multi-cloud environment because containers are portable across all infrastructures. Unlike virtual machines, which need conversion to work on a different provider's infrastructure, containers work anywhere on any public cloud provider or on-premises infrastructure.
Combined, containers and serverless offer modern application developers the ability to make modular, easily updatable applications that exist with components spread across multiple public cloud providers and on-premises data centers. Taking full advantage of these capabilities, however, requires a multi-cloud management solution.
The future of IT unquestionably lies in hybrid multi-cloud environments. Most organizations aren't comfortable relying on a single public cloud provider. Similarly, there are many workloads where on-premises IT is just the most economically efficient approach. This economic efficiency comes in large part from the ability of modern applications to increase or decrease the number of instances of a specific application component as needed. This functionality is, in turn, provided by integration into the management applications that orchestrate IT resources for each infrastructure.
Application development success will depend on organizations’ abilities not only to develop the applications themselves, but also to manage them (and all their various parts) across multiple infrastructures. The ability to monitor, report on, and secure each component will be the real challenge during the next decade of application development.
Multi-cloud management applications are likely to become an important part of application design. Multi-cloud management applications provide developers with a single API to code against, but one which can, in turn, manage multiple infrastructures. Multi-cloud management applications can also provide monitoring, reporting, and security for virtual machines, containers, and serverless functions across multiple infrastructures.