|

Apps – OpenWebUI

Ce billet vient en complément et suite de l’article Apps – Ollama. Ce dernier (promis juré, craché sera assez rapide) sera un billet court.

Pourquoi ?

Parce que tout ou une grande partie à déjà été dit dans l’article traitant d’Ollama. J’avais jugé trop indigeste de fondre les deux sujets. Donc ici, la partie théorie sera plutôt courte et en conséquence la partie Pratique un peu plus étoffée concernant l’implémentation et la configuration de l’application.

Bref, l’objectif de cet article sera d’implémenter une interface graphique publiée pour faciliter l’usage de notre solution Ollama plurimodales (LLM1) aux utilisateurs. J’ai pris le parti d’utiliser la solution OpenWebUI.

Avant-Propos

Vous l’avez bien compris, dans l’introduction, je n’ai pas l’attention de m’éterniser 10 000 ans sur le sujet.

Je pars du principe que nous avons déjà déployé et configuré la solution Ollama et quelques modèles de traitement IA2 sur un serveur.

J’ai une nouvelle fois pris le parti de déployer l’application sous une distribution RHEL3 afin d’avoir un niveau de sécurité optimale. Toutefois et vous pourrez le constatez par la suite dans la partie théorie, je ne respecterai pas l’architecture par faute de ressource disponible néanmoins, je recommande de suivre l’architecture qui sera à venir.

Dans le cadre d’une solution d’IA hébergée localement se pose la question de mettre en place un tel portail dans une organisation. A cela je répondrai que cela permettra de garder un certain contrôle des requêtes, prompts, effectués par l’ensemble des utilisateurs de l’organisation et d’en faciliter les usages. Dans des domaines sensibles tels que l’industrie et production cela peut aux yeux de la direction et de la DSI4 se révéler rassurant.

Prérequis

  • SE :
    • Rocky Linux 9.x et version ultérieures (pour ma part ça sera du 10.x)
    • macOS
    • Windows
  • Apps : 
    • Ollama
  • Autres :
    • Minimum 4 CPU5
    • Minimum 4 Gio de RAM6
    • Stockage rapide (SSD7 ou Nvme)

Théorie

KesaKo OpenWebUI

Je pense comme toujours qu’il est important de se poser la traditionnelle question. Que fait l’application que nous allons utiliser, comment elle fonctionne et qui est derrière.

Qu’est-ce que c’est ?

OpenWebUI, c’est une interface web issue d’une projet open-source (#biscotto) qui permet d’utiliser des modèles d’IA depuis un navigateur WEB hébergé localement.

Le projet est né du sud-coréen Tim Jaeryang BAEK qui prône l’autonomie numérique face aux grands du monde de la tech. Le projet est développé en Python (version supérieur ou égale à 3.11) pour la partie BackEnd et HTML/CSS, Svelte, JS et TS pour la partie FrontEnd.

Personnellement, je trouve que l’interface ressemble fortement à celle de ChatGPT. Mais son mon avis personnel hein 🙂

A quoi ça sert ?

La solution va permettre comme les outils classiques en ligne (pour ne pas dire les géants du Cloud) d’effectuer des opérations (énumération en approche) :

  • Discuter avec des modèles d’IA (LLM)
  • Exploiter des modèles localement (LLM)
  • S’Interfacer avec des sources locales et clouds (et oui nous pouvons faire de l’hybridation).
  • Téléverser (waouh, tu n’as pas écrit uploader !) des fichiers pour les analyser par l’IA

Nous retrouvons donc dans l’ensemble les mêmes fonctionnalités que les outils en ligne quant à l’usage de l’IA 🙂 C’est cool non ? <3

Avantages / Inconvénients

L’utilisation d’OpenWebUI n’est pas sans conséquences vis à vis des services qu’il peut apporter.

Il suffit de regarder le constat déjà porté sur l’utilisation d’Ollama.

AvantagesInconvénients
Open Source
Respect de la vie privée
Cross Compatibilité des modèles
Simplicité de l’interface
Installation technique
Modèles locaux couteux en ressources (RAM, CPU, GPU8, Stockage)

Bon, cela je vous dirai l’utilisateur il s’en tamponne le coquillard. Lui que ce soit technique à mettre en place, il s’en lustre l’asperge. Par contre, si la réponse est lente, c’est le Game Over.

Logique, pourquoi utiliser la solution locale qui met une plombe à répondre alors qu’un bon vieux Gémini, Copilot, ChatGPT te donne la réponse en 1/4 de seconde après une petite pression sur la touche Enter du clavier ?

Ce qui nous ramène selon moi à l’un des plus grands maux de la société actuelle. Nous ne savons ce que signifie attendre et être patient. Si dans les années 60 la compilation d’un programme pouvait prendre la nuit, aujourd’hui cela prendra quelques minutes. L’essor des nouvelles technologies, nous apportent de par les performances et leurs mobilités l’informations et l’assurance de pouvoir joindre un individu ou une ressource si quand bien même nous avons du réseau. Toutefois, force est de constaté que si comme cela s’est passé plusieurs fois au cours de l’année 2025 et 2026 l’un des maillons qui assure le fonctionnement tombe et nous coupe de nos ressources, nous nous retrouvons comme une poule qui a trouvé un couteau. Je pense et cela n’est qu’un point de vue personnel que nous devrions nous remettre en question face à cette forme de dépendance et servitude, Master & Servant, je dis STOP ! #sendkissesagain

Architecture & Best Pratices

Lors de la présentation de la solution, j’ai introduit la possibilité d’utiliser OpenWebUI en mode hybride. Ce qui implique donc une communication des flux sortants via les protocoles HTTP9 et HTTPS10 vers les IAs compatibles dans le cloud et autres repos propres au SE11 et maintien de l’application. Afin de ne pas surcharger inutilement le schéma, j’ai totalement exclu cette partie. Néanmoins il conviendra de réaliser un filtrage strict en sortie des flux pour des raisons de sécurité. Donc de jouer avec les politiques de votre UTMs12.

Bien, rentrons concrètement dans l’architecture. La solution applicative OpenWebUI vient se placer entre le poste client et l’application Ollama.

Il convient dans le respect des bonnes pratiques de dissocier OpenWebUI de Ollama puisque nous allons publier, ouvrir l’application en interne et pourquoi pas depuis nos WANs13. Cela implique de mettre OpenWebUI dans un réseau isolé (DMZ14) et Ollama dans le réseau T1, T1′ par exemple.

Par défaut, dans un fonctionnement UNIX dans un déploiement hors Docker, le port de communication de l’interface Web se fait via le protocole http sur le port 8080. Il est néanmoins possible de forcer les connexions en https sur le port 443 via nginx (je reviendrai plus en détail sur ce point).

Si nous regardons d’un peu plus près l’architecture de la VM15, nous pouvons constater que j’ai dédié un disque. Mais pourquoi ?

OpenWebUI est très très lourd. Son déploiement peut se révéler un poil pénible pour ne pas dire chiant à mourir (je reviendrai là-dessus dans la partie pratique).

https://unsplash.com/fr/photos/un-homme-portant-ses-mains-a-ses-oreilles-gK_ifZrZ0oc

Bref, pour pallier à ce problème, j’ai dédié un disque d’une capacité de 100 Gio.

Bien, on se lance dans la pratique ? Après on est bien aussi ici 🙂

Pratique

Oui j’ai dit plus haut que je ne respecterai pas cette topologie. Par manque de ressource, je vais tailler à la hussarde et tout mutualiser sur une seule machine. Il faut bien faire avec les moyens que l’on a, c’est comme ça.

Installation

OpenWebUI propose plusieurs modes de déploiement :

  • Docker : Officiellement supporté et recommandé pour la plupart des utilisateurs (avec plusieurs sous-catégorie alternatives de déploiement)
  • Python : Orienté pour les environnements faibles en ressources et pour une installation manuelle
    • uv
    • conda
    • venv
  • Kubernetes : Idéal pour les déploiements en entreprise qui nécessite une adaptabilité et ordonnancement
    • Helm
  • Third Party

Pour ma part, la seule chose qui m’intéresse et pour laquelle je suis à peu près à l’aise c’est Python. Je vois déjà les puristes de la containerisation me rétorquer sauvagement « Pourquoi pas Docker ?« . Encore une fois et je suis désolé, je ne comprends pas l’intérêt de Docker dans mon métier. Tant que je n’aurai pas pleinement assimilé ou eu le besoin de l’utilisé, je bloque. Attention, je ne dis pas que c’est de la me*de hein ? Loin de moi l’idée.

Donc je partirai sur Python en environnement virtuel (venv).

Pourquoi ce choix ? Simplement parce que RHEL utilise également Python dans une version différente et qu’à ce jour la version 3.12 n’est pas encore officiellement supporté par OpenWebUI… Rien que pour ça je déteste Python autant que Java. Pourquoi faire simple…

Dans mon répertoire ollama, j’ai créé un second répertoire openwebui.

Une fois déplacé dans le répertoire, nous créons l’environnement virtuel venv. Original comme nom n’est il pas ?

Etant toujours dans le même répertoire créé précédemment, nous allons activer notre environnement virtuel.

Maintenant que tout est bon, il est toujours dans notre environnement virtuel de démarrer l’installation. Cette dernière se déroulera en 3 temps.

  • La mise à jour de pip
  • La création d’un répertoire temporaire pour stocker les sources de l’installation qui sont plutôt volumineuse
  • L’installation en faisant référence à notre répertoire temporaire

Simple comme bonjour, et pourtant sachez que je me suis bien cassé la tête pour trouver l’astuce du répertoire temporaire…

Lors de l’installation si comme moi vous avez des ressources limitées, aucun respect des préconisations matériels. Je vous recommande de tuer le temps pendant facile une heure et de laisser tranquille votre bécanne. Tiens, je reparle de patience, comme de par hasard 🙂

Sans hésitez, la réponse D Jean-Pierre, ça sera mon dernier goulot.

Blague mise à part, 291 packages à installer avec des grosses dépendances pour certains d’entre eux (Nvidia par exemple…). Reprenez un peu de la réponse D en attendant.

Toujours pas terminé ? Bon ba comme on dit j’ai nous « On va pas la mécher [la bouteille naturellement] », terminez là et allez en chercher une nouvelle pour poursuivre la connaissance de la réponse D.

Si vous n’êtes pas tombé et que vous n’êtes plus très sobre, c’est bon signe. 🙂 Normalement, l’installation est terminée.

/!\Attention :L’abus des bons produits et notamment d’alcool est dangereux pour la santé. (Que l’on n’aille pas dire que je fasse de la publicité ou l’apologie de l’alcool #loievin)

Vérifions que cela fonctionne avec la première commande.

C’est rassurant de se dire que cela fonctionne. Pas de message d’erreur. Il ne nous reste maintenant plus qu’à démarrer notre service sur son interface et son port avec la commande ci-dessous.

Toutefois, il conviendra d’autoriser les flux au niveau de l’UTM local à notre serveur en amont. Pour cela, j’explique dans la partie suivante Configuration comment ouvrir les flux http sur le port 8080/tcp.

Normalement, l’ensemble des composants sont en cours de chargement et une fois l’ensemble chargé, vous devriez avoir dans votre CLI16 la chose suivante.

Il est à noter que des notes d’informations vont vite prendre le relai, ces derniers affichant l’ensemble des requêtes HTTP.

Si nous sommes à ce stade c’est que tout est bon 🙂 Avant de rentrer plus en détail de l’outil, il est nécessaire de regarder la partie Configuration.

Configuration

Cette partie pourrait je pense être amélioré. L’un des points les plus complexes et de mettre en place le mode service (j’y ai passé un temps certain…).

Pour des raisons de sécurité, il convient de créer un utilisateur dédié pour notre application. Nous partons sur un compte de service et non un compte utilisateur.

Un petit changement de propriétaire et le tour est joué.

Par défaut, le port qui sera utilisé pour accéder à l’interface web d’OpenWebUI est 8080/tcp en http.

On est d’accord que si nous souhaiterions mieux faire en termes de sécurité, il serait plus intelligent de créer la policy suivante :

Ayant un UTM en amont, je me permets donc de me contenter de la première solution.

Un petit test de vérification depuis le poste source :

Commençons par créer notre fichier de service. La difficulté que j’ai rencontrée ici est de jouer avec l’environnement virtuel et le service OpenWebUI à démarrer. J’ai bien trouvé une solution de contournement à mon problème toutefois cela me laisse un gout amer car la solution ne me semble pas propre du tout.

C’est je pense l’un des points qui me frustre le plus dans le monde du libre, l’imbrication de solution tierce et dans un sens une non-standardisation des méthodes d’implémentations. Bien que je doive le reconnaitre c’est ce qui fait tout son charme. Je t’aime moi non plus. On se croirait dans Andromaque…

Regardons d’un peu plus près le contenu de ce fichier.

Nous retrouvons la description de notre service ainsi que son ordre de démarrage. OpenWebUI étant intimement lié à Ollama, il convient donc de définir ce dernier comme service requis.

Au niveau du service, nous sommes sur un type simple, le service étant exécuter par l’utilisateur dédié openwebui. Il peut être acceptable bien que s’est sacrément cochon d’utiliser le compte root. Mais bon nous voulons éviter de faire étalage de notre amateurisme.

WorkingDirectory représente le répertoire de travail dans lequel le programme sera exécuté.
Environment permet de définir les variables d’environnement.

ExecStart permet d’exécuter la commande. Dans mon cas, j’en ai deux dont une qui est commentée (et qui ne fonctionne pas).

Restart permet de définir le rédémarrage automatique dans le cas où le service viendrait à crash par exemple.
RestartSec défini le délai de redémarrage en seconde.

Et pour finir, WantedBy pour spécifier le mode de service. Ici cela équivaut au mode normal.

[Unit]
Description=Open WebUI Service
After=network.target ollama.service Requires=ollama.service

[Service]
Type=simple
User=openwebui

WorkingDirectory=/mnt/ollama/openwebui Environment="TMPDIR=/mnt/ollama/tmp" Environment="PATH=/mnt/ollama/openwebui/venv/bin:/usr/bin:/usr/sbin"

#ExecStart=/mnt/ollama/openwebui/venv/bin/open-webui serve --host 10.227.250.11 --port 8080 ExecStart=/bin/bash -c 'source /mnt/ollama/openwebui/venv/bin/activate && open-webui serve --host 10.227.250.11 --port 8080'

Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Comme nous venons de créer un nouveau service, il sera nécessaire de recharger l’ensemble du systemctl pour intégrer le nouveau service à la liste des services disponible. Après, j’ai envie de dire c’est le schéma classique d’activation, démarrage et vérification.

Nginx, HTTPS

Je suis partagé quant à la rédaction de cette sous partie. Oui il est important de sécuriser l’accès à l’interface Web, mais j’ai peur d’être hors sujet et d’alourdir l’article. Mais d’un autre côté, je ne veux pas non plus bâcler la chose… Encore un pu**n de choix cornélien…

Bref, je vais faire au plus simple et si besoin écrirai un billet sur Nginx.

L’installation et activation du service, je passe mon tour.

Là encore une fois je rencontre un petit problème. Je n’ai pas de certificat public que je pourrais utiliser, ma topologie réseau ne me permet pas d’utiliser un certificat Let’s Encrypt et sans vouloir spoiler je n’ai pas envie de me prendre la tête. Donc ça va finir avec un bon vieux certificat autosigné 🙂

La génération n’est pas bien compliquée et si besoin, j’ai déjà rédigé un article sur le sujet.

Créons le répertoire qui va contenir nos éléments de sécurité :

Générons notre certificat :

Créons et éditons notre fichier de configuration. Je prendrai le temps d’expliquer l’intégralité du fichier de configuration.

server {
    listen 80;
    server_name 10.227.250.11;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name 10.227.250.11;

    ssl_certificate /etc/nginx/ssl/openwebui.crt;
    ssl_certificate_key /etc/nginx/ssl/openwebui.key;

    # TLS sécurisé
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;

    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:10m;

    # sécurité headers
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";

    # taille upload
    client_max_body_size 100M;

    # compression
    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml;

    location / {

        proxy_pass http://10.227.250.11:8080;

        proxy_http_version 1.1;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket support (important OpenWebUI)
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_read_timeout 3600;
        proxy_send_timeout 3600;
    }
}
La première section permet de réaliser la redirection du protocole HTTP vers HTTPS de manière permanente. Ainsi si je contacte l’url http://10.227.250.11, je serais automatiquement redirigé vers https://10.227.254.11.
Le second bloc indique que nous écoutons sur le port 443 sur l’interface 10.227.250.11. L’emplacement des fichiers contenant la clé privée ainsi que le certificat sont spécifiés pour activer la couche de sécurité (j’ai l’impression d’enfoncer des portes ouvertes…). J’indique qu’elles sont les versions des protocoles TLS17 autorisées et laisse le serveur choisir le chiffrement le plus sûr.
Côté en-tête, j’ai repris les suggestions de sécurité implémentée pour ITOP. Soit la restriction de mettre le site en iframe externe, de restreindre le navigateur à deviner le tye MIME qui est utilisé et surtout d’activer la protection anti-cross-site scripting. Niveau upload, j’ai vu large et j’ai autorisé le téléversement de fichier de 100 Mio.
Côté performance, j’ai activé la compression des flux pour optimiser un peu les performances réseaux.
Le troisième bloc concerne la partie Reverse Proxy.
Toutes les requêtes qui sont envoyées sur https://10.227.250.11 sont redirigées vers le port 8080.
Concernant les en-têtes il est nécessaire d’utiliser les websockets car seront transmis l’ip du client, le protocole utilisé et le NDD18 ou l’IP demandé.
Les deux lignes suivantes permettent de réaliser la jonction entre nginx et les websockets.

Une fois la configuration faite, il est important de réaliser un petit contrôle de notre petit fichier afin de s’assurer que ce dernier ne comporte pas d’erreur.

Il faudra penser à recharger la configuration et hop le tour est joué.

Nous souhaitons toujours publier notre interface web en HTTPS. Donc il va falloir jouer avec nos UTMs !

Sur le plan interne, autorisons nos flux https :

Nous pourrions comme énoncé plus haut pour le protocole HTTP être bien plus rigoureux dans notre règle de sécurité. Mais encore une fois ayant un UTM en amont, j’accepte le risque. Juste ne pas oublier de créer la règle sinon vous passez pour un c*n.

Si nous sommes joueurs, nous pouvons essayer de joindre directement notre interface web depuis notre navigateur. Normalement vous devez vous prendre dans les dents

Ca vous fait la b**e hein ? Je vous rassure je me suis pris la même rouste. J’ai oublié d’autoriser le contexte de mon SELinux pour autoriser les services http à accéder aux réseaux 😀

Un petit F5 et hop c’est tout bon <3

Normalement avec toutes nos actions, nous devons joindre notre interface web depuis un navigateur de manière sécurisée (hormis l’erreur liée à notre certificat autosigné).

Allez, on passe à la partie démonstration ?

Démonstration

Pour le test, j’ai décidé de changer un peu de format et de passer par des formats vidéo. Surtout que dans ce cas précis, cela évitera de mon côté de blablater. Cependant, ce n’est pas pour autant que le travail est plus simple ou plus rapide.

J’ai également pris le parti de commenter mes enregistrements et vidéos en anglais (oui même avec mon anglais aussi rouillé qu’une vieille cantine de bidasse, si vous voulez trikitez mes vidéos je vous laisse ce plaisir 🙂 ).

Conclusion

La combinaison des solutions OpenWebUI et Ollama fonctionne à la perfection. Si nous associons le côté multimodèle d’Ollama et son fonctionnement hybride avec la puissance de l’interface OpenWebUI il est alors possible de fournir une solution complète à destination des utilisateurs.

Toutefois, je reste profondément frustré pour l’instant. L’ajout de la couche OpenWebUI vient rallentir et allourdir l’usage d’Ollama. D’un autre côté, est ce vraiment pertinent comme remarque à savoir que je ne réponds à aucun prérequis matériel ? C’est là où le bât blesse encore une fois. Cela fonctionne mais à quel prix ?

Il est également important de prendre en compte les couts annexes et variables (comme au hasard, la consommation électrique, refroidissement…) périphériques à l’usage de l’IA. Nous parlons du réchauffement climatique, de limiter nos émissions de carbone et j’en passe pour d’un autre côté user de l’IA à tort ou à raison. Je ne tiens pas à lancer un débat climatosceptique ou sur est-ce bien ou non d’utiliser l’IA.

Je pense simplement qu’il y a encore beaucoup à faire pour accorder en synergie la puissance et l’usage de l’IA avec les convictions écologiques et environnementales. Si nous être vivant allons avoir chaud, que dire des CPUs/GPUs ? #tousdanslemêmebateau Bref, un moratoire serait nécessaire en somme. Mais je laisse ça au pisse violon que je suis.

Je reste convaincu de la fiabilité et viabilité de la solution dans un milieu professionnel, malheureusement, pour moi je préfère me passer de l’interface Web. Donc en d’autres termes, je vais rollback mon snapshot (ou restaurer mon backup au choix). Après tout, je peux toujours utiliser l’interface CLI directement depuis une session SSH19 ? Oui mais pas que.

Pourquoi ne pas faire une petite interface qui exploite l’API Ollama ?

Ah nous y voila ! Ca sent le projet long comme un jour sans pain encore en approche. Blague mis à part, peut être que cela n’aboutira pas. Mais j’ai envie de pousser relativement loin la solution Ollama.

Pour faire vraiment simple. OpenWebUI c’est supercool mais il faut les ressources pour pouvoir l’utiliser pleinement pour ne pas souffrir d’une grande frustration. 🙂

Le mot de la fin :

Je branche OpenWebUI sur une prise RJ45 en camembert ! Je contre avec un lama qui compile une girafe. Vous ne pouvez pas, il y a Sandrine ROUSSEAU au festival de la tête de veau.

Erwan GUILLEMARD

Sources

  1. LLM : Large Language Models ↩︎
  2. IA : Intelligence Artificielle ↩︎
  3. RHEL : RedHat Entreprise Linux ↩︎
  4. DSI : Direction des Services Informatiques ↩︎
  5. CPU : Central Processing Unit ↩︎
  6. RAM : Random Access Memory ↩︎
  7. SSD : Solid State Drive ↩︎
  8. GPU : Graphics Processing Unit ↩︎
  9. HTTP : Hypertext Transfer Protocol ↩︎
  10. HTTPS : Hypertext Transfer Protocol Secure ↩︎
  11. SE : Système d’Exploitation ↩︎
  12. UTM : Unified Threat Management ↩︎
  13. WAN : Wide Area Network ↩︎
  14. DMZ : DeMilitarized Zone ↩︎
  15. VM : Virtual Machine ↩︎
  16. CLI : Command Line Interface ↩︎
  17. TLS : Transport Layer Security ↩︎
  18. NDD : Nom De Domaine ↩︎
  19. SSH : Secure SHell ↩︎