CI/CD (Continuous Integration / Continuous Deployment) est le moteur des équipes DevOps modernes. C’est la différence entre :

  • ❌ Déployer manuellement (risqué, lent, sujet aux erreurs)
  • ✅ Pousser du code → tests automatiques → build → déploiement en production

GitHub Actions fait de cela une réalité simple et puissante. Combiné avec Azure, vous obtenez une pipeline de déploiement indestructible et scalable.

Ce guide vous montrera comment construire une pipeline CI/CD end-to-end capable de :

  • Tester automatiquement votre code
  • Builder des images Docker
  • Les pousser vers Azure Container Registry
  • Déployer sur Kubernetes ou App Service
  • Monitorer et alerter
  • Rollback en cas de problème

Partie 1 : Concepts Fondamentaux du CI/CD

1.1 Qu’est-ce que le CI/CD ?

CI (Continuous Integration) : À chaque commit, vous :

  • Testez automatiquement
  • Vérifiez la qualité du code
  • Détectez les bugs tôt

CD (Continuous Deployment) : Le code validé est :

  • Automatiquement déployé en production
  • Ou prêt à être déployé (Continuous Delivery)

Pipeline simple :

Developer Push Code
GitHub Actions Triggered
Run Tests
Build Docker Image
Push to Registry
Deploy to Kubernetes
Run Health Checks
Monitor & Alert

1.2 Avantages du CI/CD

BénéficePourquoi
VitesseDéploiements en minutes, pas en semaines
FiabilitéTests automatiques = moins de bugs
ConfianceRollbacks faciles si problème
ProductivitéLes devs se concentrent sur le code
Feedback rapideErreurs détectées immédiatement
ScalabilitéMême processus pour 1 ou 1000 déploiements

1.3 Acteurs Clés

GitHub : Votre repo + GitHub Actions (orchestrateur)
Docker : Conteneurisation de votre app
Azure : Infrastructure cloud (registries, clusters, app services)
Kubectl : Déploiement sur Kubernetes

Partie 2 : GitHub Actions en Profondeur

2.1 Structure d’une Workflow GitHub Actions

Une workflow est un fichier YAML dans .github/workflows/

name: CI/CD Pipeline # Nom de la workflow
on: # Quand déclencher
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs: # Tâches à exécuter
test:
runs-on: ubuntu-latest # Machine virtuelle
steps: # Étapes
- uses: actions/checkout@v3 # Action réutilisable
- name: Run Tests
run: npm test # Commande shell

2.2 Composants Clés

Events : Ce qui déclenche la workflow

on:
push: # Au commit
pull_request: # Sur PR
schedule: # Cron job (tests quotidiens)
- cron: '0 0 * * *'
workflow_dispatch: # Déclenchement manuel

Jobs : Tâches parallèles ou séquentielles

jobs:
test: # Job 1
runs-on: ubuntu-latest
steps: [...]
build: # Job 2 (peut dépendre du job 1)
needs: test # S'exécute après 'test'
runs-on: ubuntu-latest
steps: [...]

Steps : Actions individuelles

steps:
- uses: actions/checkout@v3 # Action marketplace
- name: Build
run: npm run build # Commande shell
env:
NODE_ENV: production # Variables d'env
if: success() # Condition

2.3 Actions Réutilisables Essentielles

# Checkout du code
- uses: actions/checkout@v3
# Setup Node.js
- uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
# Setup Docker
- uses: docker/setup-buildx-action@v2
# Login Azure
- uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
# Deploy sur Kubernetes
- uses: azure/aks-set-context@v3
with:
resource-group: myResourceGroup
cluster-name: myAKSCluster

2.4 Secrets et Variables

Secrets (sensibles) :

- name: Push Image
run: |
docker login -u ${{ secrets.REGISTRY_USERNAME }} \
-p ${{ secrets.REGISTRY_PASSWORD }}

Configurez les secrets dans Settings → Secrets and variables → Actions

Variables d’environnement :

env:
REGISTRY: myregistry.azurecr.io
IMAGE_NAME: myapp

Partie 3 : Intégration Azure

3.1 Authentification avec Azure

Créer une identité de service :

az ad sp create-for-rbac \
--name "github-actions" \
--role contributor \
--scopes /subscriptions/{subscription-id}

Vous recevrez un JSON. Stockez-le comme secret AZURE_CREDENTIALS

Utiliser dans la workflow :

- uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}

3.2 Intégration avec Azure Container Registry (ACR)

- name: Push to ACR
run: |
az acr build \
--registry myregistry \
--image myapp:${{ github.sha }} \
--image myapp:latest \
--file Dockerfile .

3.3 Intégration avec Azure App Service

- uses: azure/webapps-deploy@v2
with:
app-name: 'my-app'
images: 'myregistry.azurecr.io/myapp:${{ github.sha }}'

3.4 Intégration avec Azure Kubernetes Service (AKS)

- uses: azure/aks-set-context@v3
with:
resource-group: 'myResourceGroup'
cluster-name: 'myAKSCluster'
- uses: azure/k8s-deploy@v4
with:
manifests: |
k8s/deployment.yaml
k8s/service.yaml
images: |
myregistry.azurecr.io/myapp:${{ github.sha }}
imagepullsecrets: |
[{"name": "regcred"}]

Partie 4 : Workflow Complète pour Node.js

4.1 Dockerfile Optimisé

# Stage 1 : Build
FROM node:18-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# Stage 2 : Runtime
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY src ./src
COPY package.json .
EXPOSE 3000
CMD ["node", "src/app.js"]

4.2 Workflow CI/CD Node.js – Partie 1 (Tests)

name: CI/CD Pipeline - Node.js
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
REGISTRY: ${{ secrets.REGISTRY_LOGIN_SERVER }}
IMAGE_NAME: myapp
NODE_VERSION: '18'
jobs:
# Job 1 : Tests et Linting
test:
name: Test & Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test -- --coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info

4.3 Workflow CI/CD Node.js – Partie 2 (Build Docker)

  # Job 2 : Build et Push Image Docker
  build:
    name: Build & Push Docker Image
    needs: test
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    
    outputs:
      image: ${{ steps.image.outputs.image }}
    
    steps:
      - uses: actions/checkout@v3
      
      - uses: azure/docker-login@v1
        with:
          login-server: ${{ env.REGISTRY }}
          username: ${{ secrets.REGISTRY_USERNAME }}
          password: ${{ secrets.REGISTRY_PASSWORD }}
      
      - uses: docker/setup-buildx-action@v2
      
      - uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
          cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache
          cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max
      
      - id: image
        run: echo "image=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}" >> $GITHUB_OUTPUT

4.4 Workflow CI/CD Node.js – Partie 3 (Deploy)

  # Job 3 : Déployer sur AKS
  deploy:
    name: Deploy to AKS
    needs: build
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    
    steps:
      - uses: actions/checkout@v3
      
      - uses: azure/login@v1
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}
      
      - uses: azure/aks-set-context@v3
        with:
          resource-group: 'myResourceGroup'
          cluster-name: 'myAKSCluster'
      
      - name: Update deployment image
        run: |
          kubectl set image deployment/my-app \
            my-app=${{ needs.build.outputs.image }} \
            -n production
      
      - name: Verify deployment
        run: |
          kubectl rollout status deployment/my-app -n production
      
      - name: Run smoke tests
        run: |
          SERVICE_IP=$(kubectl get svc my-app-service -n production -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
          curl -f http://$SERVICE_IP/health || exit 1

4.5 Notifications Slack

  # Job 4 : Notify & Monitor
  notify:
    name: Notify on Failure
    runs-on: ubuntu-latest
    if: failure()
    needs: [test, build, deploy]
    
    steps:
      - name: Send Slack notification
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "❌ CI/CD Pipeline Failed",
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "*Pipeline Failed* for ${{ github.repository }}\n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
                  }
                }
              ]
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
          SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK

Partie 5 : Patterns Avancés

5.1 Matrix Strategy (Tester plusieurs versions)

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20]
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm test

5.2 Conditional Steps

- name: Deploy to production
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: kubectl apply -f k8s/prod/

5.3 Artifacts et Caching

- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: build-${{ github.sha }}
path: dist/
- name: Cache Docker layers
uses: actions/cache@v3
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}

5.4 Approvals et Environments

deploy-production:
environment:
name: production
url: https://prod.example.com
steps:
- run: kubectl apply -f k8s/prod/

Configure les “Required reviewers” dans Settings → Environments

Partie 6 : Sécurité dans CI/CD

6.1 Secrets Management

✅ À FAIRE :

- uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }} # Pas en clair!

❌ À NE PAS FAIRE :

- run: echo "password=mypassword123" # DANGER!

6.2 Scanning de Sécurité

- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myregistry.azurecr.io/myapp:latest'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'

6.3 Signing Images Docker

- name: Sign image
run: |
cosign sign --key ${{ secrets.COSIGN_KEY }} \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}

Partie 7 : Monitoring et Rollback

7.1 Health Checks après Déploiement

- name: Wait for deployment
run: kubectl rollout status deployment/myapp -n production
- name: Run smoke tests
run: |
for i in {1..10}; do
if curl -f http://app.example.com/health; then
echo "Health check passed"
exit 0
fi
sleep 5
done
exit 1

7.2 Rollback Automatique

- name: Verify metrics
run: |
ERROR_RATE=$(curl -s "http://prometheus:9090/api/v1/query?query=rate(errors_total[5m])" | jq '.data.result[0].value[1]')
if (( $(echo "$ERROR_RATE > 0.05" | bc -l) )); then
echo "High error rate detected, rolling back..."
kubectl rollout undo deployment/myapp -n production
exit 1
fi

7.3 Notifications Slack/Teams

- name: Notify success
if: success()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
text: '✅ Deployment to production succeeded!'

Partie 8 : Troubleshooting Courant

Problème 1 : “Permission denied” avec Docker

Cause : Credentials invalides\

Solution :

az acr login --name myregistry
docker login myregistry.azurecr.io

Problème 2 : Kubectl timeout

Cause : Contexte mal configuré

- uses: azure/aks-set-context@v3
with:
resource-group: 'myResourceGroup'
cluster-name: 'myAKSCluster'

Problème 3 : “Image not found”

Cause : Image non pushée au registre

Solution : Vérifier les logs du job “build”

Problème 4 : Workflow bloquée

Cause : Environment approval requise

Solution : Approuver dans Settings → Environments

Checklist : Prêt pour la Production

  • ☑ Tests automatisés passent
  • ☑ Linter et security checks
  • ☑ Image Docker scannée
  • ☑ Secrets configurés (pas en clair)
  • ☑ Health checks après déploiement
  • ☑ Notifications Slack configurées
  • ☑ Rollback plan en place
  • ☑ Monitoring en temps réel
  • ☑ Permissions minimales définies
  • ☑ Approvals pour production
  • ☑ Logs centralisés (Application Insights)
  • ☑ Disaster recovery testé

Ressources Supplémentaires

Documentation Officielle :

Exemples de Workflows :

Outils Complémentaires :

  • ArgoCD pour GitOps
  • Kyverno pour K8s policies
  • Prometheus + Grafana pour monitoring

Conclusion

Vous avez maintenant une pipeline CI/CD professionnelle capable de :

  • ✅ Tester chaque commit automatiquement
  • ✅ Builder et pousser des images Docker
  • ✅ Déployer sans downtime
  • ✅ Monitorer et alerter
  • ✅ Rollbacker en 1 click

Article rédigé par RiadhMNASRI pour techpassionsharing.com
Maîtrisez l’automatisation : de développeur à DevOps engineer

Leave a comment