Créer un caching HTTP façon CDN
C’est la mode au tout Cloud. Je ne m’étalerais pas sur mon avis très critique à cette mode commerciale. Cependant, le fonctionnement des sites en eux-mêmes changent peu et la mode du Cloud n’entache en rien le besoin de performance. C’est le service proposé par de nombreuses société de part de le monde avec des services de livraison de contenu (CDN) qui inclus :
- distribution HTTP de contenus statiques
- accélération HTTP de contenus statiques
- streaming audio/vidéo live ou on-demand
Je vais donc me focaliser sur comment monter une plateforme minimale de livraison HTTP et d’accélération HTTP.
Avant propos et plateforme HTTP
L’accélération web consiste en la fourniture d’un service HTTP fiable, perfomant, et allégeant la charge sur la plateforme web du client : tout le contenu statique doit être fournit par la plateforme d’accélération ou au maximum, alors que les pages dynamiques sont services par la plateforme du client. Une plateforme d’accélération est simple à mettre en place. La problèmatique par contre, va résider principalement dans les performances (systèmes et réseaux) et dans les fonctionnalités avancées offertes aux clients. Le service doit être mutualisé avec un maximum de clients mais être également flexible et adapté à chacun. La distribution HTTP repose sur le même principe à la différence que l’on doit héberger le contenu. Il est important qu’une machine qui fait de la distribution ne fasse pas d’accélération et inversement. Cette restriction est importante car les optimisations divergent quelques peu. Je ne m’attarderais pas sur l’installation des systèmes en eux-même.
Le matériel
Les performances dépendent aussi bien des logiciels et de leurs configurations, que du matériel choisi. Selon les moyens, on choisira entre l’aggrégat d’interface en gigabit ou la mise en place de cartes 10 Gbps. Il est à noté que les temps d’accès sont différents entre de l’optique et du cuivre, à l’avantage de l’optique. De même, des SSD sont à privilégier pour le cache disque lors que des disques en 15k rpm ou 10k rpm peuvent suffire sur la partie distribution. Pourquoi je me refuse à prendre des appliances ?
- limitées dans les fonctions additionnelles (ou sous licenses trop chères)
- peu évolutives
- performances largement en retrait par rapport à du home-made
Le logiciel
Il est intéressant de privilégier pour les noeuds de cache, des machines diskless, au système démarré en PXE et mis en Ramdisk. Si vous avez besoin d’un article sur ce point, laissez-moi savoir. L’avantage est de réduire les I/O disques lié au système. Côté mise en cache des objets, on se penchera sur un gros RAID0 de disques en SSD, le tout avec un formatage en XFS. Pourquoi pas en Ram ? simplement parce qu’il est facile de saturer les accès concurrents en Ram dans le cas d’un CDN et que les performances sont largement suffisantes avec une belle grappe de SSD. Côté service HTTP, on utilisera nginx, aussi bien pour la distribution que pour le caching. Pourquoi pas d’autres ?
- varnish en caching : il est moins extensible que nginx, un chouillat plus lourd et je le trouve clairement moins agréable à l’utilisation
- squid en caching : monolithique et non multithreadé, on y perd en performance et en optimisation ; de plus sa configuration est trop lourde et ses performances sont au final en retrait
- apache en caching ou en distribution : oui, pourquoi ne pas utiliser une gros usine à gaz polluante, archaïque et peu performante à la place de solution légère et performante ?
On n’écrit rien en local niveau log : on mettra en place un syslog distant et centralisé. L’attrait est double : réduire les I/O locales et pouvoir facilement générer les statistiques HTTP.
La plateforme HTTP
Celle-ci regroupe donc à minima (et hors volonté de redondances sur tout) :
- un serveur PXE pour fournir les OS aux machines diskless (non détaillé ici) (pxe)
- un serveur syslog et statistiques (stats)
- un serveur de base de données pour le backoffice offrant aux clients ou à l’équipe les outils pour le déploiement des configurations par vhosts (non détaillé ici) (db)
- les serveurs de monitoring et management (master)
- les serveurs de caching (on parle d’edges) ; il y aura 2 niveaux, donc on détaillera en edge / source
- les serveurs origins HTTP
Pour des raisons de disponibilité (et de proximité), on sera amené à dupliquer autant de fois qu’il le faut chaque élément sur des sites distants. La plateforme est duplicable à volonté. L’architecture est une pyramide inversée, à savoir à minima :
- 4 serveurs edge qui attaquent
- 2 serveurs source qui attaquent
- la plateforme du client ou 2 serveurs origin
Les autres serveurs sont mis en parallèle de ce déploiement.
Le réseau
Les VIP en mode DSR seront gérées par des LVS déployés sur les masters. On peut aussi investir dans des cartes ACE. Sur la partie GSLB (load balancing géolocalisé) je vous conseille de vous référer à votre ingé réseau sur les possibilités de votre infra au niveau déclaration des entêtes & co. En effet, faire du GSLB en DNS pour la plateforme en elle-même est un peu moins efficace :
- à cause des serveurs DNS des FAI qui ne respectent pas les TTL (Free par ex …)
- à cause des utilisateurs passant par des serveurs DNS tiers (Google, OpenDNS) qui faussent le calcul de géolocalisation
- du fait que le site mis en cache doit aussi l’appliquer dans sa zone DNS et que c’est parfois compliqué de motiver un client
La qualité du transit et des peerings est un point important pour assurer d’excellentes performances du CDN. On note :
- X.X.A.0/24 le range d’IP public du datacenter A
- X.X.B.0/24 le range d’IP public du datacenter B
- 10.0.A.0/8 le range d’IP privé du datacenter A
- 10.0.B.0/8 le range d’IP privé du datacenter B
Les zones privées communiquent entre elles.
Installation des serveurs origin
Matériel
Au niveau matériel il s’agit, dans mon cas, de serveurs Dell R510 avec 12 disques de 2 To (pour le moment), 24 Go Ram, interfaces en 10 Gbps optiques. On commence par déployer les packages dont on a besoin :
Les serveurs origin se duplique automatiquement les fichiers du client grâce à une réplication en GlusterFS :
La partie concernant l’importation des fichiers clients n’est pas exploitées ici. Du simple FTP au serveur SFTP utilisant un backend en LDAP, le choix est large.
NTP
On modifie simplement les lignes server du fichier /etc/ntp.conf :
DNS
Le service ne sera utilisé qu’en local pour la résolution DNS pour les besoins du nginx. Pour cela, on édite le fichier /etc/bind/named.conf/options :
OPTIMISATION SYSTEME
On s’attaque au scheduler disque en modifiant le fichier /etc/sysfs.conf :
On pense à adapter la partie en gras en fonction de ses disques. Dans la même idée, on modifier le bootloader grub en modifiant /etc/default/grub :
puis en mettant à jour grub :
Le filesystem de l’espace de cache a aussi son importance :
On adapte aussi le fichier /etc/fstab :
On organise ensuite la réplication entre les serveurs source. La réplication GlusterFS n’est à configurer que sur l’un des noeuds :
Notez qu’on peut répliquer autant que l’on désire. On prépare ensuite le montage du volume répliquer pour travailler dessus (/etc/fstab) :
On peut laisser le nom du premier serveur partout : le client GlusterFS l’utilise juste pour créer la connexion mais utilise le serveur qu’il considère le meilleur (en temps de réponse) pour les accès lecture/écrite. La partie sysctl est importante (/etc/sysctl.conf) :
A adapter selon les interfaces réseaux et la Ram (partie en gras). On l’applique avec un simple :
Il est important d’appliquer les limites correspondantes au système (/etc/security/limits.conf) :
Certaines modifications sont à appliquer au démarrage du système (/etc/rc.local) :
Distribution HTTP
On s’attaque donc à nginx. D’abord pour la partie globale :
{% highlight conf %} user www-data; worker_processes 2; worker_rlimit_nofile 250000;
error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid;
timer_resolution 1ms;
events { worker_connections 102400; use epoll; multi_accept on; accept_mutex_delay 1ms; }
http { include /etc/nginx/mime.types; default_type application/octet-stream;
log_format access ‘[$time_local] $request_time “$request_method $scheme://$host$request_uri $server_protocol” $request_length $status $bytes_sent “$http_referer” $remote_addr “$http_user_agent”’; access_log /data/log/access.log access; # Client connection client_body_temp_path /tmp 1 2; client_header_timeout 5s; client_body_timeout 5s; send_timeout 10m; connection_pool_size 256k; client_header_buffer_size 16k; large_client_header_buffers 1024 128k; request_pool_size 128k; keepalive_requests 1000; keepalive_timeout 10; client_max_body_size 10g; client_body_buffer_size 1m; client_body_in_single_buffer on; open_file_cache max=250000 inactive=300s; reset_timedout_connection on;
# Compression gzip on; gzip_static on; gzip_min_length 1100; gzip_buffers 16 8k; gzip_comp_level 9; gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; gzip_vary on; gzip_proxied any;
# Network optimizations output_buffers 1000 128k; postpone_output 1460; sendfile on; sendfile_max_chunk 256k; tcp_nopush on; tcp_nodelay on; server_tokens off;
# DNS resolver 127.0.0.1;
# HTTP Request ignore_invalid_headers on; index index.html; add_header X-CDN “Served by myself”; add_header Connection close;
server_names_hash_max_size 5120; server_names_hash_bucket_size 256;
upstream_fair_shm_size 1024k;
# Includes include /etc/nginx/conf.d/.conf; include /etc/nginx/sites-enabled/; } {% endhighlight %}
Ensuite, voici le template par vhost (au format twig) que votre robot déploiera :
{% highlight conf %}
{{ serial }}
server { listen :80; server_name {{ frontends|join(‘ ‘) }}; { % raw %} {% for option in default_options %} {{- option.name }} {{ option.value }}; {% endfor %} {% for location in locations %} {% for rule_expert in location.rules_expert %} # {{ location.name }} location ~ {{ rule_expert.pattern }} { {% for option in location.options %} {{- option.name }} {{ option.value }}; {% endfor %} root /data/tmp/vhosts/{{ vhost }}.nginx_backend/; } {% endfor %} {% endfor %} { % endraw %} error_page 404 /404.html; error_page 500 /500.html; error_page 502 /502.html; error_page 503 /503.html; error_page 504 /504.html; location = /404.html { root /var/www/nginx-default; } location = /500.html { root /var/www/nginx-default; } location = /502.html { root /var/www/nginx-default; } location = /503.html { root /var/www/nginx-default; } location = /504.html { root /var/www/nginx-default; } } {% endhighlight %}
Installation des serveurs source
Matériel
Au niveau matériel il s’agit, dans mon cas, de serveurs Dell R510 avec 12 disques de 2 To, 24 Go Ram, interfaces en 10 Gbps optiques. On commence par déployer les packages dont on a besoin :
Les serveurs source se duplique automatiquement les fichiers mis en cache grâce à une réplication en GlusterFS :
NTP
On modifie simplement les lignes server du fichier /etc/ntp.conf :
DNS
Le service ne sera utilisé qu’en local pour la résolution DNS pour les besoins du nginx. Pour cela, on édite le fichier /etc/bind/named.conf/options :
OPTIMISATION SYSTEME
On s’attaque au scheduler disque en modifiant le fichier /etc/sysfs.conf :
On pense à adapter la partie en gras en fonction de ses disques. Dans la même idée, on modifier le bootloader grub en modifiant /etc/default/grub :
puis en mettant à jour grub :
Le filesystem de l’espace de cache a aussi son importance :
On adapte aussi le fichier /etc/fstab :
On organise ensuite la réplication entre les serveurs source. La réplication GlusterFS n’est à configurer que sur l’un des noeuds :
Notez qu’on peut répliquer autant que l’on désire. On prépare ensuite le montage du volume répliquer pour travailler dessus (/etc/fstab) :
On peut laisser le nom du premier serveur partout : le client GlusterFS l’utilise juste pour créer la connexion mais utilise le serveur qu’il considère le meilleur (en temps de réponse) pour les accès lecture/écrite. La partie sysctl est importante (/etc/sysctl.conf) :
A adapter selon les interfaces réseaux et la Ram (partie en gras). On l’applique avec un simple :
Il est important d’appliquer les limites correspondantes au système (/etc/security/limits.conf) :
Certaines modifications sont à appliquer au démarrage du système (/etc/rc.local) :
CACHING
On s’attaque donc à nginx. D’abord pour la partie globale :
{% highlight conf %} user www-data; worker_processes 2; worker_rlimit_nofile 2500004;
error_log /var/log/nginx/error.log error; pid /var/run/nginx.pid;
timer_resolution 1ms;
events { worker_connections 102400; use epoll; multi_accept on; accept_mutex_delay 1ms; }
http { include /etc/nginx/mime.types; default_type application/octet-stream;
log_format access ‘[$time_local] $request_time “$request_method $scheme://$host$request_uri $server_protocol” $request_length $upstream_cache_status $status $proxy_host $upstream_addr $bytes_sent “$http_referer” $remote_addr “$http_user_agent”’; #access_log /data/log/access.log access; access_log off; # Client connection client_body_temp_path /tmp 1 2; client_header_timeout 5s; client_body_timeout 5s; send_timeout 10m; connection_pool_size 256k; client_header_buffer_size 16k; large_client_header_buffers 1024 128k; request_pool_size 128k; keepalive_requests 1000; keepalive_timeout 10; client_max_body_size 10g; client_body_buffer_size 1m; client_body_in_single_buffer on; open_file_cache max=250000 inactive=300s; reset_timedout_connection on;
# Compression gzip on; gzip_static on; gzip_min_length 1100; gzip_buffers 16 8k; gzip_comp_level 9; gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; gzip_vary on; gzip_proxied any;
# Network optimizations output_buffers 1000 128k; postpone_output 1460; sendfile on; sendfile_max_chunk 256k; tcp_nopush on; tcp_nodelay on; server_tokens off;
# DNS resolver 127.0.0.1;
# HTTP Request ignore_invalid_headers on; index index.html; add_header X-CDN “Served by myself”; add_header Connection close;
server_names_hash_max_size 5120; server_names_hash_bucket_size 256;
upstream_fair_shm_size 1024k; # Proxy proxy_cache_path /data/tmp/disk/ levels=1:2 keys_zone=big:1000m max_size=16000G; proxy_temp_path /data/tmp/temp/ 1 2; proxy_cache_valid 404 10m; proxy_cache_valid 400 501 502 503 504 1m; proxy_cache_valid any 4320m; proxy_cache_use_stale updating invalid_header error timeout http_404 http_500 http_502 http_503 http_504; proxy_next_upstream error timeout invalid_header http_404 http_500 http_502 http_503 http_504; proxy_redirect off; proxy_set_header Host $http_host; proxy_set_header Server Apache; proxy_set_header Connection Close; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass_header Set-Cookie; proxy_pass_header User-Agent; proxy_set_header X-Accel-Buffering on; proxy_hide_header X-CDN; proxy_hide_header X-Server; proxy_intercept_errors off; proxy_ignore_client_abort on; proxy_connect_timeout 60; proxy_send_timeout 60; proxy_read_timeout 60; proxy_buffer_size 128k; proxy_buffers 16384 128k; proxy_busy_buffers_size 256k; proxy_temp_file_write_size 128k; proxy_cache_min_uses 0; # Includes include /etc/nginx/conf.d/.conf; include /etc/nginx/sites-enabled/; } {% endhighlight %}
Ensuite, voici le template par vhost (au format twig) que votre robot déploiera :
{% highlight conf %}
{{ serial }}
proxy_cache_path /data/tmp/disk/{{ upstream }} levels=1:2 keys_zone={{ upstream }}:1000m inactive=4320m max_size=16000G; upstream {{ upstream }}.nginx_backend { { % raw %} {% for backend in backends %} server {{ backend.name }}{% for key, value in backend.options %} {{ key }}={{ value }}{% endfor %}; {% endfor %} { % endraw %} fair; } server { listen :80; server_name {{ frontends|join(‘ ‘) }}; { % raw %} {% for option in default_options %} {{- option.name }} {{ option.value }}; {% endfor %} proxy_no_cache $cookie_nocache $arg_nocache $arg_comment; proxy_no_cache $http_pragma $http_authorization; {% for location in locations %} {% for rule_expert in location.rules_expert %} # {{ location.name }} location ~ {{ rule_expert.pattern }} { {% for option in location.options %} {{- option.name }} {{ option.value }}; {% endfor %} {% for header in location.del_headers -%} proxy_hide_header “{{ header.name }}”; {% endfor %} {% for header in location.mod_headers %} proxy_hide_header “{{ header.name }}”; add_header “{{ header.name }}” “{{ header.value }}”; {% endfor %} proxy_cache {{ upstream }}; proxy_pass http://{{ upstream }}.nginx_backend; } {% endfor %} {% endfor %} { % endraw %} error_page 404 /404.html; error_page 500 /500.html; error_page 502 /502.html; error_page 503 /503.html; error_page 504 /504.html; location = /404.html { root /var/www/nginx-default; } location = /500.html { root /var/www/nginx-default; } location = /502.html { root /var/www/nginx-default; } location = /503.html { root /var/www/nginx-default; } location = /504.html { root /var/www/nginx-default; } } {% endhighlight %}
Installation des serveurs edge
Matériel
Au niveau matériel il s’agit, dans mon cas, de serveurs avec les matériels suivants :
- 2 CPU Intel E5620 (la plateforme date un peu)
- 96 Go Ram
- 6 x 100 Go en SSD
- interfaces réseaux optiques 10 Gbps
Ce qui suit est à faire sur l’image PXE ou sur le système local si vous en avez mis un. On commence par déployer les packages dont on a besoin :
NTP
On modifie simplement les lignes server du fichier /etc/ntp.conf :
DNS
Le service ne sera utilisé qu’en local pour la résolution DNS pour les besoins du nginx. Pour cela, on édite le fichier /etc/bind/named.conf/options :
OPTIMISATION SYSTEME
On s’attaque au scheduler disque en modifiant le fichier /etc/sysfs.conf :
On pense à adapter la partie en gras en fonction de ses disques. Dans la même idée, on modifier le bootloader grub en modifiant /etc/default/grub :
puis en mettant à jour* grub* :
Le filesystem de l’espace de cache a aussi son importance :
On adapte aussi le fichier /etc/fstab :
La partie sysctl est importante (/etc/sysctl.conf) :
A adapter selon les interfaces réseaux et la Ram (partie en gras). On l’applique avec un simple :
Il est important d’appliquer les limites correspondantes au système (/etc/security/limits.conf) :
Certaines modifications sont à appliquer au démarrage du système (/etc/rc.local) :
CACHING
Afin d’optimiser le nombre de socket par machine en fonction des ressources, on affecte plusieurs IP en alias à l’interface publique. De même, on pense à rajouter les IP des VIP à la loopback (/etc/network/interfaces).
Ensuite, on s’attaque à nginx. D’abord pour la partie globale :
{% highlight conf %} user www-data; worker_processes 32; worker_rlimit_nofile 262144;
error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid;
timer_resolution 1ms;
syslog local5 nginx;
events { worker_connections 262144; use epoll; multi_accept on; accept_mutex_delay 1ms; }
http { include /etc/nginx/mime.types; default_type application/octet-stream;
log_format access ‘[$time_local] $request_time “$request_method $scheme://$host$request_uri $server_protocol” $request_length $upstream_cache_status $status $proxy_host $upstream_addr $bytes_sent “$http_referer” $remote_addr “$http_user_agent”’; access_log syslog access;
# Client connection aio off; client_body_temp_path /tmp 1 2; client_header_timeout 5s; client_body_timeout 5s; send_timeout 10m; connection_pool_size 256k; client_header_buffer_size 16k; large_client_header_buffers 1024 256k; request_pool_size 128k; keepalive_requests 1000; keepalive_timeout 10; client_max_body_size 10g; client_body_buffer_size 1m; client_body_in_single_buffer on; open_file_cache max=1000 inactive=3600s; reset_timedout_connection on;
# Compression gzip on; gzip_static on; gzip_min_length 1100; gzip_buffers 16 8k; gzip_comp_level 9; gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; gzip_vary on; gzip_proxied any;
# Network optimizations output_buffers 1000 128k; postpone_output 1460; sendfile on; sendfile_max_chunk 256k; tcp_nopush on; tcp_nodelay on; server_tokens off;
# DNS resolver 127.0.0.1;
# HTTP Request ignore_invalid_headers on; index index.html; add_header X-CDN “Served by myself”; add_header Connection close;
# Proxy proxy_cache_path /data/cache/disk levels=1:2 keys_zone=big:4000m inactive=4320m max_size=500G; proxy_temp_path /data/cache/temp 1 2; proxy_cache_valid 404 10m; proxy_cache_valid 400 501 502 503 504 1m; proxy_cache_valid any 4320m; proxy_cache_use_stale updating invalid_header error timeout http_404 http_500 http_502 http_503 http_504; proxy_next_upstream error timeout invalid_header http_404 http_500 http_502 http_503 http_504; proxy_redirect off; proxy_set_header Host $http_host; proxy_set_header Server Apache; proxy_set_header Connection Close; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass_header Set-Cookie; proxy_pass_header User-Agent; proxy_set_header X-Accel-Buffering on; proxy_hide_header X-CDN; proxy_hide_header X-Server; proxy_intercept_errors on; proxy_ignore_client_abort on; proxy_connect_timeout 10; proxy_send_timeout 10; proxy_read_timeout 10; proxy_buffer_size 128k; proxy_buffers 65536 128k; proxy_busy_buffers_size 256k; proxy_temp_file_write_size 128k;
server_names_hash_max_size 5120; server_names_hash_bucket_size 256;
upstream_fair_shm_size 1024k;
# Includes include /etc/nginx/conf.d/.conf; include /etc/nginx/sites-enabled/; } {% endhighlight %}
Ensuite, voici le template par* vhost* (au format twig) que votre robot déploiera :
{% highlight conf %}
{{ serial }}
proxy_cache_path /data/cache/disk/{{ upstream }} levels=1:2 keys_zone={{ upstream }}:1000m inactive=4320m max_size=400G; upstream {{ upstream }}.nginx_backend { server X.X.A.101 max_fails=1 fail_timeout=1; server X.X.B.101 max_fails=1 fail_timeout=1; fair; } server { listen :80; server_name {{ frontends|join(‘ ‘) }}; { % raw %} {% for option in default_options %} {{- option.name }} {{ option.value }}; {% endfor %} proxy_no_cache $cookie_nocache $arg_nocache $arg_comment; proxy_no_cache $http_pragma $http_authorization; {% for location in locations %} {% for rule_expert in location.rules_expert %} # {{ location.name }} location ~ {{ rule_expert.pattern }} { {% for option in location.options %} {{- option.name }} {{ option.value }}; {% endfor %} {% for header in location.del_headers %} proxy_hide_header “{{ header.name }}”; {% endfor %} {% for header in location.mod_headers %} proxy_hide_header “{{ header.name }}”; add_header “{{ header.name }}” “{{ header.value }}”; {% endfor %} proxy_cache {{ upstream }}; proxy_pass http://{{ upstream }}.nginx_backend; } {% endfor %} {% endfor %} { % endraw %} error_page 404 /404.html; error_page 500 /500.html; error_page 502 /502.html; error_page 503 /503.html; error_page 504 /504.html; location = /404.html { root /var/www/nginx-default; } location = /500.html { root /var/www/nginx-default; } location = /502.html { root /var/www/nginx-default; } location = /503.html { root /var/www/nginx-default; } location = /504.html { root /var/www/nginx-default; } } {% endhighlight %}
SYSLOG
On envoie les logs nginx directement sur le syslog centralisé, en modifiant le fichier /etc/syslog-ng/syslog-ng.conf :
Installation d’un serveur stats
Matériel
Pour la partie matériel, j’ai pris un HP G7 avec des disques 15k rpm mis en RAID 10. Au niveau réseau, les interfaces gigabit suffisent amplement.
Packages
On installe les packages nécessaires :
NTP
On modifie simplement les lignes server du fichier /etc/ntp.conf :
DNS
Le service ne sera utilisé qu’en local pour la résolution DNS des IP utilisateurs au moment de l’écriture des logs (ce qui évitera de le faire a posteriori pendant le calcul des statistiques). Pour cela, on édite le fichier /etc/bind/named.conf/options :
SYSLOG
On centralise donc l’arrivée des logs nginx via syslog-ng. Pour cela, on édite simplement le fichier /etc/syslog-ng/syslog-ng.conf :
jdresolve est une application java permettant la résolution DNS à la volée. On le fait à ce moment plutôt que sur le nginx pour éviter de rallonger la durée de traitement de la requête HTTP. Idem, il est fait avant le calcul des statistiques pour ne pas rallonger le traitement. En effet, awstats est une véritable limace quand il s’agit de résoudre la partie DNS. Le script dispatch.php permet de dispatcher la ligne dans un fichier spécifique à son vhost.
Pensez à modifier le chemin de dépôt pour les logs (en gras).
Statistiques
La partie des statistiques repose sur awstats. Un script permet de collecter les informations en base pour chaque vhost et la génération en dynamique de sa configuration en cas de besoin. Je ne vais pas pouvoir vous fournir le script. Cependant, voici le modèle pour les fichiers de configuration d’awstats.
Il est intéressant que le script gérant ces statistiques précalcule aussi les pages statiques HTML pour éviter des chargements trop long lors des consultations.
Installation d’un serveur master
Materiel
Au niveau matériel, on recycle ce qu’on a. Il n’y a pas besoin de performances réelles sauf pour la base de données donc le choix est libre.
Packages
On installe les packages principaux :
Pour la partie backoffice et outils de cron développés en interne, je vous laisse le choix. Je ne peux pas vous fournir mon code pour le moment. Pour autant, une bonne partie de la magie se passe là concernant le déploiement automatique, la customisation par site et le monitoring.
DNS
Le serveur DNS serveur de forwarders aux autres serveurs DNS. Lui utilisera ceux d’OpenDNS tant qu’à faire. Pour cela, on édite le fichier /etc/bind/named.conf/options :
Il servira aussi la zone DNS dédiée au CDN. On préconisera au client (même si l’on perd un peu en performance) de faire des CNAME vers ces enregistrement. Cela évitera d’avoir à le prévenir si l’on fait des changements. Si les changements ne sont pas prévu, que l’on gère les zones clientes en interne, ou que l’on préfère les performances, on se refusera de faire des CNAME.
Load-balancing
On édite le fichier /etc/network/interfaces et on rajoute à l’interface publique (eth0 ici) :
On utilise donc ldirector/ipvsadm. Dans /etc/default/ldirectord on définit le chemin de la configuration :
Puis, on modifie le fichier correspondant :
Pour information sur cette configuration :
- gate permet le fonctionnement en DSR du load balancing
- persistent définit la durée du sticky pour la session (ici 5 secondes)
- autoreload déclenche le rechargement de la configuration à chaque modification du fichier
- emailalert nous préviendra par email de tout changement d’état dans la VIP
Plus qu’à lancer relancer l’interface réseau puis le service et voilà la VIP prête.
NTP
On veut que tous les serveurs soit synchro niveau temps. Autant avoir un serveur de référence à proximité. On a donc installé le package. Aucune modification de base à faire.