GitOps - A fully declarative way to deploy to Kubernetes
A lot has changed since the rise of containers & Kubernetes, especially for larger organizations. With a good Kubernetes setup, developers don’t have to worry about infrastructure. Deployment automation becomes a lot easier as containers provide a way to package, test and deploy any application to any Cloud platform, behaving in the exact same way. But when it comes to automation, we need to find the right approach for Kubernetes deployments. GitOps provides a solution embracing the Kubernetes concepts and architecture.
When the world changed from Bare-Metal infrastructure to Virtual Machines, we started automating the VM’s provisioning process. Infrastructure as Code (IaC) and configuration management tools like Terraform, Ansible and Puppet made this automation possible.
With containers we went a step further. Developers now only need to worry about the application and it’s dependencies, so they can focus on business requirements, rather than surrounding infrastructure. As with Hardware, the management of virtual infrastructure (operating systems, networking) can now completely be outsourced to the public cloud and/or a centralized platform-team. This will not interfere with the delivery cycle of DevOps teams.
Simply put: Containers enable developers to deliver faster, cheaper and more reliable.
Why is GitOps important?
Kubernetes provides the possibility for developers to deploy containers at will. However, it is easy to lose control over the applications that run in the Kubernetes clusters, especially in bigger environments with multiple independent teams. How do you keep track of your Kubernetes resources? What is your source of truth? What was the state of an application at a particular point in time? How do you rollback an application and be sure to maintain a complete version history, audit logging, and still being in control? How do you make sure people do not manually ‘fix’ or ‘break’ things in Kubernetes?
In a “non-GitOps” CD system, deployments are pushed to Kubernetes when triggered. It can be an automated trigger or a manual action, but it is not continuous.
After the trigger — a point in time — the CD System needs to:
Fetch the Kubernetes Yaml files (templated using placeholders or a tool like Helm or Kustomize) from a repository
Fetch the Environment specific configuration from a database somewhere
Generate Kubernetes resources using the yamls with configuration, creating deployable Kubernetes resources
Push the resources to the Kubernetes cluster using some credentials
The ‘truth’ for your Kubernetes resources is coming from multiple sources, and is not reliable because of the possible “configuration drift”. Also, if you ever want to migrate to a different Deployment System, you will most probably lose the deployment history and audit trail.
GitOps has a single source of truth
With GitOps, there is only one source of truth: Git. It is fully declarative, and contains all configuration. Unlike a trigger-based (push) deployment, GitOps will not leave the system vulnerable for changes from all kinds of directions. It will continuously pull from Git to prevent configuration drift in the cluster. In traditional CD systems, it is common to deploy software imperatively. GitOps will make sure you don’t.
Git automatically comes with version history, audit logging, easy diffs, repository access control, branching and approval policies. You can easily retrace your history and rollback maintaining a full audit-trail. Developers are already familiar with this, making the transition to Kubernetes a lot easier. They will know exactly how to rollback, merge, change and manage their own application, which is what is required in a DevOps world.
GitOps uses reconciliation
Kubernetes gives you the ability to describe your “desired state”. You typically define all your containers and their environment configuration as yaml files. Kubernetes then continuously tries to reconcile the actual state with your desired state. What happens if an application crashes or is unhealthy? Kubernetes will restart it. If a Node crashes? Kubernetes will run your application on another Node. If a rogue developer tries to manually kill a process making your application unstable? Kubernetes will restore the situation for you, forcing the developer to fix the issue “the right way”.
GitOps extends this reconciliation process all the way to Git, making Git the Single Source of Truth for all your applications. If you change something in Git, this will immediately be reflected in your environment, changing the runtime to the desired state. This also means that a developer will not be able to manually change a Kubernetes resource that is managed using GitOps, because the reconciliation process will revert the change immediately. This way, all changes are traceable and reproducible.
How it works
GitOps uses the Operator model — a common model used in Kubernetes. The GitOps Operator (like ArgoCD or Flux) lives inside the Kubernetes cluster and will continuously monitor the Git repositories that you configure. When someone makes a change to a GIT repository, it will pull the new configuration and sync the changes to Kubernetes. Kubernetes then in turn makes sure to update the actual state.
ArgoCD is a GitOps Operator that works with Custom Resource Definitions in Kubernetes, giving the opportunity to set up an organization-wide GitOps architecture using projects and application groups. You can enable fine-grained RBAC, setup reusable variables / parameters for teams in a flexible manner, and still give developers full control over their application within the boundaries you define.
Be sure to check it out!