Engineering4 min read

Building invisible pipelines with GitOps

How we use GitOps and Argo CD to make our deployment pipelines invisible and help us ship faster.

By Jubril Oyetunji

Ask about this postClaudeChatGPTPerplexity

One part of our infrastructure we often forget about is our deployment pipelines, and for good reason. From day one, we aimed to spend as little time worrying about how a release made it onto a cluster and more about the outcomes of the release. In this post, we'll go over how we use GitOps and Argo CD to deploy CostGraph and how it helps us ship faster.

Our deployment configuration lives in Git. As you'd imagine, there would be no GitOps if this wasn't version controlled. We commit changes to the desired state of our services, and Argo CD handles applying them to the cluster.

Argo is central to this setup. It renders our Helm charts into Kubernetes manifests and compares them with the resources in the cluster. During a sync, it applies the changes needed to bring the cluster into the state described in Git. We can also inspect sync status and application health to see whether the configuration has been applied and how the workload is running.

Imagine a generic backend service. On a push to the main branch, GitHub Actions builds a container image and publishes it to GitHub Container Registry with a tag derived from the source commit, such as sha-abc1234. Once the change is ready to be deployed, we run the deployment workflow to update the image tag in deploy/values.yaml and commit it to Git.

backend:
    deployment:
        image:
            tag: sha-abc1234

Argo reads the updated values and uses them to render and sync the manifests. The deployment workflow needs access to Git, while Argo handles access to the Kubernetes API. We can build images as changes land and choose when to deploy them without changing that process.

This pattern persists across every service we run. We keep deployments close to code so we can inspect and change configuration within the same repository. We also maintain a shared chart for dependencies common to certain services.

Taking a chart for that backend service as an example, we can define a migration Job using the same image tag as the application. Helm's pre-install and pre-upgrade annotations map to Argo's PreSync phase, so the migration runs before the application changes are applied. Keeping this ordering in the chart makes it part of the deployment process without requiring a separate manual step.

Git also keeps a record of the versions we select for deployment. We can inspect the commit that changed an image tag or update it to a previous version through the same workflow. Argo shows us how that desired state compares with what's running in the cluster.

How this makes our pipelines invisible

For us, making our pipelines invisible means we can deploy changes without creating a ticket or asking someone else to run a release. We don't have a dedicated ops team. This works in part because we're all familiar with infrastructure at different levels, but also because we set up our CI/CD from day one so nobody would need to ask how to deploy each time. We still check the outcome of a release, but getting it onto the cluster is already taken care of.

That also makes adding a service more straightforward. We can use the same arrangement of a chart, values, an image build, and a deployment workflow, then configure Argo to track it. The service's configuration can differ while the way we deploy it stays familiar.

This is how GitOps helps us ship faster. We spend less time coordinating deployments and more time on the changes we're shipping and how they behave in production.