Virtual hosting su hosting scalabile per WordPress

Nella precedente serie di articoli, abbiamo utilizzato un insieme di tre macchine per realizzare un hosting altamente ottimizzato per ospitare blog in Wordpress.
In questo articolo modificheremo la configurazione per poter ospitare più blog all’interno di un’unico insieme di VM.

1) Creazione di utenti distinti per gli script php
Sia sul Load Balancer, sia sull’application server, andremo a creare un utente specifico per far girare gli script php di ogni sito. QUesta impostazione ci consente di mantenere un sistema sicuro in quanto uno script del “sito1” non avrà accesso ai file del “sito2”.
Lanciamo quindi i seguenti comandi su entrami i server:

useradd sito1
useradd sito2

2) Modifica della path del sito web e settaggio dei permessi
Innanzi tutto, abbiamo tutti i file del nostro sito wordpress all’interno della directory /var/www e la cosa non va certo bene per l’impostazione che stiamo per realizzare.
Sul Load Balancer, lanciamo i seguenti comandi:

cd /var/www
mkdir sito1
mv * sito1
chown -R sito1:www-data sito1
chmod 750 sito1
mkdir sito2
chown -R sito2:www-data sito2
chmod 750 sito2
mkdir default
chown -R root:www-data default
chmod 750 default
touch default/index.hrml

Con questa impostazione, abbiamo creato due directory per i nostri Virtual Hosting, bloccando l’accesso in lettura/scrittura al solo proprietario della directory.

3) Configurazione di nginx sul load balancer
E’ necessario modificare la configurazione di nginx sul Load Balancer in modo da abilitare il Virtual Hosting. Con l’occasione aggiungeremo qualche limite per amentare ulteriormente la sicurezza del sistema.

<strong>/etc/nginx/nginx.conf</strong>
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;
    tcp_nodelay        on;
    gzip              on;
    gzip_comp_level   5;
    gzip_http_version 1.0;
    gzip_min_length   0;
    gzip_vary         on;

    ## Start: Size Limits & Buffer Overflows ##
    client_body_buffer_size  1k;
    client_header_buffer_size 2k;
    client_max_body_size 1k;
    large_client_header_buffers 2 2k;
    ## END: Size Limits & Buffer Overflows ##

    ## Start: Timeouts ##
    client_body_timeout   10;
    client_header_timeout 10;
    keepalive_timeout     5 5;
    send_timeout          10;
    ## End: Timeouts ##

    ### Directive describes the zone, in which the session states are stored i.e. store in slimits. ###
    ### 1m can handle 32000 sessions with 32 bytes/session, set to 5m x 32000 session ###
    limit_zone slimits $binary_remote_addr 5m;

    ### Control maximum number of simultaneous connections for one session i.e. ###
    ### restricts the amount of connections from a single ip address ###
    limit_conn slimits 5;

    upstream appservers {
        fair weight_mode=idle no_rr;
        server php-appserver1 weight=10;
    }
    server {
        listen       80;
        server_name  _;
        index index.htm
        access_log /var/log/nginx/default.access.log;

       location / {
            root /usr/share/nginx/www/default;
        }
    }

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

}

Creiamo quindi i due file sito1 e sito2 per il Virtual Hosting:

<strong>/etc/nginx/sites-available/sito1 </strong>
    server {
        listen       80;
        server_name  sito1;
        access_log /var/log/nginx/sito1.access.log;

        location / {
            ## Only allow these request methods ##
            if ($request_method !~ ^(GET|HEAD|POST)$ ) {
            return 444;
            }
            ## Do not accept DELETE, SEARCH and other methods ##

            ## Deny certain Referers ###
            if ( $http_referer ~* (babes|forsale|girl|jewelry|love|nudit|organic|poker|porn|sex|teen) )
            {
            # return 404;
            return 403;
            }
            ##
            proxy_pass http://appservers;
            include /etc/nginx/conf.d/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)$ {
            ## Only allow these request methods ##
            if ($request_method !~ ^(GET|HEAD|POST)$ ) {
            return 444;
            }
            ## Do not accept DELETE, SEARCH and other methods ##

            ## Deny certain Referers ###
            if ( $http_referer ~* (babes|forsale|girl|jewelry|love|nudit|organic|poker|porn|sex|teen) )
            {
            # return 404;
            return 403;
            }
            ##

            expires 30d;
            root /usr/share/nginx/www/sito1;
        }
    }







<strong>/etc/nginx/sites-available/sito2 </strong>
    server {
        listen       80;
        server_name  sito2;
        access_log /var/log/nginx/sito2.access.log;

        location / {
            ## Only allow these request methods ##
            if ($request_method !~ ^(GET|HEAD|POST)$ ) {
            return 444;
            }
            ## Do not accept DELETE, SEARCH and other methods ##

            ## Deny certain Referers ###
            if ( $http_referer ~* (babes|forsale|girl|jewelry|love|nudit|organic|poker|porn|sex|teen) )
            {
            # return 404;
            return 403;
            }
            ##
            proxy_pass http://appservers;
            include /etc/nginx/conf.d/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)$ {
            ## Only allow these request methods ##
            if ($request_method !~ ^(GET|HEAD|POST)$ ) {
            return 444;
            }
            ## Do not accept DELETE, SEARCH and other methods ##

            ## Deny certain Referers ###
            if ( $http_referer ~* (babes|forsale|girl|jewelry|love|nudit|organic|poker|porn|sex|teen) )
            {
            # return 404;
            return 403;
            }
            ##

            expires 30d;
            root /usr/share/nginx/www/sito2;
        }
    }

Quindi abilitiamo i due siti e ricarichiamo la configurazione di nginx:

ln -s /etc/nginx/sites-available/sito1 /etc/nginx/sites-enambled
ln -s /etc/nginx/sites-available/sito2 /etc/nginx/sites-enambled
/etc/init.d/nginx restart

3) Configurazione di nginx sull’application server
Analoga configurazione andrà eseguita sull’application server. Partiamo con il file di configurazione generale di nginx:

<strong>/etc/nginx/nginx.conf</strong>
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;

events {
    worker_connections 768;
    # multi_accept on;
}

http {

    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ##
    # Logging Settings
    ##

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    ##
    # Gzip Settings
    ##

    gzip on;
    gzip_disable "msie6";

        ##
    # Virtual Host Configs
    ##

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

E i file dei due siti ospitati:

<strong>/etc/nginx/sites-available/sito1</strong> 
           server {
                listen 80;
                server_name sito1

                root /usr/share/nginx/www/sito1;
                index index.php;

            log_not_found off;

        location / {
            try_files   $uri $uri/ /index.php;
        }

        location ~ \.php$ {
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
                    fastcgi_index  index.php;
            fastcgi_pass   127.0.0.1:9001;
                    fastcgi_param  SCRIPT_FILENAME /usr/share/nginx/www$fastcgi_script_name;
                    include fastcgi_params;
                }

       }






<strong>/etc/nginx/sites-available/sito2 </strong>
           server {
                listen 80;
                server_name sito2

                root /usr/share/nginx/www/sito2;
                index index.php;

            log_not_found off;

        location / {
            try_files   $uri $uri/ /index.php;
        }

        location ~ \.php$ {
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
                    fastcgi_index  index.php;
            fastcgi_pass   127.0.0.1:9002;
                    fastcgi_param  SCRIPT_FILENAME /usr/share/nginx/www$fastcgi_script_name;
                    include fastcgi_params;
                }

       }

Da notare la porta diversa nella direttiva fastcgi_pass: difatti ciascun sito avrà la propria istanza di php-fpm che dovrà rispondere ad una porta univoca. Verdemo come al prossimo punto, per il momento limitiamoci a rendere attive le modifiche con:

ln -s /etc/nginx/sites-available/sito1 /etc/nginx/sites-enambled
ln -s /etc/nginx/sites-available/sito2 /etc/nginx/sites-enambled
/etc/init.d/nginx restart

4) Configurazione di php-fpm
Quindi ciascun sito, avrà la propria istanza di php-fpm avviata. Per farlo è necessario creare un file di configurazione per ogni sito in /etc/php5/fpm/pool.d:

<strong>/etc/php5/fpm/pool.d/sito1</strong>
[sito1]
listen = 127.0.0.1:9001
user = sito1
group = sito1
pm = dynamic
pm.max_children = 25
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 10
pm.max_requests = 500







<strong>/etc/php5/fpm/pool.d/sito2</strong>
[sito1]
listen = 127.0.0.1:9002
user = sito1
group = sito1
pm = dynamic
pm.max_children = 25
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 10
pm.max_requests = 500

Quindi riavviamo php-fpm con:

/etc/init.d/php5-fpm restart

Hosting scalabile per WordPress – vm03

In quest’ultimo articolo, completeremo l’installazione del sistema con l’installazione del server mysql e con il download, l’installazione e la configurazione di Wordpress.
Partiamo con la creazione della VM destinata al database: sarà una semplicissima VM con installato MySQL.

Creiamo questa VM partendo da una installazione “vanilla” di Debian Squeeze (6.0.1), dotando la VM di 512MB di RAM, 1 VCPU (CPU virtuale), 0.25 CPU (assegnamo il 50% della potenza di una CPU fisica), 1 scheda di rete con IP privato, 1024 MB Disco Swap e un disco Datablock per il mysql.

Configurazione di base
Come per vm1 la prima cosa che andremo a fare è modificare alcune delle configurazoni di default, in particolare il file hosts che ci consentirà di raggiungere tramite mnemonico le VM del nostro sistema:

<strong>/etc/hosts</strong>
10.10.1.1   lb
10.10.1.2   php-appserver1 mysql
10.10.1.3   mysql-server1

e il file /etc/hostname

<strong>/etc/hostname</strong>
mysql-server1

Assicuriamoci di aver montato il datablock nella directory /var/lib/mysql:

<strong>/etc/fstab</strong>
/dev/sdc       /var/lib/mysql   ext3    defaults        0       0

Installazione di mysql
Per installare mysql è sufficiente lanciare il comando:

apt-get install mysql-server

e modifichiamo il file /etc/mysql/my.cnf per rispondere alle richieste di rete:

<strong>/etc/mysql/my.cnf</strong>
...
bind-address            = 0.0.0.0
...

Infine facciamo in modo che l’utente root possa autenticarsi sul server mysql da qualsiasi host in rete locale:

mysql -p
mysql> create database wordpress;
mysql> GRANT ALL ON wordpress.* TO 'wordpress'@'%' identified by 'password01';
mysql> flush privileges;
mysql> quit;

A questo punto potete provare a spostarvi su vm2 e lanciare il comando:

mysql -h mysql -p -u wordpress wordpress

per verificare che l’installazione di mysql sia corretta

Installazione di Wordpress
Spostamoci su vm1 e lanciamo i seguenti comandi:

cd /var/www
wget http://wordpress.org/latest.tar.gz
tar zxvf latest.tar.gz
rm latest.tar.gz
chown -R www-data:www-data wordpress
mv wordpress/* .
rmdir wordpress

Ora andando via web all’indirizzo pubblico del load balancer sarà possibile iniziare la procedura standard di installazione di wordpress. Ricordarsi di inserire come dati di autenticazione sul database;

Database Name: wordpress
User Name: wordpress
Password: password01
Database Host: mysql

Alla fine del processo di installazione, wordpress sarà attivo e funzionante come su un qualsiasi altro hosting… solo un po’ più veloce.

Ottimizzazione di Wordpress
L’ottimizzazione di Wordpress, in realtà riguarda solamente l’installazione di due plugin.
Il primo plugin da installare è senza dubbio il più importante ed è nginx Compatibility: questo permette di sfruttare la configurazione di nginx nel server vm2 per attivare i permalink di wordpress proprio come su apache. La funzionalità dei permalink, permette di modificare il comportamento dei link di wordpress per renderli più idonei al funzionalmento con i motori di ricerca.
Il secondo plugin da installare per sfruttare appieno l’installzione è W3 Total Cache: questo plugin permette di gestire in maniera efficente memcached e apc per tenere una cache sia del precompilato php (per questo il mio consiglio è di utilizzare APC) sia per tenere una cache di oggetti e query al database (e per questo è praticamente obbligatorio usare memcached).

Problemino con il link a Twitter

Purtroppo un disallineamento con la configurazione di twitter, ha causato la mancata pubblicazione degli ultimi quattro articoli su questo social network.
Mi scuso con chi mi segue attraverso twitter…

Hosting scalabile per WordPress – vm02

Nel primo articolo abbiamo creato il server che fungerà da storage NFS per le pagine PHP e per i contenuti statici e che risponderà alla porta 80 per servire direttamente i contenuti statici e girare a dei server in load balancing le richeste di elaborazione dei contenuti dinamici PHP.

In questo secondo articolo andremo a creare vm2 che è, appunto, l’application server che servirà i contenuti PHP attraverso php-fpm.

Creiamo questa VM partendo da una installazione “vanilla” di Debian Squeeze (6.0.1), dotando la VM di 1024 MB di RAM, 2 VCPU (CPU virtuale), 0.50 CPU (assegnamo il 50% della potenza di una CPU fisica), 1 schede di rete con IP privato e 2048 MB Disco Swap.

Configurazione di base
Come per vm1 la prima cosa che andremo a fare è modificare alcune delle configurazoni di default, in particolare il file hosts che ci consentirà di raggiungere tramite mnemonico le VM del nostro sistema:

<strong>/etc/hosts</strong>
10.10.1.1   lb
10.10.1.2   php-appserver1 mysql
10.10.1.3   mysql-server1

e il file /etc/hostname

<strong>/etc/hostname</strong>
php-appserver1


Installazione di nginx

Come per il load balancer, anche su questo server installeremo nginx per tre ragioni: la prima è che così il server potrà servire anche contenuti non PHP ma che non vengono serviti direttamente da vm1 (ad esempio un file statico ma con estensione diversa da quelle dichiarate su vm1), la seconda che potrà essere utile nel caso di guasto di vm1 e la terza è che così si avrà un miglior controllo del numero di richeste contemporanee servite dalla vm.
Per procedere con l’installazione, analogamente a quanto eseguito su vm1, andiamo ad aggiungere la seguente riga al file /etc/apt/sources.list

<strong>/etc/apt/sources.list</strong>
deb http://packages.dotdeb.org stable all

Aggiungiamo la chiave PGP:

wget http://www.dotdeb.org/dotdeb.gpg
cat dotdeb.gpg | apt-key add -
rm dotdeb.gpg

ed installiamo nginx

apt-get update
apt-get install nginx

Creiamo, per semplicità, il link simbolico alla directory /usr/share/nginx/www:

ln -s /usr/share/nginx/www/ /var/www

Modifichiamo il file di configurazione per il server di default:

server {
                listen 80;
                server_name _;

                root /usr/share/nginx/www;
                index index.php;

                log_not_found off;

                location / {
                        try_files       $uri $uri/ /index.php;
                }

                location ~ \.php$ {
                        fastcgi_split_path_info ^(.+\.php)(/.+)$;
                        fastcgi_index  index.php;
                        fastcgi_pass   127.0.0.1:9000;
                        fastcgi_param  SCRIPT_FILENAME /usr/share/nginx/www$fastcgi_script_name;
                        include fastcgi_params;
                }

       }

Installazione del client NFS
Ora installiamo il client nfs:

apt-get install nfs-client

e modifichiamo il file /etc/fstab in modo da montare automaticamente il disco di rete aggiungendo la seguente riga:

<strong>/etc/fstab</strong>
lb:/usr/share/nginx/www        /usr/share/nginx/www         nfs4 hard,intr,_netdev  0       0

Configuriamo identd con:

<strong>/etc/default/nfs-common</strong>
NEED_IDMAPD=yes
NEED_GSSD=no

Riavviamo il servizio nfs-common con:

/etc/init.d/nfs-common restart

e montiamo il disco con:

mount -a

Installazione di php-fpm
Per installare php-fpm utilizziamo il package manager di debian. Assieme a php-fpm installeremo alcune estensioni che ci torneranno utili nella configurazione di wordpress:

apt-get install php5 php5-fpm php-pear php5-common php5-mcrypt php5-mysql php5-cli php5-gd php5-apc php5-memcache php5-memcached php5-suhosin

Ora modifichiamo la configurazione di php-fpm:

/etc/php5/fpm/pool.d/www.conf
pm.max_children = 25
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 10
pm.max_requests = 500
request_terminate_timeout = 30s

Questa è una taratura adatta ad una VM con 512MB-1024MB di RAM, ma ovviamente la potenza reale della CPU influisce sulla validità di questi valori.
Ora riavviamo php-fpm per rendere attive le impostazioni.

/etc/init.d/php5-fpm restart

Installazione di memcached
Memcached è un demone per creare una cache distribuita che torna utile per velocizzare gli accessi al database. Per installarla è sufficiente lanciare il comando:

apt-get install memcached

Installazione di mysql-proxy
mysql-proxy è un demone che permette di utilizzare una batteria di server mysql in configurazione master-slave per distribuire il carico delle query in lettura. Al momento avremo un solo server mysql, ma in un futuro potrebbe tornare utile:

apt-get install mysql-client mysql-proxy

E configuriamolo con:

<strong>/etc/default/mysql-proxy</strong>
ENABLED="true"
OPTIONS="--proxy-address=:3306 --proxy-backend-addresses=mysql-server1:3306 --pr
oxy-read-only-backend-addresses=mysql-server1:3306"