MFormations
Modern DevOps Engineering

Chapitre 4

04 — CI/CD (Continuous Integration & Continuous Delivery)

04 — CI/CD (Continuous Integration & Continuous Delivery)

Course: CI/CD

1. CI/CD Fundamentals

1.1 What is CI/CD?

  • Continuous Integration (CI): Automatically build, test, and validate every code change merged to the main branch.
  • Continuous Delivery (CD): Automatically deploy validated changes to production or staging.
  • Continuous Deployment: Every change that passes CI is automatically deployed to production.

1.2 Pipeline Stages

Code → Build → Unit Test → Static Analysis → Security Scan → Package → Deploy → E2E Test

2. GitHub Actions

2.1 Workflow Structure

# .github/workflows/ci.yml
name: CI
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

env:
  NODE_VERSION: "20"

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20, 22]
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
    - run: npm ci
    - run: npm test
    - run: npm run lint

  docker:
    needs: [test]
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Build and push
      uses: docker/build-push-action@v5
      with:
        push: true
        tags: |
          ghcr.io/${{ github.repository }}:${{ github.sha }}
          ghcr.io/${{ github.repository }}:latest

2.2 Self-Hosted Runners

# Register a self-hosted runner
# On the runner machine:
./config.sh --url https://github.com/org/repo --token XXX

# In workflow:
jobs:
  build:
    runs-on: [self-hosted, linux, x64, gpu]

2.3 Composite Actions

# .github/actions/deploy/action.yml
name: "Deploy"
description: "Deploy to Kubernetes"
inputs:
  environment:
    description: "Target environment"
    required: true
  image-tag:
    description: "Image tag to deploy"
    required: true
runs:
  using: "composite"
  steps:
  - uses: azure/setup-kubectl@v3
  - run: |
      kubectl set image deployment/${{ inputs.environment }} \
        app=myapp:${{ inputs.image-tag }}
    shell: bash

3. GitLab CI

3.1 Pipeline Example

# .gitlab-ci.yml
stages:
  - test
  - build
  - deploy

variables:
  DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA

image: node:20-alpine

cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - node_modules/

unit-test:
  stage: test
  script:
    - npm ci
    - npm test
    - npm run lint
  coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'

build:
  stage: build
  image: docker:24
  services:
    - docker:dind
  script:
    - docker build -t $DOCKER_IMAGE .
    - docker push $DOCKER_IMAGE

deploy-staging:
  stage: deploy
  script:
    - kubectl set image deployment/myapp app=$DOCKER_IMAGE
  environment:
    name: staging
  only:
    - develop

deploy-production:
  stage: deploy
  script:
    - kubectl set image deployment/myapp app=$DOCKER_IMAGE
  environment:
    name: production
  when: manual
  only:
    - main

4. ArgoCD

4.1 Sync Policies

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
spec:
  syncPolicy:
    automated:
      prune: true       # Remove resources not in Git
      selfHeal: true    # Auto-fix drift
      allowEmpty: false
    syncOptions:
    - CreateNamespace=true
    - PruneLast=true
    - ApplyOutOfSyncOnly=true
    retry:
      limit: 3
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

4.2 Sync Hooks

apiVersion: batch/v1
kind: Job
metadata:
  name: db-migration
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
      - name: migration
        image: myapp:latest
        command: ["./run_migrations.sh"]
      restartPolicy: Never

4.3 Sync Waves

# Wave 0: Infrastructure
apiVersion: v1
kind: Namespace
metadata:
  annotations:
    argocd.argoproj.io/sync-wave: "0"
---
# Wave 1: Configuration
apiVersion: v1
kind: ConfigMap
metadata:
  annotations:
    argocd.argoproj.io/sync-wave: "1"
---
# Wave 2: Application
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    argocd.argoproj.io/sync-wave: "2"

5. Jenkins

5.1 Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any

    tools {
        nodejs 'node-20'
        docker 'docker-latest'
    }

    environment {
        DOCKER_IMAGE = "myapp:${env.BUILD_NUMBER}"
    }

    stages {
        stage('Checkout') {
            steps {
                checkout scmGit(
                    branches: [[name: '*/main']],
                    userRemoteConfigs: [[url: 'https://github.com/org/repo.git']]
                )
            }
        }
        stage('Test') {
            parallel {
                stage('Unit') {
                    steps { sh 'npm test' }
                }
                stage('Lint') {
                    steps { sh 'npm run lint' }
                }
                stage('Security') {
                    steps {
                        sh 'trivy fs --severity HIGH,CRITICAL .'
                    }
                }
            }
        }
        stage('Build') {
            steps {
                sh 'docker build -t ${DOCKER_IMAGE} .'
            }
        }
        stage('Deploy') {
            when {
                branch 'main'
            }
            steps {
                sh 'kubectl set image deployment/myapp app=${DOCKER_IMAGE}'
            }
        }
    }
    post {
        always {
            cleanWs()
        }
        success {
            emailext(
                subject: "Build ${env.BUILD_NUMBER} succeeded",
                to: 'team@example.com'
            )
        }
    }
}

6. Security Scanning

6.1 Trivy

# Scan filesystem
trivy fs --severity HIGH,CRITICAL .

# Scan container image
trivy image myapp:latest

# Scan for IaC misconfigurations
trivy config --severity HIGH,CRITICAL ./terraform

# Generate SARIF report
trivy fs --format sarif --output results.sarif .

6.2 Snyk

# Test application dependencies
snyk test --all-projects

# Monitor for continuous monitoring
snyk monitor

# Test container image
snyk container test myapp:latest

# IaC scan
snyk iac test terraform/

6.3 GitHub Actions Security

- name: Snyk Security Scan
  uses: snyk/actions/node@master
  env:
    SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
  with:
    args: --severity-threshold=high

- name: Trivy Scan
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: 'ghcr.io/org/repo:${{ github.sha }}'
    severity: 'HIGH,CRITICAL'

- name: Dependency Review
  uses: actions/dependency-review-action@v3

- name: CodeQL Analysis
  uses: github/codeql-action/analyze@v3

7. Artifact Management

7.1 Tools

ToolTypeFeatures
Docker RegistryContainerOCI images
Nexus RepositoryUniversalMaven, npm, Docker, PyPI
ArtifactoryUniversalAll package types, K8s Helm
GitHub PackagesIntegratednpm, Docker, Maven
GitLab Container RegistryIntegratedDocker images

8. Semantic Release

8.1 Conventional Commits

feat: add user authentication
       ^-- type: feat, fix, chore, docs, refactor, test

BREAKING CHANGE: remove deprecated v1 API
       ^-- triggers major version bump

fix(api): handle null pointer in user query
    ^-- scope is optional

8.2 Release Rules

{
  "branches": ["main"],
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    "@semantic-release/changelog",
    "@semantic-release/github",
    ["@semantic-release/npm", { "npmPublish": false }]
  ]
}

8.3 Version Calculation

Commitbump
fix:patch (1.0.0 → 1.0.1)
feat:minor (1.0.0 → 1.1.0)
BREAKING CHANGEmajor (1.0.0 → 2.0.0)

Summary

CI/CD is the engine of DevOps. GitHub Actions provides deep GitHub integration, GitLab CI is a complete built-in solution, ArgoCD enables GitOps deployment, and Jenkins offers enterprise maturity. Security scanning (Trivy, Snyk) must be integrated early. Semantic release automates versioning based on conventional commits.