GitOps—From beginner to professional

Introduction

GitOps entails a repository with declarative descriptions of your desired production environment infrastructure, and an automated process to ensure your actual production environment matches those declarations.GitOps provides engineers with certain benefits of developing in an already familiar environment: They can deploy more quickly and more often, recover faster and easier from errors and failures, and also maintain secure and self-documenting deployments.

This article will describe how infrastructure teams can perform operations by pull requests using GitOps principles. The reader will learn about GitOps use cases, their benefits, and the tools involved; we will also take you through an example implementation of GitOps using Kubernetes.

Prerequisites

For the purpose of this article, readers should be familiar with:

  • Git and GitHub
  • Kubernetes and containerization technology
  • DevOps practices
  • Cloud-native technologies

What is GitOps?

GitOps is a framework for automating continuous deployment and declarative infrastructure for cloud-native applications using Git as the single source of truth, infrastructure as code (IaC), and continuous integration.

For instance, to deploy a new application, add new features, or make changes to an already existing app, you only have to update the repository with the new code. Everything else is handled by the Kubernetes continuous integration and continuous deployment automated process.

GitOps is not a tool to install but a set of principles—a way of executing a deployment—for operating and managing software systems, just like DevOps.

GitOps is a relatively new method for software engineers to manage clusters and application delivery on Kubernetes. However, although a natural fit, it is not only for Kubernetes. If a system can be observed and described declaratively, it can be automated with GitOps.

The “Git”

The “Git” in GitOps brings operational tasks closer to developers and is a popular distributed version control software present in every developer’s toolkit. As stated earlier, Git is used as the single source of truth. It takes control of operations, like source control, history logs, peer code review, and rollbacks, and stores every commit, time-stamped configurations, of the software.

In simpler terms, GitOps uses Git for all of its operations from development to deployment. The application, infrastructure, and configuration codes are stored in Git as manifest files.

Git’s distributed nature, popular among developers, and its branching and merging capabilities are some of the most compelling reasons to use it.

The “Ops”

Essentially, the “Ops” represents the writing of YAML files for infrastructure configuration management, app deployment, the continuous delivery of new features, and more. This is powered by an orchestration engine like Kubernetes. Kubernetes is the most adopted deployment engine in the industry, as more companies adopt it for their software deployment cycle.

Using GitOps, the engineer:

  • First writes code and pushes it to Git
  • Produces the Kubernetes deployment artifacts for the container
  • Then builds a container image

GitOps use cases — The “Why?”

Since the advent of GitOps in 2017, use cases have proliferated. Many articles, KubeCon talks, and even books exist around the concept, and the GitOps Working Group was established in December 2020 to facilitate the GitOps ecosystem.

Use cases cover both developer and operations processes. The developer benefits include:

  • Cloud-native application deployment: GitOps enhances the continuous delivery of cloud-native applications.
  • Service rollouts: GitOps can assist in various processes, like rolling out new or updating existing applications.
  • Infrastructure management: GitOps makes infrastructure as code easier and more efficient in different ways. It makes the repetitive provisioning of infrastructure less risky by making it easier to roll back to existing configurations. It automates infrastructure configuration, leading to a reduction in human errors and application outages.
  • Easy Code Reviews: DevOps teams carry out code reviews on infrastructural changes before making deployments. In GitOps, the code reviews go beyond the application program to the Kubernetes YAML manifest to review the changes made to the operational environment.

Operationally, GitOps adopts the prominent declarative paradigm in development, making it easier to reach a particle configuration objective. It also helps in recovering lost infrastructure configurations by storing the declarative specifications in Git, its source of truth. For compliance, it supports operations by pull requests, making it difficult for a single developer to alter the software.

How GitOps works

The GitOps process can be split into four stages:

  • Describing the entire application deployment system declaratively in a YAML file
  • Versioning the desired system environment or state in Git
  • Applying automatically all approved changes
  • Ensuring the correctness of those deployments and changes (by software agents)

The application will have at least two repositories; one contains the application source code, while the other contains the infrastructure code (the Kubernetes manifests or Terraform scripts).

Testing is essential to implement GitOps, and developers must ensure it is done before rolling code to production systems. Also, it is necessary to have a plan for managing secrets, whether by adopting Kubernetes Secrets or another vault from your preferred cloud service provider.

The working environment consists of the application code, runtime dependencies, and infrastructure configuration file(s). All the dependencies and code are packaged in a Docker image as a deployable unit and orchestrated with a Docker daemon.

You can run this image on any environment—the engineer’s laptop or the production cluster in the cloud. This is possible because the image contains all the code and dependencies and abstracts away cross-platform incompatibility.

Push and pull requests deployment mechanisms

GitOps offers two methods of implementing a deployment: push or pull request mechanisms.

Push deployment method

This is the de facto deployment method for popular CI/CD tools like Jenkins and CircleCI. Push-based deployments are also called CIOps. The application source code and the Kubernetes YAML file for deployment are written in the same repository. The CI/CD system triggers the build pipeline when the developer updates the application source code. This builds the container images and adds the new deployment manifests to the environment repository. The build pipeline can make use of these templates for creating the actual manifests.

Pull method

A pull request is considered more secure and the best choice for implementing GitOps. Developers pull the system’s desired state from the Git repository into the live system.

Benefits of implementing GitOps

There are several advantages to implementing GitOps principles.

First, the pipelines are built in the cluster and run from within the cluster; you are given high-level permissions to external CD systems.

GitOps offers developers and engineers a simplistic approach to deployment that is also very consistent. They can make use of tools they are already familiar with to better gain a handle on the deployment process. GitOps also makes it easier to scale clusters, as well as deploy faster and as frequently as needed while also maintaining security and self-documentation.

Access to production environments is controlled and gated, with Git and a particular repository serving as the single source of truth. Git makes it easier to manage the team workflow, and you can also observe the desired state or version at any point in time from kubectl logs, although the state is inalterable.

With GitOps, your application development and operations codes are available in Git, heightening the developer experience due to ease of accessibility. The need to learn a new technology to carry out operations is abstracted. Developers do not need to handle Helm charts; they simply have to run Git commands and work with a Git repository—tools and techniques they are already used to.

GitOps allows a single developer to work independently and commit changes to the infrastructure’s config repository. Netflix adopted this practice to promote “freedom and responsibility” among engineers. Each engineer is responsible for the coding, testing, deployment, and support of their features; they can also push their releases individually after testing to ensure they’re functioning correctly.

Since everything is done in Git, it provides the project manager/lead/maintainer with a complete audit trail that ensures team compliance and stability. Furthermore, GitOps increases developer productivity due to simplified operations.

Finally, GitOps gives developers and operations engineers continuous security. Access is shifted away from permissions being given directly to Kubernetes clusters, with permissions granted instead to the Git repository. This follows the practice of leveraging vaults to manage secrets.

Implementing GitOps with Kubernetes

This section will implement a basic GitOps CD operator with Kubernetes. We will deploy a website using the NGINX web server on Kubernetes. Let’s walk through the procedures required, from lab setup to deployment.

Tooling for GitOps

As we learned earlier, GitOps is not a tool to be installed but rather a set of DevOps-inspired rules that can be implemented with the right tools. In this section, we’ll learn about the tools that make GitOps easy to implement.

Kubernetes

Kubernetes is an open-source container orchestration tool released by Google in 2014 as an extension of Borg. It is a platform for building, deploying, and maintaining distributed applications. Kubernetes provides software development teams with the following:

  • Speed in development
  • Software scaling
  • Team scaling
  • Infrastructure abstraction
  • Development efficiency
  • Cloud-native ecosystem

Docker

Docker, adopted by most developers and engineers, is the runtime for Kubernetes containers used for this article.

Container/Helm registry

We will use this registry to host container images or Helm charts.

Helm

Helm is a package manager for Kubernetes that makes it easier to create Kubernetes workloads, as well as install and manage those workloads in Kubernetes clusters.

Git

Git is the revision control software used as the source of truth for GitOps. For this purpose, you can use, for example, Bitbucket, GitHub, or GitLab.

Flagger

Flagger is a delivery operator that assists operations engineers with canary deployments by automating the process. Flagger is built by Weaveworkss and works for the popular Flux tool.

Prometheus

Being a monitoring and alerting system, Prometheus is a widely adopted tool in GitOps-enabled environments. It is suitable for monitoring workloads on the Kubernetes platform.

Terraform

Terraform is used to provision infrastructure on and off the cloud, including Kubernetes clusters.

Flux

Flux is a CLI tool that serves as a GitOps operator for Kubernetes.

Argo CD

Argo CD is an operator for Kubernetes using a visual approach. The tool focuses on application delivery use cases and provides three modes of computing: services, workflows, and event-based processing.

Jenkins X

Jenkins X is a full-blown CI/CD tool for cloud-native applications on Kubernetes with an operator that can be used to manage GitOps pipelines.

Git-Secret

Git-Secret is a git tool for encrypting and storing secrets in Git. It automatically encrypts and decrypts the GitOps workflow.

Git or Kubernetes backup

To keep desired system states from getting lost, you need a backup, which means you need backup tools such as:

  • kube-backup for backing up Kubernetes configurations
  • GitBackup for backing up and protecting GitHub repositories

However, the core tools for implementing GitOps include Kubernetes, Docker, Git/GitHub, and an operator like Flux or Argo CD.

GitOps lab setup and implementation process

First, set up a Linux environment with minikube to spin up a Kubernetes cluster. Minikube uses containerization and virtualization technologies like Docker, Hyper-V, and VirtualBox to boot a Linux machine containing a Kubernetes cluster.

You must install VirtualBox if you use Windows since Kubernetes only runs on Linux nodes.

Next, download and spawn a local Kubernetes cluster with minikube. Visit the releases repository for the executable that matches your computer’s operating system. Alternatives include KinD and Microk8s, as well as Kubernetes clusters from cloud service providers.

After downloading the minikube executable, you can install it, launch a terminal, and input the command:

minikube start --driver=virtualbox 

This will start downloading the VM boot image for minikube clusters. When the download and verification finishes, you will find a success message printed to the terminal console as seen below:

This installs kubernetes and kubectl, a command line utility used for interacting with Kubernetes clusters.

To spawn a Kubernetes cluster right from the installation, you would use the command below instead:

minikube start --kubernetes-version='v1.25.2' \\ --driver='virtualbox' --memory=8196 -p gitops

This makes sure the version of the Kubernetes cluster and the Kubernetes CLI tool are the same.

Breakdown of the process

First, we will create an application repository for the demo application. Then, we will create an environment repository for the infrastructure manifests. Using a pull request, we will set up GitHub Workflow to update the application’s deployment manifests from inside the environment repository.

The demo application

The repository will store the application code and be called gitops-kubernetes-demo. The demo application will be a simple web application written in any language. This article uses Go and the file name is main.go:

package main 

import (
"fmt"
"log"
"net/http"
)

func main() {
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "This is a demo app for GitOps implementation.")
})

log.Fatalf("error: %s", http.ListenAndServe(":8080", nil))
}

The Dockerfile script to match the application is:

FROM golang:1.17.6 as builder 
ENV CGO_ENABLED=0

EXPOSE 8080
WORKDIR /app

COPY . /app/
RUN go build -o go-app
FROM scratch
ENTRYPOINT [ "/go-app" ]
COPY --from=builder /app/go-app /

The two files will belong in the same directory. You can then run the container image using the Docker command:

docker build -f Dockerfile -t sampleuser.io/gitops-kubernetes-demo demo/go-app:latest

The container image begins to build as Docker fetches the existing layers from a public container registry. You can view the image in the registry with the docker images command.

You will need a GitHub account to host two repositories and to run GitHub workflows using GitHub Actions.

The environment repository

Now, we will create an environment repository for the infrastructure manifests. In this example, we are using gitops-demo-environment as the name of the repository. Next, we will create a directory named apps and a sub-directory named gitops-kubernetes-demo, which is the name of the application repository. The Kubernetes manifests will reside in this sub-directory. Alternatively, you can make use of Helm or Kustomize to store the manifest, but this is the standard method.

The manifest will be named deployment.yaml, as it is written in YAML syntax:

apiVersion: apps/v1 
kind: Deployment
metadata:
name: gitops-kubernetes-demo
spec:
replicas: 1
selector:
matchLabels:
app: gitops-kubernetes-demo
template:
metadata:
labels:
app: gitops-kubernetes-demo
spec:
containers:
- name: gitops-kubernetes-demo
image: <image-name>:<tag>
- containerPort: 8080

The service script will also be named service.yaml and will reside in the same sub-directory:

apiVersion: v1 
kind: Service
metadata:
name: gitops-kubernetes-demo
labels:
app: gitops-kubernetes-demo
spec:
type: ClusterIP
selector:
app: gitops-kubernetes-demo
ports:
- port: 8080

Using GitHub Actions for a continuous delivery pipeline

After every new release in the gitops-kubernetes-demo repository, the continuous delivery pipeline builds a container image, pushes it to the container registry, and creates a pull request to update the deployment manifest to the new container image in the gitops-kubernetes-demo.

To automate this process, we will use a GitHub workflow. First, create a new directory in the gitops-kubernetes-demo repository named .github along with a sub-directory named workflows, and add a newRelease.yaml file there:

name: new Release 
on:
push:
tags:
- v*

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Build and Push Container Image
uses: docker/build-push-action@v1
with:
username: $
password: $
dockerfile: Dockerfile
repository: $/$
tag_with_ref: true
tag_with_sha: true

release:
runs-on: ubuntu-latest
needs: build
steps:
- name: Add TAG_NAME env property
run: echo "TAG_NAME=`echo ${GITHUB_REF#refs/tags/}`" >> $GITHUB_ENV
- name: Open PR in Environment Repository for new App Version
env:
ENV_REPO: $/example-environment
uses: benc-uk/workflow-dispatch@v1.1
with:
workflow: New Application Version
token: $
inputs: '{"tag_name": "$",
"app_repo": "$", "image": "$:$"}'
ref: refs/heads/main
repo: $

This workflow runs the build and release jobs on every new release. Two secrets provide the credentials needed for pushing container images to the container registry:

  • REGISTRY_USER (your Docker Hub username)
  • REGISTRY_TOKEN (an access token for authentication at Docker Hub)

The release job runs after the build job has pushed the container image to the registry. It then triggers the GitHub workflow, which creates the needed pull request, similar to an API call.

Next, we will create a .github/workflow sub-directory with a newApplicationVersion.yaml file in the gitops-demo-environment repository. The YAML file will contain the following script:

name: New Application Version 

on:
workflow_dispatch:
inputs:
tag_name:
required: true
app_repo:
required: true
image:
required: true
jobs:
update-image-tag:
runs-on: ubuntu-latest

steps:
- name: Wrap Input
run: |
echo "APP_REPO=$" >> $GITHUB_ENV
echo "TAG_NAME=$" >> $GITHUB_ENV
echo "IMAGE=$" >> $GITHUB_ENV
echo "DEPLOY_FILE_PATH=applications/$/deployment.yaml" >> $GITHUB_ENV
- uses: actions/checkout@v2
- uses: azure/setup-kubectl@v1
id: install
- name: patch deployment manifest
run: kubectl patch --filename=$
--patch='{"spec":{"template":{"spec":{"containers":[{"name":"$","image":"$"}]}}}}' --local=true -o yaml > tmp.yaml
- name: commit change
run: |
git config user.name $
git config user.email '$@users.noreply.github.com'
rm -f $
mv tmp.yaml $
git add $
git diff-index --quiet HEAD || git commit -m "Set $ to version $"
- name: Create Pull Request
uses: macbobby-uzor/create-pull-request@v3
with:
token: $
commit-message: Update report
committer: GitHub <noreply@github.com>
author: $ <$@users.noreply.github.com>
signoff: false
branch: new_release_$-$
title: 'Set $ to version $'
body: |
This PR was automatically created.
Please review and merge to deploy.

This script takes the trigger arguments and updates the deployment manifests. The pipeline will require you to use a PERSONAL_ACCESS_TOKEN for both repositories.

Testing the pipeline

To test the pipeline we built, you need to create a tag named v1 in the gitops-kubernetes-demo repository and push it:

git tag --annotate v1 --message "Release version 1" 

git push

In the Actions tab of the repository, you will see the workflow run. After the build job completes, there will be a new container image with the v1 tag in the Docker Hub. When the release job is completed, the workflow will be triggered in the environment repository. After that, a new pull request will pop up in the environment repository, changing the deployment.yaml file.

You can use Flux to watch out for changes in your application’s deployment manifests. Flux also lets you set up a workflow with the application deployment manifest residing in the application repository. This will remove the need for a GitHub workflow; however, the downside is that you will be responsible for updating the manifests for every new release.

Conclusion

As time goes on, the GitOps Working Group, Weaveworks, and other leading companies behind GitOps (like Amazon, Codefresh, and GitHub) will continue to improve GitOps principles and the tools available for its implementation. Adopting GitOps in your development/production environment is a feasible step that would make your teams enjoy development and deployment. This is because GitOps requires them to only use tools and technologies with which they are already familiar.

Was this article helpful?

Related Articles

Write For Us

Write for Site24x7 is a special writing program that supports writers who create content for Site24x7 "Learn" portal. Get paid for your writing.

Write For Us

Write for Site24x7 is a special writing program that supports writers who create content for Site24x7 “Learn” portal. Get paid for your writing.

Apply Now
Write For Us