Rails en OS X: configuración de lighttpd

Editado a las 19:39 del 2005-08-25

Esta tarde toca desarrollo con Ruby on Rails sobre Mac OS X. Supongamos que tenemos una aplicación más o menos operativa y la queremos poner en producción. Hasta ahora hemos seguido el ciclo de desarrollo habitual usando el magnífico servidor web WEBrick que se suministra con Rails. Este servidor tiene diversas ventajas, pero su mayor inconveniente es que es lento. Y cuando digo lento es lento, lentísimo.

Toca, pues, usar otro servidor web. Las primeras miradas se depositan en nuestro viejo amigo Apache, que probablemente ya tengamos instalado (OS X no es excepción). Como es habitual, existe la posibilidad de instalar un módulo para Apache con la máquina virtual (mod_ruby, análogo al mod_php para PHP) pero en este caso tampoco queremos usarlo; al parecer el desarrollo en este módulo está estancado y su rendimiento deja que desear. La siguiente opción es usar ruby como un CGI tradicional, opción que he probado bajo Windows XP y os aseguro que es similar a WEBRick en rendimiento. El motivo es que para cada petición (para cada URL que visitemos), el servidor web tiene que ejecutar el intérprete, cargarlo en memoria, pasarle los parámetros, etc.

Y aquí aparece FastCGI, una extensión de CGI que permite acortar espectacularmente los tiempos de respuesta del servidor, de una manera sencilla: teniendo un fajo de intérpretes (pueden ser de Ruby en nuestro caso, o PHP si se tercia, etc.) que, estando arrancados de antemano, nos eviten la sobrecarga que supone arrancar un proceso cada vez. Esta técnica supone una comunicación distinta entre el servidor web y nuestro intérprete, de forma que será necesario hacer un par de cosas:

En primer lugar, instalar la librería FastCGI. En mi caso, he descargado fcgi-2.4.0.tar.gz y tras el habitual baile ./configure && make && sudo make install las librerías han acabado sin mayor inconveniente en /usr/local.

En segundo lugar, obtener la extensión FastCGI para Ruby. Afortunadamente Ruby tiene su propia herramienta de gestión de paquetes (al estilo del CPAN de Perl), llamada Gem, así que solamente es cuestión de:

sudo gem install fcgi


Este paso fallará, como es de esperar, si no hemos instalado la librería FastCGI del paso anterior.

Así que hemos llegado al punto en que tenemos nuestro Ruby con soporte de FastCGI y sus librerías a punto, ¿qué nos queda? Instalar mod_fcgi en Apache y...

Demasiado fácil. Rizando el rizo, ¿por que quedarnos con Apache? La última moda en Ruby on Rails no es usar Apache, sino el nuevo y flamante lighttpd (ojo con las letras, yo siempre escribo lighthttpd) La ventaja de lighttpd es que trae soporte de FastCGI en forma de módulo y, además, es -según se afirma, yo no lo he probado- rapidísimo. Puede que sea cuestión de moda pero, como ya sabemos, todo el rollo este de Ruby on Rails es una moda (o eso dicen) :-)

Para instalar lighttpd el camino que he seguido ha sido tirar por la vía vaga e instalar Darwinports. Se trata un proyecto libre (auspiciado por Apple) que añade a nuestro OS x algo parecido al sistema de ports de BSD en Darwin, muy al estilo de Fink. En mi caso, instalé en primer lugar Fink, que usa el sistema "apt" de Debian pero, no sé muy bien cómo, terminé destrozando la instalación. Lo más interesante es que estos sistemas de paquetes adicionales no interfieren para nada con el resto de OS X, sino que se instalan en su propia jerarquía de directorios (/sw/bin en el caso de Fink, /opt/local en el caso de Darwinports) y deisnstalarlos es tan sencillo como hacer un rm- fr (con cuidado, eso sí).

Podríamos compilarnos nosotros mismos nuestro propio port, pero este paso no es necesario porque ya está hecho para nosotros, basta con descargar la imagen DarwinPorts-1.0.dmg y ejecutar el paquete de instalación. Luego, siguiendo las instrucciones de la instalación añadimos las correspondientes variables a nuestro entorno y ya está. Como prueba, instalé Lynx y Midnight Commander y funcionaron, así que el siguiente paso era instalar lightttpd. Me encontré con que no aparecía en el repositorio, pero por lo que he leído por ahí lighttpd ya forma parte de Darwinports. Al final sospeché que se trataba de que la imagen de disco que descargué no tiene la lista de paquetes más actualizada, así que se hizo necesario hacer lo siguiente:

sudo port -d selfupdate
sudo port install lighttpd


Tras un rato, en el que port descargarán, compilarán e instalarán algunas dependencias tendremos instalado lighttpd en /opt/local/sbin. A continuación, y ya que vamos a jugar a tener nuestro propio servidor, podemos despedirnos (hasta el próximo reinicio) de nuestro querido Apache:

sudo apachectl stop


La estrategia que he seguido es la de ejecutar el servidor web desde mi cuenta personal, no como administrador (aunque esto me fuerza a usar un puerto como el 8080, no es inconveniente), así que en mi propio directorio personal, he creado un directorio para las cosas de lighttpd

mkdir $HOME/lighttpd


y en él me preparo el siguiente script:

#!/bin/bash
PID=`ps -aux | grep lighttpd | grep -v grep | awk '{ print  }'`
echo Instancia a matar: $PID
kill -9 $PID
/opt/local/sbin/lighttpd  -f lighttpd.conf
PID=`ps -aux | grep lighttpd | grep -v grep | awk '{ print  }'`
echo Instancia en ejecucion: $PID


Ejecutaremos este script cada vez que cambiemos el fichero de configuración y queramos reiniciar lighttpd (aunque supongo que el servidor atenderá alguna señal para volver a leer el archivo de configuración, no lo he terminado de investigar).

En el mismo directorio, tendremos el fichero lighttpd.conf, que es el meollo del asunto:

En primer lugar, cargamos los módulos necesarios para nuestra funcionalidad. Por supuesto, entre ellos aparece mod_fastcgi. Y usaremos mod_rewrite para cambiar las URLs para invocar transparente y adecuadamente al controlador de Rails.

server.modules = (
         "mod_rewrite",
         "mod_access",
         "mod_fastcgi",
         "mod_accesslog"
)


Definimos el directorio raíz del servidor, en nuestro caso, el directorio public de nuestra
aplicación Rails que es un directorio cualquiera de nuestra cuenta de usuario Mac.

server.document-root = "/Users/juan/Sites/rrecetario/public"


Este es el meollo del asunto, para configurar FastCGI. En esta directiva le podemos decir cuántas instancias del intérprete deseamos tener arrancadas, el tiempo de vida y cosas similares. De momento nos quedamos con los valores por defecto

fastcgi.server = ( ".fcgi" =>
       ( "localhost" =>
              ( "socket" => "/tmp/rrecetario.socket",
               "bin-path" => "/Users/juan/Sites/rrecetario/public/dispatch.fcgi"
               )
        )
)


El galimatías que viene a continuación lo he fusilado del wiki de Rails. Cada URL en una aplicación Rails tiene el formato "controlador/acción?parametros", cuando usamos FastCGI tenemos que decirle a lightttpd cómo debe pasarle los parametros a nuestro script dispatch.fcgi (que es el que, creo, se encarga de distribuir las peticiones a los intérpretes previamente arrancados). Obsérverse la última regla: si la URL solicitad es del tipo http://nombre_de_host/ entonces cargamos dispatch.fcgi sin parámetros (esto no aparece en los wikis de Rails)

url.rewrite = (
  "^/([\-_a-zA-Z0-9]+)/([\-_a-zA-Z0-9]+)/([\-_a-zA-Z0-9%]+)\??([\-_a-zA-Z0-9=&%]*)$" 
     => 
  "/dispatch.fcgi?controller=&action=&id=&",


  "^/([\-_a-zA-Z0-9]+)/([\-_a-zA-Z0-9]+)/?\??([\-_a-zA-Z0-9=&%]*)$" 
     => 
   "/dispatch.fcgi?controller=&action=&",
  
    "^/([\-_a-zA-Z0-9]+)/?\??([\-_a-zA-Z0-9=&%]*)$" 
      => 
     "/dispatch.fcgi?controller=&action=index&",
  
     "^/$" => "/dispatch.fcgi" 
)


Le indicamos dónde tiene que mostrar los mensajes de error y acceso

server.errorlog = "/Users/juan/lighttpd/logs/lighttpd.error.log"
accesslog.filename = "/Users/juan/lighttpd/logs/access.log"


Y por último los ficheros a cargar por defecto cuando se especifique un path acabado en / (he añadido dispatch.fcgi aunque entiendo que con la regla anteriormente añadida no haría falta)

server.indexfiles = (
 "index.php", "index.html",
  "index.htm", "default.htm",
  "dispatch.fcgi"
)


Los obligatorios tipos MIME (en el wiki de Rails pueden verse listados más completos)

mimetype.assign = (
     ".gz"           =>      "application/x-gzip",
     ".swf"          =>      "application/x-shockwave-flash",
     ".tar.gz"       =>      "application/x-tgz",
     ".tgz"          =>      "application/x-tgz",
     ".tar"          =>      "application/x-tar",
     ".zip"          =>      "application/zip",
     ".gif"          =>      "image/gif",
     ".jpg"          =>      "image/jpeg",
     ".jpeg"         =>      "image/jpeg",
     ".png"          =>      "image/png",
     ".css"          =>      "text/css",
     ".html"         =>      "text/html",
     ".htm"          =>      "text/html",
     ".js"           =>      "text/javascript",
     ".text"         =>      "text/plain",
     ".txt"          =>      "text/plain"
)


Y, por último, el puerto:
server.port = 8080


Tras esto, ya podemos arrancar lighttpd con el script de antes y veremos que nuestra aplicación se ejecuta (o eso esperamos) cuando accedemos a nuestra máquina por el puerto 8080.

El próximo paso sería usar hosts virtuales para diferentes aplicaciones en el mismo servidor. O, tal vez, pasar directamente a YARV...

13 comentarios

Referencias (TrackBacks)

URL de trackback de esta historia http://hronia.blogalia.com//trackbacks/32501

Comentarios

1
De: Entrambosmares Fecha: 2005-08-26 02:26

Ah. Creo q esta vez no me lo leo. ;)
Y yo q creí ayer haber escrito más de la cuenta..
Saludos,



2
De: GodEst Fecha: 2005-08-26 10:03

Sabía que antes o después esto que "está pegando tanto" iba a llegar a esta bitácora. Lo que no imaginaba es que iba a llegar tan extenso y detallado. Para los que te seguimos nos haces un gran favor divulgando este conocimiento.

Personalmente no tengo ni idea de esto de "Ruby on Rails" (vamos que no he dado ni mis primeros pasos -hola mundo-)... pienso que es algo parecido a un lenguaje interpretado empotrable en un servidor (en palabras de aprendiz) para que este lo ejecute: algo parecido a perl y php y esas hierbas (si la comparación ofende, pido perdón por anticipado)... al menos ya sé donde acudir por ayuda para su puesta en marcha (a ti ;-) ) y por ayuda documental (http://www.rubyonrails.com/). Aún disto mucho de comprender toda esa nomenclatura y arquitectura.

¡Enhorabuena por este tipo de artículos enfocados!



3
De: Epaminondas Pantulis Fecha: 2005-08-26 10:17

Una anotación como respueta a GodEst y Entrambosmares: ya sabía yo que esta entrada iba a provocar división de opiniones. :-) Dudé bastante a la hora de ponerla, primero porque es un tochazo y segundo porque en realidad no aporto demasiado que no aparezca ya en el wiki de Rails. Pensé en escribirlo todo en una libretilla para tenerlo a mano, y luego me dije: "Lo más a mano que lo voy a tener es en YG".

Supongo que a buena parte de los lectores esto les importará un bledo, y a los que les interesen este tipo de asuntos les aburrirán los posts en los que hablo de otras cosas.

En fin, este es el sino de YG: un poco de mucho y un mucho de nada.



4
De: Raúl Fecha: 2005-08-26 11:25

Cuando yo escribía en blogalia había disponible un apartado de "documentos". Quizá puedes inaugurarlo con este how-to y enlazarlo (al doc. o a la propia sección de documentos) desde la columna de la izquierda. De esa forma tienes este contenido disponible sin que el blog pierda su formato habitual.



5
De: GodEst Fecha: 2005-08-26 11:39

Yo concebí mi bitácora exactamente así: me hacía pequeñas "guia-burros" para que si ocurría cualquier catástrofe en mi PC, y tenía que volver a echar a andar desde un servicio hasta el S.O. completo, tener guardado en un sitio externo y disponible "el procedimiento". Ahora compruebo que muchos de esos pequeños "cómo-se-hace" que tengo en la bitácora son los que acaparan mayor número de lecturas, creo que este tipo de cosas le son útiles a todos: desde los que van directamente al grano hasta los que, una vez visto el grano, se interesan por el como se obtiene.

Salud.



6
De: Tyrannosaurus Reflex Fecha: 2005-08-26 11:43

Es refrescante que, por primera vez en los tres últimos meses (coincidiendo curiosamente con el verano y la falta de actividad y noticias (de hecho, ni siquiera ha aparecido nessy)) lea en una página sudo sin formar parte de sudoku...

sudo send-comment

:)



7
De: mort Fecha: 2005-08-26 11:44

Epaminondas, mola mucho y el esfuerzo de recopilar la información dispersa es tan importante o más que la generación de piezas sueltas de información.

Ya te las he dado en la lista pero gracias de nuevo.

Y en respuesta a GodEst: Ruby es el lenguaje. Rails es el framework, es decir, una estructura prefabricada que te da las bases para que construyas tu propia aplicación. Rails realiza la excavación, pone los cimientos y te deja todos los materiales a pie de obra, después tú construyes tu propio edificio (con Ruby)



8
De: Entrambosmares Fecha: 2005-08-26 14:57

Ey, q yo soy el primero q hace suya la idea de q los blogs son para escribir lo q nos apetezca, y no lo q les apetezca a los lectores. ;)
Así q hijo mío, tú pon lo q quieras.
Saludos,



9
De: Heimy Fecha: 2005-08-26 20:38

Pos yo en vez de con Rails ando con Django y Seaside :-D

Pero bueno, es cosa de elección de lenguajes.



10
De: Ismael Fecha: 2005-08-30 14:39

Enhorabuena por el tochazo. Dicho esto, ¿no será mejor llevarse artículos de esta densidad técnica a un blog más especializado? Quiero decir que luego te quejarás de que El Ojo de Turing no se mueve.

De mis breves días con OS X no recuerdo que Fink tuviese casi ningún problema. Decides el nivel de inestabilidad que quieres para tu subconjunto de aplicaciones compiladas. Rebobinar es tan sencillo como borrar /sw. Ah, y las aplicaciones de consola quedan muy cucas con el font Monaco.



11
De: koas Fecha: 2005-10-12 01:12

¿porque utilizas `ps -aux | grep lighttpd | grep -v grep | awk '{ print }' y no pidof?
¿por el formato de salida?



12
De: Epaminondas Pantulis Fecha: 2005-10-12 09:42

Por dos razones: porque pidof no existe en OS X y sobre todo porque no sabía ni que existiera en Linux.



13
De: owen Fecha: 2008-06-12 00:08

Por si acaso no tienes algun pequeño ejemplo de como configurar lighttpd para django???
estaba intentado hacer de la forma que tu hiciste pero algunos directorios cambian!!
te lo agradeceré infinitamente
....owen



Nombre
Correo-e
URL
Dirección IP: 54.162.19.123 (78e18e993c)
Comentario