WordPress con Nginx

Para correr WordPress bajo el Web Server Nginx primeramente debemos haber configurado el mismo como web server con soporte para PHP, vea: Configurar Nginx como Webserver
Asumiendo que ya tenemos Nginx corriendo como Web Server y soportando PHP por medio de FastCGI procedemos a configurar un nuevo Virtual Host para nuestra instalación de WordPress.
Vale aclarar que con la configuración por defecto de Nginx con PHP la aplicación WordPress levantará pero dará problemas con sus links por la reglas RedWrite configuradas en el archivo .htaccess las cuales no reconoce Nginx; para solucionar este problemas debemos crear nuestras propias reglas RedWrite equivalentes a la configuración por defecto de WordPress en el archivo en que configuramos el host virtual para levantar nuestra aplicación WordPress.
Procedemos a crear la configuración del virtual host para el dominio de prueba wordpress.com

vim /etc/nginx/sites-available/wordpress.com.conf

Y le agregamos el siguiente contenido correspondiente al virtual host y a las reglas RedWrite para WordPress.

server {
    server_name www.wordpress.com wordpress.com;
    access_log /var/www/wordpress.com/logs/access.log;
    error_log /var/www/wordpress.com/logs/error.log;
    root /var/www/example.com/htdocs;
    location / {
        index  index.html index.htm index.php;
    }
    # enforce NO www
    if ($host ~* ^www\.(.*))
    {
        set $host_without_www $1;
        rewrite ^/(.*)$ $scheme://$host_without_www/$1 permanent;
    }
    # unless the request is for a valid file, send to bootstrap
    if (!-e $request_filename)
    {
        rewrite ^(.+)$ /index.php?q=$1 last;
    }

    # catch all
    error_page 404 /index.php;

    location ~ \.php$ {
        include /etc/nginx/fastcgi_params;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME /var/www/wordpress.com/htdocs$fastcgi_script_name;
    }

    # deny access to apache .htaccess files
    location ~ /\.ht
    {
        deny all;
    }

    # unless the request is for a valid file, send to bootstrap
    if (!-e $request_filename)
    {
        rewrite ^(.+)$ /index.php?q=$1 last;
    }
}

Habilitamos el sitio creando un link simbólico sobre el directorio /etc/nginx/sites-enabled.

ln -s /etc/nginx/sites-available/wordpress.com.conf /etc/nginx/sites-enabled/

El próximo paso sería probar nuestra instalación de WordPress la cual asumimos ya fue completada.

curl WordPress.com

Habilitando el Monitor de estado en Nginx

Nginx tiene un módulo para el monitoreo de estado del server, el cual presenta la siguiente información.

  • Total de requests
  • Requests por segundo
  • Número de conexiones
  • Número de conexiones reading, writing and waiting.

Para que nginx provea esta información tiene que haber sido compilado con la extensión: –with-http_stub_status_module
En la versión 1.2.2 instalada usando y apt-get ya está incluido el módulo.
Para ver los módulos con los que se ha compilado Nginx ejecutamos el siguiente comando.

nginx -V
nginx version: nginx/1.2.2
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx/ --sbin-path=/usr/sbin/nginx
--conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log
--http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid
--lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp
--http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp
--user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module
--with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module
--with-http_gzip_static_module --with-http_random_index_module
--with-http_secure_link_module --with-http_stub_status_module
--with-mail --with-mail_ssl_module --with-file-aio --with-ipv6

Cómo podemos apreciar en el recuadro anterior, en esta instalación se encuentra el módulo que necesitamos para el monitoreo.
Configurando el Monitor de estado
Para configurar el monitor de estado abrimos el archivo principal de configuración de Nginx.

vim /etc/nginx/nginx.conf

Y en el mismo agregamos la configuración que habilitará el monitor de estado del server dentro de la directiva http.


server {
        listen 80;
        server_name     status_monitor;
        location /nginx_status {
            stub_status on;
            access_log   off;
            allow 127.0.0.1;
            deny all;
        }
    }

Ahora chequeamos que el archivo de configuración no contenga ningún error de sintaxis ejecutando el siguiente comando.

nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Después de haber pasado la verificación de sintaxis del archivo procedemos a reiniciar Nginx para que levante su nueva configuración o decirle explícitamente que vuelva a leer su configuración.

/etc/init.d/nginx restart OR nginx -s reload

Ahora que ya tenemos nuestro monitor de Nginx correctamente configurado procederemos a probarlo para acceder a la información de estado.

curl http://status_monitor/nginx_status
Active connections: 20
server accepts handled requests
 79933 79933 80210
Reading: 10 Writing: 1 Waiting: 3

Este monitor nos servirá para ser consumido por servicios como Rrdtool o Cacti.

Visualizar estadísticas de Nginx con rrdtool

Para poder realizar este paso es necesario que hayamos terminado el previo llamado Habilitando el Monitor de estado en Nginx.
Para poder ver los gráficos de estadísticas con Rrdtool, primeramente necesitamos instalar algunos paquetes como por ejemplo rrdtool, libwww-perl, etc.

apt-get install build-essential librrds-perl rrdtool libwww-perl

Después de instalados los paquetes necesarios, procedemos a crear el siguiente archivo perl que será el que generé las gráficas usando rrdtool.


vim /root/rrd_nginx.pl

Y le agregamos el siguiente contenido.

#!/usr/bin/perl
use RRDs;
use LWP::UserAgent;

# define location of rrdtool databases
my $rrd = '/opt/rrd';
# define location of images
my $img = '/opt/rrd';
# define your nginx stats URL
my $URL = "http://moviewer.com/nginx_status";

my $ua = LWP::UserAgent->new(timeout => 30);
my $response = $ua->request(HTTP::Request->new('GET', $URL));

my $requests = 0;
my $total =  0;
my $reading = 0;
my $writing = 0;
my $waiting = 0;

foreach (split(/\n/, $response->content)) {
  $total = $1 if (/^Active connections:\s+(\d+)/);
  if (/^Reading:\s+(\d+).*Writing:\s+(\d+).*Waiting:\s+(\d+)/) {
    $reading = $1;
    $writing = $2;
    $waiting = $3;
  }
  $requests = $3 if (/^\s+(\d+)\s+(\d+)\s+(\d+)/);
}

#print "RQ:$requests; TT:$total; RD:$reading; WR:$writing; WA:$waiting\n";

# if rrdtool database doesn't exist, create it
if (! -e "$rrd/nginx.rrd") {
  RRDs::create "$rrd/nginx.rrd",
        "-s 60",
	"DS:requests:COUNTER:120:0:100000000",
	"DS:total:ABSOLUTE:120:0:60000",
	"DS:reading:ABSOLUTE:120:0:60000",
	"DS:writing:ABSOLUTE:120:0:60000",
	"DS:waiting:ABSOLUTE:120:0:60000",
	"RRA:AVERAGE:0.5:1:2880",
	"RRA:AVERAGE:0.5:30:672",
	"RRA:AVERAGE:0.5:120:732",
	"RRA:AVERAGE:0.5:720:1460";
}

# insert values into rrd database
RRDs::update "$rrd/nginx.rrd",
  "-t", "requests:total:reading:writing:waiting",
  "N:$requests:$total:$reading:$writing:$waiting";

# Generate graphs
CreateGraphs("day");
CreateGraphs("week");
CreateGraphs("month");
CreateGraphs("year");

#------------------------------------------------------------------------------
sub CreateGraphs($){
  my $period = shift;

  RRDs::graph "$img/requests-$period.png",
		"-s -1$period",
		"-t Requests on nginx",
		"--lazy",
		"-h", "150", "-w", "700",
		"-l 0",
		"-a", "PNG",
		"-v requests/sec",
		"DEF:requests=$rrd/nginx.rrd:requests:AVERAGE",
		"LINE2:requests#336600:Requests",
		"GPRINT:requests:MAX:  Max\\: %5.1lf %S",
		"GPRINT:requests:AVERAGE: Avg\\: %5.1lf %S",
		"GPRINT:requests:LAST: Current\\: %5.1lf %Sreq/sec",
		"HRULE:0#000000";
  if ($ERROR = RRDs::error) {
    print "$0: unable to generate $period graph: $ERROR\n";
  }

  RRDs::graph "$img/connections-$period.png",
		"-s -1$period",
		"-t Requests on nginx",
		"--lazy",
		"-h", "150", "-w", "700",
		"-l 0",
		"-a", "PNG",
		"-v requests/sec",
		"DEF:total=$rrd/nginx.rrd:total:AVERAGE",
		"DEF:reading=$rrd/nginx.rrd:reading:AVERAGE",
		"DEF:writing=$rrd/nginx.rrd:writing:AVERAGE",
		"DEF:waiting=$rrd/nginx.rrd:waiting:AVERAGE",

		"LINE2:total#22FF22:Total",
		"GPRINT:total:LAST:   Current\\: %5.1lf %S",
		"GPRINT:total:MIN:  Min\\: %5.1lf %S",
		"GPRINT:total:AVERAGE: Avg\\: %5.1lf %S",
		"GPRINT:total:MAX:  Max\\: %5.1lf %S\\n",

		"LINE2:reading#0022FF:Reading",
		"GPRINT:reading:LAST: Current\\: %5.1lf %S",
		"GPRINT:reading:MIN:  Min\\: %5.1lf %S",
		"GPRINT:reading:AVERAGE: Avg\\: %5.1lf %S",
		"GPRINT:reading:MAX:  Max\\: %5.1lf %S\\n",

		"LINE2:writing#FF0000:Writing",
		"GPRINT:writing:LAST: Current\\: %5.1lf %S",
		"GPRINT:writing:MIN:  Min\\: %5.1lf %S",
		"GPRINT:writing:AVERAGE: Avg\\: %5.1lf %S",
		"GPRINT:writing:MAX:  Max\\: %5.1lf %S\\n",

		"LINE2:waiting#00AAAA:Waiting",
		"GPRINT:waiting:LAST: Current\\: %5.1lf %S",
		"GPRINT:waiting:MIN:  Min\\: %5.1lf %S",
		"GPRINT:waiting:AVERAGE: Avg\\: %5.1lf %S",
		"GPRINT:waiting:MAX:  Max\\: %5.1lf %S\\n",

		"HRULE:0#000000";
  if ($ERROR = RRDs::error) {
    print "$0: unable to generate $period graph: $ERROR\n";
  }
}

Después de tener el archivo rrd_nginx.pl procederemos a darle permisos de ejecución.

chmod +x /root/rrd_nginx.pl

Cambiamos la configuración en el archivo rrd_nginx.pl para especificarle donde tiene que guardar el archivo rrd-base y las imágenes de las estadísticas.

vim /root/rrd_nginx.pl

Cambiamos las siguientes líneas.

#!/usr/bin/perl
use RRDs;
use LWP::UserAgent;

# define location of rrdtool databases
my $rrd = '/var/www/rrd';
# define location of images
my $img = '/var/www/rrd/html';
# define your nginx stats URL
my $URL = "http://status_monitor/nginx_status";
...

Por último configuramos la ejecución de nuestro script cada un minuto en /etc/crontab

vim /etc/crontab
...
* *     * * *   root    /root/rrd_nginx.pl

Ahora podremos ver las imágenes generadas con las estadísticas en el siguiente path: /var/www/rrd/html. Podríamos crear un archivo html y configurarlo en un dominio/sub-dominio para que levante las gráficas de estas estadíscticas (las imágenes).

Mostar la cantidad de procesos apache que se están ejecutando

Muchas veces necesitamos monitorear la cantidad de proceso de apache que hay ejecutandose para comprobar la salud de nuestro servidor y de esta forma saber si un servidor esta sobrecargado o no.
La cantidad de procesos Apache ejecutandose podríamos compararla contra la cantidad de procesos configurados en Apache/Httpd en la variable MaxClients.
MaxClients: Número máximo de conexiones que apache procesa simultáneamente.

Ejemplo de como contar el número de procesos Apache/httpd en ejecución

#CentOs, RHEL, Fedora
pgrep httpd | wc -l
#En debian o Ubuntu puede que el proceso no se llame httpd sinó apache2
pgrep apache2 | wc -l

Con el comando top o htop si lo tenemos instalado podemos fijarnos si el nombre del proceso es apache2 o httpd.
También podemos crear un script que monitoree la cantidad de procesos apache2/httpd y si la cantidad de procesos ejecutandose supera un umbral notificamos vía e-mail.

#Creamos el script
touch apache-monitor.sh

#Asignamos permisos de ejecución al script
chmod +x apache-monitor.sh

Abrimos el archivo con vim y le pegamos el siguiente código

vim apache-monitor.sh
#!/usr/bin/env bash
#En la variable APTHREADS cargamos la cantidad de procesos apache en ejecucion
APTHREADS=$(pgrep httpd | wc -l)
#Creamos una variable con el valor = al MaxClients
MCLIENTES=100;

#Ahora definimos el e-mail de notificacion
EMAILTO=tucorreo@gmail.com

#Validamos si la cantidad de procesos apache superan el umbral establecido
#si es así enviamos un mail de notificacion
(( $MCLIENTES < $APTHREADS )) &&\
echo "Cantidad de procesos httpd/apache2: $APTHREADS ( $(date +'%Y-%m-%d %T') )" |\
mail -s 'Atencion!!. La cantidad de procesos apache han superado el umbral de configuracion establecido' $EMAILTO

Una vez finalizada la creación del scrip, lo ejecutamos de la siguiente manera.

./apache-monitor.sh

Finalmente podríamos configurar para que nuestro script sea ejecutado cada x tiempo por el sistema (en el crontab) y con esto estariamos monitoreando los procesos apache de nuestro servidor Web.

Agregamos la llamada en nuestro crontab para que el script se ejecute cada 5 minutos

#En Ubuntu/Debian
vim /etc/crontab

#agregamos la siguiente línea
*/5 * * * * root /root/apache-monitor.sh

Instalar eAccelerator o Cache de PHP

PHP no es un lenguage compilado sinó un lenguage interpretado, (comunmente conocido como un lenguage de scripts), esto quiere decir que está diseñado para ser ejecutado mediante un interprete; por cada petición, este interprete tiene que leer cada archivo PHP, evaluar su sintaxis, interpretarlo (compilarlo) y ejecutar su funcionalidad. Como verás todo este trabajo tiene un costo alto en CPU y según el tráfico del sitio se traduce en una sobrecarga del mismo.

Para solucionar esto podemos acceder a varios sistemas de Cache, de forma que cuando desde el browser se realiza una petición, estos sistemas acceden al caché de nuestro script que ya fue leído, evaluado he interpretado (compilado) en una solicitud anterior, sin necesidad de volver a realizar el trabajo nuevamente. De esta manera obtenemos mejoras en el rendimiento del servidor y un incremento de velocidad que puede ser de hasta 10 veces más rápido. Vale aclarar que si bién se reducirá el consumo de CPU habrá un incremento de uso de memoria (minimo dependiendo del servidor y su uso) donde trabajan estos sistemas de Cache.

Para utilizar cache de PHP no necesitamos modificar nuestros scripts, ya que se instalan como extensiones de PHP. Los sistemas más utilizados son ionCube, PHP eAccelerator, Zend Optimizer (Pago), etc.

En este Post nos centraremos en explicar el uso de Cache con PHP eAccelerator.

eAccelerator nació en diciembre de 2004 como una derivación del proyecto de la MMCache Turck. Turck MMCache fue creado por Dmitry Stogov y gran parte del código eAccelerator todavía se basa en su trabajo.

eAccelerator guarda los scripts PHP compilados en la memoria compartida y ejecuta el código directamente de ella. Se crea bloqueos sólo por un corto tiempo, mientras que se realiza la búsqueda de un script PHP compilado en el cache, por lo que un script se pueden ejecutar simultáneamente por varios motores. Solamente los archivos que no caben en la memoria compartida se guardan en caché de disco.

eAccelerator todavía en desarrollo y disponible en su última versión 0.9.6.1.1

Instalar PHP eAccelerator

Asumimos que no está instalado en nuestro servidor. Podemos consultar si está instalado o no creando en nuestro dominio un archivo con la siguiente sintaxis.

#creamos el archivo info.php
touch /var/www/midominio/info.php

Abrimos el archivo que creamos previamente con un editor, por ejemplo VIM y le agregamos el siguiente contenido.

<?php
phpinfo();
?>

Ahora para ver si está instalado ejecutamos el archivo recién creado en nuestro dominio y chequeamos que no se mencione eAccelerator en ninguna parte.
Sigue leyendo

Formatear una fecha en MySQL al español

En este ejemplo mostraremos como formatear una fecha de un campo datetime, en este caso usaremos la funcion NOW() de MySQL para la demostración.
La idea es formatear una fecha para que se pueda usar en una vista, con el formato correcto para nuestro idioma, por ejemplo para el día de hoy el resultado debería ser (Martes 22 de Mayo). Primero establecemos el idioma para el resultado de nuestra consulta y posteriormente la formateamos.

SET lc_time_names = 'es_UY';
SELECT
CONCAT_WS(' ',
              CONCAT(UCASE(SUBSTRING(DAYNAME(a.MyDate), 1, 1)),LCASE(SUBSTRING(DAYNAME(a.MyDate), 2))),
              DATE_FORMAT(a.MyDate,'%d de '),
              CONCAT(UCASE(SUBSTRING(MONTHNAME(a.MyDate), 1, 1)),LCASE(SUBSTRING(MONTHNAME(a.MyDate), 2)))
            ) AS fecha_con_formato
FROM (SELECT NOW() AS MyDate) AS a;

Ver imagen de ejemplo: