CI/CD Pipelines

Learn to build robust CI/CD pipelines for automated testing and deployment. Master popular tools like Jenkins, GitHub Actions, and GitLab CI.

What is CI/CD?

CI/CD stands for Continuous Integration and Continuous Delivery/Deployment. It is a set of practices that enable development teams to deliver code changes more frequently and reliably. CI/CD is one of the cornerstone practices of DevOps, automating the software delivery process from code commit to production deployment.

CI/CD pipelines automate building, testing, and deploying applications, reducing manual effort and human error while increasing deployment frequency. Organizations practicing CI/CD can release software multiple times per day with confidence, compared to traditional release cycles of weeks or months.

Continuous Integration (CI)

Continuous Integration is the practice of merging all developers' working copies to a shared mainline several times a day. Each integration triggers an automated build and test process to detect integration errors as quickly as possible. The key principles of CI include:

Core CI Practices

  • Frequent Commits - Developers commit code at least daily, preferably multiple times per day
  • Automated Builds - Every commit triggers an automated build process
  • Automated Tests - Unit tests, integration tests, and other automated tests run on every build
  • Fast Feedback - Developers receive immediate notification if their changes break the build
  • Fix Broken Builds Immediately - A broken build takes priority over new development
  • Keep the Build Fast - Aim for builds under 10 minutes

Benefits of CI

  • Detect integration issues early when they are easier to fix
  • Reduce integration problems that delay releases
  • Improve code quality through automated testing
  • Increase developer productivity
  • Provide visibility into build and test status

Continuous Delivery vs Continuous Deployment

Continuous Delivery

Continuous Delivery ensures that code is always in a deployable state. Every change that passes all stages of the production pipeline is ready to be released to customers. However, the final deployment to production requires manual approval. This approach works well when:

  • Regulatory compliance requires approval before deployment
  • Releases need coordination with marketing or sales
  • The team is building confidence in their pipeline

Continuous Deployment

Continuous Deployment goes one step further: every change that passes all stages of the production pipeline is automatically released to customers. There is no human intervention. This approach requires:

  • High confidence in automated testing
  • Robust monitoring and alerting
  • Ability to quickly rollback or fix issues
  • Feature flags for controlling feature visibility

CI/CD Pipeline Stages

A CI/CD pipeline consists of multiple stages that code passes through on its way to production. Each stage serves a specific purpose and provides a checkpoint for code quality.

1. Source Stage

The pipeline begins when code is committed to the source repository. Common triggers include:

  • Push to main/develop branch
  • Pull request opened or updated
  • Scheduled builds (nightly builds)
  • Manual trigger

2. Build Stage

The build stage compiles source code and creates deployable artifacts:

  • Compile source code
  • Run static code analysis (linting)
  • Resolve dependencies
  • Generate documentation
  • Create build artifacts (JARs, binaries, etc.)

3. Test Stage

Automated testing validates that code works as expected:

  • Unit Tests - Test individual functions or methods
  • Integration Tests - Test interactions between components
  • End-to-End Tests - Test complete user workflows
  • Performance Tests - Validate system performance under load
  • Security Tests - Identify vulnerabilities (SAST, DAST)

4. Security and Quality Analysis

Additional quality gates ensure code meets standards:

  • Code coverage analysis
  • Static Application Security Testing (SAST)
  • Dependency vulnerability scanning
  • Code quality metrics (SonarQube)
  • License compliance checks

5. Package Stage

Create deployable artifacts ready for deployment:

  • Build Docker container images
  • Tag images with version numbers
  • Push to container registry
  • Generate deployment manifests
  • Store artifacts in repository (Nexus, Artifactory)

6. Deploy Stage

Deploy artifacts to target environments:

  • Deploy to development environment
  • Deploy to staging/pre-production
  • Run smoke tests and acceptance tests
  • Deploy to production (with appropriate strategy)

7. Monitor Stage

Post-deployment monitoring ensures application health:

  • Monitor application metrics
  • Track error rates
  • Alert on anomalies
  • Collect user feedback

CI/CD Tools

Jenkins

Jenkins is the most widely used open-source automation server. It supports building, deploying, and automating any project through a vast plugin ecosystem.

Jenkins Pipeline Example

pipeline {
    agent any
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }
        
        stage('Build') {
            steps {
                sh 'npm install'
                sh 'npm run build'
            }
        }
        
        stage('Test') {
            steps {
                sh 'npm test'
            }
            post {
                always {
                    junit 'test-results/*.xml'
                }
            }
        }
        
        stage('Build Docker Image') {
            steps {
                script {
                    docker.build("myapp:${BUILD_NUMBER}")
                }
            }
        }
        
        stage('Deploy to Staging') {
            steps {
                sh 'kubectl apply -f k8s/staging/'
            }
        }
        
        stage('Deploy to Production') {
            when {
                branch 'main'
            }
            steps {
                input 'Deploy to production?'
                sh 'kubectl apply -f k8s/production/'
            }
        }
    }
    
    post {
        failure {
            slackSend channel: '#deployments',
                      message: "Build failed: ${env.BUILD_URL}"
        }
    }
}

GitHub Actions

GitHub Actions is a CI/CD platform built into GitHub. It uses YAML workflow files stored in the .github/workflows directory. Actions are highly composable, with thousands of pre-built actions available in the GitHub Marketplace.

GitHub Actions Workflow Example

name: CI/CD Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'
      
      - name: Install dependencies
        run: pip install -r requirements.txt
      
      - name: Run linting
        run: flake8 src/
      
      - name: Run tests
        run: pytest tests/ -v --cov=src --cov-report=xml
      
      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          files: coverage.xml

  build:
    needs: test
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      
      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}

  deploy-staging:
    needs: build
    runs-on: ubuntu-latest
    environment: staging
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up kubectl
        uses: azure/setup-kubectl@v3
      
      - name: Deploy to staging
        run: |
          kubectl set image deployment/myapp             myapp=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}

  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment: production
    if: github.ref == 'refs/heads/main'
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Deploy to production
        run: |
          kubectl set image deployment/myapp             myapp=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}

GitLab CI/CD

GitLab CI/CD is built into GitLab and configured using a .gitlab-ci.yml file. It provides integrated DevOps features including container registry, environments, and review apps.

GitLab CI/CD Configuration Example

stages:
  - test
  - build
  - deploy

variables:
  DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

test:
  stage: test
  image: python:3.11
  script:
    - pip install -r requirements.txt
    - pytest tests/ --cov=src
  coverage: '/TOTAL.*\s+(\d+%)/'

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t $DOCKER_IMAGE .
    - docker push $DOCKER_IMAGE
  only:
    - main
    - develop

deploy-staging:
  stage: deploy
  environment:
    name: staging
    url: https://staging.example.com
  script:
    - kubectl set image deployment/myapp myapp=$DOCKER_IMAGE
  only:
    - develop

deploy-production:
  stage: deploy
  environment:
    name: production
    url: https://example.com
  script:
    - kubectl set image deployment/myapp myapp=$DOCKER_IMAGE
  when: manual
  only:
    - main

Deployment Strategies

Rolling Deployment

Rolling deployment gradually replaces instances of the old version with the new version. If there are problems, the rollout can be paused or reversed. Kubernetes uses rolling deployment by default.

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 0

Blue-Green Deployment

Blue-green deployment maintains two identical production environments. At any time, only one environment is live. When deploying, the new version is deployed to the idle environment, tested, and then traffic is switched.

  • Zero downtime deployments
  • Easy rollback by switching back
  • Requires double the infrastructure

Canary Deployment

Canary deployment releases the new version to a small subset of users first. If metrics look good, the rollout continues to more users. If problems are detected, the canary is rolled back.

# Route 10% of traffic to canary
spec:
  trafficPolicy:
    canary:
      weight: 10

Feature Flags

Feature flags decouple deployment from release. Code is deployed but features are hidden behind flags that can be toggled on/off without redeploying. This enables:

  • Gradual rollout to users
  • A/B testing
  • Emergency kill switches
  • Preview features for beta users

Testing in CI/CD

Test Pyramid

The test pyramid describes the ideal distribution of tests:

  • Unit Tests (Base) - Many fast, isolated tests
  • Integration Tests (Middle) - Fewer tests of component interactions
  • End-to-End Tests (Top) - Few slow tests of complete workflows

Testing Best Practices

  • Run tests in parallel to reduce execution time
  • Use test containers for integration tests
  • Mock external services
  • Maintain test coverage above acceptable threshold
  • Run security scans on every build

Secrets Management

Managing secrets (API keys, passwords, certificates) securely is critical for CI/CD:

  • Never commit secrets to version control
  • Use CI/CD platform secret management features
  • Rotate secrets regularly
  • Use tools like HashiCorp Vault for enterprise secret management
  • Inject secrets at runtime, not build time

CI/CD Best Practices

Pipeline Design

  • Keep pipelines fast (under 10 minutes if possible)
  • Run tests in parallel
  • Fail fast: stop pipeline on first failure
  • Cache dependencies to speed up builds
  • Use incremental builds when possible

Infrastructure

  • Use ephemeral build environments
  • Practice infrastructure as code
  • Make environments identical to production
  • Use containers for consistency

Monitoring and Observability

  • Monitor pipeline metrics (duration, success rate)
  • Alert on pipeline failures
  • Track deployment frequency and lead time
  • Monitor change failure rate and recovery time

Security (DevSecOps)

  • Integrate security scanning in pipelines
  • Scan dependencies for vulnerabilities
  • Run container image scans
  • Implement least-privilege access
  • Audit pipeline access and changes

GitOps

GitOps is a paradigm where Git is the single source of truth for declarative infrastructure and applications. Changes are made via Git commits, and operators automatically reconcile the cluster state with the Git repository.

GitOps Principles

  • Declarative configuration in Git
  • Automatic synchronization to cluster
  • Controllers pull changes (not push)
  • Drift detection and correction

GitOps Tools

  • ArgoCD - Declarative GitOps for Kubernetes
  • Flux - GitOps toolkit for Kubernetes

Conclusion

CI/CD is essential for modern software delivery. By automating build, test, and deployment processes, teams can deliver software faster, more reliably, and with higher quality. Start with basic CI pipelines and gradually add stages as your confidence and requirements grow. Remember that CI/CD is not just about tools but also about culture, practices, and continuous improvement.

đŸ’ģ GitHub Actions CI/CD Workflow
name: CI/CD Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      - name: Install dependencies
        run: pip install -r requirements.txt
      - name: Run tests
        run: pytest tests/ -v --cov=src
      
  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build Docker image
        run: docker build -t myapp:${{ github.sha }} .
      - name: Push to registry
        run: |
          docker tag myapp:${{ github.sha }} registry/myapp:latest
          docker push registry/myapp:latest

  deploy:
    needs: build
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to production
        run: kubectl apply -f k8s/
Output
✓ test (2m 15s) ✓ Set up Python ✓ Install dependencies ✓ Run tests - 45 passed ✓ build (1m 30s) ✓ Build Docker image ✓ Push to registry ✓ deploy (45s) ✓ Deploy to production Pipeline completed successfully!
💡 This GitHub Actions workflow demonstrates a complete CI/CD pipeline with test, build, and deploy stages. The deploy stage only runs on the main branch.
đŸŽ¯

Test Your Knowledge

Answer these questions to check your understanding

1 What is the main difference between Continuous Delivery and Continuous Deployment?
💡 Continuous Delivery requires manual approval for production deployment, while Continuous Deployment automatically deploys every change that passes all tests.