The old saying “the only constant in life is change” is especially true when talking about technology. In software development, concepts such as agile scrum and continuous delivery attempt to embrace change and streamline its delivery. A more drastic approach to making constant operational change more efficient is the use of an immutable architecture.
Knowing that iterative changes and upgrades are essential to the success of applications, this might seem odd. After all, change also affects the underlying infrastructure and its configurations that support your applications. So how would a business benefit from adopting anything described as immutable?
In this chapter, we’ll explain the concept of immutable architecture, the advantages it offers, and how it supports the notion of Infrastructure as Code (IaC).
What is Immutable Architecture?
Immutable architecture, also known as immutable infrastructure, is a term that can be a bit misleading. Coined by Chad Fowler, an immutable architecture doesn’t mean that your environment should never change, but rather, once a specific instance (such as a container or virtual machine) is started, its configuration should never change.
Instead of upgrading or re-configuring the underlying infrastructure of that instance (of a container or virtual machine), you should simply replace it entirely with a new instance that has all of your required changes. You may need to replace the instance within minutes, days, or weeks due to a workload change, an architecture change, or simply to keep up with changes going on elsewhere in your environment. Either way, replacing the instance allows for discrete versioning in your application environment. You can think of each version as a configuration snapshot at an exact point in time. Systematic versioning, in turn, lowers the risk of making mistakes during upgrades, offers the ability to test before rolling out, and enables rolling back (to the previous version) if your application encounters a problem. Combining flexibility with precision is the primary goal of an immutable architecture.
The practice of immutable architecture doesn’t have to be limited to just the underlying infrastructure of your workloads; you can use this technique to support middleware components (such as a messaging bus, a database, or a data cache) and application software as well. You would simply release application source code as new, immutable, and versioned artifacts. You may version each package in the form of a new Docker image, a new Virtual Machine Image, or a new .jar file (Java code).
Mutable vs. Immutable Architecture
Let’s use a real-world example to illustrate the differences between a mutable and an immutable architecture. Say your company has an application web server running on a VM in the cloud. This web server has Nginx (webserver) installed on it and a specific web application version. After some time passes, you decide it’s time to upgrade the version of Nginx or switch to Apache.
Upgrading Mutable Architecture
In a mutable architecture, you would simply upgrade your existing web server to the new version. You would affect such an upgrade using a configuration tool such as Chef, Puppet, Ansible, or SaltStack, or complete a manual upgrade.
However, what happens if the upgrade doesn't go as planned? There’s a host of factors that could disrupt this process (network issues, DNS failure, dependency repository unavailability). These disruptions can result in your system being only partially upgraded, in-between your current and desired state. The implications of this can be unexpected behavior from the application because this in-between state would not have been validated and tested. This scenario might seem simple enough when considering a single VM, but this becomes exponentially complex with a large fleet of VMs.
Upgrading Immutable Architecture
In an immutable architecture, you would not upgrade your web server currently in place. To use the new version of Nginx, you would deploy the web server on a new VM. By using a different machine, you circumvent the need to upgrade any existing infrastructure. If the new machine encounters any errors, you can abort; if it’s working as expected, you can redirect traffic to the new web server and decommission the old instance, as illustrated in the diagram below.
|Immutable Architecture||Mutable Architecture|
|Streamlines operations||Requires reviews to ensure configuration consistency across nodes|
|Supports continuous deployment of a software application by matching infrastructure version to an application version||Requires ongoing configuration changes of the underlying infrastructure to support application updates|
|Mitigates manual errors that may result in security threats||Exposes risk of configuration inconsistency across instances|
|Supports scaling of infrastructure by adding and removing nodes as needed||Offers less control in rapidly replicating an exact configuration|
|Reduces operational costs||Increases operational overhead|
What is Infrastructure as Code?
The adoption of the public cloud accelerated the appeal of an immutable architecture. Not long ago, companies would have had to deal with a lot of physical infrastructure management overhead to replace computing nodes. Nowadays, implementing an immutable architecture has been simplified by cloud providers who automate resource provisioning on their self-service platforms. Today, your application's underlying infrastructure can be more easily versioned using code.
The main idea behind Infrastructure as Code (IaC) is to enable writing and executing code to define, deploy, update and destroy infrastructure by declaring the desired state. This trend has propelled Terraform to become the most popular open-source provisioning tool used by companies like Uber, Slack, Udemy, and Twitch.
Types of IaC Tools
There are five broad categories of tools used to configure and orchestrate infrastructure and application stacks, even though the last category on our list is the only one recognized as a proper Infrastructure as Code (IaC) tool. It is helpful to see them defined and represented by examples, as summarized in the table below.
|Ad hoc scripts||The most straightforward approach to automating anything is to write an ad hoc script. You take whatever task you were doing manually, break it down into discrete steps, using scripting languages like Bash, Ruby, and Python to define each of those steps in code, and execute that script on your server.|
|Configuration management tools||Chef, Puppet, Ansible, and SaltStack are all configuration management tools designed to install and configure software on existing servers that perpetually exist.|
|Server templating tools||An alternative to configuration management that has been recently growing in popularity is server templating tools such as Docker, Packer, and Vagrant. Instead of launching and then configuring servers, the idea behind server templating is to create an image of a server that captures a fully self-contained “snapshot” of the operating system (OS), the software, the files, and all other relevant dependencies.|
|Orchestration tools||Kubernetes would be an example of an orchestration tool. Kubernetes allows you to define how to manage your Docker containers as code. You first deploy the Kubernetes cluster, which is a group of servers that Kubernetes will manage and use to run your Docker containers. Most major cloud providers have native support for deploying managed Kubernetes clusters, such as Amazon Elastic Container Service for Kubernetes (Amazon EKS), Google Kubernetes Engine (GKE), and Azure Kubernetes Service (AKS).|
|Infrastructure as Code Provisioning tools||Whereas configuration management, server templating, and orchestration tools define the code that runs on each server or container, infrastructure as code provisioning tools such as Terraform, AWS CloudFormation, and OpenStack Heat define infrastructure configuration across public clouds and data centers. You use such tools to create servers, databases, caches, load balancers, queues, monitoring, subnet configurations, firewall settings, routing rules, and Secure Sockets Layer (SSL) certificates.|
The image below depicts an example flow of using Terraform to create a Virtual Machine instance and a database in the AWS cloud environment.
Using Infrastructure as Code to define application environments, companies can eliminate the risks of configuration drifts and accomplish more reliable outcomes in their architectures.
Companies looking to extend the benefits of discrete and immutable versioning from their software applications to the entire architecture would benefit a great deal from adopting the Infrastructure as Code model presented in this article. Cloud-native services, automation, immutable architecture, and Infrastructure as Code (IaC) are integral components for scaling modern applications.