21 novembre 2024

Reflexion autour d'une Architecture Web

Pour le premier billet technique, nous allons parler « Architecture Web ».
Avant toute chose, je pars du principe que vous êtes « a l’aise avec Linux » (connaissance du shell de base, installation de programmes, etc…)
Mes articles ne se veulent pas des « How-To » à suivre à la lettre en copiant-collant sans comprendre mais plus des guides avec, quand il le faut, les extraits de configs, de codes…
Bien sur, pour toute demande de compléments, d’explications, de remontée d’erreurs, d’oublis de ma part, etc…, les commentaires sont possibles et sont même les bienvenus !
Bref, revenons à nos moutons.
 

I : MISE EN PLACE

Nous allons partir idée en tête que nous voulons un service Web de qualité (dispo, temps de réponse, etc…), bref, on va essayer de faire le plus professionnel possible, le but étant d’héberger des sites de clients pro et donc de ne pas se louper !
Pour se faire :
Je rappel que je tourne sous Xen, les DomU etant en reseau local 10.1.1.0/24.
Deux DomU en réseau local dont je veux que les Apaches soient visibles de l’extérieur. Un pour les sites Web principaux et clients, l’autre pour mes sites d’administration.
Bref, pas trop le choix, il nous faut un reverse proxy en amont sur un autre DomU. De plus, un frontal Web permet de mieux contenir d’éventuels DDOS, offre une protection pour les Apaches derriere, bref, meme dans le cadre d’une seule machine, je recommande cette Architecture « Nginx >> Apache »
Nous avons donc :
Proxy 10.1.1.100 / Web 10.1.1.101 /BackWeb 10.1.1.102
Les trois machines sont en Debian 7 Wheezy.
Je pars avec l’exemple de trois machines, il est évident que vous pouvez avoir autant de backend Apache que vous le désirez…
Au niveau du Dom0, on pensera à forwarder les ports 80 et 443 vers le reverse….
Web et BackWeb hébergent chacun un Apache (leur configuration sera l’objet d’un autre billet)
Web est dimensionné pour accueillir du monde, BackWeb, plus « confidentiel ».
Sur ces deux machines, le stockage web se fait sur un LVM a part montée dans /stock.
 

II : REVERSE PROXY

Pour faire office de reverse sur notre machine Proxy, nous avons plusieurs possibilités :
– Apache avec le module Reverse
– Squid Oui, Squid peut aussi faire reverse proxy
– Nginx
– et d’autres…
J’ai retenu la solution Nginx pour sa facilité de configuration et surtout, ses performances. Je vous laisse chercher sur la toile, mais c’est sans appel…
Voila un exemple de fichier volontairement simplifié :
/etc/nginx/sites-available/default

server {
    listen   80;
    server_name     aaa.debugo.fr;
    location / {
        proxy_pass     http://10.1.1.101/;
    }
}
server {
    listen   80;
    server_name     bbb.debugo.fr;
    location / {
        proxy_pass     http://10.1.1.102/;
    }
}

Je pense que cela se passe d’explications. Une requête aaa.debugo.fr est basculé sur 10.1.1.101 et une requête sur bbb bascule sur…. oui, c’est ça, 10.1.1.102 !
Vous voyez : simple, efficace, prêt en 2 min, montre en main.
Il est possible bien sur d’héberger plusieurs vhosts sur 10.1.1.101 ou 10.1.1.102, pensez simplement a bien renseigner les ServerName côté Vhost Apache identiques aux server name de votre nginx.
La même chose avec Squid ou Apache, c’est déjà bien bien plus pénible (croyez moi, j’ai testé).
Dans un futur billet, nous verrons comment configuré un Vhost SSL en reverse sur le proxy pour atteindre un site sur un backend en HTTP simple. Très pratique, cette solution permet de ne gérer le SSL qu’entre le client et le proxy.
Derrière, votre proxy et vos backends communiquent en HTTP
 

III : DISTRIBUTION DES FICHIERS STATIQUES

Bon, c’est bien beau tout ça, mais peut on faire encore plus compliqué ?
Oui, on peut ! Et on ne va pas compliquer que pour le plaisir ! C’est aussi pour la bonne cause : réduire la consommation mémoire et CPU de nos Apaches.
Un site internet de nos jours , c’est facilement 5 à 50 fichiers statiques appelés pour un script php exécuté (le ratio dépend de votre moteur de CMS, framework, mais dans tout les cas, c’est énorme !)
Apache reste le plus performant pour s’occuper des scripts php, mais pour le statique, on fait mieux,plus rapide, et plus économe (Nginx par exemple ;))
L’astuce va être tout simplement de demander à Nginx de servir les fichiers statiques à la place d’Apache , qui lui n’aura plus qu’a traiter les pages dynamiques.
/etc/nginx/sites-available/default

server {
    listen   80;
    server_name     aaa.debugo.fr
    location / {
        proxy_pass     http://10.1.1.101/;
    }
    location ~* ^.+.(html|htm|jpg|jpeg|gif|css|png|js|ico|txt|xml)$ {
        root /stock/web/aaa;
    }
}

La, idem, c’est très simple. Tout fichier demande qui correspond au regex sera traité en interne, le reste ira sur la location /.
Oui ? J’entends dans le fond quelqu’un :  » Et les fichiers statiques, sur le proxy, ils arrivent comment ? »
J’y viens justement.
Si Nginx et Apache sont sur une même machine, pas de question à se poser, un chemin d’accès commun et « roule pedro ».
Dans notre cas, il serait tentant d’utiliser du NFS: sur le papier, c’est pénible donc intéressant, mais en réalité c’est casse-bonbon et induit un POF assez important.
Bref, la méthode a Dudulle :
Nous allons tout simplement copier le contenu web de Web, sur Proxy.
C’est lourd et pas top, je sais, mais dans notre cas, cela fonctionne bien, et je n’ai pas non plus trois millions de fichiers ….
Attention, pas top ne veut pas dire qu’on va gâcher l’espace disque et le réseau, alors oui évidement, on ne va filtrer que les fichiers statiques !
le script :
sync.sh

#!/bin/bash
rsync --delete -avm --include='*.css' --include='*.js' --include='*.ico' \
--include='*.txt' --include='*.xml' --include='*.html' --include='*.htm' \
 --include='*.jpg' --include='*.jpeg' --include='*.gif' --include='*.png' \
--exclude='*.*' /stock/www/ 10.1.1.102:/stock/web

Pour le cron, à vous de voir pour son exécution automatique. Avec Rsync, au final, cela allant plutôt vite, plusieurs fois par jour ne semble pas dérangeant.
Pour la demande de mot de passe, il est possible de passer par l’authentification a base de clé, je vous expliquerais cela dans un futur billet…
Et voila, vous avez votre petit Nginx qui se charge du statique pendant qu’Apache gère le dynamique.
 

IV : CACHE FICHIERS DYNAMIQUES ET STATIQUES AU NIVEAU REVERSE

Encore plus optimisé ?
Allez, on va rajouter du proxy_cache sur le dynamique.
Pour résumer, un php appelé par Nginx sur un des backend sera mis en cache côté Nginx pendant 2s.
– 2s, mais c’est court ?
– Oui, mais il faut bien que le site reste dynamique !
– Mais quel intérêt alors ?
– Patience jeune, je t’explique :
Vous avez plusieurs visiteurs au même moment qui demande la même page, elle ne sera demandé au serveur Apache qu’une fois toutes les deux secondes.
Personne ne remarquera que sa page « au pire » est datée de deux secondes, par contre votre Apache lui va vous remercier pour le temps libéré.
Du côté statique, on va faire en sorte qu’un fichier ouvert reste en cache mémoire un certain temps…

server {
    listen   80;
    server_name     aaa.debugo.fr
    location / {
        proxy_cache cache;
        proxy_cache_valid 2s;
        proxy_cache_valid 404 10s;
        proxy_cache_use_stale error timeout invalid_header updating;
        proxy_pass     http://10.1.1.101/;
    }
    location ~* ^.+.(html|htm|jpg|jpeg|gif|css|png|js|ico|txt|xml)$ {
        open_file_cache max=2000 inactive=20s;
        open_file_cache_valid 60s;
        open_file_cache_min_uses 5;
        open_file_cache_errors off;
        root /stock/web/aaa;
    }
}

Pour le cache PHP, il est aussi possible de faire encore des optimisations sur les backends au niveau PHP ou encore au niveau de certains Framework, CMS,… Cela sera l’objet d’un futur billet
 

V : Haute Dispo des sites Web

Bon, c’est un bien grand mot dans notre contexte, car pour une vrai Haute Dispo, il faudrait dupliquer plein de services, etc…
L’idée ici est beaucoup plus simple. Je veux pouvoir couper la machine Web (pour agrandir le LVM de stockage par exemple) sans interrompre les services Webs.
Bref, du simple LoadBalancing avec un serveur principal et un backup utilisé seulement que le principal est down.
En serveur de secours, nous allons utiliser BackWeb.
Il hébergera donc au final mes serveurs privé et une copie des serveurs principaux.

#!/bin/bash
rsync -avm --delete --include='*' /stock/www/ 10.1.1.102:/stock/www
scp /etc/apache2/sites-available/default root@10.1.1.102:/etc/apache2/sites-available/defaultweb
ssh root@10.1.1.102 'service apache2 reload'

Tout comme l’autre, a vous de voir pour la période de cron
Penser a activer le site secondaire sur le backup
Revenons sur Proxy.
Pour le LoadBalancer, nous allons utiliser le logiciel HAproxy.
Très simple à installer et à configurer, il permet de « loadbalancer » de nombreux services (serveurs Web, MySQL, messagerie, etc…)

global
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        user haproxy
        group haproxy
        daemon
defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        contimeout 5000
        clitimeout 50000
        srvtimeout 50000
        errorfile 400 /etc/haproxy/errors/400.http
        errorfile 403 /etc/haproxy/errors/403.http
        errorfile 408 /etc/haproxy/errors/408.http
        errorfile 500 /etc/haproxy/errors/500.http
        errorfile 502 /etc/haproxy/errors/502.http
        errorfile 503 /etc/haproxy/errors/503.http
        errorfile 504 /etc/haproxy/errors/504.http
    frontend front_http *:8080         # Web en http
            option forwardfor except 127.0.0.1
            capture request header X-Forwarded-For len 15
            option http-server-close
            option  httpclose
            default_backend         web_http
            backend web_http                # Web Nodes
            balance     roundrobin
            server  Server_1 10.1.1.101:80 check port 80 cookie server1 weight 1 inter 3000 rise 1 fall 2
            server  Server_2 10.1.1.102:80 check port 80 cookie server2 backup weight 10 inter 3000 rise 1 fall 2

Côté Nginx, on modifie l’adresse du Web par l’adresse du Haproxy (127.0.0.1:8080) .
On pourrait mettre HAproxy sur une machine à part, mais vu sa légèreté, aucun soucis à le faire tourner sur le même DomU que Nginx.

server {
    listen   80;
    server_name     aaa.debugo.fr
    location / {
        proxy_cache cache;
        proxy_cache_valid 2s;
        proxy_cache_valid 404 10s;
        proxy_cache_use_stale error timeout invalid_header updating;
        proxy_pass http://127.0.0.1:8080/;
    }
    location ~* ^.+.(html|htm|jpg|jpeg|gif|css|png|js|ico|txt|xml)$ {
        open_file_cache max=2000 inactive=20s;
        open_file_cache_valid 60s;
        open_file_cache_min_uses 5;
        open_file_cache_errors off;
        root /stock/web/aaa;
    }
}

Dorénavant, si le serveur Web tombe, le trafic est dirigé vers le backup.
Des que Web remonte, tout le trafic repasse dessus.
Point SQL
Dans mon cas, le serveur SQL est sur un autre domu.
Je n’ai donc pas à me soucier de l’accès à celui ci. La seule chose à faire étant de vérifier que les utilisateurs Mysql peuvent se connecter depuis le BackWeb comme il  le peuvent du Web.
Au pire, on duplique l’utilisateur en modifiant simplement le HostClient
Point Session
En théorie, si un visiteur passe sur le backup pendant sa visite, il devrait perdre sa session.
Curieusement, il s’avère qu’elles sont conservés (je suis connecté sur mon site, lien Principal, je coupe le principal, je switch sur le backup et je reste connecté…. ?)
N’ayant pas touché à cette partie de la config PHP,et me semblant de mémoire que par défaut, les sessions sont stockés par cookie si disponible sur le client, c’est peut être cela qui joue.
A retester avec des sessions uniquement coté serveurs, auquel cas, si l’on perd bien les sessions lors d’un switch (ce qui serait logique) il faudra alors passer par memcached pour les conserver (billet à venir, la aussi)
 

VI : Log

Vous l’aurez remarqué, je ne mentionne nul part ou loguer toutes ces activités.
A vrai dire, mon but est de pouvoir analyser les logs avec Awstats (la aussi, solution très complète pour en savoir plus sur ses visiteurs, et sans passer par les espions de Google and Co…
Et pour loguer au plus simple ici, c’est de tout loguer sur le reverse justement. Pas besoin de consolider des logs à droites, à gauches, etc… La, nous avons nos fichiers de logs à un seul et même endroit.
Alors oui, on pourrait faire du rsyslog, etc.. mais Apache et Rsyslog, c’est pas top « Out Of Box », alors couplé à mon architecture, cela va devenir une usine à gaz, je verrais ça plus tard 😉
Pour le moment, on va faire simple et efficace.

server {
    listen   80;
    server_name     aaa.debugo.fr
    location / {
        proxy_cache cache;
        proxy_cache_valid 2s;
        proxy_cache_valid 404 10s;
        proxy_cache_use_stale error timeout invalid_header updating;
        proxy_pass http://127.0.0.1:8080/;
        access_log /var/log/nginx/aaa_access.log;
        error_log /var/log/nginx/aaa_errors.log;
    }
    location ~* ^.+.(html|htm|jpg|jpeg|gif|css|png|js|ico|txt|xml)$ {
        open_file_cache max=2000 inactive=20s;
        open_file_cache_valid 60s;
        open_file_cache_min_uses 5;
        open_file_cache_errors off;
        root /stock/web/aaa;
        access_log /var/log/nginx/aaa_access.log;
        error_log /var/log/nginx/aaa_errors.log;
    }
}

Et on retrouve un fichier de log complet (appel aux fichiers dynamiques ET statiques) et au format standard d’Apache, donc pleinement utilisable avec Awstats ou autres.
Pour Awstats, je ferais un autre billet plus tard pour son utilisation.
Sachez simplement que nous utiliserons la génération de page statiques (le CGI Perl pour les pages Awstats dynamique sur Nginx, ça tourne, mais c’est extrêmement gourmand, et ça alourdit inutilement Nginx)
 

CONCLUSION

Et voila !
Ce guide s’avère au final assez complet, mais il permet d’avoir rapidement une architecture web plutôt correcte, et qui peut supporter beaucoup plus de charge qu’un simple Apache tout seul dans son coin.
Pour la partie Monitoring, Métrologie, Backup, nous verrons tout cela dans de futurs billets.
Des questions, remarques, etc… ?
Les commentaires sont la pour ça !

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *