Published on

Mise en place d'un ERP sur un VPS en utilisant portainer et nginx

 15 mins
Authors
  • avatar
    Name
    Léo Delpon
    Twitter

Avant de commencer à configurer ERPNext sur un environnement Docker, il semble pertinent de savoir à quoi sert un ERP. Un ERP (Enterprise Resource Planning) est un système de gestion intégré qui aide les entreprises à gérer leurs opérations commerciales en centralisant les données et les processus clés. Voici quelques-uns des avantages clés d'avoir un ERP :

  1. Amélioration de l'efficacité opérationnelle : Un ERP permet de rationaliser les processus clés de l'entreprise, ce qui peut réduire les duplications de travail et les erreurs de saisie de données.
  2. Visibilité en temps réel : Les ERP fournissent une vue en temps réel de l'ensemble des opérations de l'entreprise, ce qui permet aux entreprises de prendre des décisions éclairées plus rapidement.
  3. Amélioration de la collaboration : Les ERP permettent à différents départements de partager des informations et des données en temps réel, ce qui peut améliorer la collaboration et la communication au sein de l'entreprise.
  4. Automatisation des processus : Les ERP automatisent de nombreux processus clés de l'entreprise, tels que la facturation, la gestion des stocks et la gestion des ressources humaines, ce qui peut réduire les erreurs et améliorer l'efficacité.
  5. Meilleure gestion des finances : Les ERP permettent une meilleure gestion des finances en centralisant les données financières et en fournissant une vue en temps réel des finances de l'entreprise.

Ce tutoriel est destiné pour les personnes intermédiaires. Si tu as des questions à poser, je suis disponible sur Linkedin, lien que tu pourras trouver dans mon site portfolio depuis la barre de navigation **Me.**

Préparation de la base de données MariaDB

On considère que portainer est déjà installé chez vous. On va créer dans un premier temps la base de données en conteneur. Je vous conseille d’aller voir mon snippet que j’ai crée pour générer une base de données MariaDB avec Adminer en cliquant ici. Vous pourrez le créer directement depuis Stacks > + Add stacks > Web Editor.

Voici le fichier env que vous devrez mettre :

DB_PASSWORD=UnMotDePasseUltraLongEtFort

Si vous souhaitez créer un compte Administrateur spécifique vous pouvez le faire comme ceci :

  1. Connectez vous à MySQL ou MariaDB (en localhost donc depuis le conteneur)
debian@vps-e0a96a5e:~$ mysql -u root -p
  1. Créez un utilisateur
CREATE USER 'user_administrateur'@'%' IDENTIFIED BY 'MonMotDePasseDuTurfu';
  1. Donnez les permissions
GRANT ALL PRIVILEGES ON *.* TO 'user_administrateur'@'%' WITH GRANT OPTIONS;
  1. Flushez les privilèges
FLUSH PRIVILEGES;

Vous pouvez également intéragir avec la base de données depuis la plateforme Adminer :

Maple

Installation et configuration de base de frappe_docker

On va aller dans notre machine système (en SSH par exemple) et on va télécharger un projet github .

debian@vps-e0a96a5e:~$ git clone https://github.com/frappe/frappe_docker.git
debian@vps-e0a96a5e:~$ ls
data                frappe_docker

On voit bien que le dossier frappe_docker a été téléchargé. On va rentrer dans ce directory :

debian@vps-e0a96a5e:~/frappe_docker$ ls
CODE_OF_CONDUCT.md  devcontainer-example  docs         LICENSE    pwd.yml                resources
compose.yaml        development           example.env  nano.save  README.md              setup.cfg
CONTRIBUTING.md     docker-bake.hcl       images       overrides  requirements-test.txt  tests

Vérifiez bien que vous avez l’ensemble des fichiers présents dans le dossier overrides :

debian@vps-e0a96a5e:~/frappe_docker/overrides$ ls
compose.custom-domain-ssl.yaml  compose.mariadb.yaml          compose.postgres.yaml     compose.traefik.yaml
compose.custom-domain.yaml      compose.multi-bench-ssl.yaml  compose.proxy.yaml
compose.https.yaml              compose.multi-bench.yaml      compose.redis.yaml
compose.mariadb-shared.yaml     compose.noproxy.yaml          compose.traefik-ssl.yaml

Si vous n’avez pas tout les fichiers, il se peut que vous soyez sur une autre branche. Allez sur le repository de Frappe et allez voir quelle branche présente l’ensemble de ces fichiers : frappe_docker

Vous pourrez ensuite changer de branche depuis le dossier frappe_docker

debian@vps-e0a96a5e:~/frappe_docker$ git checkout [LE_NOM_DE_LA_BRANCHE]

Nous possédons désormais la base minimum pour générer l’ensemble des fichiers yaml dont nous aurons besoin par la suite. En effet, on va pouvoir générer directement le fichier grâce au projet frappe_docker . Nous allons donc créer un fichier erpnext-one.env , on va donc d’abord créer un dossier comme ceci: mkdir ~/gitops . Puis on va remplir ce dernier :

debian@vps-e0a96a5e:~/frappe_docker cp example.env ~/gitops/erpnext-one.env
debian@vps-e0a96a5e:~/frappe_docker sed -i 's/DB_PASSWORD=UnMotDePasseUltraLongEtFort/DB_PASSWORD=UnMotDePasseUltraLongEtFort/g' ~/gitops/erpnext-one.env
debian@vps-e0a96a5e:~/frappe_docker sed -i 's/DB_HOST=/DB_HOST=mariadb-database/g' ~/gitops/erpnext-one.env // A savoir que DB_HOST correspond au nom du conteneur docker que nous avons crée en amont.
debian@vps-e0a96a5e:~/frappe_docker sed -i 's/DB_PORT=/DB_PORT=3306/g' ~/gitops/erpnext-one.env
debian@vps-e0a96a5e:~/frappe_docker echo 'ROUTER=erpnext-one' >> ~/gitops/erpnext-one.env
debian@vps-e0a96a5e:~/frappe_docker echo "SITES=\`one.example.com\`,\`two.example.com\`" >> ~/gitops/erpnext-one.env // Si vous avez un nom de domaine, remplacez one.example.com par le votre
debian@vps-e0a96a5e:~/frappe_docker echo "BENCH_NETWORK=erpnext-one" >> ~/gitops/erpnext-one.env

Enfin, nous allons générer le fichier yaml à partir du fichier env que l’on a crée mais également des fichiers YAML du repository

docker compose --project-name erpnext-one \
  --env-file ~/gitops/erpnext-one.env \
  -f compose.yaml \
  -f overrides/compose.redis.yaml \
  -f overrides/compose.multi-bench.yaml \
  -f overrides/compose.multi-bench-ssl.yaml config > ~/gitops/erpnext-one.yaml

On va le modifier pour enlever les parties de **traefik** pour obtenir à la fin ce fichier :

name: erpnext-one
services:
  backend:
    depends_on:
      configurator:
        condition: service_completed_successfully
    image: frappe/erpnext:v14.12.1
    networks:
      bench-network: null
      mariadb-network: null
    volumes:
    - type: volume
      source: sites
      target: /home/frappe/frappe-bench/sites
      volume: {}
  configurator:
    command:
    - |
      ls -1 apps > sites/apps.txt; bench set-config -g db_host ${DB_HOST}; bench set-config -gp db_port ${DB_PORT}; bench set-config -g redis_cache "redis://$$REDIS_CACHE"; bench set-config -g redis_queue "redis://$$REDIS_QUEUE"; bench set-config -g redis_socketio "redis://$$REDIS_SOCKETIO"; bench set-config -gp socketio_port $$SOCKETIO_PORT;
    depends_on:
      redis-cache:
        condition: service_started
      redis-queue:
        condition: service_started
      redis-socketio:
        condition: service_started
    entrypoint:
    - bash
    - -c
    environment:
      DB_HOST: mariadb-database
      DB_PORT: "3306"
      REDIS_CACHE: redis-cache:6379
      REDIS_QUEUE: redis-queue:6379
      REDIS_SOCKETIO: redis-socketio:6379
      SOCKETIO_PORT: "9000"
    image: frappe/erpnext:v14.12.1
    networks:
      bench-network: null
      mariadb-network: null
    volumes:
    - type: volume
      source: sites
      target: /home/frappe/frappe-bench/sites
      volume: {}
  frontend:
    command:
    - nginx-entrypoint.sh
    depends_on:
      backend:
        condition: service_started
      websocket:
        condition: service_started
    environment:
      BACKEND: backend:8000
      CLIENT_MAX_BODY_SIZE: 50m
      FRAPPE_SITE_NAME_HEADER: $$host
      PROXY_READ_TIMOUT: "120"
      SOCKETIO: websocket:9000
      UPSTREAM_REAL_IP_ADDRESS: 127.0.0.1
      UPSTREAM_REAL_IP_HEADER: X-Forwarded-For
      UPSTREAM_REAL_IP_RECURSIVE: "off"
    image: frappe/erpnext:v14.12.1
    networks:
      bench-network: null
    ports:
      - 55257:8080
    volumes:
    - type: volume
      source: sites
      target: /home/frappe/frappe-bench/sites
      volume: {}
  queue-default:
    command:
    - bench
    - worker
    - --queue
    - default
    depends_on:
      configurator:
        condition: service_completed_successfully
    image: frappe/erpnext:v14.12.1
    networks:
      bench-network: null
      mariadb-network: null
    volumes:
    - type: volume
      source: sites
      target: /home/frappe/frappe-bench/sites
      volume: {}
  queue-long:
    command:
    - bench
    - worker
    - --queue
    - long
    depends_on:
      configurator:
        condition: service_completed_successfully
    image: frappe/erpnext:v14.12.1
    networks:
      bench-network: null
      mariadb-network: null
    volumes:
    - type: volume
      source: sites
      target: /home/frappe/frappe-bench/sites
      volume: {}
  queue-short:
    command:
    - bench
    - worker
    - --queue
    - short
    depends_on:
      configurator:
        condition: service_completed_successfully
    image: frappe/erpnext:v14.12.1
    networks:
      bench-network: null
      mariadb-network: null
    volumes:
    - type: volume
      source: sites
      target: /home/frappe/frappe-bench/sites
      volume: {}
  redis-cache:
    image: redis:6.2-alpine
    networks:
      bench-network: null
      mariadb-network: null
    volumes:
    - type: volume
      source: redis-cache-data
      target: /data
      volume: {}
  redis-queue:
    image: redis:6.2-alpine
    networks:
      bench-network: null
      mariadb-network: null
    volumes:
    - type: volume
      source: redis-queue-data
      target: /data
      volume: {}
  redis-socketio:
    image: redis:6.2-alpine
    networks:
      bench-network: null
      mariadb-network: null
    volumes:
    - type: volume
      source: redis-socketio-data
      target: /data
      volume: {}
  scheduler:
    command:
    - bench
    - schedule
    depends_on:
      configurator:
        condition: service_completed_successfully
    image: frappe/erpnext:v14.12.1
    networks:
      bench-network: null
      mariadb-network: null
    volumes:
    - type: volume
      source: sites
      target: /home/frappe/frappe-bench/sites
      volume: {}
  websocket:
    command:
    - node
    - /home/frappe/frappe-bench/apps/frappe/socketio.js
    depends_on:
      configurator:
        condition: service_completed_successfully
    image: frappe/erpnext:v14.12.1
    networks:
      bench-network: null
      mariadb-network: null
    volumes:
    - type: volume
      source: sites
      target: /home/frappe/frappe-bench/sites
      volume: {}
networks:
  bench-network:
    name: erpnext-one
  mariadb-network:
    name: mariadb_bridge
    external: true
volumes:
  redis-cache-data:
    name: erpnext-one_redis-cache-data
  redis-queue-data:
    name: erpnext-one_redis-queue-data
  redis-socketio-data:
    name: erpnext-one_redis-socketio-data
  sites:
    name: erpnext-one_sites
x-backend-defaults:
  depends_on:
    configurator:
      condition: service_completed_successfully
  image: frappe/erpnext:v14.12.1
  volumes:
  - sites:/home/frappe/frappe-bench/sites
x-customizable-image:
  image: frappe/erpnext:v14.12.1
x-depends-on-configurator:
  depends_on:
    configurator:
      condition: service_completed_successfully

Ce script permet d’exécuter plusieurs services :

  • backend
    • Le service backend est le service backend en python de l’ERP
  • configurator
    • C’est un conteneur qui va s’exécuter qu’une seule fois pour configurer le conteneur backend
    • Il n’exécute qu’une commande
  • frontend
    • Le service frontend va exécuter l’application Javascript
    • Le volume est commun avec celui du backend
  • queue-default / queue-long / queue-short
    • Ce sont des conteneurs pour ERPNext qui permet de configurer un worker
  • redis-cache / redis-queue / redis-socketio
    • Ce sont les conteneurs pour la database in memory Redis
  • scheduler
    • C’est un plannificateur pour ERPNext
  • websocket
    • Un conteneur pour les websockets, cela sert surtout pour ERPNext

Il va créer un service et se connecter à un service déjà existant (celui de la base de données que nous avons séparé):

  • bench-network
    • On va donc créer un réseau commun pour tout les services appelé bench-network
  • mariadb_bridge
    • Le fait d’être en external:true signifie que l’on se connecte à un réseau déjà existant

Après avoir exécuté le script sur **********portainer**************, vous devriez avoir 13 conteneurs au minimum ! (J’en ai 14 car j’ai un reverse-proxy Nginx qui fonctionne)

Maple

Configuration de l'ERPNext

Nous allons dans le terminal du service backend :

Maple
root@71c56c8720fd:/home/frappe/frappe-bench# bench new-site [NOM_DE_DOMAINE] --no-mariadb-socket --mariadb-root-username [USERNAME_DB] --mariadb-root-password [MOT_DE_PASSE_DB] --admin-password changeit
Installing frappe...
Updating DocTypes for frappe        : [========================================]
Updating country info               : [========================================]
root@71c56c8720fd:/home/frappe/frappe-bench# bench --site [NOM_DE_DOMAINE] --install-app payments
Installing Payments...
Updating Dashboard for ERPNext       : [========================================]
root@71c56c8720fd:/home/frappe/frappe-bench# bench --site [NOM_DE_DOMAINE] --install-app erpnext
Installing ERPNext...
Updating DocTypes for ERPNext       : [========================================]

Il se peut qu’il y ait plusieurs problèmes, en effet, nous avons exporté la base de données de la stack de l’ERPNext, il va nous falloir modifier un fichier qui est celui-ci :

root@71c56c8720fd:/home/frappe/frappe-bench/apps/frappe/frappe/database/mariadb# cat setup_db.py

Nous modifierons le fichier comme ceci:

def check_database_settings():
        versions = get_mariadb_versions()
        if versions["major"] <= "10.2":
                expected_variables = expected_settings_10_2_earlier
        else:
                expected_variables = expected_settings_10_3_later

        mariadb_variables = frappe._dict(frappe.db.sql("show variables"))
        # Check each expected value vs. actuals:
        result = True
        for key, expected_value in expected_variables.items():
                if mariadb_variables.get(key) != expected_value:
                        print(
                                "For key %s. Expected value %s, found value %s"
                                % (key, expected_value, mariadb_variables.get(key))
                        )
                        result = False
        if not result:
                print(
                        (
                                "=" * 80 + "\n"
                                "Creation of your site - {x} failed because MariaDB is not properly {sep}"
                                "configured.  If using version 10.2.x or earlier, make sure you use the {sep}"
                                "the Barracuda storage engine. {sep}{sep}"
                                "Please verify the settings above in MariaDB's my.cnf.  Restart MariaDB.  And {sep}"
                                "then run `bench new-site {x}` again.{sep2}"
                                ""
                                "=" * 80
                        ).format(x=frappe.local.site, sep2="\n" * 2, sep="\n")
                )

        return result

Un autre problème peut survenir dans :

root@71c56c8720fd:/home/frappe/frappe-bench/apps/frappe/frappe# cat installer.py

Ce sont ces méthodes en particulier qui peuvent poser problème car entre la version 13 et 14, il se peut que des repositorys n’existent plus :

def find_org(org_repo: str) -> tuple[str, str]:
        """find the org a repo is in

        find_org()
        ref -> https://github.com/frappe/bench/blob/develop/bench/utils/__init__.py#L390

        :param org_repo:
        :type org_repo: str

        :raises InvalidRemoteException: if the org is not found

        :return: organisation and repository
        :rtype: Tuple[str, str]
        """
        import requests

        from frappe.exceptions import InvalidRemoteException
        for org in ["frappe", "erpnext"]:
                response = requests.head(f"https://api.github.com/repos/{org}/{org_repo}")
                print(response)
                if response.status_code == 400:
                        response = requests.head(f"https://github.com/{org}/{org_repo}")
                if response.ok:
                        return org, org_repo

        raise InvalidRemoteException(f"{org_repo} not found in frappe or erpnext")

def fetch_details_from_tag(_tag: str) -> tuple[str, str, str]:
        """parse org, repo, tag from string

        fetch_details_from_tag()
        ref -> https://github.com/frappe/bench/blob/develop/bench/utils/__init__.py#L403

        :param _tag: input string
        :type _tag: str

        :return: organisation, repostitory, tag
        :rtype: Tuple[str, str, str]
        """
        app_tag = _tag.split("@")
        org_repo = app_tag[0].split("/")
        try:
                repo, tag = app_tag
        except ValueError:
                repo, tag = app_tag + [None]

        try:
                org, repo = org_repo
        except Exception:
                org, repo = find_org(org_repo[0])
        return org, repo, tag

Normalement, vous n’avez pas besoin de lancer bench start mais si l’application ne se lance pas, je vous invite à le faire.

  1. Ouvrez un navigateur Web sur votre ordinateur local et dirigez-le vers https://your_erpnext_domain/. La page de connexion s'affiche. Saisissez le nom d'utilisateur Administrator dans la boîte d'adresse électronique, et le mot de passe que vous avez défini lors de la configuration. Cliquez ensuite sur Connexion :
Maple
  1. Choisis ton langage
Maple
  1. Choisi le pays, la zone ainsi que la devise
Maple
  1. Configurez votre premier compte utilisateur. Saisissez votre nom complet, votre adresse électronique et un mot de passe secret. Vous pouvez également choisir d'ajouter une photo. Cliquez sur Suivant pour continuer :
Maple
  1. Sélectionnez vos domaines. Les domaines comprennent un ensemble prédéfini de modules conçus pour différents types d'utilisation. Vous pouvez choisir un ou plusieurs domaines. Si vous n'êtes pas sûr du domaine à choisir, sélectionnez Distribution et cliquez sur Suivant pour passer à l'étape suivante :
Maple
  1. Configurez votre marque : Saisissez le nom de votre entreprise et une abréviation de celui-ci. Vous pouvez également choisir d'ajouter une photo. Une fois cela fait, cliquez sur Suivant pour continuer :
Maple
  1. Saisissez des informations sur les activités de votre entreprise, le nom de votre banque, le modèle de diagramme à utiliser, ainsi que des informations sur votre exercice financier. Cliquez ensuite sur Terminer la configuration :
Maple

ERPNext est en train de configurer vos paramètres. La configuration peut prendre un moment. La barre d'état vous donne des informations sur l'avancement de cette tâche :

Maple

Lorsque tout aura été lancé et configuré vous serez sur le site :

Maple