Rails en producción - Parte 3 - MySQL, Ruby y Rails

mysql, ruby, rails, sysadmin 2 comentarios »

Postfix

Para que la instalación de MySQL no nos instale exim, instalaremos el servidor de correo Postfix.

$ sudo aptitude install postfix -y

MySQL

Instalamos los paquetes de MySQL. Esto además nos creará el usuario mysql en nuestro sistema:

$ sudo aptitude install mysql-server mysql-client libmysqlclient15-dev libmysql-ruby -y

Configuraremos una contraseña para el usuario root de la base de datos:

$ mysqladmin -u root password mipassword

Ruby

La versión de Ruby paquetizada en debian 4.0 es la 1.8.5. Si quisiéramos una versión superior, deberíamos compilar Ruby desde el código fuente.

Nosotros instalaremos los paquetes disponibles para debian. Para ello:

$ sudo aptitude install ruby1.8-dev ruby1.8 ri1.8 rdoc1.8 irb1.8 libreadline-ruby1.8 libruby1.8 libopenssl-ruby -y

A continuación crearemos los siguientes enlaces:

$ sudo ln -s /usr/bin/ruby1.8 /usr/local/bin/ruby
$ sudo ln -s /usr/bin/ri1.8 /usr/local/bin/ri
$ sudo ln -s /usr/bin/rdoc1.8 /usr/local/bin/rdoc
$ sudo ln -s /usr/bin/irb1.8 /usr/local/bin/irb
$ sudo ln -s /usr/bin/gem /usr/local/bin/gem

Rubygems

Rubygems es la herramienta para gestionar librerías y programas Ruby en forma de paquetes. Instalaremos Rubygems compilando el código fuente. Para ello, nos crearemos, en el directorio raíz de nuestro usuario, un directorio llamado sources. En él haremos:

$ mkdir sources
$ cd sources
$ wget http://rubyforge.org/frs/download.php/38646/rubygems-1.2.0.tgz
$ tar xzvf rubygems-1.2.0.tgz
$ cd rubygems-1.2.0
$ sudo ruby setup.rb
$ sudo ln -s /usr/bin/gem1.8 /usr/bin/gem
$ sudo gem update
$ sudo gem update --system
$ gem -v
1.2.0

Con esto habremos instalado la versión 1.2 de Rubygems. Comprueba cuál es la última versión estable cuando sigas estas instrucciones.

Rails

En teoría, si hemos congelado Rails en el directorio vendor de nuestra aplicación, no es necesario disponer de una instalación de Rails global en la máquina. A pesar de ello, si quisiéramos instalar Rails:

$ sudo gem install rails --include-dependencies --no-rdoc -no-ri

Esto me instalaría la gema rails y las gemas de las que depende. Como veis, gracias a que Rails está paquetizado en gemas, el proceso de instalación es muy sencillo.

Si hemos congelado en vendor Rails 1.2, no tendremos todas las gemas necesarias. En este caso deberíamos al menos instalar la gema actionwebservice y las que dependen de ella:

$ sudo gem install actionwebservice --include-dependencies --no-rdoc -no-ri

RMagick

Una librería Ruby que es frecuente utilizar es RMagick. Para que funcione, deberemos instalar ImageMagick en nuestro sistema:

$ sudo aptitude install imagemagick librmagick-ruby1.8 librmagick-ruby-doc libfreetype6-dev xml-core -y

Y para comprobar que funciona correctamente:

$ irb
irb(main):001:0> require 'RMagick'
=> true

Tutorial completo


Rails en producción - Parte 2 - Configuración del servidor debian

ruby, rails, sysadmin 0 comentarios »

La versión de Debian GNU/Linux con la que trabajaremos es la 4.0 etch.

Los pasos para instalar el sistema operativo dependerán de nuestro proveedor de hosting. Normalmente, dispondremos de un panel de control para poder instalar nuestro servidor. Partiremos, por lo tanto, de una instalación totalmente nueva y operativa, de la que tendremos la password de root y la dirección IP para poder conectarnos por SSH.

Nos conectamos a la máquina recien instalada. Lo primero que haremos, por seguridad, será cambiar la contraseña de root:

# passwd

Creación de un usuario no privilegiado

Creamos un usuario no privilegiado, el cual ejecutará el servidor web y los servidores de aplicaciones:

# useradd -d /home/usuario -s /bin/bash usuario
# mkdir /home/usuario
# chown usuario:usuario /home/usuario
# passwd usuario

Por comodidad, instalaremos la herramienta sudo y le daremos a nuestro usuario privilegios para poder utilizarla:

# apt-get install sudo
# export EDITOR=vi
# visudo

Y añadimos al final del fichero:

usuario ALL=(ALL) ALL

Podemos crear un fichero ~/.bash_profile para el usuario. Podemos utilizar como ejemplo:

PS1='\[\033[035m\]\u\[\033[033m\] \w\[\033[00m\]$ '
EDITOR=vi
RAILS_ENV=production
export PS1 EDITOR RAILS_ENV

Configuración de SSH

Editamos el fichero de configuración de SSH, /etc/ssh/sshd_config, de manera que:

  • por seguridad, cambiamos el puerto TCP 22 por otro no estándar, por ejemplo el 30000.
  • nos aseguramos de que la versión del protocolo es la 2.
  • no será posible conectarse directamente como root.
  • permitiremos la autenticación mediante password (a menos que definamos una relación de confianza).
  • no permitiremos X11Forwarding.
  • deshabilitaremos la autenticación vía PAM.
  • deshabilitaremos las resoluciones inversas de DNS.
  • permitiremos solamente el acceso por SSH al usuario que hemos creado anteriormente.

Es decir, en el fichero /etc/ssh/sshd_config deberemos comprobar las siguiente líneas:

Port 30000
Protocol 2
PermitRootLogin no
PasswordAuthentication yes
X11Forwarding no
UsePAM no
UseDNS no
AllowUsers usuario

Una vez editado, le indicamos al demonio sshd que lo relea:

# /etc/init.d/ssh reload

Antes de desconectarnos como root, deberemos probar a conectarnos con el usuario no privilegiado que hemos creado, ya que si no hemos editado correctamente el fichero de configuración de SSH podríamos peder el acceso a la máquina:

$ ssh -l usuario IP -p 30000

Configuración del firewall iptables

Si acabamos de instalar debian, la tabla de reglas del firewall estará vacia:

$ sudo -i
# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
 
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination
 
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Editamos el fichero /etc/iptables.test.rules y pegamos:

*filter
 
 
#  Allows all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0
-A INPUT -i lo -j ACCEPT
-A INPUT -i ! lo -d 127.0.0.0/8 -j REJECT
 
 
#  Accepts all established inbound connections
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
 
 
#  Allows all outbound traffic
#  You can modify this to only allow certain traffic
-A OUTPUT -j ACCEPT
 
 
# Allows HTTP and HTTPS connections from anywhere (the normal ports for websites)
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT
 
 
#  Allows SSH connections
#
# THE -dport NUMBER IS THE SAME ONE YOU SET UP IN THE SSHD_CONFIG FILE
#
-A INPUT -p tcp -m state --state NEW --dport 30000 -j ACCEPT
 
 
# Allow ping
-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
 
 
# log iptables denied calls
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
 
 
# Reject all other inbound - default deny unless explicitly allowed policy
-A INPUT -j REJECT
-A FORWARD -j REJECT
 
COMMIT

Si hemos elegido para el demonio sshd un puerto diferente al 30000, tendremos que editar la línea del fichero que permite las conexiones SSH entrantes.

Cargamos el firewall con las nuevas reglas:

# iptables-restore < /etc/iptables.test.rules

Comprobamos que las reglas se han cargado correctamente.

# iptables -L

Comprobamos también que podemos conectarnos de nuevo por SSH. En caso afirmativo, podemos guardar las reglas:

# iptables-save > /etc/iptables.up.rules

Para que los cambios sean tenidos en cuenta al rearrancar la máquina, deberemos editar el fichero /etc/network/interfaces. Después de las líneas:

# The loopback network interface
auto lo
iface lo inet loopback

añadiremos:

pre-up iptables-restore < /etc/iptables.up.rules

Para comprobar que todo ha ido bien, rearracamos la máquina:

# shutdown -r now

Actualizaciones

Una vez conectados de nuevo, realizaremos una actualización de los paquetes de la distribución debian. Tendremos en cuenta que la lista de repositorios en los que se buscan las actualizaciones está en /etc/apt/sources.list. En principio no será necesario cambiar la lista de repositorios.

Actualizamos la lista de paquetes disponibles en el repositorio:

$ sudo aptitude update

Reconfiguramos los paquetes de locales. Para ello, editamos el fichero /etc/environment:

LANG=es_ES.UTF-8

y ejecutamos:

$ sudo aptitude install locales
$ sudo dpkg-reconfigure locales

Nos aparecerá un menú donde tendremos que seleccionar los locales:

es_ES.UTF-8 UTF-8

Como veis, he escogido el español de España (es_ES). Cada uno puede elegir el locale que le interese.

Ahora sí, actualizamos los paquetes de nuestra máquina (esto puede tardar un rato):

$ sudo aptitude upgrade
$ sudo aptitude dist-upgrade

Rearracamos:

$ sudo shutdown -r now

Y por último, instalamos los paquetes necesarios para la compilación, ya que los necesitaremos más adelante:

$ sudo aptitude install build-essential

Tutorial completo


Rails en producción - Parte 1 - Introducción

ruby, rails, sysadmin 0 comentarios »

La puesta en producción de una aplicación Rails no es trivial. Tanto si disponemos de un VPS como de un servidor dedicado, tendremos que instalar y configurar, a parte del propio servidor, un servidor web, un servidor de aplicaciones Rails y una base de datos.

Las posibilidades de elección son numerosas. A día de hoy, una de las arquitecturas más utilizadas para poner en producción una aplicación Rails sería la de la siguiente figura:

rails producción

Es decir, utilizaremos:

  • como sistema operativo, Debian GNU/Linux.
  • como servidor web, nginx, mucho más liviano que Apache.
  • como servidor de aplicaciones, mongrel.
  • como base de datos, MySQL.

Las peticiones HTTP son recibidas por nginx, el cual se encarga de redirigirlas y balancearlas hacia los mongrels. Cada mongrel ejecutará nuestra aplicación Rails en el MRI, es decir, en el intérprete de Ruby tradicional. Finalmente, nuestra aplicación Rails se comunicará con la base de datos MySQL a través de ActiveRecord.

He dividido este tutorial en varias entradas:

La documentación que he utilizado ha sido, principalmente:


Engine Yard consigue $15 millones de financiación

ruby, rails 0 comentarios »

Engine Yard, tal y como cuentan en su blog, ha conseguido $15 millones de financiación que se suman a los $3.5 millones que consiguió en enero.

Engine Yard

Los inversores esta vez han sido Benchmark Capital, que fue el inversor de la anterior ronda de financiación, New Enterprise Associates, Inc. (NEA) y Amazon.

Para quien no lo sepa, Engine Yard es una de las empresas líderes en ofrecer hosting para aplicaciones Rails. El actual modelo de negocio consiste en ofrecer una plataforma fiable y escalable donde poder ejecutar aplicaciones Rails. La empresa ofrece porciones de su infraestructura, de manera que si una aplicación necesita escalar, el cliente deberá ir contratando más porciones.

Según comenta en su blog Ezra Zygmuntowicz, uno de los arquitectos de Engine Yard, la tendencia hacia el cloud computing es clara. Es decir, la industria del hosting se está moviendo hacia un nuevo paradigma en el cual se ofrecerá a los desarrolladores una plataforma escalable de forma transparente, altamente disponible y en la que el paso a producción es sencillo.

Es destacable que uno de los inversores en esta ronda ha sido Amazon, quien ya dispone de los Amazon Web Services. ¿Será una jugada para luchar contra la competencia de Google App Engine?


6 consejos de optimización para Ruby MRI

ruby, rails 1 comentario »

Ilya Grigorik ha publicado un interesante post con 6 consejos para optimizar nuestro código Ruby si utilizamos el intérprete MRI.

ruby

Minimizar las búsquedas en el AST

Si utilizamos Ruby 1.8, nuestro MRI no generará bytecode. Por ello, cada llamada a un método, cada utilización de una variable, etc., realizará una búsqueda en el Abstract Syntax Tree (AST), es decir, en una estructura en árbol que representa la sintaxis de nuestro código fuente.

Debemos, por lo tanto, tener en cuenta que abusar de la metaprogramación puede hacer que el rendimiento de nuestra aplicación se vea degradado.

Optimizar para la caché

En relación con el punto anterior, para minimizar las búsquedas en el AST, Ruby MRI mantiene una caché de las variables locales. Es decir, utilizar variables locales será más rápido:

@var = "local variable, which is cached by Ruby, and requires a single lookup"
self.var = "requires walking the AST, and results in multiple lookups"

Mejor la interpolación que la concatenación

puts "This string embeds #{var1} and #{var2} through interpolation"  # faster
puts "This string concatenates " << var1 << " and " << var 2  # slower

Utilizar métodos destructivos

Cuando disponemos de un método no destructivo y su pareja destructiva, como gsub y gsub!, suele ser más eficiente utilizar el método destructivo. Los métodos no destructivos suelen ser más lentos ya que tienen que realizar copias de los objectos.

hash = {}
hash = hash.merge({1 => 2}) # duplicates the original hash
hash.merge!({1 => 2}) # equivalent to previous line, and faster

Utilizar bloques en lugar de Symbol.to_proc

@widget_ids = @widgets.map(&:id) # Symbol.to_proc method, order of magniture slower...
@widget_ids = @widgets.collect {|w| w.id } # faster and simpler

Compruebalo tú mismo

Si tienes dudas sobré qué código será más rápido, es sencillo comprobarlo:

require 'benchmark'
 
n = 100000
Benchmark.bm do |x|
   x.report('copy') { n.times do ; h = {}; h = h.merge({1 => 2}); end }
   x.report('no copy') { n.times do ; h = {}; h.merge!({1 => 2}); end }
end
copy  0.350000   0.060000   0.410000 (  0.419445)
no copy  0.250000   0.020000   0.270000 (  0.276030)