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.
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/
Test Your Knowledge
Answer these questions to check your understanding