Table des matières de l'article :
Lors d'une migration Linux en entreprise, le problème réside rarement dans la simple mise à niveau du système d'exploitation. Le véritable problème est l'ensemble des éléments construits sur ce système au fil des ans : paquets hérités, dépendances obsolètes, scripts d'initialisation anciens, configurations d'applications, hypothèses implicites et composants qui, malgré leur ancienneté, continuent de jouer un rôle crucial en production.
Il s’agit du cas d’un client qui devait migrer 43 systèmes de CentOS 7 vers RHEL 9 , tout en maintenant une exigence technique très stricte : continuer à utiliser Varnish Cache 3.0.7.
Il ne s'agissait pas d'une préférence arbitraire pour des logiciels anciens. L'infrastructure utilisait de nombreuses VCL 3.x personnalisées , développées au fil des ans, souvent composées de milliers de lignes et profondément intégrées à la logique applicative. Ces VCL géraient la mise en cache, le contournement, les backends, les en-têtes, la normalisation des requêtes, les exceptions, les comportements spécifiques et la logique métier.
Les réécrire pour Varnish 4, Varnish 6 ou une version ultérieure n'impliquait aucune modification des directives. Cela impliquait de lancer un projet distinct pour l'analyse, le portage, les tests fonctionnels, les tests de charge, la validation de l'application et une comparaison approfondie des comportements ancien et nouveau.
C’est pourquoi nous avons opté pour une approche pragmatique : migrer le système d’exploitation vers RHEL 9 sans modifier le comportement de Varnish 3. Pour ce faire, il ne suffisait cependant pas d’installer les anciens paquets RPM EL7. Sous EL9, ces paquets ne fonctionnaient plus correctement. Une seule solution s’imposait : récupérer le code source officiel et recompiler le paquet pour RHEL 9 et ses dérivés.
Le dépôt GitHub de reconstruction de Varnish 3 pour EL9
Les résultats de ce travail ont été publiés dans un dépôt GitHub dédié :
Dépôt : https://github.com/MarcoMarcoaldi/VARNISH3-EL9
Le dépôt contient la reconstruction des paquets RPM officiels de Cache Varnish 3.0.7 / RHEL 9, AlmaLinux 9 et Rocky Linux 9, basé sur le RPM source original varnish-3.0.7-1.el7.centos.src.rpm publié par Varnish Software.
Il ne s'agit pas d'une version dérivée fonctionnelle de Varnish, ni d'une version modernisée du logiciel. C'est une reconstruction ciblée, avec un minimum de correctifs et de modifications de la spécification RPM, conçue pour un cas d'utilisation spécifique : maintenir la compatibilité avec les anciennes configurations VCL 3.x lors d'une migration vers EL9.
Cette distinction est cruciale. Varnish 3 n'est plus pris en charge depuis 2015, ne bénéficie plus de correctifs de sécurité et ne doit pas être utilisé pour les nouveaux déploiements. Pour les nouveaux projets, il est recommandé d'utiliser Varnish 6.0 LTS ou une version ultérieure. Dans ce cas précis, cependant, l'objectif n'était pas de concevoir une nouvelle architecture de cache, mais de permettre la migration de 43 systèmes existants depuis CentOS 7 sans impacter les milliers de lignes de VCL déjà en production.
Pourquoi Varnish 4 a-t-il rompu la compatibilité avec Varnish 3 ?
Le principal problème technique est la rupture de compatibilité introduite à partir de Varnish 4. Le langage VCL n'est pas resté inchangé : il a été considérablement restructuré, avec des changements incompatibles par rapport à la syntaxe et au modèle logique de Varnish 3.
Quelques exemples permettent de comprendre immédiatement l'ampleur du changement :
| Vernis 3 | Vernis 4 et suivants | Impacter |
|---|---|---|
vcl_fetch |
vcl_backend_response |
Modifiez le point du flux où la réponse est traitée par le backend. |
req.request |
req.method |
Modifiez la variable utilisée pour lire la méthode HTTP |
error |
synth |
Modifier la logique de génération des réponses synthétiques |
Actions return VCL3 |
Actions return réarrangé |
Modifier le comportement du flux de décision |
Dans une configuration simple, ces modifications peuvent être prises en charge avec un minimum d'interventions manuelles. Dans une configuration d'entreprise, avec des milliers de lignes VCL et des années de logique en couches, la situation est différente.
Un fichier VCL de production n'est pas qu'un simple fichier de configuration. Il fait souvent partie intégrante du comportement de l'application. Il détermine quand une requête doit être mise en cache, quand elle doit être transmise au serveur, quand un cookie doit entraîner un contournement, quand un en-tête doit être supprimé, quand une URL doit être normalisée, quand une réponse doit avoir des TTL différenciés ou quand une erreur doit être transformée en une réponse synthétique.
Une mauvaise traduction, même d'une seule de ces logiques, peut entraîner de graves problèmes : mise en cache de contenu privé, contournements manqués, surcharge du serveur dorsal, erreurs d'API, perte d'en-têtes d'application, modifications inattendues des TTL ou différences difficiles à diagnostiquer dans le comportement côté client.
C’est pourquoi, dans le cadre de ce projet, la migration vers VCL 4 ou une version ultérieure a été délibérément exclue de la migration du système d’exploitation. Cela aurait été techniquement possible, mais pas raisonnable compte tenu des délais impartis.
Pourquoi les anciennes lampes RPM EL7 ne pouvaient pas être réutilisées sur les EL9
La première étape naturelle lors de la maintenance d'une version existante consiste à vérifier si les paquets existants peuvent être installés sur le nouveau système d'exploitation. Dans notre cas, le paquet historique était varnish-3.0.7-1.el7.centos.
Sous EL8, certaines dépendances restaient disponibles via des paquets de compatibilité. Sous EL9, en revanche, l'ancien binaire était devenu incompatible pour des raisons structurelles. Les dépendances d'exécution requises par le paquet EL7 n'étaient plus présentes ou ne pouvaient plus être résolues de la même manière.
| Dépendance d'exécution EL7 | Situation sur EL8 | Situation sur EL9 |
|---|---|---|
libncurses.so.5 / libtinfo.so.5 |
Disponible via ncurses-compat-libs |
Supprimé |
libnsl.so.1 |
Disponible via libnsl |
Supprimé, disponible uniquement libnsl.so.2 |
/sbin/chkconfig e /sbin/service |
Dépendances encore résolubles | Dépendances non résolues dues à UsrMove |
Cela excluait une adaptation superficielle. Créer des liens symboliques, forcer l'installation ou ignorer les dépendances ne suffisait pas. Le problème était plus profond : le binaire avait été compilé pour un environnement d'exécution différent.
La conclusion était claire : Varnish 3.0.7 devait être recompilé à partir de la source , générant des RPM compatibles avec RHEL 9, AlmaLinux 9 et Rocky Linux 9.
Récupération du RPM source officiel
Le travail a débuté à partir du RPM source original déclaré dans les métadonnées des paquets historiques : varnish-3.0.7-1.el7.centos.src.rpm.
L'ancien dépôt historique repo.varnish-cache.org Il n'était plus disponible, mais le paquet RPM source était toujours disponible dans le dépôt. varnishcache/varnish30 sur packagecloud. Une fois téléchargé et extrait, le paquet contenait les deux éléments essentiels pour continuer :
varnish-3.0.7.tar.gz, c'est-à-dire l'archive source en amont ;varnish.spec, c'est-à-dire le fichier de spécification RPM original pour EL7.
À partir de là, le travail d'adaptation a commencé. L'objectif n'était pas de modifier le comportement de Varnish, mais d'appliquer le minimum de changements nécessaires pour obtenir un paquet qui compile et, surtout, qui s'exécute correctement sur EL9.
Version 3.0.7-2 : Première adaptation des spécifications RPM à EL9
La première version reconstruite, 3.0.7-2 , impliquait l'adaptation initiale de la spécification et de la source à l'environnement de construction EL9.
Le premier problème résidait dans le code source inclus dans l'archive tar. La version historique de jemalloc intégrée à Varnish 3 contenait l'en-tête. <sys/sysctl.h>Cette référence a été supprimée dans les versions modernes de glibc. Sur EL9, qui est basé sur une version moderne de glibc, cette référence empêchait la compilation.
Un correctif minimal a ensuite été appliqué pour supprimer l'inclusion devenue obsolète. Il ne s'agit pas d'une modification fonctionnelle, mais d'une correction nécessaire pour permettre la compilation du code source de 2015 dans un environnement 2020+.
Le fichier de spécifications a ensuite nécessité plusieurs ajustements :
pythonil a été remplacé parpython3dansBuildRequires;groffil a été remplacé pargroff-base;libedit-develIl était géré via le référentiel CRB ;- Les dépendances des scriptlets sont désormais exprimées par nom de paquet, et non plus par chemin.
/sbin/*; - LTO a été désactivé ;
- La suite de tests a été rendue optionnelle via
--with check; - La vérification RPATH de rpmdevtools a été désactivée, car le RPATH vers les bibliothèques privées dans
/usr/lib64/varnishil est nécessaire.
Un point important concerne les scriptlets. Les anciens paquets s'appuyaient sur des fichiers de dépendances comme /sbin/chkconfig e /sbin/serviceSur EL9, avec UsrMove, ce modèle n'est plus fiable. La solution correcte consistait à déclarer les dépendances à des paquets comme chkconfig, initscripts e initscripts-service.
Les scripts SysV ont été conservés pour assurer la compatibilité avec EL7. Cela a permis de préserver le comportement opérationnel existant, notamment l'utilisation de /etc/sysconfig/varnish.
Initialisation SysV sur EL9 : choix hérité, mais utile pour la compatibilité
Varnish 3 a vu le jour à une époque où l'intégration de systemd n'était pas encore la norme. Afin de garantir une compatibilité maximale avec l'environnement précédent, les scripts SysV ont été conservés en grande partie inchangés.
Sur EL9, le service peut toujours être géré via systemd grâce à systemd-sysv-generator, qui génère dynamiquement des unités systemd à partir d'anciens scripts SysV.
systemctl enable --now varnish systemctl status varnish
Cela vous permet de conserver une gestion des services familière sans avoir à réécrire immédiatement toute la couche d'initialisation. Cette solution est conforme à l'objectif du projet : éviter les modifications inutiles lors de la migration du système d'exploitation.
Il subsiste toutefois une limitation connue. Ce choix convient à EL9, mais ne doit pas être considéré comme une stratégie à long terme. Dans les scénarios futurs, notamment avec RHEL 10 et les versions ultérieures, l'utilisation d'unités systemd natives sera nécessaire.
Version 3.0.7-3 : Plantage SIGSEGV causé par jemalloc
Après la première reconstruction, le paquet a été compilé et installé. Le problème suivant n'est apparu qu'à l'exécution : varnishd plantage immédiat au démarrage avec un SIGSEGV.
Ce fut l'un des aspects les plus intéressants du dépannage, car la compilation était formellement correcte. Le problème n'apparaissait pas lors de la compilation, mais uniquement lorsque le démon s'exécutait sur EL9.
Débogage avec gdb, pris en charge par les paquets -debuginfo produit au cours de la construction, a montré la véritable chaîne :
varnishd
-> getgrnam("varnish")
-> nss-systemd
-> malloc_usable_size(NULL)
-> jemalloc legacy
-> SIGSEGV
Dans la phase de démarrage, varnishd résout le groupe varnish moyens getgrnam("varnish"). EL9 entre en jeu nss-systemd, que l'on peut appeler malloc_usable_size(NULL)Ce comportement est légal pour glibc.
Le problème est que Varnish 3 intégrait une version très ancienne de jemalloc, datant de 2008, qui interceptait les fonctions d'allocation au niveau du processus. Son implémentation de malloc_usable_size() n'a pas géré correctement le pointeur NULLLe résultat fut une erreur de segmentation immédiate, avant même varnishd pourrait imprimer un message utile.
La correction apportée dans la version 3.0.7-3 consistait à compiler Varnish avec :
--without-jemalloc
Si varnishd Utilise la bibliothèque malloc moderne de glibc, en contournant l'ancienne bibliothèque jemalloc intégrée. Ceci a résolu le plantage au démarrage sous EL9.
Version 3.0.7-4 : La compilation de VCL échoue sur les hôtes EL9 propres.
Une fois le plantage corrigé, un second problème d'exécution, encore plus subtil, est apparu. Le service démarrait correctement sur la machine de compilation, mais échouait sur un hôte EL9 minimal et propre lors de la compilation du VCL.
L'erreur était la suivante :
gcc: fatal error: cannot read spec file '/usr/lib/rpm/redhat/redhat-hardened-cc1'
Pour comprendre le problème, il faut se rappeler un détail architectural de Varnish : Varnish compile les VCL en code C à l'exécution. Cela signifie que gcc Il ne s'agit pas seulement d'une dépendance de compilation, mais aussi d'une dépendance d'exécution. Chaque VCL est transformée en code C, compilée et chargée par le démon.
Lors de la phase de compilation RPM sur EL9, la commande cc enregistré par Varnish incorporant des indicateurs dérivés de l'environnement RPM, y compris des références à :
-specs=/usr/lib/rpm/redhat/redhat-hardened-cc1
Ce fichier est présent sur la machine de compilation, où il est installé. redhat-rpm-configMais il n'est généralement pas présent sur un serveur de production minimal. Le paquet semblait donc fonctionner sur la machine de compilation, mais a échoué lors de son déploiement sur un système propre.
Le correctif a été appliqué dans la version. 3.0.7-4 il imposait une commande VCC_CC propre et portable lors de la configuration :
export VCC_CC='exec gcc -std=gnu99 -O2 -g -fpic -shared -Wl,-x -o %o %s'
Si varnishd Il enregistre une commande de compilation d'exécution indépendante des fichiers présents uniquement sur la machine de compilation. La VCL peut donc également être compilée sur des hôtes EL9 minimaux, à condition qu'elle soit installée. gcc, nécessaire à la conception.
Installation des paquets RPM
Une fois les RPM produits ou téléchargés depuis la page Releases du dépôt, l'installation se fait via dnf, laissant au gestionnaire de paquets le soin de résoudre automatiquement les dépendances :
cd /path/to/downloaded/rpms/ dnf install ./varnish*.rpm
Après l'installation, le service peut être activé et démarré normalement :
systemctl enable --now varnish curl -I http://localhost:6081/
La configuration reste dans les chemins historiques utilisés sur EL7 :
/etc/varnish/default.vcl /etc/sysconfig/varnish
Il s'agit d'un aspect pratique très important lors de la migration de 43 systèmes. Le maintien des mêmes chemins d'accès réduit les différences opérationnelles, simplifie le déploiement et permet la réutilisation des procédures, modèles et automatisations existants.
Compiler à partir des sources sur AlmaLinux 9 ou Rocky Linux 9
Le dépôt permet également de recompiler les paquets localement. Sur une machine AlmaLinux 9 ou Rocky Linux 9, la procédure est celle classique des paquets RPM :
dnf install -y rpm-build rpmdevtools gcc make ncurses-devel libxslt groff-base pcre-devel pkgconf-pkg-config python3 dnf config-manager --set-enabled crb dnf install -y libedit-devel rpmdev-setuptree cp SOURCES/* ~/rpmbuild/SOURCES/ cp SPECS/varnish.spec ~/rpmbuild/SPECS/ rpmbuild -ba ~/rpmbuild/SPECS/varnish.spec
Les RPM sont générés dans :
~/rpmbuild/RPMS/x86_64/
Le RPM source reconstruit est en revanche produit en :
~/rpmbuild/SRPMS/
Cela permet un processus reproductible, versionné et vérifiable, plutôt que de s'appuyer sur des installations manuelles ou des solutions de contournement non documentées.
Tests sur EL9 : la compilation du paquet ne suffit pas.
L'un des principaux enseignements de cet article est que, lors de la reconstruction d'un système existant, la compilation n'est que la première étape . Un paquet RPM peut être formellement correct, s'installer sans erreur, et pourtant ne pas être utilisable en production.
Dans notre cas, les problèmes les plus importants ne sont apparus qu'après :
- la référence à
<sys/sysctl.h>elle est apparue lors de la phase de compilation ; - Le bug de jemalloc n'est apparu qu'au démarrage
varnishdsur EL9 ; - le problème de
VCC_CCLe problème n'est apparu que sur un hôte propre autre que la machine de compilation.
Pour cette raison, les tests ont été effectués sur des systèmes EL9 minimaux, vérifiant non seulement le démarrage du service, mais aussi le chargement des VCL, des en-têtes HTTP, des caches HIT, le comportement sous charge et la visibilité via des outils tels que varnishstat.
Une vérification de base après le démarrage peut être effectuée avec :
curl -I http://localhost:6081/
Les en-têtes attendus comprennent des éléments tels que :
Via: 1.1 varnish X-Varnish: ...
Dans un environnement réel, cela ne suffit évidemment pas. Il faut valider les VCL d'application, les backends, les règles de contournement, la gestion des cookies, les TTL, les purges, les chemins d'erreur et tous les autres cas spécifiques qui ont été intégrés à la logique VCL 3.x au fil des ans.
Limitations connues de la solution
Cette solution ne transforme pas Varnish 3 en un logiciel moderne. Il s'agit d'un composant ancien, conservé ici uniquement pour des raisons de compatibilité.
Les principales limitations sont claires :
- Varnish 3 est en fin de vie et ne reçoit pas de mises à jour de sécurité en amont ;
- ne devrait pas être utilisé pour les nouveaux déploiements ;
- ne prend pas en charge le protocole TLS natif ;
- ne prend pas en charge HTTP/2 ;
- Utilise toujours des scripts SysV, gérés sur EL9 via
systemd-sysv-generator; - exige
gccau moment de l'exécution, car la VCL est compilée à la volée en code C.
En production, le protocole TLS doit être terminé en amont de Varnish, par exemple via nginx, HAProxy ou hitch. Pour les environnements futurs, il sera également judicieux d'envisager une migration vers des versions plus récentes de Varnish et une réécriture complète des VCL.
Parce que c'était le bon choix pour le client
En théorie, la réponse la plus simple aurait été : « Mettons également à jour Varnish. » En pratique, cependant, cela aurait été le choix le plus risqué.
Le client n'avait pas seulement besoin d'un nouveau système d'exploitation. Il devait assurer la continuité des applications sur 43 systèmes sans introduire simultanément quatre changements critiques :
- passage de CentOS 7 à RHEL 9 ;
- modification des bibliothèques d'exécution et système ;
- Changement majeur de version de Varnish ;
- Réécriture complète des VCL de la version 3.x à la version 4 ou ultérieure.
Il était plus judicieux de dissocier ces problèmes. Le système d'exploitation a été mis à jour en premier, tout en conservant le comportement du cache existant. Ce n'est que plus tard, dans le cadre d'un projet dédié, que nous avons pu envisager le portage des VCL vers Varnish 6 LTS ou des versions ultérieures.
C’est une approche que nous, chez Managed Server Srl, adoptons souvent dans des contextes complexes : éviter les migrations « big bang » lorsque les risques sont trop élevés et privilégier plutôt des solutions progressives, vérifiables et réversibles.
Conclusions
La reconstruction de Varnish Cache 3.0.7 pour RHEL 9, AlmaLinux 9 et Rocky Linux 9 est née d'un besoin concret : migrer 43 systèmes CentOS 7 vers RHEL 9 sans réécrire immédiatement VCL 3.x complexe et déjà validé en production.
Le travail ne se limitait pas à la relance d'un rpmbuildIl a fallu récupérer le RPM source officiel, adapter les spécifications à EL9 et corriger le problème de <sys/sysctl.h>, corriger le plantage induit par jemalloc, rendre la commande de compilation d'exécution VCL portable et tester le comportement sur des hôtes propres.
La compilation en elle-même était facile. Le plus difficile était d'obtenir un paquet qui s'installe, s'exécute, compile les VCL et fonctionne réellement sur EL9, et pas seulement sur la machine de compilation.
Le dépôt public est disponible ici :
https://github.com/MarcoMarcoaldi/VARNISH3-EL9
Il s'agit d'une solution de compatibilité, et non d'une recommandation pour les nouveaux projets. Mais dans un contexte réel, avec des systèmes existants, des VCL critiques et une migration d'infrastructure à mener à bien sans interruption de production, c'était précisément le type d'intervention nécessaire.
Car sur les systèmes Linux réels, la tâche la plus importante n'est pas toujours d'installer la dernière version disponible, mais plutôt de construire l'interface technique permettant d'y parvenir sans perturber le fonctionnement du service.


