GitOps is a concept that not everyone knows about. I only realized this when I started preparing a paper and article on the GitOps topic. The basic definition of GitOps refers to "storing state in Git," but it is far from the only one and not the most important one.
Weaveworks first introduced the term, but its meaning is broader than just storing data in a repository. The word "GitOps" probably sounds more like a marketing ploy than a complete reflection of the methodology. The basic idea behind GitOps is to keep the state of an application continuously synchronized with its real-world environment, be it a Kubernetes cluster or another environment.
Let me introduce myself. I am Tanya Chayka. Previously, I worked for the Ukrainian hosting company CityHost. It is the largest hosting provider in the Ukraine. In this article, I want to share my GitOps implementation story, using CityHost as an example. My previous articles on Hackernoon are:
I encourage you to read the articles above as they are no less valuable than this guide on GitOps.
We had 2 in-house data centers where we hosted hundreds of thousands of websites and virtual machines. To manage our infrastructure and services, we made heavy use of Kubernetes. It allowed us to describe the infrastructure declaratively and manage the physical servers and all workloads.
Our infrastructure included:
It's important to note that our specialty was using Kubernetes primarily to deploy stateful applications, of which we had almost 80%. Kubernetes played a key role in managing dozens of stateful applications. To ensure that multiple development teams could work together seamlessly, we needed to set up effective collaboration.
Let's break it down in more detail. To eliminate misunderstandings and ensure common terminology, let's define key terms.
Let's start with the terms Continuous Integration and Continuous Delivery. Not everyone can tell how one differs from the other. Let's get to the bottom of it.
When discussing CI/CD, we usually mean some kind of Pipeline.
It runs and automates the build and testing, creates the release, and frequently delivers our application to the end environment (staging, prod).
Unfortunately, it doesn't always happen the way we want it to. Because we are dependent on external factors, the Pipeline can break, and the application stack can disconnect and have to be fixed manually. One of the important things that GitOps helps to solve is to greatly simplify pipelines by doing away with the stage of delivering the application to the final environments.
This diagram shows the complete life cycle of an application, from its planning and code development to the moment when the application is successfully deployed and functioning in the production environment. The 2 main environments highlighted here are Staging and Production. It is important to note that the cycle is not considered complete even after deployment to production, as it is necessary to continuously monitor the application and ensure it is running smoothly.
Continuous Integration is a set of practices that help customize the collaborative development process and automate many steps, including building, testing, and publishing releases. When developers push new changes into a branch, merge them into a master, or create a tag, automation starts the process: application images are built, automated testing is performed, and other steps are performed independent of the specific target environment.
At this stage, the application does not yet know exactly where it will be deployed. In theory, temporary test environments can be created at this stage for validation, but the main purpose of this stage is to provide automation of the build and test processes. However, the process of delivering the application to the real environment itself is not usually described by the CI methodology and may even be done manually.
Continuous Delivery includes practices related to the specific delivery of the application to specific environments, such as staging or production. The delivery process in this stage is automated but may include manual activities.
For example, deployment to a staging environment may occur automatically, while deployment to a production environment may require manual manipulation. Deployment to production may require pressing an abstract "button,” provided that on the staging environment, the current version of the application has been successfully deployed and is running smoothly.
These are 2 important steps in the application development and delivery cycle, ensuring that the application continues to evolve and run smoothly in real-world environments.
There is a third term, Continuous Deployment. It means automating the delivery of the application to the production environment as well.
These highlighted steps are the tasks GitOps solves when delivering the application to the environment.
GitOps is a concept that combines Git as a single source of truth and a continuous process of synchronization from that source. In the context of GitOps, a Git repository contains a declarative description of the state of an application as it should be in a particular environment. The continuous synchronization process ensures that the environment always matches this desired state. An essential part of GitOps is observability, which provides real-time assurance that the application works correctly.
Let me explain the scheme in detail.
Suppose you have a Git repository with an application and manifest to deploy it to different environments. You also have a pipeline that automatically reacts to changes in the repository and deploys the application to Kubernetes. For example, it executes the kubectl apply or helm install commands to align the state of the deployed application in Kubernetes with the state described in the Git repository.
This may seem sufficient to call it GitOps, but according to the concept created by Weaveworks, there is another important element.
GitOps, as presented by Weaveworks, is based on the idea of a continuous reconciliation loop. This loop continuously monitors the source of truth (Git repository) and keeps its state constantly synchronized with the real world (Kubernetes cluster).
If you examine how controllers work in Kubernetes, you'll find similar logic, except that they keep track of state in the Kubernetes API rather than in the Git repository.
For example, there is a Deployment object that generates a ReplicaSet resource. Each ReplicaSet controller keeps track of its object and manages the creation and deletion of pods. If any pod is manually deleted, the ReplicaSet controller notices this and tries to restore the state according to the description in the object. The same happens with a ReplicaSet object. If it is deleted, the Deployment controller will create a new ReplicaSet to match the new state described in the Deployment. This logic works in the opposite direction as well. If we update the specification of a Deployment object, this results in the creation of a new ReplicaSet and pods.
This idea of continuous consistency inspired the creators of GitOps, based on the principles underlying Kubernetes controllers’ work.
That's right, GitOps assumes a dedicated GitOps operator or controller that performs functions similar to controllers in Kubernetes. However, instead of monitoring the Kubernetes API, this operator monitors a specific Git repository. It takes the declarative state stored in the Git repository and ensures that it is synchronized with the actual state in Kubernetes. This means that the GitOps operator takes data from the Git repository, applies it to the Kubernetes cluster, and ensures that the state always matches the description in the repository.
It is important to emphasize that GitOps is not necessarily tied to Git as a data source. While Git is the most common source, GitOps can use other systems to store declarative descriptions, such as S3 buckets or even Helm repositories. The key is that the system must be capable of providing a declarative application state that can be used for automatic synchronization and infrastructure management.
Thus, GitOps is a methodology that provides continuous alignment of the application state with its declarative description. This can be implemented using various data sources, not just Git.
Let's look at delivery automation solutions for Kubernetes using ArgoCD and Flux as examples.
Using 2 Git repositories within GitOps, as suggested by the ArgoCD reference implementation, is an excellent practice to effectively manage application state and deployment.
Here's how it works:
This approach provides a clear separation between the application and its deployment, which simplifies management and ensures deployment consistency. At the same time, the first repository (Application Source Repository) remains stable and environment-independent, allowing development teams to work on the application regardless of the infrastructure.
Also, the second repository (Application Manifest Repository) can be updated from the first repository using pipelines or other automation tools. This provides flexibility and the ability to respond quickly to changes in application source code, the need to make deployment changes, or the addition of new environments.
Using ArgoCD and Flux as fully multitenant systems provides flexibility in organizing the GitOps process for multiple development teams and applications. Here are the key aspects of this multitenant model:
Notably, the choice between a mono repository and separate repositories depends on the specifics of your organization and projects. Most importantly, ArgoCD and Flux can work with different models of Git repository organization to meet the needs of your development team.
This multitenant approach, with a central dashboard to monitor the status of all the repositories, allows you to centrally manage multiple applications and environments. It provides transparency and efficient infrastructure management. This approach has proven to be the simplest and avoiding Dependency hell.
Validating manifests before they are deployed is an essential aspect of GitOps practices that helps ensure the reliability and security of application deployments. Let's look at methods for validating and parsing manifests in more detail:
These practices have their benefits and can be utilized depending on your specific needs and level of confidence in the security of your deployment. The key is paying attention to validating and testing manifests before deploying them to a live cluster. This helps mitigate risk and ensures the stability of your GitOps process.
This point is rather Argo-specific. It primarily concerns passwords and certificates. Before applying any changes to the cluster, ArgoCD generates a YAML file to apply to the cluster.
In ArgoCD terms, there is a desired and live state of your application. Desired is the manifest obtained from our source of truth (Git repository). Live is what is already there and running in the cluster.
ArgoCD compares them and tells you which fields should be changed. So if the helm template returns a different value every time, for example, a string with a random password, ArgoCD will endlessly try to bring them to the OutOfSync state. So manifests should be idempotent.
Remember that every field described in the Git repository will be checked against the one in Kubernetes.
Flux2 doesn't have this problem because it uses native helm to apply helm charts. Thus, all native helm functions will work in Flux2 as if you were deploying with a normal helm. Even an abstract hook to generate a random password won't give you any problems.
These 2 tools have different logic. Each of them has a right to exist. It's all about what is more important for you - strict control over the manifests you use or the convenience of using the tool you are used to.
Managing and securely storing secrets is an important part of any infrastructure, especially in Kubernetes and GitOps. ArgoCD does not provide a built-in mechanism for secret management and leaves it up to the users. In this context, it is important to choose an approach that meets your security and secret management requirements.
However, ArgoCD can be extended with custom plugins, or you can use separate controllers running on the Kubernetes side. Let's take a look at a few of them:
The choice between these solutions depends on your specific security, complexity, and flexibility requirements. It is essential to consider security and secrets control to avoid leaks of sensitive information and ensure regulatory compliance.
ArgoCD is an open-source declarative, GitOps continuous delivery tool for Kubernetes. It helps automate and simplify the deployment and management of applications in Kubernetes clusters by using a GitOps workflow.
Let's consider the scheme of ArgoCD.
There are several deployments in the system, but the most exciting one is argocd-repo-server. This container includes Git and various manifest generation tools such as Helm, Kustomize, and others. It is important to note that argocd-repo-server does not have access to the Kubernetes API. The way ArgoCD works is that we first generate deployment-ready YAML files by executing the binary in the repo-server container. Then, argocd-application-controller synchronizes them with the current state in the Kubernetes cluster.
Argocd-server provides an attractive user interface for interacting with the system. It uses argocd-dex-server and argocd-redis for its operation, but their functions can be considered internal details that do not need to be discussed in detail.
Let's see how to add a custom plugin to ArgoCD.
We have a ConfigMap in which we can describe configManagementPlugins. Each plugin has 2 stages: init and generate.
The init stage runs a script to prepare the repository to generate the manifest. For example, we can run a helm dependency update to download all dependencies for our helm chart. If using repository-level encryption, a decryption operation can be added here.
The generate stage is the command to retrieve the YAML file. In the case of the helm, here would be the helm template command. The manifests obtained in this way are applied to the cluster by an already separate GitOps engine.
Flux2 is a software project that is part of the Cloud Native Computing Foundation (CNCF) landscape. It is an open-source tool designed to help automate the deployment and lifecycle management of applications and infrastructure in Kubernetes environments.
Flux2 works a little differently. It implements the Kubernetes pattern with custom resources and controllers.
Flux2 has a source controller that downloads changes from the source repo. In addition to Git, this could be S3 or the Helm Chart Repository, as well as a separate Kustomize controller and Helm controller that handles applying manifests to the cluster. If we wanted to extend Flux, we would have to write a separate controller and CustomResource.
|
ArgoCD |
Flux2 |
---|---|---|
GitHub Stars |
14.5k |
5.4k |
Architecture |
Multi-tenant |
Multi-tenant |
Source Repo |
Helm, Git |
Helm, Git, S3 |
Configuration Management |
YAML-manifests, Helm, Kustomize, Ksonnet, Custom Plugin |
YAML-manifests, Helm, Kustomize |
Lifecycle Hooks |
Helm-style hooks |
- |
Image Updater |
Helm, Kustomize |
YAML-manifests, Helm, Kustomize |
Security Model |
RBAC, sync windows |
Kubernetes RBAC |
Secrets Management |
- |
SOPS |
Observability |
Dashboard, CLI, Notifications, Grafana |
CLI, Notifications, Grafana |
Thanks to Flux2 controllers, the system supports all Helm and Kustomize features. However, it is worth considering that ArgoCD has some limitations in using Helm, as it works with it via the helm template. This can cause problems when using Kustomize Helm Charts, especially for features like lookup that do not work in ArgoCD. Some helm hooks may need to be reconsidered, as they are not executed by Helm but by ArgoCD's GitOps Engine and function differently.
ArgoCD's Image Updater also differs from Image Updater. It generates a file that can only be used with Helm or Kustomize, while Flux2 uses conditional comments, allowing you to substitute image names almost anywhere in the YAML file.
Regarding the security model, both tools use RBAC, but Flux2 offers a more straightforward approach by reusing standard Kubernetes policies, while ArgoCD has its own format. In addition, ArgoCD provides the ability to customize sync windows, which allows you to limit the deploys of certain clusters to only certain time slots, such as at night.
Regarding handling secrets, Flux2 provides SOPS support out of the box. ArgoCD can be a bit more complicated with this.
An important aspect is the observability of the system. ArgoCD offers wonderful and convenient dashboards out of the box, which is a big advantage in terms of observability. Flux2, on the other hand, does not have such nice dashboards but provides similar features through CLI utilities, notifications-controller, and integration with Grafana for creating custom dashboards.
We've talked about Continuous Deployment, which is responsible for delivering our application to Kubernetes. But what about Continuous Integration?
The approach to Continuous Integration (CI) can be varied, and there are several popular tools and methods for implementing it. Let's take a look at a few options you can use:
The choice of tool depends on your needs, project complexity, and preferences. It is important to choose a tool that best fits your specific tasks and integrates with your infrastructure.
Werf presents an interesting approach to the concept of GitOps and CI/CD automation. This utility was designed with CI/CD principles in mind and can be easily integrated into various CI/CD systems, offering solutions to typical problems encountered when building such processes.
One of the key features of Werf is its use of determinism. It is similar to GitOps in organizing declarative and versioned infrastructure. However, unlike the pull model used by Flux and ArgoCD, Werf favors a push model for applying changes.
Werf covers aspects of CI/CD but does not fully implement GitOps by itself. Therefore, integrating with ArgoCD provides an interesting solution for building a complete CI/CD cycle, including local development, building images, and running tests. This integration allows you to use ArgoCD to deploy to the production loop and, optionally, to deploy to a production-like environment.
Using Werf and ArgoCD together can provide a more complete and automated infrastructure and application management, combining the benefits of GitOps and CI/CD.
GitOps is an important methodology that is revolutionizing the way Kubernetes applications are managed. By storing configuration and state in a Git repository, development teams can achieve greater automation and transparency in managing applications in Kubernetes clusters.
GitOps provides a more secure and efficient way to manage Kubernetes applications, foster better collaboration within the development team, and increase process transparency. Properly implemented, GitOps can significantly improve infrastructure and application management, making it an important tool in today's development and DevOps world.
My previous articles on Hackernoon are:
I encourage you to read the articles above as they are no less valuable than this guide on GitOps.