Aller au contenu

Exploitation

Commandes courantes

Toutes se lancent depuis le dossier du dépôt.

docker compose ps                 # état et santé des quatre services
docker compose logs -f backend    # suivre un service (whisper, ollama, frontend)
docker compose restart backend    # redémarrer un service
docker compose down               # tout arrêter
docker compose up -d              # tout démarrer
nvidia-smi                        # occupation du GPU
du -sh ~/.cause/*                 # place occupée par dossier

Dans l'application, l'endpoint /api/health, accessible connecté, vérifie Whisper, Ollama et le stockage et indique le modèle en service. Portainer, sur le port 9443, offre la même vue par une interface web.

Logs

Tous les fichiers sont sous ~/.cause/logs/, un par jour et par service :

Fichier Service
cause-backend.log et cause-backend-<date>.log Backend. Le niveau est DEBUG pour le code de l'application.
cause-whisper-<date>.log Transcription des actes interactifs. Niveau réglé par LOG_LEVEL.
cause-qwen-asr-<date>.log Transcription des actes dictés, uniquement si le profil qwen est activé.
cause-ollama-<date>.log Modèle de langue.
cause-frontend-http-*.log et cause-frontend-https-*.log nginx, accès et erreurs.

Aucune rotation ni suppression n'est configurée. Surveillez la taille du dossier et purgez les anciens fichiers vous-même. Le mot de passe initial de l'administrateur figure dans le log du premier démarrage.

Purges automatiques

Le backend fait le ménage toutes les heures :

  • les fichiers audio orphelins, ceux qu'aucun segment ne réclame, sont supprimés après 168 heures ;
  • les consultations créées par l'infirmier mais jamais démarrées le sont après 48 heures ;
  • les corrections des rapports validés sont moissonnées pour enrichir le glossaire.

L'audio d'un segment n'est plus supprimé, même une fois le rapport rédigé et la consultation close. C'est la pièce qui permet à un médecin de vérifier ce qu'il a dicté et de faire reprendre une transcription douteuse. En contrepartie le dossier audio/ grossit sans borne : au-delà de 50 Go, chaque passage de nettoyage inscrit un avertissement dans le log du backend, avec la place occupée. Surveillez cette ligne, et l'espace libre du disque.

La remontée des évaluations ajoute deux passages : la file est construite le 1er du mois à 2 h 30, et reprise toutes les cinq minutes jusqu'à ce que chaque envoi aboutisse. Sans seau ni clés, un seul avertissement est inscrit au journal et rien ne part. Voir Données et sauvegarde.

Toutes les minutes, les verrous de visibilité expirés depuis plus de trente minutes sont libérés, et les rédactions restées en cours sans personne pour les finir sont closes en erreur. Ce dernier balayage sert surtout au redémarrage, la file de génération vivant en mémoire. Au démarrage, toute rédaction en cours est reprise comme interrompue et le médecin est invité à la relancer ; ses enregistrements sont intacts.

Mises à jour du système

Les mises à jour des paquets du système ne se subissent pas, elles se décident : tout ce qui est automatique est coupé, et l'installation se fait à la main dans une fenêtre de maintenance qui se termine par un redémarrage et une vérification. Une mise à jour de sécurité peut avancer les bibliothèques NVIDIA en laissant le module noyau en arrière, auquel cas le GPU devient inutilisable jusqu'au redémarrage ; les conteneurs déjà debout continuent de tourner et seuls les nouveaux échouent, ce qui rend la panne trompeuse pour qui regarde l'écran.

Il s'agit ici du système seul. La montée de version de l'application est une autre opération, voir Mettre à jour.

Couper les mises à jour automatiques

Ubuntu met à jour par unattended-upgrades, appelé par deux minuteurs systemd et commandé par deux fichiers de /etc/apt/apt.conf.d/. Les deux gestes sont nécessaires : masquer les minuteurs, et mettre les interrupteurs à zéro.

sudo systemctl mask --now apt-daily.timer apt-daily-upgrade.timer unattended-upgrades.service
sudo tee /etc/apt/apt.conf.d/20auto-upgrades > /dev/null << 'EOF'
APT::Periodic::Update-Package-Lists "0";
APT::Periodic::Unattended-Upgrade "0";
EOF

apt-daily.timer rafraîchit les listes de paquets, apt-daily-upgrade.timer lance l'installation, et unattended-upgrades.service reprend les mises à jour en attente à l'extinction de la machine : les trois sont à masquer. mask et non disable, parce qu'une réinstallation du paquet réactive un minuteur seulement désactivé.

Le fichier 20auto-upgrades porte les deux interrupteurs. Il est lu après 10periodic, qui déclare les mêmes clés sur une installation de bureau, donc c'est bien lui qui tranche. L'autre fichier, 50unattended-upgrades, dit quoi mettre à jour : il reste tel quel, plus rien ne l'appelle. Supprimez en revanche tout fichier local du dossier qui déclare Unattended-Upgrade::Automatic-Reboot, par exemple un 52unattended-upgrades-local posé pour redémarrer à 3 h : il est sans effet tant que rien n'appelle unattended-upgrades, et le laisser en place le remettrait en service au premier retour en arrière.

Vérifier, plutôt que de le croire. Les réponses attendues sont en commentaire :

systemctl list-timers 'apt-*'      # 0 timers listed.
systemctl is-enabled apt-daily.timer apt-daily-upgrade.timer unattended-upgrades.service  # masked
apt-config dump APT::Periodic      # les deux clés à "0"

apt-config dump est le seul témoin qui vaille pour la configuration : il rend la valeur effective, tous les fichiers du dossier fusionnés dans l'ordre, et non ce qu'un fichier déclare.

Les réactiver

sudo systemctl unmask apt-daily.timer apt-daily-upgrade.timer unattended-upgrades.service
sudo systemctl enable --now apt-daily.timer apt-daily-upgrade.timer
sudo systemctl enable unattended-upgrades.service
sudo tee /etc/apt/apt.conf.d/20auto-upgrades > /dev/null << 'EOF'
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
EOF

systemctl list-timers 'apt-*' nomme alors les deux minuteurs avec leur prochaine échéance. Les gels posés par apt-mark hold survivent à l'opération et continuent de protéger la pile du pilote.

Chercher ce qui est en attente

Rien de tout ceci n'installe : hors le rafraîchissement des listes, qui demande sudo, tout se lit sans droits.

sudo apt-get update                 # rafraîchit les listes, n'installe rien
apt list --upgradable               # ce qui attend, et de quel canal
/usr/lib/update-notifier/apt-check  # deux nombres : total ; dont sécurité
apt-get -s dist-upgrade             # simulation complète
apt-mark showhold                   # ce qui est gelé et ne bougera pas

Le canal se lit après la barre oblique du nom de paquet, <nom-de-code>-updates pour une mise à jour ordinaire et <nom-de-code>-security pour une mise à jour de sécurité. Le nom de code est celui de la version installée, que . /etc/os-release && echo $VERSION_CODENAME donne : resolute sur le serveur du CHU, en 26.04. Un même correctif étant souvent publié dans les deux canaux, c'est le second nombre d'apt-check qui dit combien relèvent de la sécurité, 14;0 se lisant quatorze mises à jour dont aucune de sécurité.

Deux familles de lignes sont à chercher dans cette liste, parce qu'elles imposent un redémarrage : celles qui portent nvidia, et celles qui portent linux-image, linux-headers ou linux-modules. Après coup, le système le dit lui-même : le fichier /var/run/reboot-required existe tant qu'un redémarrage est dû.

Installer à la main

Dans une fenêtre de maintenance annoncée, aucun médecin connecté.

docker compose down
sudo apt-get update
sudo apt-get dist-upgrade
sudo reboot

dist-upgrade et non upgrade, pour que les nouveaux paquets qu'un noyau amène soient installés plutôt que retenus. Le résumé qu'apt affiche avant de demander confirmation doit correspondre à la simulation déjà lue : une suppression de paquet non prévue est un motif d'arrêt.

Au retour, la pile ne remonte pas seule, down ayant supprimé les conteneurs :

nvidia-smi
docker compose up -d
docker compose ps

nvidia-smi passe en premier, avant de démarrer quoi que ce soit : il doit nommer la carte et une version de pilote, et non répondre « No devices were found » ni Failed to initialize NVML: Driver/library version mismatch. docker compose ps doit ensuite montrer les quatre services whisper, ollama, backend et frontend en healthy ; comptez quelques minutes, le backend attendant Whisper et Ollama, dont le premier chargement de large-v3 dépasse deux minutes. Le serveur n'est rendu aux médecins qu'après ces deux témoins.

Si le GPU ne revient pas

nvidia-smi en échec après le redémarrage veut dire que le module manque pour le noyau qui tourne. Sans GPU, Whisper ne démarre pas, donc le backend non plus : l'application est indisponible, et cela se tranche avant de rendre le serveur.

uname -r
modinfo nvidia | grep -E 'version|license'
dkms status
grep -iE 'nvidia|linux-image' /var/log/dpkg.log | grep -E 'upgrade|install'

Trois sorties, dans cet ordre de préférence :

  • poser le module précompilé du nouveau noyau, ce qui ne demande pas de redémarrage : sudo apt install linux-modules-nvidia-595-open-$(uname -r) puis sudo modprobe nvidia ;
  • revenir au noyau précédent, encore installé, en le choisissant dans le menu avancé de GRUB au démarrage : le GPU revient le temps de réparer ;
  • revenir à la version précédente d'un paquet du pilote, que le journal dpkg nomme : sudo apt install <paquet>=<version>, l'archive étant encore dans /var/cache/apt/archives.

Une fois la carte revenue, gelez toute la pile du pilote et non ses seuls modules, pour qu'une mise à jour manuelle ne la fasse pas diverger à son tour :

sudo apt-mark hold $(dpkg-query -W -f='${Package}\n' | grep -E '^(lib)?nvidia|^xserver-xorg-video-nvidia')

Le détail des deux pannes de pilote, leurs messages et la solution durable par DKMS sont dans Dépannage GPU.

Relancer le script

Le script d'installation peut être relancé à tout moment, par exemple pour poser le certificat ou après une mise à jour du serveur. Sur un serveur livré, c'est celui du disque, sudo bash /media/<vous>/<disque>/livraison-cause-<version>/setup.sh ; depuis les sources, c'est sudo ./scripts/setup.sh. Il réécrit les fichiers qu'il gère : .env.backend, certs/https.conf, qrcodes/print.html, et les configurations hostapd et dnsmasq du hotspot. Du fichier .env, il ne réécrit que la ligne CAUSE_HOME. Vos modifications manuelles dans ces fichiers sont perdues.

Un fichier échappe à cette règle, et c'est celui où vos réglages doivent vivre : ~/.cause/.env.backend.overrides vous appartient, et le script n'y écrit jamais rien. Les identifiants du stockage qu'il vous a demandés, il les garde dans son propre fichier, donc un second passage ne vous repose aucune question, pas même celles de la remontée des évaluations. Voir Votre fichier de réglages.

Sur un serveur installé depuis un disque de livraison, il réécrit aussi ~/.cause/docker-compose.yml et ~/.cause/.env, qui viennent du disque. La ligne OLLAMA_MODEL de ce .env est la seule exception : c'est là qu'on change de modèle, et le script la réécrit telle que le disque la porte. Notez votre choix ailleurs si vous l'avez modifié. Il en va de même de CAUSE_VERSION : relancer le script d'un disque plus ancien que la version qui tourne ramène celle du disque, et le docker compose up -d suivant redescend d'une version.

Quel compose fait foi

Sur un serveur livré, les commandes docker compose doivent viser le compose du client, ~/.cause/docker-compose.yml, et non celui du dépôt, qui construit les images depuis les sources. Le script d'installation déclare donc COMPOSE_FILE dans votre profil, ce qui rend justes toutes les commandes des manuels, où que vous les lanciez :

echo $COMPOSE_FILE          # /home/<vous>/.cause/docker-compose.yml
docker compose ps           # vise bien le compose du client

Si la variable est vide dans une session ouverte avant l'installation, rouvrez le terminal, ou passez le chemin à la main : docker compose -f ~/.cause/docker-compose.yml ps.

Hotspot Wi-Fi

Le script configure un point d'accès Cause, sans mot de passe, sur 192.168.4.1, qui répond à tous les noms de domaine par cette adresse et annonce un portail captif. Les QR codes de connexion au Wi-Fi et d'ouverture de l'application sont dans ~/.cause/qrcodes/, avec une page print.html servie en HTTP sur /print. Ils ne sont jamais régénérés tant que les fichiers existent, pour que les impressions restent valables.

Les deux familles de gestionnaire réseau

Prendre l'interface Wi-Fi n'est pas le même geste selon qui la tient. Le script reconnaît deux familles, et nomme le cas où il n'en reconnaît aucune.

Ce que le script trouve Ce qu'il fait de l'interface
nmcli présent et NetworkManager actif Pose /etc/NetworkManager/conf.d/99-unmanaged-<iface>.conf et recharge NetworkManager.
systemd-networkd actif (machine en netplan) Arrête et masque netplan-wpa-<iface>.service, puis pose /etc/systemd/network/00-cause-hotspot-<iface>.network avec Unmanaged=yes. Ce fichier est dans /etc, qui prime sur le /run où netplan engendre le sien : vos fichiers netplan ne sont jamais réécrits.
ni l'un ni l'autre L'étape échoue en le disant : personne à qui demander l'interface. Installez avec CAUSE_NO_HOTSPOT=1, ou démarrez l'un des deux gestionnaires.

Le refus de couper votre propre session

Activer le point d'accès coupe l'interface qu'il prend. Si cette interface porte la route par défaut de la machine, ou la session SSH par laquelle vous installez, le script refuse et s'arrête là.

Trois sorties, dans cet ordre de préférence :

  • désignez une autre interface : CAUSE_WIFI_IFACE=<iface> ;
  • installez sans point d'accès : CAUSE_NO_HOTSPOT=1 ;
  • assumez la coupure, devant la machine : CAUSE_HOTSPOT_COUPE_MA_SESSION=1.

Revenir en Wi-Fi client

sudo ./scripts/back_to_wifi_client.sh                 # auto-détection de l'interface
sudo ./scripts/back_to_wifi_client.sh --name wlp10s0  # interface désignée

Le script arrête hostapd, dnsmasq et cause-hotspot-ip, retire les deux marqueurs unmanaged et le masque de netplan-wpa-<iface>.service, puis rend l'interface au gestionnaire actif : rechargement de NetworkManager, ou relance de systemd-networkd et démarrage du service wpa pour une machine en netplan. Tout ce que posait le point d'accès se défait, et le réseau de station reste déclaré là où il l'était, dans /etc/netplan/.

Le chatbot de l'aide

L'entrée Aide de l'application sert les manuels et répond aux questions à partir d'eux. Deux conséquences pour vous :

  • une question passe par la même file que les générations de rapport. Sur un serveur occupé, la réponse attend son tour ;
  • le manuel administrateur n'est visible que des comptes administrateur, et le manuel développeur n'est pas installé du tout sur un serveur de production.

Changer de modèle de langue

OLLAMA_MODEL=gemma4:26b docker compose up -d

Ollama télécharge le modèle au démarrage du conteneur. Vérifiez la VRAM disponible avant : un modèle trop gros pour la carte se traduit par des générations très lentes ou des gels.