Cet article documente le service de cache HTTP : d'où vient réellement le VCL, comment les backends sont routés, la stratégie de cache, la purge, et la montée en charge par shard director. L'image qui exécute tout cela — et pourquoi elle embarque un compilateur — a le sien.
Position dans la chaîne
flowchart LR
WAF["Requête inspectée<br/>par le pare-feu applicatif"] --> V["Varnish"]
V -->|"HIT"| R["Réponse depuis<br/>la mémoire"]
V -->|"MISS"| WP["Pod WordPress<br/>nginx + php-fpm"]
WP --> V
Varnish est derrière le pare-feu applicatif, pas devant : le trafic est inspecté avant d'être mis en cache. Sans cet ordre, une page mise en cache serait resservie indéfiniment sans jamais repasser par le contrôle. La chaîne complète depuis Internet est décrite dans le reverse proxy.
Le VCL n'est pas un fichier monté, c'est un produit du démarrage
C'est le point le plus contre-intuitif de ce service, et celui qu'on décrit généralement de travers.
Il existe bien une ConfigMap qui porte un default.vcl. Rien ne la monte. Le
pod n'a que deux volumes utiles : un emptyDir pour le VCL vivant, et un tmpfs
partagé pour le segment de mémoire du démon.
Le VCL réellement chargé est écrit au démarrage par un initContainer, dans cet
emptyDir. Il n'est pas rendu à partir d'un gabarit figé : l'init résout par DNS
lesquels de ses pairs répondent, puis compose le fichier en conséquence — un
backend par pair joignable, le shard director s'il y en a plus d'un, et les
sous-routines de routage.
La conséquence pratique compte : éditer la ConfigMap ne change rien. La configuration effective dépend de la topologie du StatefulSet au moment du boot, pas d'un fichier qu'on aurait posé à côté. C'est aussi ce qui rend la montée en charge décrite plus bas possible sans gabarit à rallonge.
Ce qui recharge le VCL surveille les pairs, pas la configuration
Un sidecar accompagne chaque pod. Il ne surveille pas la ConfigMap : il
surveille la joignabilité des pairs. Quand l'ensemble des pairs qui répondent
change — un pod ajouté, un pod parti — il régénère le VCL et l'active à chaud par
le port d'administration, sous un nom versionné (vcl.load puis vcl.use).
Ce sidecar tourne sur une image busybox distincte, pas sur l'image durcie de Varnish : il lui faut un shell pour faire ce travail. C'est une distinction qui a son importance quand on audite ce que contient une image durcie.
Backends et routage
Le VCL définit le backend applicatif avec sa sonde de santé :
backend jbsky {
.host = "wordpress.svc";
.port = "80";
.probe = {
.url = "/healthcheck";
.timeout = 2s;
.interval = 30s;
.window = 3;
.threshold = 2;
}
}
Le routage se fait sur l'en-tête Host.
L'URL de sonde doit être /healthcheck, pas /. L'application répond une
redirection permanente sur la racine, ce que Varnish interprète comme un échec :
le backend est alors marqué malade alors qu'il va parfaitement bien. C'est le
genre de sonde qui ment dans le mauvais sens — elle déclare une panne qui n'existe
pas.
Stratégie de cache
- Les pages sont mises en cache, la durée étant pilotée par les en-têtes du backend
- Les requêtes portant un cookie de session authentifiée contournent le cache
- Les requêtes
POSTne sont jamais mises en cache - L'administration et la page de connexion ne sont jamais mises en cache
- Les ressources statiques sont mises en cache longtemps
Purge
La purge passe par le port d'administration, depuis le container :
kubectl exec -n varnish statefulset/varnish -c varnish -- \
varnishadm -n /var/lib/varnish 'ban req.url ~ .'
Cette forme invalide tout le cache du site. En usage courant, il vaut mieux cibler par motif d'URL, ce qui laisse le reste du cache chaud :
varnishadm 'ban req.url ~ /mon-article'
StatefulSet plutôt que Deployment
Le réflexe serait un Deployment : un cache est sans état, ses données sont perdues au redémarrage de toute façon. C'est justement l'argument qui ne tient pas ici, et pour une raison propre à Varnish.
Varnish ne lit pas sa configuration, il la compile : le VCL devient du C, puis un objet partagé écrit dans son espace de travail. Cet espace n'est pas le cache — il est l'exécutable de la configuration en cours.
Deux conséquences suivent du même choix :
- Le nom de pod est stable. Les métriques restent comparables dans le temps et les commandes de purge reproductibles.
- Les pods se voient entre eux. Le shard director a besoin d'adresses fixes pour ses pairs ; un Deployment ne les fournit pas.
Le cache en mémoire, lui, est bien perdu à chaque redémarrage — et ce n'est pas un problème : c'est de la mémoire, elle se remplit à nouveau en quelques minutes.
Métriques
Le démon expose son compteur interne, lu par un container dédié qui partage le segment de mémoire : taux de succès du cache, santé des backends, occupation mémoire, connexions actives.
Montée en charge : le shard director
flowchart TB
IN["Requêtes entrantes"] --> V0["varnish-0"]
IN --> V1["varnish-1"]
IN --> V2["varnish-2"]
V0 -.->|"si pas propriétaire"| V1
V1 -.->|"si pas propriétaire"| V2
V2 -.->|"si pas propriétaire"| V0
V0 --> WP["Backend applicatif"]
V1 --> WP
V2 --> WP
Le shard director répartit les URL entre les pods par hachage. Chaque URL n'est mise en cache que par un seul pod, son propriétaire. C'est ce qui évite de dupliquer le même objet dans chaque réplique et fait monter le taux de succès global au lieu de le diluer.
Le trajet d'une requête :
- elle arrive sur n'importe quel pod, la répartition en amont étant indifférente
- le VCL calcule le propriétaire de cette URL
- si le pod courant est le propriétaire, il sert et met en cache localement
- sinon il transmet au pair propriétaire, marqué par un en-tête dédié
- le propriétaire met en cache et répond
Chaque pod connaît son propre rang, ce qui lui permet de savoir s'il est propriétaire sans aucune requête réseau.
Découverte des pairs
Les pods se découvrent par un Service headless : chaque réplique a un nom DNS stable. L'init ne consulte pas l'API du cluster — il tente simplement de résoudre chaque pair possible et retient ceux qui répondent. Un pair injoignable est absent du VCL généré, pas une erreur.
Mettre à l'échelle
kubectl scale statefulset varnish -n varnish --replicas=N
Les nouveaux pods composent leur VCL au démarrage. Les pods déjà en place n'ont pas besoin d'être redémarrés : leur sidecar voit apparaître un pair, régénère le VCL et l'active à chaud — en général en moins de dix secondes après le boot du nouveau pod.
Vérifier que la montée en charge a bien eu lieu
Le seul contrôle qui compte est celui qui prouve le rechargement à chaud, pas celui qui compte les pods :
kubectl scale statefulset varnish -n varnish --replicas=2
# Le rechargement a-t-il eu lieu sur le pod deja en place ?
kubectl logs varnish-0 -n varnish -c vcl-reload --tail=5
# attendu : un VCL versionne devenu actif
# Les deux pairs sont-ils vus sains ?
kubectl exec varnish-0 -n varnish -c vcl-reload -- \
sh -c 'printf "backend.list\n" | nc -w 2 localhost 6082' | grep peer
kubectl scale statefulset varnish -n varnish --replicas=1