To GitOps or not?
GitOps is something which I’ve heard of, but I didn’t fully understand the added value. Similar with DevOps being more than Dev and Ops being in one team, there is more to GitOps than listening to a Git repo for deployments. Before I dived into GitOps I didn’t fully understand how it worked and what the advantages are over a single CI/CD pipeline. In this blog I’ll be tackling my questions about GitOps and find out whether it will be the new standard for CD.
Before GitOps
Before GitOps I was used to the following pipeline:
- A merge is done on the main branch in the repository
- The merge triggers the CI/CD pipeline
- For CI some steps are executed, like: linting, static code analysis, unit tests, compiling of code
- With the compiled code a container is built, which is stored in a (builtin) container registry
- Deployment to staging environment is done, after which smoke tests on the environment is run
- If the deployment fails or the smoke tests don’t pass, the pipeline is stopped and will result in a failed deployment
- If both pass, the same is done for production
- If the pipeline has passed, it means the merge was successful and the code is deployed on production
- If the pipeline didn’t pass, the engineer should create a new merge on the main branch to fix the issue and the pipeline will be run again
In this example there is no manual action necessary between pressing the merge button and it being deployed to production. If the tests are good enough, the engineer doesn’t have to continuously check the deployment. The pipeline will do the work, while the engineer can already work on different things, increasing velocity.
Principles of GitOps
This page already nicely describes how GitOps works and the advantages of it. In short with GitOps your git repo is the central place for almost everything. The CD tool according to GitOps watches the git repo and makes sure the actual state matches the described state in git. This means that CI and CD isn’t a single pipeline, but split up with the git repo and artifact storage being the link between them. The CI pipeline isn’t different, but it ends with an artifact being stored in an artifact storage and bumping the definition of the deployment with the new version. This change will be picked up by the CD tool and the sync will make sure the described and actual state match again.
That made me stumble onto another substantial difference: in GitOps there are two repositories, one for the code of the application and one defining the deployment. It decouples the application with where it is ran, allowing a single deployment containing multiple applications. Another advantage is that for a change in the deployment, the application doesn’t have to be built and given a new version. Online I also saw that some people choose to split them in different branches within a single repository, but I think the advantages of trunk based development are lost.
In short a GitOps flow looks as follows:
- A new tag is added to a commit in the main branch in the repository
- The new tag triggers the CI/CD pipeline
- For CI some steps are executed the same as before, like: linting, static code analysis, unit tests, compiling of code
- With the compiled code a container is built, which is stored in a central container registry
- The version of the application is bumped in the deployment repository, either as a final step of the CI pipeline or manually
- The CD tool watches the git repository where the deployment code is stored and will notice the version bump
- The CD tool will compare the desired state in the git repo with the actual state
- The CD tool will update the deployment, making sure to make the actual state match the desired state
GitOps in practice
To show how this works, I created a simple GitOps example. Of course in line with GitOps there are two repositories:
Adding a new tag to a commit in the main branch will trigger the CI, which results in a Docker image being stored in the Github artifact storage and a bump of the version of the application in the other repository. The other repository contains an ArgoCD application (hello-world-cd.yaml
) which watches the deployment repository and will be triggered to update the new version of the application.
That sounds nice, but…
The above simple example works really nice, but only for a single environment. Normally the successful deployment on staging would result in a trigger for deployment to production, but this is not something which is part of GitOps. According to FAQ of GitOps: GitOps doesn’t provide a solution to propagating changes from one stage to the next one. We recommend using only a single environment and avoid stage propagation altogether. But if you need multiple stages (e.g., DEV, QA, PROD, etc.) with an environment for each, you need to handle the propagation outside of the GitOps scope, for example by some CI/CD pipeline. Well it seems we still can’t lose the ‘classic’ CI/CD pipeline…
Conclusion
For me it took some time before I understood the power of GitOps, but I can definitely see it fixing some flaws in the single-pipeline CI/CD approach. Still there are also flaws in GitOps, and in my opinion these are bigger. In my (relatively short) experience almost all application in real life have multiple environments, thus a single environment as proposed by GitOps wouldn’t work. This post also describes some other flaws of GitOps. I don’t think these flaws can’t be overcome, but it might result in a mix between a CI/CD pipeline and GitOps.
Also the uses of GitOps seems to be very limited to Kubernetes at the moment. I can see GitOps also work nicely for Infrastructure as code of Cloud services (AWS, Azure, GCP), but the tools aren’t there yet. I think if the tools will come and the current tools will grow, GitOps will be good enterprise-ready solution. For example I used ArgoCD, which is one of the most used GitOps tools at the moment. If the tool will find a solution for deployments to multiple environments, things will get interesting.