djbdns dns server: dnscache

Il mondo dei server DNS è sicuramente dominato da bind in ambito linux ma esistono altri prodotti interessanti soprattutto se la necessità non è quella di dare il servizio DNS su internet.

Trovo particolarmente interessante djbdns. Questo daemon è stato sviluppato da D.J. Bernstein, autore, tra l’altro, anche di qmail. La caratteristica più interessante di djbdns è quella di separare in tre daemon differenti tre funzionalità differenti generalmente unificate sotto un’unico daemon:

  • caching DNS: dnscache
  • authoriting delle zone: tinydns
  • trasferimento di zone: axfrdns and axfr-get

Di questi il più utile secondo me è dnscache che, essendo un sistema estremamente leggero è ideale per essere istallato e ridurre il carico di lavoro di tutti quei server che hanno la necessità di gestire un elevato numero di connessioni che giungono da svariati IP.

Djbdns richiede due software: daemontools e ucspi-tcp. Questi li istalliamo da pacchetto.

aptitude install daemontools
aptitude install ucspi-tcp

djbdns non è però presente sulla distribuzione debian. Scarichiamo quindi i sorgenti. La compilazione richiede i pacchetti make, gcc

 wget http://cr.yp.to/djbdns/djbdns-1.05.tar.gz
tar -zxvf djbdns-1.05.tar.gz
cd djbdns-1.05
echo gcc -O2 -include /usr/include/errno.h > conf-cc
make
make setup check

L’autore richiede poi di segnalare l’avvenuta istallazione con

( echo 'First M. Last'; cat `cat SYSDEPS` ) \
| mail djb-sysdeps@cr.yp.to

Passiamo ora alla configurazione del servizio. Come sempre non è il caos di di far girare dei daemon come root; aggiungiamo quindi due utenti al sistema inserendo nel file */etc/passwd*

dnscache:x:999:999::/dev/null:/usr/sbin/nologin
dnslog:x:998:999::/dev/null:/usr/sbin/nologin

e al file /etc/group

dnscache:x:999:

Prepariamo la configurazione di base

dnscache-conf dnscache dnslog /etc/dnscache 127.0.0.1

Con questo comando abbiamo generato la configurazione in /etc/dnscache specificando che l’utente che dovrà eseguire il daemon sarà dnscache, che i log saranno gestiti dall’utente dnslog e che il daemone sarà in ascolto sull’IP 127.0.0.1.

La directory creata conterrà 5 sottodirectory: env, log, root, run e seed. In particolare env e root contengono le configurazioni vere e proprie e log i log. Vedremo qualche dettaglio in seguito.

Occupiamoci ora dell’avvio del sistema. Vogliamo sfrutatre i daemontools e per farlo dovremo far si che svscanboot venga avviato all’avvio del sistema attraverso i meccanismi della distribuzione che stiamo utilizzando. Per le finalità di questo tutorial ci accontentiamo di lanciarlo a mano:

mkdir /etc/service/
cd /tmp/
nohup svcscanboot &

Il sistema dei daemontools si aspetta una directory in /etc/service per ogni servizio da gestire e monitorare. Aggiungiamo quindi la nostra cache dns

ln -s /etc/dnscache /etc/service/dnscache

Dopo qualche secondo il daemon sarà avviato

svstat /etc/service/dnscache/

ci restituirà il tempo di esecuzione e il PID del demone.

Interessanti un paio di *ps*

root@debian:/tmp# ps aux|grep sv
root      7605  0.0  0.0   4180   580 pts/0    S    22:24   0:00 /bin/sh /usr/bin/svscanboot
root      7607  0.0  0.0   4120   460 pts/0    S    22:24   0:00 svscan /etc/service
root@debian:/tmp# ps aux|grep dns
root      7609  0.0  0.0   3948   316 pts/0    S    22:24   0:00 supervise dnscache
dnslog    7611  0.0  0.0   4092   468 pts/0    S    22:24   0:00 multilog t ./main
dnscache  7612  0.0  0.1   5484  1492 pts/0    S    22:24   0:00 /usr/local/bin/dnscache

che mostrano tutti i processi coinvolti. Notare che la gestione dei log è fatta attraverso un daemon separato e infatti si troverà all’interno della directory log in /etc/dnscache una directory *service* esattamente come in */etc/dnscache*.

Per fermare e riavviare dnscahe si può ricorrere a

svc -d /etc/service/dnscache
svc -u /etc/service/dnscache

o anche a un più brutale kill del daemon dnscache: ci penseranno i daemontools a riavviarlo.

Per meglio gestire le zone interne o se si vuole sfruttare qualche dns interno si consideri che in /etc/dnscache/root/servers si possono aggiungere file, denominati come un dominio e contenenti una lista di DNS interrogabili per quel dominio. Ad esempio con:

cd /etc/dnscache/root/servers
echo 213.251.188.150 > linuxandcompany.it
echo 213.251.128.150 > linuxandcompany.it
svc -d /etc/service/dnscache
svc -u /etc/service/dnscache

posso dire alla cache dove di preferenza deve rivolgersi per le risoluzioni del dominio di questo sito.

Questa directory contiene di base un file denominato @ in cui è contenuta la lista dei root dns di internet.

squid come reverse proxy

Squid è un prodotto eccellente per fare da web cache; sia che si voglia alleggerire il carico sulla connessione, sia che si voglia fare del filtraggio a livello applicativo sia che si abbia la necessità di fare del monitoraggio molto dettagliato dell’uso del web su di una rete, la flessibilità di questo applicativo permette di intervenire praticamente su ogni aspetto del protocollo http e non solo.

Segnalo fin da subito che squid, come tutti i prodotti di web cache, richiede un’ attenzione particolare alla configurazione, in particolare per l’aspetto dei permessi; si corre il rischio, infatti, di mettere su internet un server proxy che può essere usato da terzi per mascherare le proprie attività.

Oggi voglio mostrare come configurare squid per usarlo come reverse-proxy. Si parla di proxy quando la cache viene messa davanti ai client e risolve al posto di questi le chiamate su internet. Si parla di reverse-proxy quando il server viene posto davanti ai web server e raccoglie al posto dei server web le chiamate dei client.

La ragione base per cui si fa questo è migliorare le prestazioni incidendo il meno possibile sui costi. I server cache infatti mantengono in memoria e/o su disco le risposte alle richieste più frequenti dei client e rispondono direttamente senza convolgere i server web; infatti  sono in genere in grado si rispondere ad un numero di richieste molto più alto di un server web che sempre più spesso deve essere configurato per gestire elaborazioni pesanti.

Squid è un prodotto molto consolidato ed è presente oramai sui tutte le distribuzioni linux. Per istallarlo quindi in genere la cosa più semplice è ricorrere ai tool della propria distribuzione. Su Debian/Ubuntu:

sudo aptitude update
sudo aptitude search squid
sudo aptitude install squid

La configurazione di default di squid è molto ben fatta e contiene così tanti commenti da essere quasi completamente autoesplicativa; merita un’esame se si vuole utilizzare questo prodotto. Nel nostro caso però la costruiremo da zero. Come risulta dalla documentazione ufficiale ci sono più possibilità per la configurazione in modalità Reverse Proxy; ne analizzeremo una di uso ragionevolmente generale.

http_port 80 accel defaultsite=[server name] vhost

cache_peer [IP sorgente 1] parent 80 0 no-query originserver name=RevPro1
acl web1 dstdomain [accelerated domains list 1]

cache_peer [IP sorgente 2] parent 80 0 no-query originserver name=RevPro2
acl web2 dstdomain [accelerated domains list 2]

http_access allow web1
http_access allow web2
cache_peer_access RevPro1 allow web1
cache_peer_access RevPro2 allow web2

cache_dir diskd /var/cache/squid3 1000 32 32 Q1=64 Q2=72
cache_mem 1024 MB

cache_replacement_policy heap LFUDA
memory_replacement_policy heap GDSF
cache_swap_low  90
cache_swap_high 95

Vediamo ora questa configurazione in maggiore dettaglio:

http_port 80 accel defaultsite=[server name] vhost

In questo modo si dice a squid che lavorerà in modalità reverse-proxy e con che tipo di configurazione. Defaultsite conviene valorizarlo con il nome del server web, quali sono i siti di cui si fa reverse proxy viene specificato più avanti. Vhost specifica che la discriminante per la scelta del server di origine sarà il dominio.

cache_peer [IP sorgente 1] parent 80 0 no-query originserver name=RevPro1

Definiamo qui un server web di cui fare reverse proxy e assegniamo un nome a questa definizione (RevPro1).

acl web1 dstdomain [accelerated domains list 1]

Definiamo una lista di siti web (separati da spazi) di cui si vuole fare reverse proxy e le assegnamo un nome (web1).

cache_peer [IP sorgente 2] parent 80 0 no-query originserver name=RevPro2
acl web2 dstdomain [accelerated domains list 2]

Ripeto per un secondo server origin e una seconda lista di siti.

http_access allow web1
http_access allow web2

dico a squid che deve rispondere alle richieste (dei client) relative alle due liste di siti web1 e web2.

cache_peer_access RevPro1 allow web1
cache_peer_access RevPro2 allow web2

Associo le origin alle liste dei siti supponendo che un dato server web di origine serve alcuni dei siti mentre l’altro serve gli altri.

cache_dir diskd /var/cache/squid3 1000 32 32 Q1=64 Q2=72
cache_mem 1024 MB

cache_replacement_policy heap LFUDA
memory_replacement_policy heap GDSF
cache_swap_low  90
cache_swap_high 95

Infine queste righe configurano la cache.

Come quasi sempre su squid si definiscono degli oggetti per definire attività da permettere o vietare; si definiscono delle acl per definire il “chi” ed infine si associano questi due tipi di informazione in righe dove si definisce chi può fare cosa.

Come sempre prima di mettere in produzione un server con squid è bene leggerne la documentazione ufficiale.

perl e memcached

Creaiamo un ambiente di test  per memcached e testiamolo con perl.

Utilizzeremo:

  • memcahced
  • perl
  • il modulo perl Cache::Memcached
  • il modulo perl String::Random
  • il comando di monitoraggio memcache-top

Do per scontato che perl sia installato. Questo è, credo, praticamente sempre vero in ambiente UNIX/LInux.

Potrebbe non essere invece presente il modulo perl per la gestione della cache con memcached. Lo si può istallare o come pacchetto dell distribuzione (es. su Debian/Ubuntu qualche cosa come sudo aptitude install libcache-memcached-perl)  o come modulo cpan:

$ cpan
cpan[1]> install Cache::Memcached

Analogamente istalliamo il modulo String::Random (libstring-random-perl).

Passiamo ora al daemone.memcached. Consideriamo come sempre un sistema Debian/Ubuntu dove basterà:

# aptitude update
# aptitude install memcached
# update-rc.d -f memcached remove
# /etc/init.d/memcached stop

Il primo comando aggiorna le liste dei pacchetti, il secondo istalla il demone. Dato che stiamo lavorando su di un ambiente di test, con il terzo comando si facciamo in modo che il demone non venga avviato all’avvio del pc e con l’ultimo fermiamo il demone lanciato con le configurazioni di default.

Memcached è presente in quasi tutte le distribuzioni e se non si utilizza un sistema Debian o derivato si può far ricorso al sistema di pacchettizzazione della propria distribuzione o anche ai sorgenti del demone recuperabili dal sito ufficiale.

L’ultimo elemento per l’ambiente di test è memcache-top, che può essere scaricato dal sito ufficiale.

wget -O memcache-top  http://memcache-top.googlecode.com/files/memcache-top-v0.6

Ora che abbiamo tutti gli elementi possiamo aprire una shell e avviare 3 demoni per avere un pool non banale. Li metteremo in ascolto su localhost ma, ovviamente, su porte differenti.

/usr/bin/memcached -d -m 64 -p 11211 -u memcache -l 127.0.0.1
/usr/bin/memcached -d -m 64 -p 11212 -u memcache -l 127.0.0.1
/usr/bin/memcached -d -m 64 -p 11213 -u memcache -l 127.0.0.1

possimao poi avviare in un’altra shell memcache-top con cui monitereremo il nostro pool di server

memcache-top --commands --instance=127.0.0.1:11211,127.0.0.1:11212,127.0.0.1:11213

Vediamo ora un primo semplice script perl che chiameremo memcahced.pl:

#!/usr/bin/perl -w

use strict;
use Cache::Memcached;

my $memd = new Cache::Memcached {
        'servers' => [ "localhost:11211", "localhost:11212", "localhost:11213" ],
        'debug' => 0,
        'compress_threshold' => 10_000,
};

$memd->set("my_key", "Some Value\n");
my $val = $memd->get("my_key");
if ($val) { print $val; }

In dettaglio:

#!/usr/bin/perl -w

use strict;
use Cache::Memcached;

Richiama l’eseguibile attivando i warning e i moduli strict e Memcached.

my $memd = new Cache::Memcached {
        'servers' => [ "localhost:11211", "localhost:11212", "localhost:11213" ],
        'debug' => 0,
        'compress_threshold' => 10_000,
};

definisce l’oggetto di connessione a memcached inserendo una lista di server.

$memd->set("my_key", "Some Value\n");
my $val = $memd->get("my_key");
if ($val) { print $val; }

scrive una chiave su memcached e la rilegge andando poi a scrivere a video il risultato.
Per eseguire questo script non bisogna dimenticare di rendere eseguibile il file o richiamarlo esplicitamente con l’interprete perl.

Con una singola esecuzione memcache-top non dice gran che ma se si esegue ripetutamente lo script ad esempio con

while [ true ]; do ./memcached.pl ; done

si vedranno numerosi accessi ad un singolo server; come ci si sarebbe dovuti aspettare trattandosi di una sola chiave.
Nel mio caso memcahce-top riporta:

memcache-top v0.6	(default port: 11211, color: on, refresh: 3 seconds)

INSTANCE		USAGE	HIT %	CONN	TIME	EVICT/s GETS/s	SETS/s	READ/s	WRITE/s	
127.0.0.1:11211		0.0%	0.0%	5	1.0ms	0.0	0	0	2	344	
127.0.0.1:11212		0.0%	0.0%	5	0.8ms	0.0	0	0	2	344	
127.0.0.1:11213		0.0%	0.0%	5	0.6ms	0.0	14	14	647	992	

AVERAGE:		0.0%	0.0%	5	0.8ms	0.0	5	5	217	560	

TOTAL:		83B/	0.2GB		15	2.4ms	0.0	14	14	651	1680	
(ctrl-c to quit.)

ma soprattutto dopo aver ucciso il demone in ascolto sulla porta 11213

memcache-top v0.6	(default port: 11211, color: on, refresh: 3 seconds)

INSTANCE		USAGE	HIT %	CONN	TIME	EVICT/s GETS/s	SETS/s	READ/s	WRITE/s	
127.0.0.1:11211		0.0%	0.0%	5	0.6ms	0.0	0	0	2	344	
127.0.0.1:11212		0.0%	0.0%	5	0.4ms	0.0	15	15	662	1007	
127.0.0.1:11213 is DOWN.

AVERAGE:		0.0%	0.0%	3	0.3ms	0.0	5	5	221	450	

TOTAL:		83B/	0.1GB		10	1.0ms	0.0	15	15	664	1351	
(ctrl-c to quit.)

dove so vede che le connessioni si sono spostate su di un’altro demone. Su questo aspetto bisogna fare molta attenzione dato che in un sistema di produzione la gestione dei fault  è cruciale e dato che questo aspetto è demandato alla libreria del linguaggio che si utilizza  bisogna averne molto chiaro il comportamento. Vale comunque in generale che bisognerebbe avere in numero sufficiente di server per cui se se ne dovesse perdere uno i restanti dovrebbero essere sufficienti a svolgere i loro compiti; in altre parole è molto meglio distribuire il servizio su molti serevr che averne uno o due dedicati.

Per vedere in che modo le chiavi si distribuiscono sul cluster ora inseriamo un valore fisso in una chiave di nome casuale (la distribuzione è legata al nome della chiave). Per farlo utilizziamo un altro script perl che chiameremo memcahced_random.pl:

#!/usr/bin/perl -w

use strict;
use Cache::Memcached;
use String::Random;

my $memd = new Cache::Memcached {
        'servers' => [ "localhost:11211", "localhost:11212", "localhost:11213" ],
        'debug' => 0,
        'compress_threshold' => 10_000,
};

my $foo = new String::Random;
my $random_key = $foo->randpattern("...");
my $value = "chiave casuale";

$memd->set("$random_key", "$value\n");
my $val = $memd->get("$random_key");
if ($val) { print $val; }

Non è molto diverso dal precedente tranne nella riga:

my $random_key = $foo->randpattern("...");

in cui viene generata casualmente una stringa di 3 caratteri da utilizzare come nome della chiave.
Anche in questo caso conviene inserire lo script in un loop per visualizzarne meglio gli effetti con memcache-top:

while [ true ]; do ./memcached_random.pl; done

Le connessioni si andranno a distribuire sui vari nodi in modo ragionevolmente equilibrato

memcache-top v0.6	(default port: 11211, color: on, refresh: 3 seconds)

INSTANCE		USAGE	HIT %	CONN	TIME	EVICT/s GETS/s	SETS/s	READ/s	WRITE/s	
127.0.0.1:11211		0.1%	0.0%	5	0.9ms	0.0	7	7	317	686	
127.0.0.1:11212		0.1%	0.0%	5	0.6ms	0.0	9	9	375	748	
127.0.0.1:11213		0.1%	0.0%	5	0.6ms	0.0	9	9	403	779	

AVERAGE:		0.1%	0.0%	5	0.7ms	0.0	8	8	365	738	

TOTAL:		132.7KB/	0.2GB		15	2.2ms	0.0	25	25	1095	2213	
(ctrl-c to quit.)

Memcached – 2

Come già accennato nel precedente post, memcached è un daemon piuttosto semplice da configurare e istallare.

Sui sistemi Debian e derivati memcached può essere istallato con

sudo aptitude install memcached

altrimenti si può ricorrere ai sistemi di pacchettizzazione delle altre architetture o direttamente ai sorgenti rilasciati dagli sviluppatori.

Se si istalla da pacchetto debian a valle dell’istallazione verrà lanciato un daemon che, come risulta da ps aux, sarà:

/usr/bin/memcached -m 64 -p 11211 -u memcache -l 127.0.0.1

La configurazione completa sarà invece in

/etc/memcached.conf

Analizziamo i parametri di avvio

  • -l: l’ip su cui il demone è in ascolto
  • -p: la porta TCP su cui il demone è in ascolto
  • -m: la quantità di memoria (in MB) che viene assegnata al demone memcached

e non visibili perché con impostazioni di default (ma riportati sul file di configurazione)

  • -d: memcached viene lanciato in modalità demone e quindi rilascia il controllo all’ambiente che lo ha avviato
  • logfile: path del file di log
  • -c: numero massimo di connessioni contemporanee accettate
  • -M: ritorna errore nel caso non ci sia memoria disponibile
  • -v: incremento del livello di dettaglio nei log
  • -vv: ulteriore incremento del livello di dettaglio nei log

I più imprtanti per il tooning sono ovviamente -m, -c e -M. Nel determinare il primo bisogna tener conto che se l’uso di memcached fa si che il server debba appoggiarsi sullo swap l’effetto complessivo sarà con ogni probabilità quello di peggiorare le prestazioni.

Vale la pena spendere due parole su -M; di default memcached quando non ha più spazio disponibile sovrascrive i valori più vecchi e meno utilizzati. Questo viene indicato come evictions nei report e nella documentazione. Se questo sia accettabile o no dipende da considerazione sull’applicazione che si sta realizzando ma è in genere molto ragionevole per della cache. -M permette di cambiare questo comportamento nel caso dovesse essere necessario.

Per connettersi ad un server memcached è possibile utilizzare un semplice telnet:

telnet localhost 11211

Una volta connessi si possono in teoria eseguire tutte le operazioni; ad esempio salvare una variabile

set pluto 123 0 3
o

dove viene detto di salvare nella variabile pluto il valore o. 123 è un numero arbitrario usato nel protocollo di comunicazione, 0 il tempo di expire del valore (in questo caso infinito) e 3 la dimensione dei dati che verranno inviati.

Si può poi rileggere lo stesso valore con un

get pluto

Attenzione che anche un solo spazio in più fa fallire l’esecuzione del comando.

Questo uso con telnet è paragonabile all’invio della posta con lo stesso strumento ma rimane utile per test e troubleshooting. Qualsiasi uso applicativo deve passare attraverso un’apposita libreria in un qualche linguaggio (php, perl…). Oltretutto a questa libreria sono completamente demandate le funzionalità necessarie per l’uso in cluster.

Il telnet inoltre permette di accedere a ben quattro report molto dettagliati sullo stato interno del daemon:

stats
stats items
stats slabs
stats sizes

Questi quattro comandi sono alla base di report più ad alto livello. Decisamente più maneggevole è ad esempio memcache-top. Questo permette di avere un’istantanea molto comprensibile  dello stato di un pool e dell’efficacia dell’uso che se ne fa:

memcache-top --commands --instance=serv1:port1[,serv2:port2...]

il report sarà qualche cosa come:

memcache-top v0.5       (default port: 11211, color: on, refresh: 3 seconds)

INSTANCE                USAGE   HIT %   CONN    TIME    EVICT   READ    WRITE
serv1:port1             90%     80%     800     0.6ms   20      10G     20G    
serv2:port2             90%     80%     700     0.5ms   10      15G     30G  
...

AVERAGE:                90%     80%     750     0.5ms   15      12.5G   25G  

TOTAL:                  2GB            1500     11.0ms  30      25G     50G    
(ctrl-c to quit.)

Questo report, se non specificato diversamente, viene rigenerato ogni 3s e si riferisce a questo intervallo temporale.