Déploiement de Docker pour l'ERP de production : un guide d'exploitation complet

Déployez des systèmes ERP avec Docker en production. Couvre les builds en plusieurs étapes, l'orchestration Docker Compose, la gestion des volumes, la mise en réseau et les stratégies de mise à l'échelle.

E
ECOSIRE Research and Development Team
|16 mars 202610 min de lecture2.2k Mots|

Docker pour le déploiement de l'ERP de production : un guide complet des opérations

Les organisations exécutant des systèmes ERP dans des conteneurs Docker signalent des cycles de déploiement 73 % plus rapides et 45 % d'incidents liés à l'environnement en moins par rapport aux déploiements nus traditionnels. Docker transforme le déploiement ERP d'un processus sur plusieurs jours et sujet aux erreurs en une opération reproductible et contrôlée par les versions que n'importe quel membre de l'équipe peut exécuter.

Ce guide couvre le cycle de vie complet de l'exécution des systèmes ERP d'entreprise --- y compris Odoo, les backends NestJS personnalisés et les frontends Next.js --- dans les environnements Docker de production.

Points clés à retenir

  • Les versions Docker en plusieurs étapes réduisent la taille des images des conteneurs ERP de 60 à 80 %, améliorant ainsi la vitesse de déploiement.
  • Docker Compose orchestre les services ERP, base de données, proxy inverse et cache comme une seule unité déployable
  • Les volumes nommés et les montages de liaison garantissent la persistance des données lors des redémarrages et des mises à niveau des conteneurs.
  • Les contrôles de santé et les politiques de redémarrage permettent une récupération automatique en cas de pannes transitoires.

Architecture d'une pile ERP Dockerisée

Un déploiement ERP de production implique généralement cinq services interconnectés ou plus. Docker Compose définit ces services de manière déclarative, garantissant un déploiement cohérent dans tous les environnements.

Topologie des services

La pile ERP Dockerisée standard :

  1. Serveur d'applications : Le runtime ERP (Odoo, NestJS ou similaire)
  2. Base de données : PostgreSQL avec stockage de volume persistant
  3. Proxy inverse : Nginx gère la terminaison SSL, les fichiers statiques et le routage des requêtes
  4. Couche de cache : Redis pour le stockage de sessions, les files d'attente de tâches et la mise en cache des applications
  5. Travailleurs d'arrière-plan : processeurs de tâches asynchrones pour les e-mails, les rapports et les intégrations

Les services optionnels incluent des conteneurs de sauvegarde (pg_dump sur cron), des side-cars de surveillance (exportateurs Prometheus) et des expéditeurs de journaux (Fluent Bit).


Constructions en plusieurs étapes pour les applications ERP

Les builds en plusieurs étapes sont essentielles pour la production d’images Docker. Ils séparent les dépendances au moment de la construction du temps d'exécution, produisant des images simples et sécurisées.

Construction du backend NestJS

# Stage 1: Install dependencies and build
FROM node:20-alpine AS builder
WORKDIR /app

# Install pnpm
RUN corepack enable

# Copy workspace configuration
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
COPY packages/ ./packages/
COPY apps/api/package.json ./apps/api/

# Install dependencies
RUN pnpm install --frozen-lockfile

# Copy source and build
COPY apps/api/ ./apps/api/
RUN pnpm --filter @ecosire/db build
RUN pnpm --filter @ecosire/types build
RUN pnpm --filter @ecosire/validators build
RUN pnpm --filter @ecosire/api build

# Stage 2: Production runtime
FROM node:20-alpine AS runner
WORKDIR /app

RUN addgroup -g 1001 -S appgroup && \
    adduser -S appuser -u 1001 -G appgroup

COPY --from=builder --chown=appuser:appgroup /app/apps/api/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/apps/api/package.json ./

USER appuser
EXPOSE 3001

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3001/health || exit 1

CMD ["node", "dist/main.js"]

Construction frontale Next.js

FROM node:20-alpine AS builder
WORKDIR /app
RUN corepack enable

COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
COPY packages/ ./packages/
COPY apps/web/package.json ./apps/web/

RUN pnpm install --frozen-lockfile

COPY apps/web/ ./apps/web/
RUN pnpm --filter @ecosire/web build

FROM node:20-alpine AS runner
WORKDIR /app

RUN addgroup -g 1001 -S appgroup && \
    adduser -S appuser -u 1001 -G appgroup

COPY --from=builder --chown=appuser:appgroup /app/apps/web/.next/standalone ./
COPY --from=builder --chown=appuser:appgroup /app/apps/web/.next/static ./.next/static
COPY --from=builder --chown=appuser:appgroup /app/apps/web/public ./public

USER appuser
EXPOSE 3000
ENV NODE_ENV=production
CMD ["node", "server.js"]

Comparaison de la taille de l'image

Type de constructionTaille de l'imageTemps de construction
En une seule étape (image complète du nœud)1,8 Go4 minutes
Mono-étage (alpin)650 Mo3,5 minutes
Multi-étapes (alpin)180 Mo5 minutes
Dépôts multi-étages + taillés120 Mo5,5 minutes

Le temps de construction de 5,5 minutes est acceptable car cela se produit dans CI, pas sur les machines des développeurs.


Docker Compose pour la production

version: "3.8"

services:
  api:
    build:
      context: .
      dockerfile: apps/api/Dockerfile
    environment:
      - DATABASE_URL=postgresql://app:${DB_PASSWORD}@db:5432/ecosire
      - REDIS_URL=redis://redis:6379
      - NODE_ENV=production
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    restart: unless-stopped
    networks:
      - backend
      - frontend

  web:
    build:
      context: .
      dockerfile: apps/web/Dockerfile
    environment:
      - API_URL=http://api:3001
      - NODE_ENV=production
    depends_on:
      - api
    restart: unless-stopped
    networks:
      - frontend

  db:
    image: postgres:17-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: ecosire
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d ecosire"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    networks:
      - backend

  redis:
    image: redis:7-alpine
    command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory 256mb --maxmemory-policy allkeys-lru
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    networks:
      - backend

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./infrastructure/nginx/production.conf:/etc/nginx/conf.d/default.conf:ro
      - ./certbot/conf:/etc/letsencrypt:ro
      - ./certbot/www:/var/www/certbot:ro
    depends_on:
      - web
      - api
    restart: unless-stopped
    networks:
      - frontend

volumes:
  postgres-data:
  redis-data:

networks:
  frontend:
  backend:

Isolement du réseau

La configuration ci-dessus utilise deux réseaux :

  • frontend : Nginx, Web et API (proxys Nginx pour les deux)
  • backend : API, base de données et Redis

La base de données et Redis ne sont pas accessibles depuis le conteneur Nginx ou le réseau externe. Cette segmentation du réseau est une pratique de sécurité critique.


Gestion des volumes et persistance des données

Les volumes constituent la partie la plus critique d’un déploiement ERP Dockerisé. Perdez vos volumes et vous perdez vos données.

###Types de volumes

TapezCas d'utilisationPersistancePerformances
Volumes nommésBase de données, RedisSurvit au retrait du conteneurVitesse du système de fichiers natif
Lier les monturesFichiers de configuration, journauxLié au système de fichiers hôteVitesse du système de fichiers natif
montages tmpfsFichiers temporaires, secretsMémoire uniquement, perdue au redémarrageVitesse de la mémoire

Stratégie de sauvegarde pour les volumes Docker

#!/bin/bash
# backup-volumes.sh - Run via cron every 6 hours

TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/opt/backups"

# Stop the application briefly for consistent backup
docker compose stop api web

# Backup PostgreSQL
docker compose exec -T db pg_dump -U app ecosire | gzip > "$BACKUP_DIR/db_$TIMESTAMP.sql.gz"

# Backup Redis
docker compose exec -T redis redis-cli -a "$REDIS_PASSWORD" BGSAVE
sleep 5
docker cp $(docker compose ps -q redis):/data/dump.rdb "$BACKUP_DIR/redis_$TIMESTAMP.rdb"

# Restart services
docker compose start api web

# Upload to S3
aws s3 sync "$BACKUP_DIR" "s3://company-backups/docker-volumes/" --exclude "*.tmp"

# Retain 30 days locally
find "$BACKUP_DIR" -name "*.gz" -mtime +30 -delete
find "$BACKUP_DIR" -name "*.rdb" -mtime +30 -delete

## Vérifications de santé et politiques de redémarrage

Les conteneurs de production doivent signaler eux-mêmes leur état de santé et se remettre automatiquement des pannes.

Point de terminaison de vérification de l'état de l'application

// health.controller.ts
@Controller('health')
export class HealthController {
  constructor(
    private readonly db: DatabaseService,
    private readonly redis: RedisService,
  ) {}

  @Get()
  @Public()
  async check() {
    const checks = {
      database: await this.checkDatabase(),
      redis: await this.checkRedis(),
      uptime: process.uptime(),
      memory: process.memoryUsage(),
    };

    const healthy = checks.database && checks.redis;
    return { status: healthy ? 'ok' : 'degraded', checks };
  }

  private async checkDatabase(): Promise<boolean> {
    try {
      await this.db.execute('SELECT 1');
      return true;
    } catch {
      return false;
    }
  }

  private async checkRedis(): Promise<boolean> {
    try {
      await this.redis.ping();
      return true;
    } catch {
      return false;
    }
  }
}

Redémarrer la sélection de la stratégie

PolitiqueComportementCas d'utilisation
noNe redémarrez jamaisDéveloppement, tâches ponctuelles
on-failureRedémarrer uniquement en cas de sortie non nulleTravailleurs, travaux par lots
alwaysToujours redémarrer (y compris lors du redémarrage du démon Docker)Prestations de production
unless-stoppedComme always mais respecte les arrêts manuelsLa plupart des services de production

Utilisez unless-stopped pour les services de production. Cela garantit le redémarrage des conteneurs après le redémarrage du serveur ou le redémarrage du démon Docker, mais respecte les commandes manuelles docker compose stop pendant la maintenance.


Flux de travail de déploiement

Mises à jour progressives avec Docker Compose

#!/bin/bash
# deploy.sh - Zero-downtime deployment

set -e

echo "Pulling latest code..."
git pull origin main

echo "Building new images..."
docker compose build --no-cache api web

echo "Rolling update - API first..."
docker compose up -d --no-deps api
sleep 10

# Verify API health
if ! curl -sf http://localhost:3001/health > /dev/null; then
  echo "API health check failed, rolling back..."
  docker compose up -d --no-deps api
  exit 1
fi

echo "Rolling update - Web..."
docker compose up -d --no-deps web
sleep 5

# Verify Web health
if ! curl -sf http://localhost:3000 > /dev/null; then
  echo "Web health check failed, rolling back..."
  docker compose up -d --no-deps web
  exit 1
fi

echo "Deployment complete!"
docker compose ps

Sécurité de la migration des bases de données

N’exécutez jamais de migrations au démarrage de l’application. Au lieu de cela, exécutez-les dans le cadre d'une étape distincte :

# Run migrations before deploying new containers
docker compose run --rm api npx drizzle-kit push

# Then deploy the new version
docker compose up -d

Ce modèle garantit qu'en cas d'échec d'une migration, l'ancienne version continue de s'exécuter sans être affectée.


Journalisation et débogage

Journalisation centralisée

# Add to docker-compose.yml
services:
  api:
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "5"
        labels: "service"
    labels:
      service: "ecosire-api"

Commandes de débogage courantes

# View logs for a specific service
docker compose logs -f api --tail 100

# Execute a shell inside a running container
docker compose exec api sh

# View resource usage
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}"

# Inspect container networking
docker compose exec api ping db

# View container environment variables
docker compose exec api env | sort

Questions fréquemment posées

Comment gérer les migrations de bases de données dans Docker ?

Exécutez les migrations dans le cadre d’une étape distincte avant de déployer de nouveaux conteneurs d’applications. Utilisez docker compose run --rm api npx drizzle-kit push (ou la commande de migration de votre ORM) comme étape de pré-déploiement. N'intégrez jamais l'exécution de la migration dans la commande de démarrage du conteneur : un échec de migration ne devrait pas empêcher la version actuelle de continuer à s'exécuter.

Quelle est la surcharge de performances de Docker ?

Sous Linux, la surcharge de performances de Docker est négligeable : généralement inférieure à 2 % pour les charges de travail liées au processeur et aucune différence mesurable pour les charges de travail liées aux E/S. Sous macOS et Windows, Docker s'exécute dans une machine virtuelle, ce qui ajoute 5 à 15 % de surcharge. Pour la production (qui devrait être Linux), Docker ne constitue pas un problème de performances significatif.

Comment gérer les secrets dans Docker ?

Ne mettez jamais de secrets dans les fichiers Dockerfiles ou docker-compose.yml. Utilisez des fichiers de variables d'environnement (.env) exclus du contrôle de version, des secrets Docker (pour le mode Swarm) ou des gestionnaires de secrets externes (AWS Secrets Manager, HashiCorp Vault). Pour Docker Compose, un fichier .env à la racine du projet est l'approche la plus simple.

Devrions-nous utiliser Docker Swarm ou Kubernetes ?

Pour la plupart des déploiements ERP PME, Docker Compose est suffisant. Docker Swarm ajoute une orchestration multi-hôtes avec une complexité minimale. Kubernetes est approprié lorsque vous avez besoin d'une mise à l'échelle automatique, de politiques de réseau complexes ou de fonctionnalités de maillage de services. Consultez notre guide de mise à l'échelle Kubernetes et notre guide d'architecture des microservices pour connaître les cadres de décision.

Comment gérer les modules personnalisés Odoo dans Docker ?

Montez les modules personnalisés en tant que volume de montage de liaison pointant vers votre répertoire de modules complémentaires. Dans le Dockerfile, assurez-vous que le chemin des modules complémentaires est configuré dans odoo.conf. Pour CI/CD, créez une image Docker personnalisée qui s'intègre dans vos modules, garantissant ainsi la cohérence des versions. Consultez notre Guide de déploiement Docker Odoo existant pour la configuration spécifique à Odoo.


Ce qui vient ensuite

Docker est la base du déploiement ERP moderne. Une fois que votre pile conteneurisée est stable, explorez les stratégies de déploiement sans temps d'arrêt, surveillance de la production et infrastructure as code pour créer un pipeline d'opérations entièrement automatisé.

Contactez ECOSIRE pour des conseils en matière de déploiement Docker, ou explorez nos services de mise en œuvre Odoo pour un déploiement ERP conteneurisé entièrement géré.


Publié par ECOSIRE – aider les entreprises à déployer des logiciels d'entreprise en toute confiance.

E

Rédigé par

ECOSIRE Research and Development Team

Création de produits numériques de niveau entreprise chez ECOSIRE. Partage d'analyses sur les intégrations Odoo, l'automatisation e-commerce et les solutions d'entreprise propulsées par l'IA.

Discutez sur WhatsApp