Noeud duniter via Docker - Unhandled rejection: Error: ruleVersion

Bonjour,

Disposant d’un serveur dédié avec plusieurs services tournant via Docker, j’ai voulu faire l’experience d’installer un noeud serveur duniter de la même façon. J’utilise docker-compose + ansible pour le déploiement. Voici à quoi ressemble le fichier docker-compose.yml :

version: "3.5"

services:
  duniter:
    image: duniter/duniter:latest
    container_name: duniter
    restart: unless-stopped
    ports:
      - "10901:10901"
      - "20901:20901"
    volumes:
      - etc:/etc/duniter
      - data:/var/lib/duniter
    environment:
      VIRTUAL_HOST: duniter.example.org
      VIRTUAL_PORT: 9220
      CERT_NAME: example.org
    networks:
      proxy:

volumes:
  etc:
  data:

networks:
  proxy:
    external: true
    name: reverse-proxy_bridge

Notes :

  • les variables VIRTUAL_HOST, VIRTUAL_PORT et CERT_NAME sont relatives au reverse proxy nginx-proxy
  • je protège l’accès à l’interface web via une authentification du client par certificat SSL ; configuration non visible ici.

Le service se lance, et je peux demander la synchronisation avec g1.duniter.org. La synchro semble fonctionner et dure dans les 5h40. Mais si je relance une synchro ensuite, tout se bloque rapidement avec cette erreur dans les logs :

2021-04-04T22:44:47+00:00 - info: Mem2File [wotb]...
2021-04-04T22:44:52+00:00 - info: Connecting to address <redacted>...
2021-04-04T22:44:52+00:00 - info: Connecting to address <redacted>...
2021-04-04T22:44:52+00:00 - info: Connecting to address <redacted>...
2021-04-04T22:44:52+00:00 - info: Connecting to address <redacted>...
2021-04-04T22:44:52+00:00 - info: Connecting to address <redacted>...
2021-04-04T22:44:56+00:00 - error: Unhandled rejection: Error: ruleVersion
2021-04-04T22:44:56+00:00 - error:  Error: ruleVersion
    at Function.checkBlock (/duniter/duniter/app/lib/blockchain/DuniterBlockchain.js:54:19)
    at process._tickCallback (internal/process/next_tick.js:68:7)

L’erreur est 100% reproductible. Comme un certain nombre d’informations de la page L’écosystème logiciel de Duniter semble obsolète, je me demande si cette méthode d’installation est toujours supportée.

La version rapportée par la commande duniter --version est 1.8.1.

1 J'aime

L’image est surement une version de dév qui n’est pas gérée par le réseau.

As-tu pris la v1.8.1 ? Ça doit être la même chose sur DockerHub.

L’image est celle présente sur Docker Hub avec le nom duniter/duniter:latest. Elle a le même digest que celle taguée v1.8.1, donc c’est bien celle-ci. Je n’ai pas trouvé duniter/duniter-ts mentionnée dans la doc.

Et il s’agit bien de la version 1.8.1 :

$ docker exec duniter duniter --version
1.8.1

Je gère aussi mon serveur avec ansible et docker.

Je ne pense pas que ce soit un problème d’image ou de version, mais de config.

Pour que ton fichier de config soit fiable, tu dois le supprimer, puis lancer une synchro. Duniter va alors remplir le fichier de config avec les bons paramètres de la monnaie (car il vérifie des règles ensuite avec les valeurs de ce fichier). Tu stop aussitôt la synchro (c’est juste pour générer le fichier, et tu te sert de ce fichier comme modèle pour ta config réseau (soit par l’interface, soit en éditant le fichier, mais uniquement pour les adresses réseaux).

Pour la synchro, je te conseille de faire une synchro en mémoire, puis de copier les données sur le disque, cela sera nettement plus rapide. J’ai un playbook et des roles ansible pour cela que je publierai un jour…

Merci @vit pour ces infos. La partie sur le fichier de config me semble toutefois un peu obscure. Voici comment j’ai procédé jusqu’à présent :

  1. Démarrage from scratch du service, avec volumes vides
  2. Lancement de la synchro via l’interface web, mode simplifié, noeud https://g1.duniter.org
  3. La synchro démarre ses premiers téléchargements
  4. Il n’y a aucun élément dans l’IHM permettant de stopper la synchro. Je fais donc un docker stop duniter un peu bourrin.

Mais en examinent ensuite le volume /etc/duniter, je constate qu’il est vide. Comment ce fichier de conf est-il donc censé être généré la première fois ?

J’ai fini par comprendre que le fichier de conf en question est certainement /var/lib/duniter/duniter_default/conf.json.

Oui c’est celui là.

Mon docker-compose:

      duniter_g1:
        image: duniter/duniter:v1.8.1
        ports:
          - 9220:9220
          - 11900:11900
          - 21900:21900
        volumes:
          - "{{ duniter_g1_data_path }}:/var/lib/duniter"
        networks:
          - network

Note que le montage de /etc/duniter ne sert pas.

Je te conseil de lancer la commande de synchro en mémoire à la main en ligne de commande en suivant la procédure dont je t’ai donné le lien.

Une fois la configuration générée par Duniter créée, conserve la précieusement et utilise là comme template Ansible, avec des variables pour tes hosts et ports.

Tu dois avoir une config pour G1 et une pour G1-test si tu veux participer au deux réseaux.

Je te conseil de lancer la commande de synchro en mémoire à la main en ligne de commande en suivant la procédure dont je t’ai donné le lien.

Le souci c’est que mon serveur n’est pas hyper bien doté en RAM (4Go), et que j’ai pas mal d’autres services dessus. J’ai un peu peur que ça me plante tout. Si la synchro n’est à faire qu’une fois pour toutes, je peux certainement m’accommoder de 5h+ de délai.

Une fois la configuration générée par Duniter créée, conserve la précieusement et utilise là comme template Ansible, avec des variables pour tes hosts et ports.

Y a-t-il une page quelque part qui documente ce fichier ? J’ai cherché mais pas encore trouvé.

La synchro sera à faire tant que « ça ne marche pas ». Donc beaucoup au début, le temps que tu règles les problèmes.

Après nettement moins, mais faire une synchro ne doit pas être un problème, sinon tu vas finir par abandonner… :wink:

Je te suggère de faire la synchro sur une autre machine et de copier ensuite le résultat dans le dossier utilisé par ton serveur. Une fois automatisé le processus avec un playbook ansible comme le mien, on ne redoute plus les synchros…

Voici mon playbook brut, à adapter à tes besoins :

duniter_sync_g1.yml (2,4 Ko)

Non, pas à ma connaissance. Ce fichier n’est pas censé être manipulé par un humain. Mais comme l’interface graphique a ses limites et ses bugs, on va plus vite à éditer le fichier, juste sur les valeurs nécessaires (hosts, ports). Mais tu peux chercher sur ce forum des exemples de modification de config.

1 J'aime

La synchro n’est-elle pas en quelque sorte idempotent? J’imaginais que si j’avais une synchro apparemment réussie, genre celle-ci :

2021-04-04T22:36:45+00:00 - info: Sync finished (duration 20519214 ms).

alors les changements de config qui ne touchent pas aux paramètres de la monnaie ne devraient pas en nécessiter de nouvelles.

Dis autrement, ne pourrait-on pas sauvegarder une fois pour toute la première synchro, et repartir de celle-ci pour chaque nouvel essai ?

En théorie oui, en l’état des développements : non, je n’ai pas constaté cela en tout cas.

Si je diverge de la blockchain majoritaire d’une centaine de blocs, la synchro met aussi longtemps qu’avec un reset data complet.

La réécriture de la synchro est un gros chantier qui n’est pas dans les priorités pour l’instant, contrairement aux améliorations et correctifs sur le fonctionnement de serveur. C’est un confort de maintenance pour des admins de serveurs.

Dans ton cas, le principal est de t’assurer déjà que tu as un fichier config.json de la G1 pour la G1 et un fichier config.json G1-test pour la G1-test. C’est une des causes de problèmes, la synchro ne changera pas le fichier si il existe et n’est pas sur la bonne monnaie.

Si cela ne corrige pas le problème, on peut alors mettre en cause la synchro ou les paramètres réseau ou autre chose.

Utilise en priorité la ligne de commande, fait un help, tu as des wizards qui modifient le fichier de config pour toi.

Sur mon serveur dédié la synchro en mémoire ne change pas grand chose : 4h25 au lieu de 5h40 C’est la CPU qui fait goulet j’imagine. Je me suis donc rabattu sur mon PC perso, et ça va sensiblement plus vite, mais la différence entre stockage mémoire et disque SSD n’est pas déterminante : 50min vs 1h15 (facteur 1.5).

Ça m’embête vraiment de continuer sur mon PC, car de toute façon je ne vais pas être du tout dans la même configuration que sur mon serveur : reverse proxy d’un côté, box ADSL + routeur de l’autre, avec les redirections de ports à configurer. :confused:

Bref… Je vais rester côté serveur, tant pis pour le délai de synchro.

Passons à l’aspect configuration. en parcourant le fichier conf.json, je déduis qu’il y a :

  • les paramètres de la monnaie (pas touche !)
  • les paramètres réseau (à adapter)
  • plein d’autres choses auxquelles je ne comprends rien mais pour lesquelles la valeur par défaut devrait être bonne (croisons les doigts).

Pour la conf réseau j’ai tenté cette manip :

  1. Partant d’une configuration vierge, démarrage de sync sur g1.duniter.org:443
  2. Interruption de la sync dès que la conf est en place (les barres de progression s’affichent)
  3. Configuration réseau avec wizard network-reconfigure qui a le mérite de poser des questions :
    • VPS : non
      La question me semble mal tournée : « UPnP is not available: is this a public server (like a VPS)? » Je suis sur un VPS mais je dois répondre « non » car derrière un reverse-proxy. Ce qu’on veut savoir c’est si l’interface réseau sur laquelle on écoute correspond à l’adresse IP qui sera publiée. Et je ne comprends pas du tout le lien avec le protocole UPnP.
    • IPv4 : IP de l’instance
    • IPv6 : non
    • Port : 10901
    • Remote IPv4 : IP de mon serveur
    • Remote port : 10901
    • DNS : duniter.mon.domaine

Je vais utiliser le fichier de conf généré comme template, puis retenter une sync from scratch. On verra bien ce que ça donne.

L’upnp c’est pour ouvrir les ports sur une box pilotable en upnp, chez soi. Ce n’est pas ton cas.

Pour Duniter dans Docker, l’ip réseau locale doit être 0.0.0.0.

Voici mon fichier pour la Ğ1, c’est un template Ansible (jinja2) :

duniter_g1_conf.json (1,7 Ko)

Compare le avec le tien, tu devrais juste avoir à changer les domain/ip remote et les ports.

J’expose l’api BMA en https, c’est pour cela qu’il y a un endpoint déclaré dans le fichier, en plus de ceux par défaut.

Hope it helps.

OK. J’avoue que je ne connaissais pas du tout ce mécanisme des box modernes.

Je chipote un peu : 0.0.0.0 c’est pour écouter sur toutes les interfaces. Si je spécifie l’IP interne de mon instance, ça doit marcher aussi, non ?

Aha ! Grâce à ces mots clefs j’ai pu retrouver ce post puis le lien vers la doc. Je vais potasser tout ça !

It does :slight_smile:

1 J'aime

Tu as raison de chipoter ! Je ne suis pas encore à l’aise avec les IPs docker, mais comme j’utilise le mode swarm, je ne connais pas mon IP locale d’instance. Avec 0.0.0.0, ça juste marche… Mais là, c’est toi qui peut me conseiller sur le sujet ! :wink:

Effectivement en mode swarm il n’y a pas d’autre choix.

Ça y est, mon instance a l’air de tourner. C’est duniter.pini.fr, si jamais quelqu’un peut vérifier que ça répond correctement.

Voici un screenshot du tableau de bord sur l’interface web. Merci de me dire si ça vous semble normal :

Notes :

  • Je ne suis pas membre, donc aucun bloc produit
  • Le WS2P privé n’est pas activé, ce qui devrait expliquer le « 0 pairs connectés »

Je me suis fabriqué une image Docker maison à partir de l’image officielle, dans laquelle j’ai ajouté :

  • python3 (pour permettre les interactions avec ansible)
  • un script entrypoint qui ordonance les actions selon l’état de l’instance et quelques variables d’environnement, ce qui permet de :
    • attendre un fichier de configuration (déposé par ansible, ou via docker cp par exemple) avant d’aller plus loin
    • lancer une première synchro avant de démarrer l’interface web si on démarre from scratch

Merci @vit pour l’accompagnement dans ce démarrage laborieux :slight_smile:

Update : il y a une poignée de pairs connectés qui apparaissent de temps en temps.

2 J'aime

C’est bon, bravo !

Une connection sécurisée avec let’s encrypt qui plus est…

Pour vérifier que tout fonctionne je te conseille l’onglet réseau de l’accueil.

  • Si ton WS2P public fonctionne, tu dois avoir des connexions entrantes (INCOMING).
  • Si ton WS2P privée est activé, tu dois avoir des connexions sortantes (OUTCOMING)
  • Si tu es à jour des blocs de la blockchain, tu dois avoir le même HEAD que la majorité des nœuds listés.

Pour rentrer tes identifiants secrets de compte membre, tu peux le faire dans l’interface qui va créer un fichier keyring dans le dossier des données à côté du fichier de config. c’est un fichier sensible qui ne doit pas être accessible à une personne malveillante.

Chez moi Ansible reste externe au container.
Je ne peux pas vraiment partager ma config Ansible, car elle inclus mes applications serveur type contact/calendrier…

Si tu as un dépôt git qui versionne ta config Ansible/nginx/letsencrypt, bref la totale pour installer un serveur Duniter à la sauce devops, ce serait une belle contribution de la partager en retour sur notre Gitlab.

Je n’ai pas de WS2P incoming car pas encore activé (c’est la prochaine étape).
Je vois bien des connexions outcoming de temps à autre.
Et la HEAD correspond bien à la majorité des autres.
\o/

Côté configuration, je compte bien la partager une fois qu’elle sera stabilisée. Mais c’est un exercice pas si simple car chacun a son éco-système docker privilégié. Pour ma part j’utilise docker-compose (sans Swarm) et un fork perso de la combinaison nginx-proxy + docker-letsencrypt-nginx-proxy-companion. Mon fork m’apporte en particulier les fonctionnalités suivantes :

  • Certificat wildcard sur mon domaine (ça évite d’avoir une ribambelle de certificats pour chaque service)
  • Possibilité de mapper des ports différents sur le reverse-proxy pour un conteneur donné. J’ai implémenté cette dernière modif hier pour me simplifier la terminaison SSL sur les trois ports du service duniter : chacun arrive sur le port 443 de mon serveur, avec un nom DNS différent.

Enfin côté ansible je suis un débutant moins moins (à peine quelques semaines d’expérience). Rien de partageable pour l’instant. Mes playbooks se contentent de démarrer / mettre à jour depuis mon PC perso les services installés sur mon serveur dédié. Je vise une certaine homogénéïté d’utilisation des playbooks quelque soit le service : on lance le playbook sans paramètres et ça juste marche.

Le plus simple à partager dans un premier temps sera mon script entrypoint, qui pourra peut-être trouver son chemin dans l’image officielle si ça convient à son mainteneur.

1 J'aime