The Emergence of GitOps

Understand the technological shifts and innovations that led to GitOps.

How GitOps emerged

Over the past two decades, the need to deliver software faster and more efficiently has fueled several innovations that have transformed the way systems are built, deployed, and managed. GitOps emerged in response to these technological shifts, making them important to understand before embarking on a GitOps journey.

Agile methodologies and DevOps

One of the earliest transformations in this space was the rise of Agile methodologies, which challenged the thinking behind the traditional waterfall model for delivering software. These Agile processes emphasized working iteratively in shorter cycles, which was a more effective way of building software in an environment where business needs frequently changed. As development cycles shortened, the release cadence of software development teams increased. Teams that formerly shipped their software quarterly or annually using the waterfall approach began deploying smaller changes in a monthly, weekly, or daily cadence.

This placed new demands on the end-to-end processes for releasing system changes because the merging, building, and deployment needed to be easily and quickly repeated. These responsibilities were often spread across multiple teams inside an organization, and each team was accountable for supporting one aspect of the end-to-end delivery process. For example, an application development team would typically be responsible for changes to an application’s source code. An operations team might manage the platforms used to deploy and run the system. In many cases, manual tasks were performed as part of an application’s release. These manual tasks may have been part of the actual delivery pipeline or as updates to the system’s infrastructure that was necessary for the new version of the application to function correctly.

To support the high release cadences of Agile methodologies, better collaboration across these teams and higher degrees of automation were established. This spawned the DevOps movement that caused organizations to rethink how they managed their IT (information technology) systems. Silos between teams were broken down to improve communication between them, and investments were made to automate common manual tasks.

Cloud computing

Cloud computing is another major innovation that changed how systems were engineered and operated. With the rise of major cloud providers, it became possible to quickly provision the infrastructure such as computing, networking, and storage required to host an application. Additionally, cloud services emerged that offered capabilities such as database and messaging platforms as services that were available on demand. Infrastructure and platform build that once took weeks or months could now be accomplished in a matter of seconds, making it effortless to scale or recreate these resources. Seeing these benefits, some organizations opted to build private clouds to provide similar elastic capabilities but only within their internal networks.

Regardless of where it resides, the capability to self-service the provisioning of these resources is game-changing for IT operations. It unlocks the ability to deliver and innovate faster than ever. However, to unlock the full potential of cloud computing, a different approach is required when architecting, developing, or operating a system.

Containers

The emergence of container technologies drastically changed how software was packaged and released. Before containers, most web-based software was packaged into a binary and then deployed into a piece of middleware hosted on a server to run. Any disruptions to this runtime environment were likely to degrade or crash the application, making it unavailable to end users.

This model had several disadvantages centered around discrepancies between the environments that host the software. For example, suppose the configurations between the middleware in the development environment differed from those in the production environment. In that case, the application could crash upon its release to the live production environment. Sometimes, these discrepancies could lay dormant in an environment for years before they were uncovered, typically during the troubleshooting of a production incident.

Container technologies provide the ability to virtualize the operating system in which the application is executed. This allows developers to include resources the application depends upon, such as runtimes or configurations, within the deployable unit.

When applications are packaged along with their dependencies, it creates an immutable artifact that can be deployed as an instance on any host capable of running containers. The template that specifies the instructions for creating the container is known as a container image. It defines the application binary, runtime dependencies, and configuration files that should be packaged into the container. The container image may also specify commands that determine which application should run when the container is started up. Docker is the most popular platform for creating and running containers, which is why most container images are defined using a Dockerfile.

Press + to interact
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["LibraryService.csproj", "LibraryService/"]
RUN dotnet restore "LibraryService/LibraryService.csproj"
COPY . .
WORKDIR "/src/LibraryService"
COPY . .
RUN dotnet build "LibraryService.csproj" -c Release -o /app/build -r linux-x64
FROM build AS publish
RUN dotnet publish "LibraryService.csproj" -c Release -o /app/publish -r linux-x64
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "LibraryService.dll"]

The container-based model eliminated the discrepancies between environments that plagued many applications and their engineers, because, all of a sudden, the same immutable artifact with all its dependencies could be deployed. The application gets deployed with its environment, which is packaged inside the container and deployed to the container runtime installed on the host system.

Containerized applications also benefit from improved scalability and portability because new instances of a container can be rapidly provisioned and run on any compute resources where a container runtime is present. However, this introduces additional complexity because engineers must now manage a potentially massive number of containers. Adding to this complexity is the ephemeral nature of containers, which are meant to be spun up or down with little regard for their longevity. The ability to rapidly create and destroy a container is often cited as an advantage of this packaging unit.

Container orchestration platforms, like Kubernetes, aim to reduce this complexity by providing capabilities that simplify the management of containers. These platforms can schedule the provisioning of containers across a set of worker nodes that provide the resources such as CPU and memory that are necessary to run the desired workload.

Container orchestrators also deliver other capabilities necessary to run containerized applications, such as the following:

  • Service discovery

  • Load balancing

  • Self-healing

  • Storage management

  • Cluster security

Kubernetes is the most popular container orchestration platform for modern systems that leverage containers. The resources and workloads on Kubernetes can be described via a declarative configuration that serves as a form of infrastructure as code (IaC). Since IaC is a prerequisite for GitOps, it makes Kubernetes an excellent platform for using the approach.

Note: Throughout the lessons, references to the term container orchestration platform can be considered synonymous with Kubernetes.

Distributed systems

Around the same time containers were revolutionizing application packaging, the term microservices was gaining attention. It promoted the idea of breaking down large monolithic applications into smaller isolated services that collaborate to provide the overarching system’s capabilities. While the term was new, the idea behind it was conceived in the 1970s with the concept of distributed systems. The architecture of a distributed system places different components in the system on separate nodes where they communicate with each other by exchanging messages over a network. Systems architected in this manner benefit from the following characteristics:

  • Resiliency

  • Scalability

  • Parallelism

  • Decoupling and encapsulation

While the initial intention of microservices was to solve performance and availability problems that arose when services were handling massive loads, some of their other benefits were equally attractive to the engineers of services without these demands. The ability to independently develop and release a small service as opposed to an entire system complemented the Agile mindset adopted by many teams. Eventually, microservice architectures became the preferred approach for greenfield projects and caused some teams to decompose their existing monolithic applications into smaller services that exposed their capabilities and data to other services via APIs.

Although microservice architectures may not be suitable for every circumstance (or meet every engineer’s preference), they have driven an increase in the number of systems comprising distributed components. Implementation of this architecture requires careful engineering to address the complexities of networking and operating a set of distributed components. In many cases, developers turn to containers and container orchestration platforms to help manage this complexity, making it achievable for the everyday engineer.

Cloud-native applications

Developing, delivering, and operating systems under the modern techniques described above is best described by the term cloud native. Cloud-native applications are engineered to take advantage of modern practices to produce a loosely coupled system consisting of isolated container-based services that run on elastic infrastructure.

Cloud-native applications benefit from a wide array of operational advantages inherited from adopting the following engineering principles and technologies:

  • DevOps

  • Continuous delivery

  • Elastic infrastructure

  • Containers

  • Infrastructure as code

  • Distributed systems (microservices)

The cloud-native approach allows teams to deliver changes to their system at the pace demanded by their business and its customers. However, it can be difficult to maintain system stability at this faster cadence, especially on the operations side where there are no established best practices.

GitOps to the rescue

Luckily, this is where GitOps excels! The principles, practices, and technologies GitOps introduce focus primarily on managing and operating infrastructure for cloud-native applications.

Notice that when GitOps is applied, all changes made to the system are performed through Git, and from there, they are automatically applied to the system. Most application developers have followed this practice for decades, using version control as the entry point for all changes to their application code. GitOps extends this practice to the operations side and applies the concept to infrastructure configuration, which gets stored as code and applied to the system through automation.

So, the concept isn’t entirely new, but it wasn’t until 2017 that Alexis Richardson, the CEO of Weaveworks, first coined the term GitOps in a blog post titled “Operations by Pull Request.” This launched the concept of GitOps, which has grown into the sandbox project known as OpenGitOps within the CNCF. Among the artifacts released by the project is a set of official principles that define the GitOps approach.

So, it’s important to keep in mind that while the underlying GitOps principles are solidified, the practices and tooling continue to evolve. Currently, the most common application of GitOps is to manage applications that run in immutable containers on Kubernetes.