Archives mensuelles : octobre 2018

[SQL] : créez des constantes et des noms de résultats

DBA et développeur, cet article est pour vous… y’en faut pour tous les goûts comme on dit. Voici deux outils peu utilisés dans les bases de données que j’ai pu voir récemment. Et pourtant, ils permettent de simplifier votre script sql et de le rendre plus lisible et plus structuré (génial lol).

Les constantes en SQL

Ce n’est pas révolutionnaire mais si vous ne le savez pa déjà, vous pouvez définir un groupe de constante en sql avec la clause « WITH … AS ». Cela vous permettra d’utiliser ces données fixes comme des tables pratiquement.

Par exemple si on définit les constantes de temps :

WITH timeconstants AS (
SELECT 60 as seconds,
       60 as minutes,
       24 as hours,
       365 as days,
       7 as week,
       ['Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi','Dimanche'] as daysweek,
),

C’est pas mal mais comment utiliser ces constantes ? comme une requête.

SELECT seconds, minutes, hours, days FROM timeconstants;

Enfantin comme dirait l’autre.

Les noms de résultats

Je parlerai plutôt d’alias mais souvent en sql on parle d’alias pour les alias de table par exemple et on l’utilise plutôt dans la clause FROM. En totu cas le principe est presque el même sauf qu’il s’agit à la manière d’un vue d’attribuer un alias à une requête entière.

On le fait ainsi :

myextract AS (
   SELECT
   IF(colonne1 < 10, "little", "big") AS qualified_sale,
   colonne1,   nb_hours,   colonne2 / 10  FROM mytable)

Et si on combine les deux on retrouve des trucs du genre :

SELECT nb_hours * minutes * secondes AS timer
FROM mytable, timeconstants;

Alors je fais un peu n’importe quoi mais c’est un exemple.Ici on utilise les constantes minutes et secondes pour calculer les heures passées en secondes.

L’intérêt vous le voyez c’est que désormais tout possède un nom et que pour appeler les éléments ils doivent être définis à travers le « FROM ». Derrière il est beaucoup plus simple de faire de la modélisation et surtout si vos constantes évoluent vous n’avez pas besoin de revoir toutes vos requêtes.

 

Pour ces éléments SQL que nous venons de voir, on parle de CTE dans le jargon DBA. CTE pour Common Table Expression. Ces petites clauses permettent de jouer sur les « alias » pour créer des pseudos tables qui ne sont pas non plus des vues et qui visent à simplifier le travail de maintenance.

Ansible – les différents niveaux de variables

Ansible est un orchestrateur simple d’utilisation dès lors que l’on en retient les concepts de base. Parmi ces concepts, la hiérarchie des variables fait partie des essentiels.

Je vous propose d’y revenir en vidéo.

[youtube https://www.youtube.com/watch?v=_8Az2egUKwY&w=700&h=500]

D’ailleurs n’hésitez pas à vous abonner à la chaine xavki.

Au total, Ansible possède plus de 20 manières pour définir des variables. On peut les classer par ordre hiérarchique en fonction de s’imposer les unes aux autres. Je ne reviendrais pas sur l’ensemble des 20 variables mais sur les plus courantes. Néanmoins voici la liste de ces variables ordonnée hierarchiquement :

ansible-v2-and-beyond-ansible-nyc-meetup-19-638

Les principales qui me semblent importantes à retenir sont :

  • 1. extra vars : passable à partir de la command line
  • 2. task vars : variable de tâche (dans le playbook)
  • 3. block vars : la variable de block
  • 4. role vars : dans le répertoire /vars d’un rôle
  • 5. set_facts : définie dans les set_facts
  • 6. playbook vars : dans le fichier playbook
  • 7. host_vars : précisée dans les fichiers individuels des machines de l’inventaire
  • 8. inventory vars : dans l’inventaire (par exemple hosts.yml)
  • 9. group_vars : dans les fichiers de groupes de l’inventaire
  • 10. role defaults vars : la variable par défaut

Un rôle bien rédigé doit être très largement paramétrable : OS, paquets, version, user, passwords… Ainsi un bon rôle possède souvent un fichiers de variables par défaut (répertoire defaults du rôle) très fourni. Cela vous permet de personnaliser très largement l’application de ce rôle.

En outre, un bon rôle doit aussi avoir l’intégratilité de ses variables définies dans son README, en tout cas au moins toutes les variables par défaut. Elles doivent alors être documentées sur la manière de les définir (liste, string, booléens…).

Par ailleurs, vous pouvez utiliser le module debug pour afficher le contenu des variables à n’importe quel moment de votre playbook.

- name: "affichage contenu de var1"
  debug:
    msg: "Voici le contenur de var1 => {{ var1 }}"

Il faut aussi savoir que ansible n’aime pas traiter les variables vides.

Enfin ansible possède des variables particulières propres à son fonctionnement, on parle de facts (version OS, etc…). Celles-ci sont utilisables à tout moment. Pour les connaître utilisez le module setup :

 ansible all -m setup

Vous pouvez également créer vos propres facts.

[Python] : comment simplifier vos switch case avec la fonction lambda ?

Lambda, un mot qui peut faire peur pour les débutants en python. Pourtant les fonctions anonymes lambda permettent de simplifier le code et de le rendre plus compact. Certe leur lecture nécessite un peu de pratique mais elles réduisent la longueur de vos scripts.

En voici encore un exemple avec le classique switch/case. Python n’est pas équipé d’outils spécifique comme bash, php ou autres… Du coup il faut passer par le classique « If … elseif … else ». C’est dommage et je dirais que c’est presque moche non ?

C’est là que labda le vengeur masqué intervient et va nous réduire et embellir tout cela.

Voyons le cas classique dans un premier temps

def sans_lambda(op, a, b):
    if op == 'addition':
        return a + b
    elif op == 'soustraction':
        return a - b
    elif op == 'multiplication':
        return a * b
    elif op == 'division':
        return a / b
    else:
        return None

Et maintenant avec lambda :

def avec_lambda(op, c, d):
    return {
        'addition': lambda: c + d,
        'soustraction': lambda: c - d,
        'multiplication': lambda: c * d,
        'division': lambda: c / d,
    }.get(op, lambda: None)()

Vous le voyez avec lambda c’est mieux. Une lambda pour chaque cas. On intègre directement cela dans le return.

[Sécurité] : Comment encrypter vos fichiers avec Vim ?

Cet article aurait peut être eu simplement sa place dans le top commande de la semaine mais c’est un focus qui parle un peu sécurité et je pense que l’on y fait pas assez attention. J’imagine que sur votre micro vous disposez de fichiers sensibles avec des données perso voir pire avec des mots de passe et des secrets. C’est pas bien lol.

Comment remédier à cela ? en encryptant vos fichiers.

Tout de suite le mot est laché et certains ont les poils qui se dressent. Mais n’ayons pas peur des mots. Encrypter peut se limiter à n’avoir qu’un mot de passe pour consulter vos fichiers. Et c’est ce que Vim permet de faire très simplement avec l’option « :X ». Déjà ça rassure un peu de se dire que Vim peut le faire lol.

Comment encrypter avec Vim ?

  • éditer votre fichier
  • lancer « :X » avant de sauvegarder
  • rentrer votre mot de passe

Et voilà seul Vim peut ouvrir votre fichier et il vous demandera systématiquement votre mot de passe. C’est un premier pas.

Vim propose un simple cryptage symétrique avec mot de passe donc. Mais attention à la méthode que celui-ci utilise. Pour consulter la méthode par défaut faites :

:setlocal cm?

Et pour définir une méthode un peu plus fiable vous pouvez définir la méthode blowfish (ou blowfish2 suivant votre version de Vim). Pour cela lancez :

:setlocal cm=blowfish

Pour encrypter d’autres fichiers (hors de portée pour Vim comme images…), vous pouvez aussi utiliser :

Et surtout n’oublions pas qu’au minimum pour éviter les problèmes en cas de vol, vous pouvez encrypter facilement votre partition de travail sur Ubuntu si c’est votre distribution.

Par ailleurs, s’il s’agit de stocker des mots de passe tournez vous plutôt vers des outils spécifiques comme keepass par exemple.

[Oracle] : comment connaître la taille d’un index avant de le créer ? explain plan est ton ami

Lorsque l’on réalise un explain plan, on peut en tirer pour conclusion qu’il est nécessaire de créer un index. Attention, je ne dis pas qu’il faut abuser de ces créations d’index car trop d’index ou des index mal placés ou mal constitués peuvent causer au contraire de al consommation de ressources pour rien.

Ce qui peut être intéressant avant de créer cette index c’est d’évaluer sa taille pour voir le volume qu’il prendra au regarde de votre base. Mais comment faire ?

La encore explain plan d’Oracle est notre ami( c’est pas si souvent que Oracle est ton ami c’est pas faux). Et en plus il est gratuit… youpi !

Pour cela nous allons pratiquer un explain plan sur le create index… eh oui c’est possible.

Ainsi :

#création de l'explain
explain plan for create index monindex on toto_table ( champ_1 );

#consultation du résultat
select * from table(dbms_xplan.display);

Et voilà le tour est joué.

Pas très compliqué donc. Pour en savoir un peu plus sur les explains planset surtout comment les interpréter, je ne peux que vous recommander de consulter cette vidéo de Maria Colgan, expert de chez Oracle.

[Python] : comment requêter une base mysql avec mysql.connector ?

Mysql Connector est le module le plus répandu pour utiliser une base de données mysql à partir de python. Il est relativement facile à utiliser. Nous aurons l’occasion de voir dans d’autres articles des manipulations plus complexes mais il faut bien commencer par… le commencement.

Comment réaliser un simple SELECT et afficher son résultat en Python ? voici comment faire si vous êtes débutant.

import mysql.connector

madatabase = mysql.connector.connect(
  host="localhost",
  user="nom_utilisateur",
  passwd="mon_password",
  database="mabase"
)

session = madatabase.cursor()
sql = "SELECT * FROM matable;"
session.execute(sql)

resultat = session.fetchall()

for x in resultat:
  print(x)

session.close()


Donc quelles commandes avons nous passé ?

  • import du module
  • création d’une instance madatabase avec les paramètres de connexion. C’est tout à fait classique on retrouve cela avec de nombreux modules de moteurs de bases de données
  • ouverture d’une connexionavec l’instanciation de la classe cursor
  • la variable sql contient la requête à passer
  • on lance la requête avec notre variable
  • on récupère tous les résultats (sous forme de liste)
  • il ne reste plus qu’à parcourir la liste
  • et on oublie pas de dire au revoir et merci au serveur en fermant la session

Vous pouvez d’ores et déjà retrouver plus d’infos en ligne sur le site mysql et sa rubrique dédiée à ce module Python.

Ansible – prise en main de checksum, set_facts, register et block

Ansible c’est un peu le fil rouge du moment. Certains l’ont bien vu avec le lancement de la chaine youtube xavki. L’idée c’est de vous montrer comment j’ai appris à utiliser et développer avec ansible.

La vidéo ci-dessous présente ce que nous allons découvrir dans l’article.

[youtube https://www.youtube.com/watch?v=YqKAXnmAetY&w=700&h=300]

L’objectif du jour, c’est de charger un fichier avec l’aide d’ansible, de le modifier et surtout faire en sorte qu’une fois ces opérations réalisées nous gérions la réentrance (ou idempotence). C’est à dire que si nous relançons aussitôt le playbook ansible aucun opération ne soit réalisée.

Or sans vérification du contenu du fichier, le fichier est systématiquement rechargé. Pourquoi ?

Tout simplement car ansible va contrôler l’état du fichier présent sur la cible avec celui présent sur la source. Et malheureusement ansible ne sait pas prendre en compte les diverses opérations qu’il aura réalisé sur le fichier après son chargement. Donc il est systématiquement différent du fichier source.

Comment vérifier l’état du fichier ? de cette manière peut être (si vous avez mieux je suis preneur.

---
- name: "[XAVKI]"
  hosts: all
  vars:
    - check5: "2789ebeb61ab3b1985e9f6df9256d8a1" 
  tasks:

      - name: "[XAVKI] - check md5"
        stat:
          path: /tmp/xavki.txt
          get_checksum: yes
          checksum_algorithm: md5
        register: sum5

      - set_fact:
          data: "0"
        when: sum5.stat.checksum is not defined

      - set_fact:
          data: "{{ sum5.stat.checksum }}"
        when: sum5.stat.checksum is defined

      - name: "[XAVKI] - Bloc"
        block:
        - name: "[XAVKI] - copie du fichier" 
          copy:
            src: ./monfichier.txt
            dest: /tmp/xavki.txt

        - name: "[XAVKI] - add line"
          lineinfile:
            path: /tmp/xavki.txt
            line: "ajout d'une ligne" 

        when: data != check5

Voici le cheminement :

  • tout d’abord on calcul et on place dans une variable le md5 du fichier dans l’état final souhaité (check5 en l’occurence), c’est notre référence
  • à l’aide du module stat on place avec register la valeur du fichier déjà présent dans une variable (sum5)
  • nous devons traiter alors 2 cas de figures : la variable est définie car le fichier existe ou il n’existe pas et la variable n’est pas définie
  • si la variable existe on attribue à la variable data la valeur du sum5 sinon on lui donne la valeur « 0 »
  • enfin on encapsule dans un bloc avec le module block les actions de chargement de fichier et de modification si data ne vaut pas la valeur finale souhaitée (avec when)

[Awk] : comment lister les ports des vhosts d’un apache ? (gsub, match, substr et regex)

Awk c’est un peu mon petit préféré pour faire du oneline en manipulant des lignes. Et une fois de plus, il est encore bien sympa pour nous lister les ports utilisés pour chaque vhost d’un apache. Bon on se fait un peu plaisir vous allez voir… et c’est pas pour frimer.

En gros, on veut un truc qui nous dise :

nom_du_vhost  =>> numéro_port

y compris si nous avons plusieurs ports (80 et 443).

Et en une ligne, voici le code que j’ai pu écrire :

awk '/.*:[0-9]{1,3}>/ && /[^#]/
{match($0,/:[0-9]{1,3}>/);
gsub("/.*/"," ",FILENAME);
print FILENAME"=>>", 
substr($0, RSTART+1, RLENGTH-2)}'
/etc/apache2/*.*

Explications de cette commande d’une ligne un peu chargée :

  • 1ère ligne : restriction des lignes traitées en limitant aux lignes contenant « : » puis 1 à 3 chiffres allant de 0 à 9 et qui ne contient pas de « # » (pour éviter les lignes de commentaires)
  • 2ème ligne : capture du pattern du port encadre de « : » et « > »
  • 3ème ligne : nettoyage du filename en retournant uniquement le nom de fichier
  • 5ème ligne : récupération du pattern de la regex

C’est un peu compliqué mais cela permet surtout de faire un peu de la manipulation pour s’entretenir en awk.

Et vous vous feriez comment pour réaliser ce genre d’opération ?

[Linux] : comment rechercher un texte dans plusieurs fichier pdf ?

Pdftotext est un binaire bien tuile sur linux. En effet, il permet assez facilement de convertir du texte au format pdf en format texte (comme son nom l’iniduqe ;)). Son petit avantage c’est aussi qu’il est installé de base sur pas mal de version (ubuntu…) et ça on a tendance à l’oublier.

Donc celui-ci possède aussi sa page de man officielle et c’est bien pratique pour faire connaissanceavec les options de l’outil.

En guise d’exemple, je vous propose de faire une manip toute bête : chercher un pattern dans vos fichier pdf (je partirai de la racine pour le fun… non faut pas faire ça car ca va durer toute votre vie un truc pareil) :

 find / -name '*.pdf' -exec sh -c 'pdftotext "{}" - \
| grep --with-filename --label="{}" --color "monpattern"' \; 

Donc si on rentre dans le détail :

  • on fait un find ou :
    • on récupère que les pdf
    • on exécute un shell pdftotext sur les fichiers trouvés dans lequel on grep  (en récuprant le nom de fichier et la couleur)

[Bash] : couper un fichier en plusieurs avec split

Fichier trop gros ou besoin de découper votre fichier en parts égales ou splitter toutes les X lignes, c’est possible. Split est un binaire que j’utilise peu mais qui peu s’avérer très efficace. Voyons un peu comment celui-ci fonctionne avec quelques exemples.

Créons tout d’abord un fichier de test :

└─ $ ▶ for i in {0..100000};do echo "ligne ${i}">>texte.txt; done

Avec ceci nous avons donc un fichier texte de 100 000 lignes.

Lançons un split sans option :

split texte.txt

Nous obtenons de nombreux fichiers de 1000 lignes chacun.

└─ $ ▶ wc -l x*
   1000 xaa
   1000 xab
   1000 xac

Remarquez que tous commence par un x.

Utilisons l’option -l pour définir le nombre de ligne par fichier :

└─ $ ▶ split -l50000 texte.txt 


└─ $ ▶ wc -l x*
50000 xaa
50000 xab

De la me manière nous aurions pu découper par taille de fichier en bytes avec l’option -b.

La nomenclature imposé dans le nommage du fichier est pas très fun mais on peut la retoucher en définissant un suffix numérique par exemple avec l’option -d. On a alors des fichiers nomé x01, x02…

Mais ce qui est le plus intéressant c’est de combiner l’utilisation d’un suffix et d’une numérotation numérique. Pour cela utilisons :

└─ $ ▶ split -d -l50000 texte.txt suffix-
└─ $ ▶ wc -l suffix*
50000 suffix-00
50000 suffix-01

Jusqu’ici je dirais que ce qui est embêtant c’est de se poser la questionde quel pas pour splitter les fichiers et combien vais-je en obtenir ? et bien au lieu de définir une taille ou un nombre de ligne, on peut définir le nombre de fichiers à la sortie avec l’option -n.

└─ $ ▶ split -d -n4 texte.txt suffix-
└─ $ ▶ wc -l suffix*
25694 suffix-00
24769 suffix-01
24769 suffix-02
24769 suffix-03

Comment fusionner tous les fichiers à l’arrivée si on a besoin du fichier d’origine ?

└─ $ ▶ md5sum texte.txt 
d4da8315d007541dfdcf61e2ea2fbecd texte.txt
└─ $ ▶ cat suffix-* | md5sum
d4da8315d007541dfdcf61e2ea2fbecd -

Un cat suffit et vous le voyez vous pouvez faire des vérifications sur le contenu avec le fameux md5sum.

[Python] : comment charger des datas en base de données mysql avec mysql.connector ?

Revenons à nos pythons. Objectif du jour faire découvrir comment charger des données dans une base mysql à partir du module mysql.connector ?

Extrêmement facile. Vous me direz c’est normal car c’est le but des modules de python.

Premier cas on va chercher une ligne simple :

# import du module
import mysql.connector

# paramètres de connexion à la base
mabase = mysql.connector.connect(
  host="localhost",
  user="monutilisateurdb",
  passwd="motdepassedb",
  database="madatabase"
)

cursor = mabase.cursor()

sql = "INSERT INTO matable (id, valeur) VALUES (%s, %s)"
data = [("1", "Xavki"),("2", "Pierre"),("3", "Jacques"),("4", "Paul")]
cursor.executemany(sql, data)

mabase.commit()

Donc là on vient d’inséré une ligne. Mais bon on veut en insérer plusieur voir mieux insérer une liste. Alors il faut utiliser « executemany » :

# import du module
import mysql.connector

# paramètres de connexion à la base
mabase = mysql.connector.connect(
  host="localhost",
  user="monutilisateurdb",
  passwd="motdepassedb",
  database="madatabase"
)

cursor = mabase.cursor()

sql = "INSERT INTO matable (id, valeur) VALUES (%s, %s)"
data = ("1", "Xavki")
cursor.execute(sql, data)

mabase.commit()
print(cursor.rowcount, " insérées") 

[Nginx][Varnish] : comment installer un vhost et mettre en place le système de cache ?

Vous voulez apprendre nginx et tuner un peu les performance d’un site web ? cet article est un début pour apprendre à mettre en place votre vhost et installer un cache avec varnish. L’intérêt du cache est d’éviter d’utiliser le serveur web pour charger des éléments statiques de votre site (images, publicités…). Vous gagnez ainsi en vitesse de chargement et réduissez l’utilisation de votre serveur web.

Commençons par Nginx

L’installation est simple :

sudo apt-get install nginx

Une fois installé il nous faut configurer notre vhost. L’objectif du vhost est de faire correspondre une adresse/ip/port à un répertoire où sont stockés les fichiers web (html, php…).

Vous disposez d’un modèle de vhost dans /etc/nginx/sites-available/default donc copions-le pour le modifier :

cp /etc/nginx/sites-available/default /etc/nginx/sites-available/xavki.fr

Puis éditons-le en indiquant la localisation des fichiers du site (la racine étant /var/www/ :

server {
        listen 80;
        listen [::]:80;
        root /var/www/xavki;
        index index.html index.htm;
        server_name xavki.fr www.xavki.fr;
        location / {
                try_files $uri $uri/ =404;
        }
}

Donc le serveur web nginx écoute sur le port 80. Le répertoire stockant les fichiers est /var/www/xavki. L’index (racine du site) est un fichier nommé index.html ou index.htm. L’url du site xavki.fr ou www.xavki.fr. Et si la page n’existe pas on renvoi une erreur 404.

Nous allons donc créer le répertoire et y charger un squelette de site :

mkdir/var/www/xavki

Vous pouvez télécharger un modèle de site sur cette page. Décompressez le et placez le dans ce nouveau répertoire. Avec ce contenu nous avons une structure assez classique de site web : css, html, fonts, images…

Maintenant il nous reste à activer le vhost. Comme pour les serveurs apache Nginx requiert la création d’un lien symbolique entre /etc/nginx/sites-available et /etc/nginx/sites-enabled.

Donc :

cd /etc/nginx/sites-enabled
ln -s /etc/nginx/sites-availble/xavki.fr

Et comme pour apache il vaut mieux vérifier si la conf est bonne :

nginx -t
#désactivez le défaut
rm -rf /etc/nginx/sites-enabled

Et notre vhost est bien là. Pour le tester si vous développez sur votre localhost tapez directement l’ip 127.0.0.1 sinon tapez l’ip de la machine distante dans votre navigateur.

Et si vos voulez simuler un dns éditez le /etc/hosts de la machine qui émule la vm. Par exemple :

172.20.10.2 xavki.fr www.xavki.fr

Avec cela vous pouvez même taper l’adresse dans votre navigateur.

xavki.png

Nous venons de faire la moité du chemin, il reste plus que le cache et pour cela nous allons utiliser varnish.

Et le cache de varnish

Là encore l’installation est simple :

sudo apt-get install varnish
#et lançons le service
service varnish start

Réalisez alors un netstat -ntaup. Vous vous rendez compte que varnish écoute sur le port 6081 et nginx sur le port 80. Le problème c’est qu’en l’état votre système de cache n’est pas opérationnel car il ne répond pas sur le port 80.

Il est donc nécessaire de :

  • modifier le port d’écoute du vhost nginx (par exemple le 8080
  • modifier le port d’écoute de varnish pour passer sur le 80

Donc modifions notre vhost /etc/nginx/sites-available/xavki.fr et modifions le port 80 en 8080 :

server {
        listen 8080; 
        listen [::]:8080;
...

Relançons le service :

nginx -t
service nginx reload

Maintenant modifions la configuration de varnish. Editons le fichier /etc/varnish/default.vcl avec :

backend default {
     .host = "127.0.0.1";
     .port = "8080";
}

Puis changeons la conf de varnish pour le faire écouter sur le port 80. En changeant le fichier /etc/default/varnish :

DAEMON_OPTS="-a :80 \
             -T localhost:6082 \
             -f /etc/varnish/default.vcl \
             -S /etc/varnish/secret \
             -s malloc,256m"

Remarquez au passage que vous pouvez aussi retoucher la mémoire alloué au cache.

Modifiez également /lib/systemd/system/varnish.service

ExecStart=/usr/sbin/varnishd -j unix,user=vcache -F -a :80 -T localhost:6082 -f /etc/varnish/default.vcl -S /etc/varnish/secret -s malloc,256m

Voilà maintenant redémarrons le service varnish :

service varnish restart

et testons l’utilisation du cache.

Et pour terminer testons l’utilisation du cache :

└─ $ ▶ curl -I xavki.fr
HTTP/1.1 200 OK
Server: nginx/1.14.0 (Ubuntu)
Date: Mon, 01 Oct 2018 20:05:18 GMT
Content-Type: text/html
Last-Modified: Mon, 01 Oct 2018 12:38:18 GMT
ETag: W/"5bb2153a-105e"
Vary: Accept-Encoding
X-Varnish: 2
Age: 0
Via: 1.1 varnish (Varnish/5.2)
Accept-Ranges: bytes
Connection: keep-alive

Nous constatons bien l’utilisation de varnish pour répondre à la requête vers notre site.
Attention, il ne s’agit que d’une base. Il faut ensuite ajouter des règles de gestion à varnish (cache pour les images, pas de cache pour les cookies…) et c’est là que commence le vrai taff.