domingo, 27 de noviembre de 2011

Un servidor web mas rápido: cambiando Apache por Nginx


Apache es un icono entre los servidores web, hoy sirve en el 63% de los sitios de toda la Internet y muchos de los que entran al mundo *nix entran a través de la necesidad de alojar un sitio web, es decir a través de Apache. Pero no es la única herramienta en la granja y aquí les traemos un artículo sobre el tercer lugar entre los servidores web de todos los dominios (según Netcraft). Su nombre es Nginx (se pronuncia engine-x) y no hace todo lo que puede hacer Apache, pero lo que hace lo hace increíblemente rápido. Según el autor del original, las pruebas que ofrece son puramente subjetivas, pero dado que su reputación le precede (la de Nginx, no la del autor) creo que valdrá la pena darle una probada y eventualmente ejecutar alguna clase de workbench para sacar algunos números en claro. Si quieren iniciar, este es el sitio oficial del proyecto. Es distribuido usando una licencia BSD modificada, y aquí pueden encontrar la documentación oficial. Eso, es lunes  y aquí estamos :-p  la traducción es de Jorge, les deseamos feliz inicio de semana y provecho!

Un servidor web mas rápido: cambiando Apache por Nginx.
Soy, en el mejor de los casos, un administrador de sistemas nocturno. Crecí como nerd dando soporte técnico y posteriormente administrando en una tienda de Windows con un puñado de *nix, la mayoría de los cuales eran atendidos por ancianos barbudos encerrados en habitaciones blancas y frías. No fue hasta que empecé a manejar equipos de almacenamiento empresarial que llegué a apreciar el poder de la shell bash, y mi red doméstica cambió gradualmente de un dominio Windows 2003 con soporte a unas cuantas PCs a una mezcla de servidores GNU/Linux y sistemas de escritorios y portátiles OS X.

Como tantos otros, al final me decidí a colocar mi propio sitio web en Internet, y utilice el servidor HTTP Apache para alojarlo. ¿Por qué? Tenía un cajón con Ubuntu Server frente a mí, y Apache fue el servidor Web del que más había oído hablar. Si Apache era lo suficientemente bueno para los sitios grandes, debía ser lo suficientemente bueno para mi pequeña web estática. ¿No? 
Pero no fue del todo bien para mí. He aquí por qué - y lo que aprendí luego de pasar un fin de semana desinstalando mi Apache y sustituyéndolo por un ligero demonio de la velocidad, un servidor web llamado Nginx.

Viejo y roto 
Apache fue fácil de configurar. Casi escribo "trivialmente" fácil, pero entrar en una configuración de Apache con nada más que una actitud valiente y el único conocimiento de que "Apache es un software que aloja sitios web" significa que vamos a enfrentar una curva de aprendizaje. Sin embargo, después de no más de una hora o dos de búsquedas en Google en busca de ayuda, y de hacer algunas pruebas con los archivos de configuración del Apache, logré publicar una página web en internet. Unos meses más tarde, Ars publicó un trabajo sobre cómo conseguir certificados SSL / TLS gratis. Inmediatamente quise probarlo, no porque tuviera alguna necesidad real para ello, sino sólo para ver cómo funcionaban los certificados. Menos de un día después aquello funcionó, tenía 2 clases de comodines certificados SSL / TLS para mi dominio, y mi servidor Web bailaba con https. 

Las cosas funcionaron bien de esta manera durante un par de años, pero cuando empecé a hacer más con el servidor Web, comenzó a hacerse evidente que mi configuración, aunque perfectamente factible, podría ser mejor. En particular, la adición de Tectonicus (un renderizador de mapas de Minecraft que genera millones de diminutos azulejos al estilo de la interfaz de Google Maps) me enseñó que las cosas estaban lejos de ser óptimas. Incluso a través de mi  red local, Apache luchaba por servir el mapa a un ritmo adecuado. El servidor web es un AMD de doble núcleo E-350 con 2 GB de RAM y una unidad de estado sólido (SSD) Vertex 2, que servía las imágenes estáticas del sitio al instante. Pero la herramienta htop mostró que los procesos de Apache volvían loco al procesador cada vez que el mapa de Tectonicus estaba siendo servido; ambos núcleos se disparaban al 100 por ciento del uso mientras la pantalla se llenaba lentamente de cuadritos azules.

Además, comencé a correr un pequeño wiki en el mismo servidor. Esta usaba Dokuwiki, un servidor wiki que puede utilizar temas para asemejarse a MediaWiki, pero que almacena sus datos en archivos planos en lugar de requerir una base de datos. Dokuwiki requiere PHP, un lenguaje de script muy usado que se ejecuta en un gran número de servidores Web en todo el mundo, por lo que esto significaba que tenía que instalar algún tipo de paquete de PHP en mi configuración actual.

Había muchos caminos a tomar. Dado que me había instalado Apache en Ubuntu de la forma fácil, escribiendo "sudo apt-get install apache2:" Tengo lo que se conoce como la versión de Apache MPM prefork. Esta es la versión más instalada de Apache, y funciona iniciando una serie de distintos procesos de Apache para manejar las solicitudes Web. No utiliza hilos, sino que divide el trabajo generando procesos hijos de Apache (para repasar las diferencia entre un hilo y un proceso, echa un vistazo a este articulo destacado de Ars). Prefork es la instalación de Apache por defecto ya que Apache es un servidor web extensible que se puede personalizar para hacer todo tipo de cosas útiles con la adición de módulos, y algunos de los módulos que la gente desean instalar no funcionan bien cuando se ejecutan en un modo multihilo. 

El inconveniente de hacer todo con procesos es que Apache prefork puede ser un poco abusador con el uso de la memoria, especialmente bajo carga. Puede ser instalada otra versión precompilada de Apache como una alternativa: Apache MPM Worker. "Worker" difiere de "prefork" en que los procesos de la versión "Worker" utilizan múltiples hilos (multithreaded), dándole la capacidad de atender más peticiones con menos uso de recursos del sistema. Esto puede traducirse en páginas servidas  con más velocidad utilizando menos RAM y CPU. Sin embargo, debido a que algunos módulos de Apache no funcionan bien cuando se ejecutan en Apache multi-hilo, tiene que seleccionar específicamente esta versión para instalar en Ubuntu y otras distribuciones GNU / Linux con los administradores de paquetes. 

Un poco de búsqueda mostró que Apache Worker puede mejorar ampliamente el funcionamiento de Tectonicus  al servir sus toneladas de cuadros azules más rápido, pero el cambio podría causar algunos problemas con PHP. El módulo de PHP compilado para Apache, "mod_php," es uno de los módulos que pueden tener problemas con la ejecución multi-hilo. Terminé desinstalando y reemplazando software para cambiarme de "mod_php" (es decir la version modular) a un php independiente.

Un post de un miembro del fórum de Ars, Blacken00100, me condujo por una dirección completamente diferente. Apache con PHP independiente podría resultar mucho menos óptimo que un servidor web ligero como Nginx con Php independiente y manejado por eventos. Comencé a analizar y  descubrí que aun cuando iba a tener que trabajar un buen rato, podía llegar hasta el final y conseguir configurar lo que es ampliamente reconocido como el servidor web mas rapido disponible. 

El nuevo centro de atención
Nginx (pronunciado "engine-ex") es un servidor web ligero con reputación de rapidez. Se diferencia de Apache en un aspecto fundamental, Apache es una aplicación orientada y manejada en base a procesos e hilos, pero Nginx es orientada a eventos. El efecto práctico de esta diferencia de diseño es que un pequeño número de procesos Nginx "worker" pueden pueden lidiar con montones de solicitudes sin tener que esperar los unos por los otros ni sincronizarse entre ellos, simplemente "cierran los ojos" y literalmente se tragan el provervial elefante tan rápido como les es posible, una mordida a la vez. 

Apache, por el contrario, atiende un gran número de peticiones generando subprocesos para manejarlas, por lo general consumiendo una gran cantidad de memoria RAM en la forma que lo hace. Apache ve al elefante y piensa en lo grande que es mientras se lo come, y a veces pensar en esto le genera un poco de ansiedad. Nginx, por el contrario, simplemente comienza masticar. 

La diferencia se resume sucintamente en una cita de Chris Lea en la página Why Use Nginx? (¿Por qué usar Nginx?): ".. Apache es como Microsoft Word, que tiene un millón de opciones, pero sólo necesita seis, Nginx hace esas seis cosas, y hace cinco de ellas 50 veces más rápido que Apache" 

Nginx en particular sobresale en servir archivos estáticos, como los mapas de imágenes de Tectonicus. Para grandes sitios web, a menudo es empleado como un servidor Web front-end para servir rápidamente el contenido de las páginas estáticas, mientras se pasan las peticiones de páginas dinámica a un servidor web con Apache corriendo en otro lugar. Sin embargo, yo estaba interesado en el sólo como un simple servidor web rápido. 

Como todo lo que se refiere en este artículo, Nginx está disponible desde los repositorios de paquetes de Ubuntu con un rápido "sudo apt-get install Nginx." Después de parar Apache, en un momento ya tenía instalado Nginx. Avance un poco mas siguiendo los consejos de Blacken, incluso instalé php-fpm, que es un paquete  de PHP con grandes modificaciones e integración con funciones CGI.  La recomendación de Blacken de cambiar php5-fpm por php5-cgi se debe a que la habilidad de fpm de iniciar o detener nuevos procesos PHP según la carga de trabajo del servidor lo convierte en un paquete mucho mas inteligente y poderoso; consume muy pocos recursos y al mismo tiempo trabaja de forma transparente bajo carga y conserva la velocidad.
  
Si tus necesidades son simples, como las mías, entonces será un asunto fácil conseguir una instalación de PHP con php-fpm que sea operacional. El archivo de configuración principal (/etc/php5/fpm/php-fpm.conf en Ubuntu 11.10) no debe ser alterado en absoluto, mientras que el archivo de configuración del grupo (pool configuration file en el original ndt) (/etc/php5/fpm/pool.d/www.conf) sólo necesita algunos ajustes ligeros. El archivo de configuración de  grupo define como php5-fpm aceptará peticiones CGI desde el servidor Web, por defecto, php5-fpm escucha las solicitudes del servidor web por el puerto TCP 9000, pero he cambiado esta opción para utilizar un archivo de socket Unix en su lugar, dado que correr peticiones CGI a través de un puerto TCP local introduce una pequeña demora. Es probable que este detalle no importe a menos que tu sitio web esté sirviendo un montón de páginas, pero quería hacer las cosas de la manera correcta. Además, el archivo de configuración le permite especificar el usuario y el grupo en que los procesos se ejecutarán, es muy buena idea configurarlo para que corra bajo el mismo usuario y grupo que utiliza el servidor Web. 

Lo más importante es que el archivo de configuración  le permite definir el número máximo y mínimo de procesos PHP que se generarán, si php-fpm está configurado en modo "dinámico". Esto le permite iniciar sólo uno o dos procesos activos para atender las solicitudes de PHP, pero se le puede especificar a  php-fpm que pueda generar tantos procesos como sea necesario. El único límite real es la cantidad de memoria RAM y CPU disponible. Para mi pequeño sitio web, configuré php-fpm para comenzar un proceso único, con la opción de reproducir hasta 10. Finalmente, el archivo de configuración  te permite especificar los valores tradicionales de configuración de PHP, como el máximo uso de memoria, tamaño máximo de subida de ficheros (upload size), la localización del ejecutable de sendmail, y otros. 

Configurando Nginx. 
Después de guardar el archivo de configuración y reiniciar el daemon a través de su script de inicio, había conseguido un ambiente PHP totalmente funcional, y ya estaba listo para centrar mi atención en el servidor Web. ¿Cómo podría adaptar la configuración de Apache existente, que incluía SSL / TLS, a Nginx? 
Resulta que en realidad es bastante fácil, dado que Nginx no tiene la riqueza de configuración de Apache. Para los sitios pequeños, ¡esto es algo muy bueno! Nginx, cuando se instala en Ubuntu a través de un gestor de paquetes, utiliza una estructura de directorios como Apache. Todos los archivos configurable por el usuario residen en /etc/Nginx, hay un archivo Nginx.conf que contiene las configuraciones globales, un directorio conf.d para la colocación de archivos adicionales que serán analizados e incluidos en la configuración en ejecución, y los directorios sites-available y sites-enabled, para la definición de los sitios web publicados y sus configuraciones específicas. 

El contenido de /etc/Nginx será algo familiar para los usuarios de Apache

Es notable lo que Nginx no utiliza en la configuración, no hay soporte para archivos .htaccess. Cualquier configuración que desee hacer en subdirectorios específicos se debe manejar o en un archivo de configuración o en uno de los archivos de definición de sitio. Si está pensando en cambiase a Nginx y su sitio web depende en gran medida del uso de .htaccess, ya sea para definir accesos o para agregar reglas de reescritura o cualquier otra cosa, es necesario reevaluar el asunto y ver si lo que está haciendo puede ser recreado en los  archivos de configuración en lugar de en .htaccess. Esto también significa que algunas aplicaciones Web que dependen específicamente de la presencia de .htaccess probablemente no funcionen bien  (o no funcionen) en Nginx. 
A pesar de esto, mi sitio se adaptó bien a Nginx, y el fichero de configuración principal casi no requirió edición. El ajuste más importante en el archivo principal es la configuración de "worker_processes", el cual define el número de procesos que Nginx ejecutará. Teniendo en cuenta que un proceso puede manejar miles de peticiones simultáneas, una buena opción es tener un proceso trabajando por cada núcleo de CPU. En mi caso, lo definí en 2. El archivo también le permite especificar como que usuario se ejecutarán los procesos de Nginx, como instalé Nginx a través de un gestor de paquetes, esto fue pre-configurado para ejecutarse como el usuario www-data, al igual que Apache. 

El resto de la configuración se realiza en el directorio sites-available. Al igual que en Apache, con Nginx puedes crear definiciones de sitios en el directorio sites-available y luego creas enlaces simbólicos en el directorio sites-enabled, que Nginx analiza durante el inicio. Sin embargo, a diferencia de Apache, donde tenía un archivo separado para no utilizar SSL y otro para SSL, los archivos por defecto incluidos en sites-available tienen definidos ambos host virtuales HTTP y HTTPS. 

Nginx utiliza el concepto de hosts virtuales (virtual host) de Apache, y ofrece suficientes opciones de configuración para satisfacer la mayoría de los sitios. Puedes definir un host virtual, un directorio Web que referencie la ubicación de los archivos a publicar, y colocarle permisos y directivas a lugares específicos. También se manejan los Rewrites en el mismo archivo, en lugar de ser referenciado en diferentes lugares como en Apache. Esto puede hacer que los archivos de definición de sitios sean más complejos que en Apache, pero la configuración es centralizada. 

Hay alguna diferencia también al configurar SSL, como Nginx no es compatible con cadenas de certificados separados como Apache. Si el certificado de su sitio web requiere un paquete certificado intermedio, deberás concatenar el certificado de tu sitio en el paquete antes de poder usarlo. Además, cuando se utilizan archivos con SSL/TLS, por defecto Nginx utiliza el cifrado altamente seguro pero a la vez extremadamente lento DHE-RSA-AES256-SHA, en las conexiones cifradas. Esto es bueno si usted necesita un cifrado complejo para sus páginas, pero no tan bueno si usted necesita que sus páginas se entreguen velozmente, ya que el cifrado Diffie-Hellman que añade es computacionalmente intenso. Si va a servir páginas cifradas, podría ser una buena idea deshabilitar el cifrado RSA-DSE-AES256-SHA y tendrás a Nginx usando encriptación plana con AES256-SHA. El método para hacerlo, así como alguna información más sobre el tema, lo pueden encontrar en esta página

Con el fin de hacer que Nginx pase correctamente los archivos PHP a la versión php-fpm, todo lo que tienes que hacer es crear un manipulador en cada virtual host  que va a utilizar PHP, así: 

location ~ \.php$ { try_files $uri =404; fastcgi_pass unix:/var/run/php5-fpm.soc;}

Esto le dice a Nginx que los archivos ubicados en cualquier lugar bajo la raíz Web y que terminan en .php deben ser pasados usando FastCGI a través del socket en /var/run/php5-fpm, que es donde los procesos de php5-fpm escuchan peticiones. 

Un tip importante es que existe una vulnerabilidad bien conocida al combinar Nginx con FastCGI y PHP que puede permitir a los visitantes enviar archivos no PHP a través del controlador de PHP. La vulnerabilidad se aprovecha de que PHP trata de ser lo más útil posible con las cosas que se le entregan a través el servidor Web, es posible decirle a PHP que ejecute archivos que en realidad no terminan en .PHP enviándole nombres de archivos incorrectos que si terminan en PHP. Hay una opción en el php.ini que se puede configurar para evitar que esto suceda, pero un buen número de aplicaciones PHP populares en realidad dependen de este comportamiento, por lo que es más fácil de abordar desde el lado del servidor. Eso es para lo que son las lineas "try_files $uri = 404;",  instruye a Nginx a que primero intente servir la URI exacta que le fue dada, y si la URI no existe reporte un error 404 en lugar de pasarle el URI a PHP. 
La gran mayoría de los tutoriales de Nginx + PHP pasan por alto esta configuración, a pesar de que ha existido por casi dos años. (¡Lo menciono aquí porque es un problema fácil de evitar!)
El resto de las  configuraciones que tenía que hacer era con rewrite, y estaba relacionado con limpiar las URL de Dokuwiki y hacerlas más fáciles de leer. Traducir del lenguaje rewrite extremadamente rico de Apache a  Nginx significó que tuve que inferir bastantes detalles, y si tu sitio requiere el uso de rewrites complejos probablemente no desees utilizar Nginx. Su motor de reescritura es bastante plano y no tan poderoso como el de Apache. 

Un conjunto de reglas de rewrite .htaccess para mi wiki alojado, para limpiar las URLs y lidiar con inicios de sesión seguros ..


...y algunas de sus reglas equivalentes en Nginx

Viento en popa 
Y con eso, había terminado. ¿Cómo fue todo? 
Funciona muy bien de hecho. Dejando de lado lo relacionado con PHP por un momento, la razón principal por la que quería cambiar era debido a la velocidad a la hora de servir archivos planos. En mis pruebas, totalmente subjetivas, aquí Nginx  destruye Apache. El mismo mapa Tectonicus que anteriormente tomaba segundos para llenar totalmente la pantalla ahora aparece al instante, y responde de inmediato a la entrada del mouse. Las operaciones de arrastrar y hacer zoom son fluidas y rápidas, a diferencia del mismo mapa bajo Apache, que se retrasara  como si estuviese publicado en un servidor energizado por hamsters en la Antártica  en vez de un servidor LAN Box con una conexión Ethernet a un Gigabit. Estoy muy contento. 


Los dos procesos de Nginx Worker y el proceso único PHP-FPM pool utilizando solo cerca de 14 Mb de memoria RAM.

La mejora es un poco menos evidente con el wiki basado en PHP, pero no es decididamente peor que antes. Hay varias páginas con imágenes grandes en el wiki y cargarlas a través de Apache tomaría quizás  de 3 a 5 segundos, usando Nginx cargan en aproximadamente la misma cantidad de tiempo. Sin embargo, el consumo de memoria RAM en Nginx + php-fpm es considerablemente menor que el de la inmensa configuración de Apache prefork que había originalmente, y la utilización de la CPU durante el servicio de alrededor  de una docena de cargas de la página es mucho menor. 
El cambio tomó la mayoría del sábado entre varios arranques en falso y un montón de búsquedas en Google, con una gran parte del tiempo empleado en arreglar las reglas de rewrite y hacer feliz al wiki. He aprendido mucho de la experiencia, al menos que Nginx hace lo que dice en la lata, que es un servidor Web endiabladamente rápido.