In modern software development, writing source code is just part of the journey of getting your ideas into the wild aka production. The ability to confidently build, package, test, validate, and deploy safely into production crosses several domains and disciplines. A common eponym describing the development and deployment pipeline is CI/CD. Continuous Integration (CI) represents all of the steps required to automate the build and packaging of software. Continuous Delivery (CD) presents all of the confidence-building, infrastructure, and deployment steps to safely deploy your artifacts into production.
What is a CI/CD pipeline?
A CI/CD pipeline is a series of orchestrated steps with the ability to take source code all the way into production. The steps include building, packaging, testing, validating, verifying infrastructure, and deploying into all necessary environments. Depending on organizational and team structures, there might be multiple pipelines required to achieve this goal. A CI/CD pipeline can be triggered by some sort of event, such as a pull request from a source code repository (ie: a code change), the presence of a new artifact in an artifact repository, or some sort of regular schedule to match a release cadence.
Typically, CI/CD pipelines are orchestrated by CI/CD platforms that are purpose-built to manage the cross-discipline orchestration needed. CI/CD pipelines themselves can be represented as a Domain Specific Language (DSL) inside CI/CD tools, or in a declarative format (such as YAML). Because of the wide amount of language and technology choices out there, modern CI/CD pipelines focus on outcomes.
Benefits of CI/CD pipeline
Unlike software languages where you can take approaches and design patterns with you from firm to firm, deployments are almost always unique. Software is the culmination of decisions before and during your time. Very rarely are two applications (and their supporting infrastructure) the same. Software development and delivery are iterative exercises, and pipelines are expected to be executed multiple times daily, for bug fixes, among other reasons. By taking a systematic approach with a CI/CD pipeline, teams can have a clearer understanding of what is required to get their ideas into production. As new confidence-building exercises become popular, they can be added as quality gates in the CI/CD pipelines. Because the pipelines are systemic, bottlenecks can easily be seen and remedied versus a disjointed people-driven process with lots of handoffs.
Goal of your CI/CD pipeline
There can be many goals represented in a CI/CD pipeline. The structure of a CI/CD pipeline tends to follow the goals that they are driven by.
Driven by environments
As systems become more distributed, the number of locations a service needs to be deployed to increases. If your main goal is to deploy to multiple environments/locations, your CI/CD pipelines will tend to be more deployment-centric, favoring the orchestration of all the environments a service has to traverse through.
Driven by tests
Test automation and orchestration are popular uses of CI/CD pipelines. Having to chain together several different testing methodologies, a natural home for the automation progressing the testing is in your pipeline. Ass testing rigor increases, longer “time per stage” occurs as the pipeline gets closer to production.
Driven by services
With the rise of microservices, deployments tend to include more than one service. If the pipeline is used for service orchestration, several services in parallel (or sequentially) need to be deployed. These pipelines tend to be used for the orchestration of several services ensuring uniformity across their deployments.
Driven by outcome
Eventually, the feature has to match the expectation. Pipelines that focus on outcomes tend to have longer final stages and the ability not to end when the deployment is over. An outcome can mean ensuring SLAs/SLOs/SLIs, and if breached, the pipeline is a conduit to restore MTTR. Regression, or a change in outcome, can happen hours or days after a deployment.
Driven by people
Before there were pipelines, people were highly involved with progressing deployments. If the deployment process is still fairly manual with lots of approval gates (ie: driven by people), these pipelines tend to be long-running and used to keep track of human workflow. Modern pipelines try to avoid this anti-pattern.
CI/CD pipeline elements
Typical building blocks in CI/CD pipelines encompass the gamut from source code to being deployed into production.
Source code needs to be built and packaged for potential deployment. There are many different continuous integration tools available to automate this phase of the CI/CD pipeline. Being language-dependent, the build tools of languages being used in the deployable units need to be called and executed. If leveraging JAVA, for example, calling Maven or Gradle is needed to build a JAVA distribution. The automated build elements can also be part of a packaging step. Taking the JAVA example a step further, if needing to build a Docker Image of the JAVA app, calling the required Docker Compose steps. Build-centric tests can be run in the build elements such as unit tests and dependency scanning.
Modern generations of CI/CD pipelines are infrastructure-aware. Compared to pipelines of the past where infrastructure was waiting ahead of an application deployment, with the rise of infrastructure-as-code, now the infrastructure is provisioned during pipeline execution. The success or failure of the infrastructure provisioning are gates for the progression of the CI/CD pipeline. As an artifact progresses through environments, infrastructure provisioning, such as executing a Terraform Script or Cloud Provider Script, is used to ready the next environment(s).
A major goal of most pipelines is to instill confidence. The textbook approach to instill confidence in software is to run tests. Test elements come in many shapes and forms. As test methodologies evolve, CI/CD pipelines are natural places to execute the tests as quality gates. Above and beyond build-centric tests, tests that require application in its entirety, such as integration tests, soak tests, load tests, and regression tests, are natural fits. Modern testing approaches, such as Chaos Engineering, can extend to infrastructure levels as well.
The act of the actual deployments that helps achieve Continuous Deployment is release elements. Need to deploy in a rolling, blue-green, or canary fashion? Release elements in your CI/CD pipeline will take care of that orchestration.
A rolling deployment is a release strategy where running instances are updated in a sequence. To expand on that, the old application version is brought down, and then a new version is brought up in its place until all nodes in the sequence are replaced.
A blue-green deployment is a release strategy designed for safety. With two parallel versions of production running, the new release (blue) will replace the stable version (green) via a load balancer that keeps the stable version running until it is deemed safe to repurpose or decommission it. Implementing blue-green deployments makes rollbacks much easier. Though, on the flip side, the infrastructure required (two copies of production) can be costly to provision and run.
A canary deployment is an incremental release strategy where the new change (the canary) is incrementally rolled out, eventually replacing the stable version. Canary deployments are run in multiple phases. For example, the first phase might swap 10 percent of the nodes, and upon success, it increases to 50 percent of the nodes, and then finally, 100 percent of the nodes. The main reasons to implement canary deployments are the safety they provide during a release, and also using fewer resources than a blue-green deployment. On the flip side, canary deployments can be complex due to the validation needed to promote canaries.
Validation elements can be used as decision points to progress through the pipeline, continue to promote artifacts, and monitor deployments after they have occurred. There is a wide swatch of monitoring and observability tools that are best of breed for their ability to look for regression. Modern CI/CD pipelines can make decisions from multiple markers, including these monitoring and observability tools, and decide if a progression or rollback should occur. Modern CI/CD pipelines are also able to systematically validate the health of what is – or has been – deployed. Testing and validation should not end with a deployment, which is key for continuous testing goals.
Characteristics of good CI/CD pipelines
Good pipelines are fast and repeatable. Great pipelines are fast, safe, and repeatable.
The book Accelerate states that elite performers have a lead time of less than 1 hour, and a change failure rate of less than 15 percent for production deployments. Therefore, a great pipeline will complete in under an hour and catch 95 percent of anomalies and regressions, before code reaches an end-user.
If your code takes longer than an hour to reach production, or if more than 2 out of 10 deployments fail, you might want to reconsider your CI/CD pipeline design and strategy.
CI/CD pipeline examples
Putting all of the pipeline elements together can be challenging, especially when crossing disparate skill sets and teams. Modern CI/CD tools allow for modeling pipelines in a declarative format, making it possible to quickly stitch together all required elements, resulting in flexibility and robustness in the pipeline.
Distributed applications rarely have one piece of infrastructure that you need to deploy to. For example, if your application has serverless pieces, longer running workloads might be deployed to Kubernetes and the serverless portions deployed to a cloud provider, for example, AWS Lambda. By leveraging a CI/CD pipeline, you have the ability to maintain context and configuration across the pipeline driving towards the goal of deploying to multiple pieces of infrastructure across stages. The below example shows how this pipeline might look.
Validation elements are challenging to include in CI/CD pipelines because they usually require some sort of decision to be codified into the pipeline. They are important pieces to include with goals marching towards automation. In a canary deployment scenario, automating the promotion of the canary to the next phase is crucial. Automation should cover the promotion and demotion [rollback] of the canary. The below pipeline shows the relationship between validation and canary promotion.
How automated CI/CD pipelines help developer teams
In modern organizations, the CI/CD pipeline is the conduit to get the developer’s code into production. Software engineering is an iterative exercise, and by having automated CI/CD pipelines, engineers are able to execute the pipelines without human intervention. Imagine every time you had to run a test suite or prepare the infrastructure for the next environment, an external team had to be coordinated with. This would cause less iteration and prolong the confidence-building steps to not look silly in front of your peers.
By allowing for self-service, developers can learn what rigor their changes need to go through more quickly and make adjustments. A major goal of DevOps teams is to allow for greater learnability and usability of pipelines. Certain pipelines that are driven by environments might let developers execute all the way through pre-prod, then require another pipeline to reach production environments. This would allow for lots of iteration until the development team gets comfortable with the pipeline. Fully automated CI/CD pipelines would take that step further and fully automate into production.
Ravi Lachhman, evangelist, Harness