Introduction : Pourquoi le CI/CD est essentiel pour votre PME
Le CI/CD (Continuous Integration/Continuous Deployment) permet d’automatiser les tests et les déploiements de vos applications. Pour une PME, ça signifie moins d’erreurs humaines, des mises en production plus rapides et une équipe qui peut se concentrer sur le développement plutôt que sur les tâches répétitives.
GitHub Actions et Docker forment le duo parfait pour les équipes IT réduites : GitHub Actions offre 2000 minutes gratuites par mois pour les repositories privés, et Docker garantit que votre application tourne de la même façon partout.
Ce tutoriel s’adresse aux développeurs, CTO et responsables techniques de PME qui veulent industrialiser leurs déploiements sans investir dans des outils complexes. On va construire ensemble un pipeline complet qui teste, build et déploie automatiquement votre application à chaque push sur la branche principale.
Prérequis techniques et configuration initiale
Avant de commencer, assurez-vous d’avoir :
- Un compte GitHub avec un repository contenant votre code source
- Docker et Docker Compose installés sur votre machine locale
- Des connaissances de base en Git et ligne de commande
- Un serveur de déploiement accessible en SSH (VPS OVH, Coolify, ou autre)
Étape 1 : Dockeriser votre application
La première étape consiste à créer un Dockerfile optimisé. Voici un exemple pour une application Node.js avec multi-stage build :
# Stage 1: BuildFROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./RUN npm ci --only=production
COPY . .RUN npm run build
# Stage 2: ProductionFROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modulesCOPY --from=builder /app/dist ./distCOPY --from=builder /app/package.json ./
ENV NODE_ENV=productionEXPOSE 3000
CMD ["node", "dist/index.js"]Le multi-stage build réduit la taille finale de l’image en ne gardant que les fichiers nécessaires à l’exécution. Ici, on passe de ~1.2GB à ~150MB.
Créez ensuite un fichier docker-compose.yml pour l’environnement local :
version: '3.8'
services: app: build: . ports: - "3000:3000" environment: - DATABASE_URL=${DATABASE_URL} - API_KEY=${API_KEY} volumes: - ./logs:/app/logs restart: unless-stopped
postgres: image: postgres:16-alpine environment: - POSTGRES_PASSWORD=${DB_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data
volumes: postgres_data:Testez votre configuration localement :
docker-compose up --buildPour les variables d’environnement, créez un fichier .env.example avec des valeurs fictives que vous commiterez, et un .env local ignoré par Git.
Étape 2 : Créer votre premier workflow GitHub Actions
GitHub Actions utilise des fichiers YAML dans le dossier .github/workflows. Créez le fichier .github/workflows/deploy.yml :
name: CI/CD Pipeline
on: push: branches: [ main ] pull_request: branches: [ main ] workflow_dispatch:
env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }}
jobs: build: runs-on: ubuntu-latest permissions: contents: read packages: write
steps: - name: Checkout code uses: actions/checkout@v4
- name: Set up Docker Buildx uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=ref,event=branch type=sha,prefix={{branch}}-
- name: Build and push Docker image uses: docker/build-push-action@v5 with: context: . push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=maxCe workflow se déclenche sur trois événements :
- Push sur la branche
main - Pull request vers
main - Déclenchement manuel via l’interface GitHub
Le job build utilise GitHub Container Registry (gratuit et intégré) pour stocker vos images Docker. Le tag inclut le SHA du commit pour tracer chaque version.
workflow_dispatch permet de déclencher manuellement le pipeline depuis l’onglet Actions de GitHub, pratique pour les déploiements exceptionnels. Étape 3 : Automatiser les tests et la construction de l’image Docker
Ajoutons un job de tests avant le build. Modifiez votre workflow :
jobs: test: runs-on: ubuntu-latest
steps: - name: Checkout code uses: actions/checkout@v4
- name: Set up Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm'
- name: Install dependencies run: npm ci
- name: Run linter run: npm run lint
- name: Run tests run: npm test
- name: Check build run: npm run build
build: needs: test runs-on: ubuntu-latest # ... reste du job buildLe needs: test garantit que le build ne se lance que si les tests passent. La mise en cache npm (cache: 'npm') accélère les installations.
Pour les tags Docker dynamiques, la configuration docker/metadata-action génère automatiquement :
main-abc1234pour un commit sur mainpr-42pour une pull request
Le système de cache GitHub Actions (cache-from/cache-to: type=gha) réutilise les layers Docker entre les builds. Sur un projet moyen, ça réduit le temps de build de 5 minutes à 1 minute.
- name: Build and push Docker image uses: docker/build-push-action@v5 with: context: . push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=maxmode=max du cache stocke tous les layers intermédiaires, pas seulement le résultat final. C’est plus gourmand en stockage mais beaucoup plus rapide. En cas d’échec, GitHub Actions envoie automatiquement une notification par email. Vous pouvez aussi configurer Slack :
- name: Notify on failure if: failure() uses: 8398a7/action-slack@v3 with: status: ${{ job.status }} webhook_url: ${{ secrets.SLACK_WEBHOOK }}Étape 4 : Déployer automatiquement sur votre serveur
Pour le déploiement, on ajoute un job qui se connecte en SSH au serveur et redémarre les conteneurs. Ajoutez d’abord ces secrets dans les paramètres GitHub (Settings > Secrets and variables > Actions) :
SSH_HOST: IP ou domaine de votre serveurSSH_USER: utilisateur SSH (généralementrootoudeploy)SSH_KEY: clé privée SSH (générez-la avecssh-keygen -t ed25519)
Voici le job de déploiement :
deploy: needs: build runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps: - name: Deploy to production uses: appleboy/ssh-action@v1.0.0 with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USER }} key: ${{ secrets.SSH_KEY }} script: | cd /opt/app
# Login to GitHub Container Registry echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
# Pull latest image docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main-${{ github.sha }}
# Update docker-compose to use new image export IMAGE_TAG=main-${{ github.sha }}
# Zero-downtime deployment docker-compose up -d --no-deps --build app
# Cleanup old images docker image prune -af --filter "until=72h"Cette approche garantit un déploiement sans interruption : Docker Compose démarre le nouveau conteneur avant d’arrêter l’ancien.
Pour un rollback automatique en cas d’échec, ajoutez un healthcheck :
script: | cd /opt/app
# Save current version CURRENT_VERSION=$(docker-compose images -q app)
# Deploy new version docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main-${{ github.sha }} export IMAGE_TAG=main-${{ github.sha }} docker-compose up -d --no-deps app
# Wait and check health sleep 10 if ! curl -f http://localhost:3000/health; then echo "Health check failed, rolling back..." docker tag $CURRENT_VERSION app:latest docker-compose up -d --no-deps app exit 1 fi
echo "Deployment successful"/health qui retourne un statut 200 quand tout fonctionne correctement. Sur le serveur, créez un fichier docker-compose.prod.yml :
version: '3.8'
services: app: image: ghcr.io/votre-org/votre-app:${IMAGE_TAG} ports: - "3000:3000" environment: - NODE_ENV=production - DATABASE_URL=${DATABASE_URL} restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 3Conclusion et optimisations avancées
Vous avez maintenant un pipeline CI/CD complet qui teste, build et déploie automatiquement votre application. À chaque push sur main, GitHub Actions :
- Exécute les tests et le linting
- Build l’image Docker avec un tag unique
- Push l’image vers GitHub Container Registry
- Se connecte au serveur et déploie la nouvelle version
- Vérifie la santé de l’application
Les coûts sont maîtrisés : GitHub Actions offre 2000 minutes gratuites par mois pour les repositories privés (illimité pour le public). Pour une PME avec 20 déploiements par mois à 3 minutes chacun, vous restez largement dans la limite gratuite.
Dans un prochain article, on verra comment ajouter du monitoring avec Prometheus et Grafana, centraliser les logs avec Loki, et gérer des déploiements multi-environnements (staging, production) avec des workflows réutilisables.
Ressources complémentaires :