Post thumbnail

Decouple CI and CD

It always depends

One important note that this post must start with is that there is not one way to implement a CI/CD pipeline. There are a lot of tools and a lot of different ways to implement the flow. That said, I think it's important to know the common practices in the industry and to build our CI/CD based on the common practice. This helps avoid common pitfalls.

I see this recommendation as part of the modern practices of DevOps and CI/CD. This is definitely an advice that I would keep in mind when designing a DevOps workflow.

Decouple Build and Deploy

The recommendation in this post is to decouple CI (to simplify: the build part) and CD (the deployment part).

There are multiple ways to see it:

In this setup, CI and CD are not one continuous action, they are building blocks. This doesn't mean that we can't deploy automatically—the CD building block can be triggered automatically after the CI building blocks. It means that you have control over how and when to sequence them.

A pattern that is not decoupling CI and CD can be as follows:

Representation of a coupled CI/CD

On the opposite, a pattern that is decoupling CI and CD can be as follows:

Representation of a decoupled CI/CD

The benefits

There are a few benefits that I would like to highlight.

Decouple CD from your source control.

By nature, the CI is more strongly coupled to source control. Services are built from the code. On the other hand, CD is more coupled to the way you handle release. The idea is to have one more level of abstraction between your code and your production.

Source control is a very important aspect of DevOps. There are topics like monorepos, library management, branching strategies. You want to be able to experiment with those practices and modernize your source control practice without directly impacting your production environment.

Some applications may also come from different kinds of sources that you don't control directly. It may be another vendor or an open source component.

This is also a discussion that will be important in the context of Microservices. One of the key aspects of Microservices is their ability to be deployed independently. Repositories for monolithic applications, containers, and cloud functions can be different.

Handle multiple environments

You will often be handling complex environments. You may have multiple environments, some environments may be connected to the Internet, some environments may be offline, the application source control may even be unreachable from the environment.

You may even have a number of environments that is more dynamic that you initially thought. For example because of a new request, a hotfix that need to be tested in a temporary environment.

You don't want to redesign your CI/CD for each change in the environment.

Security

There is an added benefit of using an artifact repository as intermediary: security.

An artifact can be scanned for vulnerabilities. An extra mesure that you can take is to make artifacts immuables. Once an artifact is built, if they don't change you can guarantee that a given version is correctly validated and and exact version of an artifact is deployed in an environment. It you build a service twice you can end up with 2 different services. The same source code will produce different build artifacts depending on the library used, the Docker base image used or other components that can be variable during the build phase.

GitOps

This can appear like a paradox but decoupling CI and CD is one of the key of GitOps.

GitOps practices states that each environment should be declared in a manifest, ideally in Git. So it means that the environments are directly inferred from the source control.

That said one important distinction here is that the deployment manifest is different from the application source that is used to build the artifacts.

GitOps is using the description of the required state but not the build instructions.

Simplicity(?)

Decoupling introduce a bigger initial effort in the CI CD design and potentially more steps. That said, once done a less coupled architecture and based on CI and CD building blocks can also generate a system that is more simple to reason about. Less coupling tend also to simplify the workflow.

Advanced release

Release management is becoming more complex. CI and CD have been the norm for some time but there are discussion of Continuous Release. This can include simple release policies or more advanced patterns like blue/green deployment and A/B testing.

The drawback: the need of an artifact repository

The consequence of this decoupling is that a there is the need for an artifact repository. This introduce a necessary additional component in the system.

This will then depends on the technology used. If you are using containers artifact repository is pretty much a must (for image resgisty) and GitOps is becoming a standard. If you are using other technologies, there can still be a lot of benefit to this approach. The artifact repository can be a dedicated software or even a cloud storage bucket (AWS S3, Azure Storage Account, AlibabaCloud OSS,...)

Final notes....

The goal of this post is to share what are in my opinion some key aspects of modern CI CD:

Both CI and CD are evolving and new pattern are emerging. You may want to decouple them to benefit from the best practices of each of them