No space left on device: mod_rewrite… Solución para servidor web Apache

servidor apache

servidor apache

En alguna ocasión me he topado con que al reiniciar Apache después de matar todos los procesos con killall -9 httpd el servidor Apache no quiere arrancar, Apache start [FALLA], dando en el log el error de más arriba. Se trata de que Apache no ha liberado recursos del sistema y debemos hacerlo nosotros. En concreto se trata de semáforos. Linux tiene dos comandos que me ayudan a hacer esto (ipcs e ipcrm). Por si acaso antes vamos a eliminar los procesos zoombie:

Ver procesos zoombie:

# ps -A -ostat,ppid,pid,cmd | grep -e ‘^[Zz]’

Matar procesos zoombie (tardará un poco):

# kill -HUP `ps -A -ostat,ppid,pid,cmd | grep -e ‘^[Zz]’ | awk ‘{print $2}’`

Este es el error al reiniciar o arrancar Apache:

[crit] (28) No space left on device: mod_rewrite: could not create rewrite_log_lock Configuration Failed

Solución:

Revisar los semáforos de Apache

# ipcs -s | grep apache

Eliminamos todos los semáforos de los procesos de Apache

# ipcs -s | grep apache | perl -e ‘while () { @a=split(/\s+/); print `ipcrm sem $a[1]`}’

Iniciar Apache

# service httpd start

Instalar Memcached en CentOS 5.3

Memcached is a generic purpose distributed high performance memory object caching system to use in speeding up dynamic database driven websites by caching data and objects in memory to reduce the amount the database needs to be read.

Memcached was originally developed by Danga Interactive for LiveJournal but is now used by many popular and large community driven websites like Slashdot, Wikipedia, SourceForge, GameFAQs, Facebook, Digg, Fotolog, Kayak and like. It is being distributed under a permissive free software licence. Know more about who all are using memcached

Things to consider before Installing memcached.

  1. First, decide how much memory you want to give memcached to use for caching.
  2. Then decide if you want to run memcached on the default port (11211) or not.
  3. Next decide if you want memcached to listen to a specific IP address if you have multiple IP addresses on your server
  4. Finally decide, what user you want to run memcached as; typically, you want to run it using Apache user so that Apache processes can access memcache data

Installation Process

1. If you don’t have rpmforge installed, follow this step.

wget http://dag.wieers.com/rpm/packages/rpmforge-release/rpmforge-release-0.3.6-1.el5.rf.i386.rpm
rpm –install rpmforge-release-0.3.6-1.el5.rf.i386.rpm
yum install –enablerepo=rpmforge memcached

2. Start memcached.

memcached -d -m 512 -l 127.0.0.1 -p 11211 -u nobody

The “-m SIZE” is the flag for setting the memory requirements in MBs. Once this cache is filled memcache will just start to overwrite with newer content. Please experiment with this setting to find what works best for you.

3. Install PHP extension.

wget http://pecl.php.net/get/memcache-2.2.5.tgz

4. Extract tar file.

tar -xvf memcache-2.2.5.tgz

5. Open the directory.

cd memcache-2.2.5

6. Install the memchaced PHP extension.

phpize && ./configure –enable-memcache && make

7.  Copy the extension.

cp modules/memcache.so {PHP extension directory}

8. Edit your php.ini and add the following line.

extension=memcache.so

9. Last is restart your webserver.

10. If you check your server using a phpinfo page you should now see a MemCache section on the page. You can now fully use the MemCache functionality in your PHP.

memcached pre-requissites

yum -y install libevent libevent-devel

How to make memcached run automatically when you restart your server?. Add this line to rc.local

#!/bin/sh
echo “# Start memcached” >> /etc/rc.local
echo “/usr/local/bin/memcached -d -m 1024 -u httpd -l 127.0.0.1″ >> /etc/rc.local

How to have a multiple memcached server.

Create LocalSettings.php file and this line.

$wgMainCacheType = CACHE_MEMCACHED;
$wgParserCacheType = CACHE_MEMCACHED; # optional
$wgMessageCacheType = CACHE_MEMCACHED; # optional
$wgMemCachedServers = array( “127.0.0.1:11211″ );

$wgSessionsInMemcached = true; # optional

To use multiple servers (physically separate boxes or multiple caches on one machine on a large-memory x86 box), just add more items to the array. To increase the weight of a server (say, because it has twice the memory of the others and you want to spread usage evenly), make its entry a subarray:

$wgMemCachedServers = array(“127.0.0.1:11211″, # one gig on this box
array(“127.0.0.1:11211″, 2 ) # two gigs on the other box
);

Security Note:

Memcached has no security or authentication. Please ensure that your server is appropriately firewalled,
and that the port(s) used for memcached servers are not publicly accessible. Otherwise, anyone on the internet can put data into and read data from your cache.

ENJOY FRIENDS AND MAKE EASY¡¡¡¡¡¡¡¡¡¡¡

Restablecer los permisos predeterminados en el directorio del virtualhost en Plesk

Síntomas

Algunos archivos y directorios del dominio han perdido sus permisos. Varios de ellos son propiedad del usuario “apache” o “root” de Apache.

Por ejemplo:

# ls -l /var/www/vhosts/blogofsysadmins.com/
total 48
drwx------ 5 apache     apache 4096 Mar 16 20:07 anon_ftp
drwx------ 2 root       psaserv 4096 Mar 16 20:07 bin
drwx------ 3 apache     apache 4096 Mar 16 20:07 cgi-bin
drwx------ 2 root       psaserv 4096 Aug  4 12:56 conf
drwx------ 2 root       psaserv 4096 Mar 16 20:07 error_docs
drwx------ 7 apache     apache 4096 Mar 16 20:07 httpdocs
drwx------ 7 apache     apache 4096 Mar 16 20:07 httpsdocs
drwx------ 2 root       psaserv 4096 Aug  4 12:56 pd
drwx------ 2 root       root    4096 Mar 16 20:07 private
dr-x------ 7 root       psaserv 4096 Mar 16 20:07 statistics
drwx------ 2 root       psaserv 4096 Mar 16 20:07 subdomains
drwx------ 2 root       psaserv 4096 Mar 16 20:07 web_users
~#

Cuando intento cargar archivos a través del Administrador de Archivos o de un cliente FTP, obtengo los errores “Permiso denegado” o “Acceso denegado“.

Resolución

Use la utilidad “vhostmng” para restablecer los permisos. El comando a ejecutar es el siguiente:

~# /usr/local/psa/admin/sbin/vhostmng --install-vhost --vhost-name=<domain name> --user-name=<username> --set-content-permissions

Donde <domain name> es el dominio donde deben restaurarse los permisos y <username> es el usuario FTP del dominio configurado en Parallels Plesk Panel en la página Dominios -> <domain name> -> Opciones de Alojamiento Web (versión 9.x) o Dominios -> <domain name> -> Configuración (versión 8.x).

El comando restaurará los permisos predeterminados en el directorio del virtualhost:

drwxr-x--- 5 ftp_user   psaserv 4096 Mar 16 20:07 anon_ftp
drwxr-xr-x 2 root       psaserv 4096 Mar 16 20:07 bin
drwxr-x--- 3 ftp_user   psaserv 4096 Mar 16 20:07 cgi-bin
drwxr-x--- 2 root       psaserv 4096 Aug  4 12:56 conf
drwxr-xr-x 2 root       psaserv 4096 Mar 16 20:07 error_docs
drwxr-x--- 7 ftp_user   psaserv 4096 Mar 16 20:07 httpdocs
drwxr-x--- 7 ftp_user   psaserv 4096 Mar 16 20:07 httpsdocs
drwxr-x--- 2 root       psaserv 4096 Aug  4 12:56 pd
drwx------ 2 ftp_user   root    4096 Mar 16 20:07 private
dr-xr-x--- 7 root       psaserv 4096 Mar 16 20:07 statistics
drwxr-xr-x 2 root       psaserv 4096 Mar 16 20:07 subdomains
drwxr-xr-x 2 root       psaserv 4096 Mar 16 20:07 web_users

Script en BASH para comparar tráfico de los access_logs de Apache

Antes que nada debo decir que este script fue algo que hice en un rato tratando de solucionar un problema, y tiene varios errores y cosas mejorables. De más está decir que no cuenta con ninguna garantía, sino que lo comparto porque quizás pueda ayudar a alguien que enfrente un problema similar.

Mi problema es que necesitaba saber cuáles eran los dominios de un servidor que alojaba más de 300 que estaban teniendo mayor tráfico. Como no tenía ninguna herramienta en tiempo real que me pudiera reportar esta información para el Apache, lo que hice es un script que revisa los access_log de cada dominio y obtiene la cantidad de bytes traficados por cada uno.

En este caso, cada VirtualHost define un archivo de logs diferente para cada dominio. Los access_log usan el formato combined (que incluye User-Agent, etc.) y no especifican nada más. El script no hace ninguna validación al respecto, así que podría no funcionar con logs en otros formatos.

Debe tenerse en cuenta que si los archivos de logs rotan en base a su tamaño, es probable que se presente el siguiente caso: El dominio web1.com tiene gran cantidad de tráfico y el dominio web2.com todo lo contrario. El access_log correspondiente al primer dominio puede reportar el tráfico de 1 Gb en un día y el access_log del segundo puede reportar tráfico también de 1 Gb pero en 6 meses. Por lo tanto, es importante mantener referencia de las fechas extremas de los logs, para poder dar cuenta de estas circunstancias.

Hechas estas aclaraciones, pasamos a la explicación del script.

¿Cómo funciona?

Muy sencillo. El script recorre todos los archivos de logs (1 por dominio). Por cada uno obtiene la fecha inicial (la del primer request registrado) y la final (la del último). Luego suma la cantidad de bytes de todos los requests registrados, e imprime una fila con esa cifra en megabytes, kilobytes, bytes y el rango de fechas que abarca. El conjunto de todas esas líneas es enviada al sort de GNU para ordenar las líneas de menor a mayor cantidad de tráfico.

Quizás suene medio complejo, pero se entenderá mejor viendo el código.

#!/bin/bash

BASE_DIR=/tmp/domain_logs

dateToStamp ()
{
    if [ ${#1} -eq 19 ]; then
        date --utc --date "$1" +%s;
    else
        echo 0
    fi
}

formatDate ()
{
    echo $1 | awk 'BEGIN { \
                    dict["Jan"]=1; \
                    dict["Feb"]=2; \
                    dict["Mar"]=3; \
                    dict["Apr"]=4; \
                    dict["May"]=5; \
                    dict["Jun"]=6; \
                    dict["Jul"]=7; \
                    dict["Aug"]=8; \
                    dict["Sep"]=9; \
                    dict["Oct"]=10; \
                    dict["Nov"]=11; \
                    dict["Dec"]=12 \
                    } { \
                        split(substr($1, 2), a, /[\\\/:]+/); \
                        print a[3] "-" dict[a[2]] "-" a[1] " " a[4] ":" a[5] ":" a[6] \
                    }'
}

for file in `ls $BASE_DIR/*`;
do
    from=$(formatDate `head -1 $file | awk '{print($4)}'`)
    to=$(formatDate `tail -1 $file | awk '{print($4)}'`)
    stamp_from=$(dateToStamp "$from")
    stamp_to=$(dateToStamp "$to")
    total_time=$(($stamp_to-$stamp_from))
    bytes=`cat $file | awk '{a+=$10}END{print a}'`
    kbytes=$((bytes/1024))
    mbytes=$((kbytes/1024))
    echo  "$mbytes MB | \
           $kbytes KB | \
           $bytes Bytes | \
           From: $from ($stamp_from) | \
           To: $to ($stamp_to) | \
           $total_time secs | \
           ${file:${#BASE_DIR}+1}"
done \
| sort -nb

Como se puede ver, al principio se define la variable BASE_DIR, que es el directorio base donde el script va a buscar los access logs. Cuando ejecuto el script, para evitar problemas de cambios en tiempo real, creo un snapshot con todos los archivos de logs y los guardo en una carpeta (en este caso /tmp/domain_logs).

<pre># cp /var/log/httpd/domains/*.access.log /tmp/domain_logs/</pre>

Luego, trabajo directamente sobre el snapshot. Entonces, como se observa en el script, defino dos funciones que utilizo más abajo, dateToStamp y formatDate. Como sus respectivos nombres indican, la primera convierte una fecha en un unix timestamp y la segunda cambia el formato de la fecha que figura en el archivo de logs por el que a mi me sirve para pasarle a date. Esto seguramente es mucha complicación para algo que podría hacer usando mejor el comando date, pero fue lo que más rápido me resultó hacer.

Y luego está el for, que recorre los archivos y por cada uno obtiene las fechas límite ($from y $to), de allí obtiene los timestamps ($stamp_from, $stamp_to), el tiempo total en segundos y la cantidad de bytes (la cual sumo con awk).

Por último, verán que la salida del for tiene un pipe a sort, a quien le paso los parámetros “n” y “b”. Para que haga un ordenamiento numérico ignorando los espacios en blanco.

Ejemplo

# ./access_log_parser.sh
97 MB | 99901 KB | 102299281 Bytes | From: 2008-11-28 02:25:54 (1227839154) | To: 2008-11-28 15:34:12 (1227886452) | 47298 secs | domain1.com.ar.log
115 MB | 118260 KB | 121098817 Bytes | From: 2008-11-28 02:27:45 (1227839265) | To: 2008-11-28 15:34:26 (1227886466) | 47201 secs | domain2.com.ar.log
123 MB | 126652 KB | 129691792 Bytes | From: 2008-11-28 02:27:53 (1227839273) | To: 2008-11-28 15:33:01 (1227886381) | 47108 secs | domain3.com.ar.log
145 MB | 149279 KB | 152862681 Bytes | From: 2008-11-28 02:30:29 (1227839429) | To: 2008-11-28 15:34:31 (1227886471) | 47042 secs | domain4.com.ar.log
189 MB | 194517 KB | 199185477 Bytes | From: 2008-11-28 04:35:20 (1227846920) | To: 2008-11-28 15:32:58 (1227886378) | 39458 secs | domain5.com.log
197 MB | 202034 KB | 206883560 Bytes | From: 2008-11-28 02:35:55 (1227839755) | To: 2008-11-28 15:34:29 (1227886469) | 46714 secs | domain6.com.ar.log
499 MB | 511816 KB | 524099937 Bytes | From: 2008-11-28 02:25:44 (1227839144) | To: 2008-11-28 15:34:29 (1227886469) | 47325 secs | domain7.com.ar.log
#

NOTA: si conocen alguna forma de monitorear esto en tiempo real, también me sería de gran utilidad.

Contar peticiones/request por IP de un access_log de Apache en bash

Aqui os dejo una pequeña linea en bash para contar la cantidad de requests por IP en un access_log de Apache.

cat /var/log/httpd/access_log |awk '{print($1)}'|sort |uniq -c |sort

Aqui la explicacion de como funciona:
Lo primero que hacemos es abrir el archivo con cat, el contenido se pasa a awk con el cual obtenemos la primera columna (lo mismo lo podríamos hacer con cut, pero estoy más acostumbrado a awk). Eso nos devuelve la lista de las IPs. Eso lo pasamos a sort que ordena las IPs antes de poder pasarselo a uniq. Éste último comando, con el parámetro -c se ocupa de contar las líneas repetidas en un listado ordenado (por eso el sort anterior). Y por último le volvemos a pasar todo a sort para que lo ordene en forma ascendente de acuerdo a la cantidad de ocurrencias de cada IP.