mar 162011
 

Riprendiamo il filone dei test di velocità con ab per capire la differenza che intercorre tra apache ed nginx per servire contenuti statici. Questo è per far capire quanto si migliorano le prestazioni di un sito utilizzando il sistema descritto in questo articolo. I parametri dell’ambiente di test sono gli stessi dell’articolo precedente.

Caso 1

Filesystem: ext3 locale
Contenuto servito tramite Apache
Richieste contemporanee: 10

Time taken for tests:   3.670543 seconds
Requests per second:    272.44 [#/sec] (mean)
Time per request:       36.705 [ms] (mean)
Time per request:       3.671 [ms] (mean, across all concurrent requests)
Transfer rate:          1368.73 [Kbytes/sec] received

Caso 2

Filesystem: ext3 locale
Contenuto servito tramite nginx
Richieste contemporanee: 10

Time taken for tests:   0.689000 seconds
Requests per second:    1451.38 [#/sec] (mean)
Time per request:       6.890 [ms] (mean)
Time per request:       0.689 [ms] (mean, across all concurrent requests)
Transfer rate:          7354.14 [Kbytes/sec] received

Caso 3

Filesystem: DRBD 8/OCFS2
Contenuto servito tramite nginx
Richieste contemporanee: 10

Time taken for tests:   4.439284 seconds
Requests per second:    225.26 [#/sec] (mean)
Time per request:       44.393 [ms] (mean)
Time per request:       4.439 [ms] (mean, across all concurrent requests)
Transfer rate:          1133.07 [Kbytes/sec] received

Da questi testi si evince che la velocità di nginx nel servire contenuti statici rispetto ad apache è notevole, però il filesystem DRBD/OCFS2 è comunque il “collo di bottiglia” della configurazione, cosa che nei test precendenti sui contenuti dinamici non appariva. Seppur da questi test non appaia evidente il beneficio che si ha adottando nginx rispetto ad apache per servire i contenuti statici, questo appare evidente se, durante i test, si lancia sul server il comando “top”: il carico di lavoro sul server prodotto da nginx rispetto ad apache è notevolemte inferiore, per cui visto che i nostri siti si compongono sia di parti statiche sia di parti dinamiche, serverndo i contenuti statici con il “caso 3″ inceve che con il “caso 1″ non si ha un aumento di prestazioni sui singoli contenuti statici, ma lo si ha sulla stabilità del webserver in quanto si lasciano libere più risorse.

mar 152011
 

Faccio seguito agli articoli sui diversi metodi di configurazione del web server per esporvi alcune statistiche realizzate attraverso il comando ab sulla home page di un sito WordPress particolarmente pesante ed esoso di risorse, utilizzando i diversi metodi di esecuzione di php, diversi filesystem e diverse distibuzioni Linux.

Il comando ab permette di calcolare i tempi medi di download di una pagina web settando il numero di richieste totali e la contemporaneità delle stesse. L’ambiente di test prevede una macchina virtuale dedicata su VMWare esxi con assegnati 4 GB di Ram e 4 core CPU. L’hardware fisico è un server Dell PowerEdge R410 con due CPU Xeon E5520 QuadCore a 2.27 GHz. La macchina ospita localmente solo il server Web Apache/PHP, mentre il database MySQL è su un secondo server accessibile sempre in rete locale. Il PC su cui viene lanciato il comando ab è in rete locale e si richiedono 1000 pagine con una contemporaneità di 10 (tranne il caso 9).

(valore più basso, risultato migliore)

 

Caso 1: la situazione iniziale da migliorare

Distribuzione: CentOS 5.5
Posizione file PHP: SAN in rete locale 1 Gbit con FileSystem OCFS2
Metodo di esecuzione PHP: SuPHP
Plugin WordPress attivi: moltissimi tra cui nextgen gallery e WassUp

Time taken for tests:   221.287876 seconds
Requests per second:    4.52 [#/sec] (mean)
Time per request:       2212.879 [ms] (mean)
Time per request:       221.288 [ms] (mean, across all concurrent requests)
Transfer rate:          2.15 [Kbytes/sec] received

Caso 2

Distribuzione: CentOS 5.5
Posizione file PHP: SAN in rete locale 1 Gbit con FileSystem OCFS2
Metodo di esecuzione PHP: mod_PHP
Plugin WordPress attivi: moltissimi tra cui nextgen gallery e WassUp

Time taken for tests:   167.713406 second
Requests per second:    5.96 [#/sec] (mean)
Time per request:       1677.134 [ms] (mean)
Time per request:       167.713 [ms] (mean, across all concurrent requests)
Transfer rate:          2.84 [Kbytes/sec] received

Caso 3

Distribuzione: CentOS 5.5
Posizione file PHP: SAN in rete locale 1 Gbit con FileSystem OCFS2
Metodo di esecuzione PHP: mod_PHP
Plugin WordPress attivi: nessuno

Time taken for tests:   78.987 seconds
Requests per second:    12.66 [#/sec] (mean)
Time per request:       789.875 [ms] (mean)
Time per request:       78.987 [ms] (mean, across all concurrent requests)
Transfer rate:          3.97 [Kbytes/sec] received

I primi 3 test riguardano esclusivamente un ambiente di test standard in cui modifichiamo prima il metodo di esecuzione di PHP passando dal “comodo” suPHP al più performante mod_PHP in modalità prefork (non multithreading, quindi). Il miglioramento di prestazioni si evidenzia, così come “predetto” nel precedente articolo: 6 decimi di secondo per ogni richiesta. Con il test 3, abbiamo disabilitato tutti gli esosi plugin di wordpress e si può notare come le prestazioni aumentino in maniera considerevole su scala di valori immensamente più grande (1,5 secondi a pagina), segno che comunque un buon sistemista non può fare miracoli se il codice da eseguire non è ottimizzato per la velocità.
Purtroppo non possiamo prescindere dall’utilizzo degli esosi plugin di WordPress, per cui proviamo a vedere se cambiando distribuzione le cose migliorano.

Caso 4: come caso 1 con diversa distribuzione

Distribuzione: Gentoo Linux
Posizione file PHP: SAN in rete locale 1 Gbit con FileSystem OCFS2
Metodo di esecuzione PHP: SuPHP
Plugin WordPress attivi: moltissimi tra cui nextgen gallery e WassUp

Time taken for tests:   198.396024 seconds
Requests per second:    5.04 [#/sec] (mean)
Time per request:       1983.960 [ms] (mean)
Time per request:       198.396 [ms] (mean, across all concurrent requests)
Transfer rate:          1.37 [Kbytes/sec] received

Bene, un ambiente “su misura” come quello creato da una distribuzione in cui si compila ogni singolo elemento porta i suoi frutti: circa 2 decimi di secondo in meno a richiesta per un totale di 20 secondi sullo svolgimento del test. Da notare che nel caso di Gentoo, abbiamo attivo anche il sistema Grsecurity che comunque rallenta leggermente i processi, mentre su CentOS abbiamo disabilitato SELinux.

Caso 5: come caso 4 ma con mmm_itk
Distribuzione: Gentoo Linux
Posizione file PHP: SAN in rete locale 1 Gbit con FileSystem OCFS2
Metodo di esecuzione PHP: Apache ITK
Plugin WordPress attivi: moltissimi tra cui nextgen gallery e WassUp

Time taken for tests:   182.14380 seconds
Requests per second:    5.49 [#/sec] (mean)
Time per request:       1820.144 [ms] (mean)
Time per request:       182.014 [ms] (mean, across all concurrent requests)
Transfer rate:          1.49 [Kbytes/sec] received

Il passaggio da suPHP ad Apache compilato in modalità ITK permette di guadagnare altri 2 decimi a richiesta, mantenendo quindi un ambiente del tutto similare per l’utente a quello iniziale con CentOS/suPHP. Non si hanno le prestazioni del mod_php su CentOS (mancano ancora 2 decimi), però la situazione non è poi così diversa e… meglio perdere 2 decimi e guadagnare un cliente!
Abbimo preso per buono il fatto che i file php del sito stiano su una SAN accessibile via rete. Vediamo come si influenzano i tempi spostando i file php in locale ed utilizzando diversi filesystem.

Caso 6

Distribuzione: Gentoo Linux
Posizione file PHP: locale con filesystem ext3
Metodo di esecuzione PHP: Apache ITK
Plugin WordPress attivi: moltissimi tra cui nextgen gallery e WassUp

Time taken for tests:   147.679954 seconds
Requests per second:    6.77 [#/sec] (mean)
Time per request:       1476.799 [ms] (mean)
Time per request:       147.680 [ms] (mean, across all concurrent requests)
Transfer rate:          1.84 [Kbytes/sec] received

Niente da dire, siamo sulla strada giusta. Seppure la SAN sia in rete locale a 1 Gbit, le prestazioni di un filesystem locale sono nettamente superiori guadagnando ben 4 decimi a pagina rispetto al test 5. Seppure questa soluzione rappresenti un buon passo in avanti è però impossibile da utilizzare in produzione in quanto non garantisce il funzionamento in caso di guasto del server; inoltre un sistema in alta affidabilità (acceso/spento) non ci garantisce nel caso di intenso traffico sul sito per cui vogliamo avere almeno due server web in load balancing. Dobbiamo quindi dotare il filesystem locale di un sistema di replica in modo che sia disponibile in due diversi server.

Caso 7

Distribuzione: Gentoo Linux
Posizione file PHP: locale con filesystem GlusterFS
Metodo di esecuzione PHP: Apache ITK
Plugin WordPress attivi: moltissimi tra cui nextgen gallery e WassUp

Time taken for tests:   467.476497 seconds
Requests per second:    2.14 [#/sec] (mean)
Time per request:       4674.765 [ms] (mean)
Time per request:       467.477 [ms] (mean, across all concurrent requests)
Transfer rate:          0.58 [Kbytes/sec] received

Decisamente non ci siamo. Nonostante Gluster ci permetta di accedere localmente ai file e di replicarli tra di loro, il sistema impiega ad eseguire il test un tempo due volte maggiore rispetto alla situazione iniziale. Proviamo quindi ad usare un filesystem cluster come OCFS2 e DRBD 8 configurato in modalità attivo-attivo:

Caso 8

Distribuzione: Gentoo Linux
Posizione file PHP: locale con DRBD su filesystem OCFS2
Metodo di esecuzione PHP: Apache ITK
Plugin WordPress attivi: moltissimi tra cui nextgen gallery e WassUp

Time taken for tests:   149.53515 seconds
Requests per second:    6.71 [#/sec] (mean)
Time per request:       1490.535 [ms] (mean)
Time per request:       149.054 [ms] (mean, across all concurrent requests)
Transfer rate:          1.82 [Kbytes/sec] received

Bene, DRBD 8 in modalità attivo-attivo e il filesystem OCFS2 utilizzato inizialmente sulla SAN iSCSI portano a delle velocità paragonabili a quelle del filesystem locale e alla fine migliori di circa 8 decimi di secondo rispetto alla situazione iniziale. Il sistema evidentemente è meno scalabile in quanto con un filesystem su SAN possiamo aggiungere infiniti nodi di calcolo in load balancing, mentre con DRBD siamo limitati a 2 nodi; questa mancanza di scalabilità migliora di 4 decimi l’output della pagina rispetto al caso 5: non si può dire se è meglio la soluzione 5 o la 8, ciascuno deve trarre le proprie conclusioni in base al tipo di servizio che deve offrire.
Inoltre c’è da dire che l’installazione di wordpress presa in esame è particolarmente pesante e lenta. Giusto a titolo comparatvo, per far felice il mio amico Maurizio del sito www.contaotutorial.com, ecco un test eseguito su un sito da lui realizzato con Contao.

Caso 9

Distribuzione: CentOS 5.5
Posizione file PHP: SAN in rete locale 1 Gbit con FileSystem OCFS2
Metodo di esecuzione PHP: mod_php
ATTENZIONE, in questo caso utilizziamo una contemporaneità di 100 richieste, non 10, quindi per avere un valore della stessa scala, nel grafico iniziale l’abbiamo diviso per 10!

Time taken for tests:   99.116 seconds
Requests per second:    10.09 [#/sec] (mean)
Time per request:       9911.572 [ms] (mean)
Time per request:       99.116 [ms] (mean, across all concurrent requests)
Transfer rate:          128.59 [Kbytes/sec] received

Ribadisco quanto già detto a commento del caso 3: un buon sistemista non può fare i miracoli se il codice è scritto senza alcuna ottimizzazione per la velocità.

mar 092011
 

Concludiamo questa prima carrellata di articoli sui web server con le istruzioni per utilizzare un vecchio sistema per velocizzare il servizio web.

Premettiamo una cosa: apache è un ottimo web server, ci si può fare anche il caffelatte, però è tutto tranne che “light”. Pensiamo solo a come funziona il metodo prefork: per servire un qualsiasi contenuto, ammettiamo una immagine gif di pochi kbytes, viene istanziato un processo che occupa svariati Mbytes di RAM! Oltre ad essere un sistema non proprio veloce, nel caso di siti molto visitati questo può arrivare a generare problemi di saturazione del server… e non è nemmeno troppo difficile arrivarci!
Visto che non tutto va bene per tutto, in questo articolo vedremo come configurare il proprio server in modo da servire i contenuti dinamici (script php ad esempio) attraverso apache e i contenuti statici attraverso nginx, che è un web server molto light e, quindi, molto veloce.

Il sistema che utilizzaremo è di far rispondere alle richieste dirette all’indirizzo IP esterno su porta 80 nginx, mentre apache lavorerà solo sull’indirizzo IP di loopback (127.0.0.1) su porta 80. Nginx si prenderà in carico qualsiasi richiesta è la soddisferà autonomamente nel caso in cui si tratti di contenuti statici (immagini, script javascript, css, ecc.) e la girerà al corrispondente virtualhost di apache nel caso in cui si tratti di contenuti dinamici.

Una volta installati i pacchetti, andiamo ad editare il file di configurazione di nginx.

/etc/nginx/nginx.conf
user www-data;
worker_processes  2;
error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;
events {
    worker_connections  1024;
}
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    access_log /var/log/nginx/access.log;
    server_names_hash_bucket_size 64;
    sendfile        on;
    tcp_nopush     on;
    #keepalive_timeout  0;
    keepalive_timeout  65;
    tcp_nodelay        on;
    gzip              on;
    gzip_comp_level   5;
    gzip_http_version 1.0;
    gzip_min_length   0;
    gzip_types        text/plain text/html text/css image/x-icon application/x-javascript;
    gzip_vary         on;
}
server {
    listen       XXX.XXX.XXX.XXX:80 default;
    server_name  _;
    access_log /var/log/nginx/default.access.log main;

    location / {
        proxy_pass http://127.0.0.1:80;
        include /etc/nginx/conf.d/proxy.conf;
   }
}

include /etc/nginx/sites-enabled/*;

Attraverso questa configurazione, i file inviati direttamente da nginx saranno compressi con un fattore di 5. Più alto è questo valore, più alto sarà il carico di CPU e minore sarà la quantità di banda utilizzata; 5 mi sembra un buon compromesso.
La sezione server specifica un virtual host di default che sarà utilizzato nel caso in cui non venga trovato un virtual host specifico: in questo caso tutte le richieste saranno reindirizzare al web server apache che è in ascolto su localhost porta 80. Ovviamente sostiruite a xxx.xxx.xxx.xxx l’indirizzo IP del vostro server.
Attraverso l’ultima riga andremo ad includere tutti i file presenti nella directory /etc/nging/sites-enabled/ dove specificheremo tutti i virtual host del nostro server.
Andiamo ora ad editare la configurazione del proxy che sarà utilizzata da tutti i virtual host.

/etc/nginx/conf.d/proxy.conf
proxy_redirect          off;
proxy_set_header        Host            $host;
proxy_set_header        X-Real-IP       $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size    10m;
client_body_buffer_size 128k;
client_header_buffer_size 64k;
proxy_connect_timeout   90;
proxy_send_timeout      90;
proxy_read_timeout      90;
proxy_buffer_size   16k;
proxy_buffers       32   16k;
proxy_busy_buffers_size 64k

L’unica impostazione degna di nota il proxy_buffers e proxy_buffer_size a 16k: questo valore è piuttosto elevato in quanto, se settato più basso, potrebbero esserci problemi con i cookies, nel qual caso il server ritorna un errore 5xx.

Ecco infine il template per un file di virtualhost per il dominio dominio.tld da mettere nella directory /etc/nginx/sites-enabled:

server {
    listen XXX.XXX.XXX.XXX:80;
    server_name dominio.tld www.dominio.tld;

    location / {
        proxy_pass http://127.0.0.1:80;
        include /etc/nginx/proxy.conf;
    }

    location ~* ^.+.(jpe?g|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js|swf|avi|mp3)$ {
        expires 30d;
        root /var/www/dominio.tld/htdocs;
    }
}

Anche in questo caso, ricordarsi di sostituire xxx.xxx.xxx.xxx con il proprio indirizzo IP. Il file di configurazione usa il proxy per tutti i contenuti, tranne quelli statici definiti nella seconda parte del file di configurazione. La root dei file del sito (/var/www/dominio.tld/htdocs) è comune a nginx e ad apache.

Passiamo ora alla configurazione di apache. Innanzi tutto bisogna far si che apache risponda solo alla porta 80 di localhost. In Ubuntu/Debian si setta in /etc/apache2/ports.conf:

Listen 127.0.0.1:80

Inoltre verificate che il file di configurazione dei VirtualHost di apache non includano l’indirizzo IP:

NameVirtualHost *:80
<VirtualHost *:80>
ServerName foobar.com
...
</VirtualHost>

Ultima configurazione da fare in Apache riguarda i log. Difatti con la configurazione qui sopra, apache memorizzerà l’indirizzo IP locale nei file di log. Fortunatamente nginx invia ad apache l’header HTTP X-Forwarded-For: si tratta solo di far si che apache legga questo dato. In soccorso ci viene il modulo mod_rpaf di apache, la cui configurazione è la seguente:

<IfModule mod_rpaf.c>
 RPAFenable On
 RPAFsethostname On
 RPAFproxy_ips 127.0.0.1
</IfModule>
mar 082011
 

Come detto nel precedente articolo di introduzione al funzionamento di apache,  l’utente con cui vengono creati i processi di apache è univoco e viene settato nel file di configurazione principale di apache attraverso le direttive “User” e “Group”; a seconda delle distribuzioni, l’utente principale con cui vengono creati i processi si chiama “apache” oppure “www-data”. Se apache dovesse servire esclusivamente contenuti statici, questa non sarebbe una limitazione eccessiva; capita sempre più spesso, però, che si utilizzino script lato server (php, python, perl, ruby…) che hanno la necessità di scrivere delle informazioni sul server. Come fare?

La vecchia scuola
Normalmente i file di un sito web hanno come proprietario l’utente FTP con cui sono stati caricati; dato che un file non può avere più proprietari (l’utente dell’ftp e l’utente “apache”, in questo caso), la “vecchia scuola” consiglia di configurare il linugaggio di script direttamente come modulo di apache (mod_ruby, mod_perl, mod_python e mod_php) e  di aggiungere i privilegi di scrittura all’utente apache esclusivamente dove deve scrivere modificando i privilegi dei file a 666 (lettura e scrittura per tutti) o delle directory a 777 (lettura, scrittura ed esecuzione per tutti).
I vantaggi di usare questo metodo sono:
1) l’utente apache ha i permessi di scrittura sono assegnati solo dove serve
2) si utilizzano i moduli di apache per eseguire gli script: la velocità di esecuzione è quindi notevole
Gli svantaggi di questo metodo, invece, sono:
1) complessità: è necessario specificare manualmente dove apache deve scrivere e, quindi, è necessario conoscere nel dettaglio il codice per sapere dove applicare i nuovi permessi
2) sicurezza: l’utente apache è unico per tutti i siti in virtual hosting e, quindi, teoricamente uno script presente in un altro sito potrebbe scrivere all’interno della directory su cui si sono sbloccati i permessi.
Molti sono stati i tentativi per ovviare a quest’ultimo problema (ad esempio le funzioni OpenBaseDir e SafeMode di PHP), però nessuna riesce a risolvere il problema alla radice: se la situazione è questa, nella relaizzazione di un silto il consiglio è quello di limitare al massimo la scrittura di file, utilizzando il più possibile i database per memorizzare le informazioni. Dove è proprio necessario scrivere file su disco (ad esempio per procedure di autoaggiornamento) non sbloccare i permessi di scrittura ma utilizzare le funzionalità di client FTP integrate al linguaggio di scripting per connettersi localmente ed acquisire quindi i permessi di scrittura. Questo sistema è, senza dubbio, il metodo più sicuro e veloce per un sito web ed è supportato nativamente da sistemi come Joomla e WordPress.

La nuova scuola
La nuova scuola indica come via maestra quella di sollevare il webmaster dall’onere di pensare ai permessi, facendo si che l’utente con cui vengono eseguiti gli script non sia l’utente apache, ma l’utente ftp. Per far ciò non è possibile eseguire gli script internamente ad apache attraverso i moduli, ma è necessario affidarsi a sistemi esterni ad apache che vengono eseguiti ogni volta che viene richiamato uno script PHP. La configurazione di questo sistema avviene direttamente all’interno della sezione VirtualHost del sito web, pertanto è possibile assegnare utenti diversi per ogni sito web.
I due eseguibili esterni più gettonati per compiere questo lavoro sono SuPHP e SuEXEC/FastCGI; vediamo un esempio di configurazione per entrambi, tralasciando solo l’installazione dei pacchetti che varia a seconda della distribuziona scelta.

Partiamo dai seguenti dati:
Home directory: /home/www/vhosts/myuser/myhost.localdomain/htdocs
User: myuser
Group: mygroup
URL: myhost.localdomain

Configurazione di suEXEC/FastCGI

Per la configurazione di suEXEC/FastCGI, innanzi tutto creare una nuova directory .cgi-bin all’esterno della home directory del sito e copiare al suo interno il file di configurazione del php:

# mkdir /home/www/vhosts/myuser/myhost.localdomain/.cgi-bin
# cp -fv /etc/php5/cgi/php.ini /home/www/vhosts/myuser/myhost.localdomain/.cgi-bin/

Assicuriamoci che all’interno del file di configurazione appena copiato ci sia il parametro OpenBaseDir settato:

/home/www/vhosts/myuser/myhost.localdomain/.cgi-bin/php.ini
open_basedir = /home/www/vhosts/myuser/myhost.localdomain/htdocs

Quindi creare un file php5.fcgi all’interno della nuova directory:

/home/www/vhosts/myuser/myhost.localdomain/.cgi-bin/php5.fcgi
   #!/bin/bash
   #
   # php5.fcgi
   # Shell Script per usare PHP5 con mod_fastcgi su Apache 2.x
   #
   USER=$(/usr/bin/whoami)
   PHPRC="/var/www/vhosts/$USER/myhost.localdomain/.cgi-bin/php.ini"
   PHP_FCGI_CHILDREN=5
   #PHP_FCGI_MAX_REQUESTS=1000
   export PHPRC
   export PHP_FCGI_CHILDREN
   #export PHP_FCGI_MAX_REQUESTS
   exec /usr/bin/php5-cgi

Settiamo tutti i permessi:

# chmod 755 /var/www/vhosts/myuser/myhost.localdomain/.cgi-bin/php5.fcgi
# chown -Rf myuser:mygroup /var/www/vhost/myuser/myhost.localdomain/htdocs
# chown -Rf myuser:mygroup /var/www/vhost/myuser/myhost.localdomain/.cgi-bin
# chown root:root /var/www/vhost/myuser/myhost.localdomain/php.ini
# chmod 710 /var/www/vhost/myuser/myhost.localdomain
# chown myuser:apache /var/www/vhost/myuser/myhost.localdomain

Quindi andiamo a creare il virtualhost

<VirtualHost *:80>
    ServerName myhost.localdomain
    DocumentRoot /var/www/vhost/myuser/myhost.localdomain/htdocs
    ServerAdmin hostmaster@myhost.localdomain

    ErrorLog /var/log/apache2/myhost.localdomain_error.log
    CustomLog /var/log/apache2/myhost.localdomain_access.log combined

    SuexecUserGroup myuser mygroup
    ScriptAlias /cgi-bin/ "/var/www/vhost/myuser/myhost.localdomain/.cgi-bin/"

    <Directory /var/www/vhost/myuser/myhost.localdomain/.cgi-bin/>
        AllowOverride None
        Options None
        Order allow,deny
        Allow from all
    </Directory>

    <Directory "/var/www/vhost/myuser/myhost.localdomain/htdocs">
        Options Indexes Includes FollowSymLinks ExecCGI
        AllowOverride All
        AddHandler php5-fastcgi .php .php5 .php4
        Action php5-fastcgi /cgi-bin/php5.fcgi
        Order allow,deny
        Allow from All
    </Directory>
</VirtualHost>

Configurazione di suPHP

La configurazione di suPHP è forse un po’ più semplice rispetto a quella di FastCGI/suEXEC: suPHP è meno esoso in fatto di RAM rispetto alla precedente configurazione, però ogni tanto rimangono processi in stato “defunct”. A livello si velocità i due sistemi si equivalgono.

Come per FastCGI/suEXEC andiamo a creare una directory esterna al web server e copiamo il file di configurazione di php:

# mkdir /home/www/vhosts/myuser/myhost.localdomain/.etc
# cp -fv /etc/php5/cgi/php.ini /home/www/vhosts/myuser/myhost.localdomain/.etc/

Assicuriamoci che all’interno del file di configurazione appena copiato ci sia il parametro OpenBaseDir settato:

/home/www/vhosts/myuser/myhost.localdomain/.etc/php.ini
open_basedir = /home/www/vhosts/myuser/myhost.localdomain/htdocs

Andiamo ad editare il file di configurazione di suPHP (normalmente /etc/suphp.conf):

[global]
webserver_user=www-data
docroot=/var/www:${HOME}
check_vhost_docroot=false

[handlers]
;Handler for php-scripts
application/x-httpd-php="php:/usr/bin/php-cgi"

Quindi creiamo la configurazione del virtualhost:

<VirtualHost *:80>
    ServerName myhost.localdomain
    DocumentRoot /var/www/vhost/myuser/myhost.localdomain/htdocs
    ServerAdmin hostmaster@myhost.localdomain

    ErrorLog /var/log/apache2/myhost.localdomain_error.log
    CustomLog /var/log/apache2/myhost.localdomain_access.log combined

    suPHP_UserGroup myuser mygroup
    suPHP_Config "/var/www/vhost/myuser/myhost.localdomain/.etc/"

    <Directory "/var/www/vhost/myuser/myhost.localdomain/htdocs">
        Options Indexes Includes FollowSymLinks ExecCGI
        AllowOverride All
        AddHandler application/x-httpd-php .php .php5 .php4
        suPHP_AddHandler application/x-httpd-php
        suPHP_Engine on
        Order allow,deny
        Allow from All
    </Directory>
</VirtualHost>

La nuovissima scuola: mpm_itk e mpm_peruser
Il difetto principale dell’approccio di suPHP e suEXEC/FastCGI è di dover far eseguire un applicativo esterno da apache, cosa che genera un notevole consumo di risorse CPU e RAM. L’idea è quindi di creare un Multi Processing Module di apache, in grado di gestire direttamente l’esecusione di processi con diversi utenti e, quindi, tornare ad utilizzare direttamente il linguaggio di scripting attraverso i moduli.
mpm_itk è un mpm non sviluppato direttamente da Apache, ma è basato in tutto e per tutto su mpm_prefork e quindi è compatibile appieno con mod_php. D’altro canto, essendo basato su prefork e non su worker, si perde il sistema di threading di apache 2.0. Una volta installato, la configurazione è estremamente semplice: all’interno della direttiva virtualhost è sufficiente inserire il paramatero AssignUserID myuser mygroup ed il gioco è fatto. Interessante è la possibilità di settare il parametro MaxClientsVHost attraverso il quale è possibile limitare il numero di connessioni contemporanee a quello specifico virtualhost.
mpm_peruser e un mpm basato su metuxmpm che, a sua volta, è basato su mpm_perchild di apache (ques’ultimo mpm è marcato come testing non funzionante). Peruser è simile ad itk, però aggiunge a questo la funzionalità thread: all’avvio di apache vengono creati uno o più processi ciascuno con un user diverso, e ciascuno di questi crea autonomamente dei thread in base alle richieste. Questo sistema permette di aumentare notevolmente la velocità rispetto ad itk (un po come il worker aumenta la velocità di perchild) però se si hanno molti virtualhost che girano con diversi utenti, lo spreco di risorse lato server è notevole.

mar 072011
 

Se nel vostro lavoro sviluppate siti web, vi consiglio caldamente di proseguire nella lettura, perchè la maggior parte dei problemi in cui incappano i webmaster deriva proprio dalla mancata conoscenza di queste informazioni.

Innanzi tutto: tre basi tecniche che non possono mancare.
1) in programmazione ci sono due modi per parallelizzare l’esecuzione delle applicazioni: i processi e i thread. I processi sono delle entità a se stanti ciascuno con la sua memoria e le sue informazioni di stato; i processi possono dialogare tra loro attraverso sistemi che normalmente sono delegati al sistema operativo. I thread, invece, sono dei sottoprocessi che condividono le stesse variabili e quindi comunicano tra loro direttamente
2) ogni processo in Unix/Linux viene eseguito da un utente a livello di Filesystem: le autorizzazioni di ciò che può fare il processo all’interno del sistema operativo dipendono quindi dalle autorizzazioni che ha quell’utente
3) in Unix/Linux ogni file o directory ha un proprietario e un gruppo. Si devono definire le autorizzazioni (lettura, scrittura ed esecuzione) che ha il proprietario, il gruppo e tutti gli altri su ciascun file o directory. In Unix/Linux le autorizzazioni sono rappresentate da un numero di tre cifre, ciascuna delle quali indica il diritto del proprietario, del gruppo e degli altri. Ciascuna cifra è formata dalla somma di quattro valori: 0 equivale a nessun diritto, 1 è il diritto di esecuzione, 2 di scrittura e 4 di lettura. Ad esempio un file con permessi 740 vuol dire che il proprietario ha tutti i permessi (1+2+4=7), il gruppo ha solo i permessi di lettura e tutti gli altri nessun permesso.

Un po’ di storia: cos’è Apache?
Apache è un pacchetto inizialmente sviluppato per il mondo Unix/Linux per attivare le funzionalità di Web Server, cioè rendere il computer in grado di ospitare siti web. La diffuzione di questo pacchetto è notevole: secondo una statistica stilata nell’anno 2000, 15 milioni di web server pari al 60% del totale utilizzava Apache, al secondo posto c’era Microsoft IIS che però si fermava solo al 21%; il trend dell’ultimo decennio vede Apache ancora in forte crescita per cui stiamo parlando di ciò che è lo standard “de facto” dei web server.
E’ importante precisare che apache lavora autonomamente solo per fornire contenuti statici: per la parte dinamica (script CGI o PHP ad esempio) Apache si appoggia a programmi esterni oppure a moduli aggiuntivi eseguiti internamente.
La precedente master version di Apache (la 1.3) già implementava il multiprocessing per esaudire le richieste: all’avvio di Apache venivano istanziatiati un numero varibile di processi (si dichiarava un numero minimo e un numero massimo) atti a soddisfare le richieste web. Ad esempio, con un minimo 5 e un massimo di 20, all’avvio venivano creati 5 processi di Apache; se la quantità di richieste contemporanee di pagine web superava le 5 unità, venivano creati dinamicamente nuovi processi fino ad un massimo di 20 che si autodistruggevano al completamento della richiesta.
Apache 2.0, invece, permette di scegliere tra più modi di gestire i processi: questa configurazione è chiamata Multi Processing Mosule (MPM). Vediamoli nel dettaglio.

Prefork
Prefork è il modulo che lavora esattamente come la versione 1.3 di Apache e viene mantenuto sia per ragioni di compatibilità con la vecchia versione, sia perchè se si usano moduli aggiuntivi per servire contenuti dinamici (mod_php su tutti) è l’unico worker che garantisce piena compatibilità. Tutti i processi di apache vengono eseguiti da un unico utente di sistema dichiarato nel file di configurazione principale.

Worker
Worker è il modulo MPM introdotto con la versione 2.0 di Apache che introduce i thread all’interno dei processi per migliorare in modo significativo le prestazioni. Come nel prefork si dichiara il numero minimo e massimo di processi istanziabili, ma anche il numero minimo e massimo di thread e il numero massimo di thread per processo. Purtroppo questo modulo MPM è incompatibile con alcuni moduli esterni, ad esempio il mod_php. Come in prefork, tutti i processi di apache (e quindi anche tutti i thread) vengono eseguiti da un unico utente di sistema dichiarato nel file di configurazione principale.

Da questa lettura risulta evidente una cosa: se l’utente con cui sono creati i processi di apache non ha i permessi di scrittura su un determinato file o directory, non può scrivere. Punto. Se dovete far scrivere un file dall’utente con cui viene eseguito apache, dovete dargli i permessi di scrittura.

I VirtualHost
VirtualHost è una configurazione di apache che permette di ospitale pià siti all’interno di un unico server. In pratica, apache riconosce qual è l’URL digitata, cerca tra i VirtualHost se una di queste direttive corrisponde e, in caso affermativo, risponde con il sito corretto.
All’interno della configurazione di un VirtualHost, è possibile specificare moltissimi parametri, tra cui, le impostazioni di sicurezza di determinate directory e il metodo attraverso il quale servire contenuti dinamici come gli script PHP.