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éfice | Pourquoi |
|---|---|
| Vitesse | Déploiements en minutes, pas en semaines |
| Fiabilité | Tests automatiques = moins de bugs |
| Confiance | Rollbacks faciles si problème |
| Productivité | Les devs se concentrent sur le code |
| Feedback rapide | Erreurs 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 workflowon: # 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 : BuildFROM node:18-alpine as builderWORKDIR /appCOPY package*.json ./RUN npm ci --only=production# Stage 2 : RuntimeFROM node:18-alpineWORKDIR /appCOPY --from=builder /app/node_modules ./node_modulesCOPY src ./srcCOPY package.json .EXPOSE 3000CMD ["node", "src/app.js"]
4.2 Workflow CI/CD Node.js – Partie 1 (Tests)
name: CI/CD Pipeline - Node.json: 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 myregistrydocker 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 :
- GitHub Actions : https://docs.github.com/en/actions
- Azure Pipelines vs GitHub Actions : Comparer les deux
- Dockerfile best practices : https://docs.docker.com/develop/dev-best-practices/
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