jueves, marzo 29, 2007

Deploy de mas de una aplicacion Ruby, en minutos...

Revision 2

PREAMBULO
En internet se encuentran ciertas recetas las cuales indican paso a paso como instalar y configurar un servidor con Ubuntu para poder realizar un deploy de nuestra aplicación en Ruby, pero... ¿qué pasa cuando quiero hacer mas de una aplicación?, para ello esta breve explicación de como hacerlo. Nuestra intención es facilitar a los usuarios de esta plataforma su instalación y configuración, mencionando y resolviendo ciertos obstaculos y errores que encontramos en el camino de nuestra instalación (Best Practice).


1.0 PREPARANDO EL SERVIDOR
Para comenzar debemos realizar una instalación fresca del sistema operativo en el servidor, el cual será en nuestro caso Ubuntu 6.06. Al momento de instalarlo nos preguntará el nombre de usuario y contraseña con el que iniciaremos la sesión en Ubuntu, en nuestro caso le hemos puesto 'deploy' al nombre de usuario y 'deploy' como contraseña.


1.1 Configurando la red
Luego de haber terminado la instalación del sistema operativo verificamos configuramos adecuadamente los parametros de la red (IP, máscara de red, DNS's y puerta de enlace) para que podamos realizar las descargas necesarias desde los repositorios. Hay que acordarnos de la IP de nuestro servidor, en nuestro caso le hemos puesto la 192.168.0.10, ya que la utilizaremos mas adelante para que la maquina cliente sepa cual es nuestro servidor y pueda resolver los dominios virtuales que realizaremos por cada aplicación.


1.2 Verificando repositorios
Aparte de esto, también debemos de verificar que nuestros repositorios a los cuales va a buscar Ubuntu las descargas se encuentren bien, es decir, que no esten comentareadas en el archivo '/etc/apt/sources.list'; de no ser asi debemos descometarear esas líneas:

# Para editar la lista de los repositorios de Ubuntu

sudo vi /etc/apt/sources.list
# Actualiza la lista de repositorios
sudo apt-get update

1.3 Instalando OpenSSH
Por último debemos de instalar OpenSSH para poder conectarnos desde nuestro cliente y realizar todo desde ahi.

# Para instalar OpenSSH
sudo apt-get install openssh-server

También tenemos que crear una carpeta en la que se creará el esqueleto donde estará almacenada la aplicación, pero dicha carpeta debe ser accesible con los permisos necesarios para poder escribir en ella por el usuario que realizara el deployment. Esto ultimo tenemos que hacerlo en el servidor, que en nuestro caso se creará la carpeta '/var/www/apps'.

# Crea la carpeta www
sudo mkdir /var/www
# Crea la carpeta apps
sudo mkdir /var/www/apps
# Se le asignan los permisos necesarios para escritura
sudo chmod gu+w /var/www -R
# Se asignan los dueños y grupos a la carpeta
sudo chown deploy:deploy /var/www/apps -R



2.0 PREPARANDO LA MAQUINA CLIENTE
Debemos instalar el sistema operativo en nuestra máquina cliente, que para nuestro caso, también será Ubuntu 6.06.


2.1 Instalando Ruby
Luego debemos de instalar Ruby.

# Para instalar Ruby
sudo apt-get install ruby ri rdoc irb ri1.8 ruby1.8-dev libzlib-ruby mysql-server libmysql-ruby


Notese que se instala también 'mysql-server' y 'libmysql-ruby' en caso que quieran conectarse a una base de datos de MySQL localmente para realizar pruebas, lo cual es normal en este tipo de desarrollo y entorno, pero no es obligatorio instalar estos paquetes a menos que se utilizen.

Posteriormente, debemos de instalar Ruby Gem, el cual es un administrador de paquetes para Ruby al igual de como lo es apt para Debian/Ubuntu. Desafortunadamente no es un paquete autoinstalable en Debian, por lo que no podremos instalarlo con apt-get, sino que tendremos que descargarlo e instalarlo nosotros mismos.

# Descargamos el paquete
sudo wget http://rubyforge.org/frs/download.php/16452/rubygems-0.9.1.tgz
# Desempaquetamos el paquete
tar -zxvf rubygems-0.9.1.tgz
# Entramos al directorio que se creo
cd rubygems-0.9.1
# Ejecutamos el instalador
sudo ruby setup.sh


Una vez tenemos el administrador de paquetes 'gem' instalado, podemos instalar nuestro primer gem llamado 'deprec' (deployment recipes) el cual es un conjunto de recetas para realizar un deployment con Capistrano.

Capistrano es también un gem que se utiliza para ejecutar comandos paralelamente en multiples maquinas remotas por medio de SSH. El principal objetivo de este gem es automatizar las tareas de deployment, y es aqui donde radica la magia y la conceptualizacion de todo esto.

Todo lo necesario para realizar el deployment desde nuesta maquina cliente será instalado al instalar 'deprec'.

# Instala todo lo necesario en la PC cliente
sudo gem install deprec -y


Con esto hemos instalado deprec pero aun no estan las tareas disponibles para su utilización, por lo que en la mayoria de post en Internet agregan la línea 'deprec_dotfiles', pero pueda ser que esto no funcione, asi que podemos parchar esto manualmente tal y como se muestra a continuación.

echo "require 'deprec/recipes' " >> ~/.caprc

Esto es necesario ya que Capistrano buscará tareas extra y el archivo '~./caprc' contiene la linea que le indica acerca de esas tareas.

Para verificar podemos realizar un 'cap show_tasks' y listar las tareas de Capistrano que ya estan incluidas a nuestra disposición.

# Muestra las tareas disponibles, entre ellas las de Capistrano
cap show_tasks


En algun momento nos solicitará la contraseña del usuario con la que haremos el deployment, y para que dicha contraseña no sea vista en la consola al momento de digitarla es necesario instalar un gem llamado termios.

# Para instalar el gem termios
sudo gem install termios



2.2 Preparando nuestro primer deployment
Una vez hecho todo esto, ya tenemos listo nuestro cliente, pero aun no tenemos nada instalado en nuestro servidor; para ello debemos de ejecutar el comando 'cap install_rails_stack' para que instale automaticamente todo lo necesario en el servidor.

Antes de ejecutar 'cap install_rails_stack' debemos de tener, por lo menos, nuestra primera aplicacion de Ruby on Rails la cual queremos hacer un deploy. El motivo de esto es que necesitamos configurar ciertos parametros que Capistrano necesita saber, como por ejemplo el nombre del servidor en el que hara el deployment, el nombre de la DB, etc.

En nuestro caso, nuestra primera aplicación la llamaremos 'proyecto1'. Ahora aplicaremos un deprec para preparar nuestra aplicación. Debemos de tener muy en cuenta que para la mayoria de los comandos que ejecutaremos a continuación es recomendable estar parados en el directorio de nuestra aplicación, que para nuestro caso es '/home/luis/proyecto1'.

#Nos ubicamos en la carpeta raiz de nuestro proyecto
cd /home/luis/proyecto1
# Aplicamos deprec a nuestro proyecto
deprec --apply-to . --name proyecto1 --domain www.proyecto1.com


Esto nos generará el archivo 'config/deploy.rb'. Notese que '--name proyecto1' esta indicando el nombre del proyecto, y '--domain www.proyecto1.com' indica el nombre del dominio por el que será reconocido este proyecto cuando este en producción. Estos parametros son importante porque son utilizados para generar el archivo 'config/deploy.rb'.

Hay que tener en cuenta tambien que el dominio 'www.proyecto1.com' no esta registrado o no existe para nuestra maquina, por lo que tenemos que indicarle cual es la dirección a la que resolvera ese dominio, y esto lo podemos hacer de dos maneras.

Primero, si tenemos un servidor de nombres (DNS) interno que nos resuelve dominios internos o locales, podemos agregar el registro de este dominio apuntando a la IP del servidor de producción, o tambien podemos hacer esto si en caso queremos que nuestra aplicación sea de acceso público en Internet, pero tendriamos que hacerlo con nuestro proveedor de Internet y comprar el dominio en (en caso no lo tengamos). Este es material de otro tutorial. :D

Segunda opción, ya que no tenemos DNS interno y estamos solo realizando aunj pruebas, lo que haremos es decirle a la maquina cliente que www.proyecto1.com apunte a 192.168.0.10 el cual es nuestro servidor de producción en el que instalamos Ubuntu con OpenSSH al principio de este tutorial. Esto lo realizamos ingresando este registro en el archivo '/etc/hosts', y nos debería de quedar algo similar al archivo que se muestra a continuación.

192.168.0.10 proyecto1 www.proyecto1.com

Ahora si, para probar podemos hacer una simple prueba de ping a www.proyecto1.com, y si responde es porque llevamos buen camino. Lo mismo deberiamos hacer en el servidor, registrar ese dominio apuntando a esa IP para que el servidor resuelva internamente ese dominio, ya que en un determinado momento (mas adelante se mensionara) es necesita resolverlo.

Demole un vistazo ahora a nuestro archivo 'config/deploy.rb' para ver la configuración. A continueción se muestra un ejemplo de lo mínimo que debería tener para que nos funcione el deployment en nuestro caso, aunque se le pueden agregar mas opciones dependiendo de nuestras necesidades.

require 'deprec/recipes'

# =============================================================================
# ROLES
# =============================================================================
# You can define any number of roles, each of which contains any number of
# machines. Roles might include such things as :web, or :app, or :db, defining
# what the purpose of each machine is. You can also specify options that can
# be used to single out a specific subset of boxes in a particular role, like
# :primary => true.

set :domain, "www.proyecto1.com"
role :web, domain
role :app, domain
role :db, domain, :primary => true
role :scm, domain

# =============================================================================
# REQUIRED VARIABLES
# =============================================================================
# You must always specify the application and repository for every recipe. The
# repository must be the URL of the repository you want this recipe to
# correspond to. The deploy_to path must be the path on each machine that will
# form the root of the application path.

set :user, "luis"
set :application, "proyecto1"
set :deploy_to, "/var/www/apps/#{application}"
#set :svn_root, "#{deploy_to}/repos"
#set :svn_repos, "#{svn_root}"

# XXX we may not need this - it doesn't work on windows
# XXX set :user, ENV['USER']
set :repository, "svn+ssh://#{user}@#{domain}#{svn_root}/#{application}/trunk"
set :rails_env, "production"

# Automatically symlink these directories from current/public to shared/public.
# set :app_symlinks, %w{photo, document, asset}

# =============================================================================
# APACHE OPTIONS
# =============================================================================
set :apache_server_name, domain
# set :apache_server_aliases, %w{alias1 alias2}
set :apache_default_vhost, false # force use of apache_default_vhost_config
# set :apache_default_vhost_conf, "/etc/httpd/conf/default.conf"
# set :apache_conf, "/etc/httpd/conf/apps/#{application}.conf"
set :apache_conf, "/usr/local/apache2/conf/apps/#{application}.conf"
# set :apache_ctl, "/etc/init.d/httpd"
set :apache_proxy_port, 8010
set :apache_proxy_servers, 4
# set :apache_proxy_address, "127.0.0.1"
# set :apache_ssl_enabled, false
# set :apache_ssl_ip, "127.0.0.1"
# set :apache_ssl_forward_all, false
# set :apache_ssl_chainfile, false


# =============================================================================
# MONGREL OPTIONS
# =============================================================================
set :mongrel_servers, apache_proxy_servers
set :mongrel_port, apache_proxy_port
# set :mongrel_address, apache_proxy_address
set :mongrel_environment, "production"
set :mongrel_config, "/etc/mongrel_cluster/#{application}.conf"
# set :mongrel_user, user
# set :mongrel_group, group

# =============================================================================
# MYSQL OPTIONS
# =============================================================================


# =============================================================================
# SSH OPTIONS
# =============================================================================
ssh_options[:keys] = %w(/home/luis/.ssh/id_rsa)
# ssh_options[:port] = 25



Trataremos de explicar este archivo para su mayor comprensión. Primero que todo, este es un archivo en el que se setean variables al estilo Ruby, de la forma " set: variable, 'valor' ".

Al inicio del archivo declararemos primero los roles de los servidores dentro del deployment que haremos. Ahi podemos ver como se setea la variable 'domain' con el valor 'www.proyecto1.com' el cual es puesto automaticamente de acuero a los parametros que le pasamos cuando creamos el archivo 'config/deploy.rb'. Si somos observadores veremos que la variable 'domain' es puesta tambien en los roles web, app y db; los cuales indican que el mismo servidor, es decir en nuestro caso 'www.proyecto1.com', será el servidor web (con Apache y Mongrel), el servidor de aplicaciones (con Subversion), y el servidor de base de datos (con MySQL). En caso tuvieramos servidores diferentes para cada uno de estos roles, deberíamos de indicarselo en este archivo cada rol.

Notemos también un rol nuevo llamado 'scm', el cual no viene en el archivo original, pero que nosotros hemos agregado con el objetivo de configurar el repositorio SVN mas adelante.

Luego veremos una sección en la que declaramos algunas variables requeridas para el deployment, como lo son el nombre de usuario con el cual realizaremos el deployment, que en nuestro caso es el usuario 'deploy'; el nombre de la aplicación, la cual la seteamos en la variable 'application'. Esta variable es muy importante, ya que en base a este nombre se realizaran muchas cosas, como por ejemplo, decirle al Apache a donde estará nuestra aplicación, la cual la definimos en la variable 'deploy_to'.

Otras variables importantes son también la ruta del repositorio SVN la cual es construida automaticamente utilizando el nombre de usuario, el dominio y la ruta de la aplicacion.

Luego tenemos la sección en la que configuramos ciertos parámetros del Apache y de los correspondiantes Virtual Host. Aqui definimos el nombre del dominio con el que Apache reconocera el Virtual Host, que en este caso es el mismo de la variable 'domain'. También en esta sección le indicamos a Apache que no utilize su archivo de configuración predeterminado. Le definimos también la ruta a donde creara los archivos de configuración de los ViertualHost de cada aplicación. Y por último, le indicamos el número del puerto pordonde realizara el cluster con Mongrel y asi también le definimos el numero de nodos dentro del cluster. Esto últimos es importante, ya que en nuestro primer deployment le hemos puesto que comienze del puerto 8010 y que genere 4 nodos dentro del cluester, por lo que posiblemente los genere los otros tres en el puerto 8011, 8012 y 8013. Hay que tener en cuenta que para nuestro segundo deploymente tendremos que cambiar este rango de puertos y asi sucesivamente, por ejemplo, asignarle el puerto 8020, y para nuestro tercer deployment asignarle el puerto 8030 y asi sucesivamente para nuestras n aplicaciones.

Vemos también la sección en la que se definen parametros para Mongrel en la que utilizaremos las opciones definidas anteriormente en Apache para que coincidan el número de nodos en el cluester y los puertos, asi como también le definimos la ruta en la que se colocarán los archivos de configuración del cluster Mongrel para cada aplicación.

Y por último tenemos la sección en la que definimos la ruta en la que se encuentran nuestras llaves SSH para conectarnos al servidor.

Antes de continuar, debemos de copiar nuestra llave de autenticación hacia el servidor para que podamos estar autorizados de entrar via SSH hacia él, y para ello debemos de tener generadas nuestras llaves del tipo RSA, que en nuestro caso se encuentran en la carpeta '/home/luis/.ssh/' tal y como lo hemos configurado en el archivo 'config/deploy.rb'.

Si no tenemos generadas nuestras llaves aún, entonces debemos generarlas con el comando 'ssh-keygen' y aceptar la ruta predeterminada que nos suguiere, la cual es en el home directory de nuestro usuario, y también debemos de presionar la tecla ENTER para dejar en blanco la frase de paso (passphrase).

# Generamos las llaves de autenticación
ssh-keygen -t rsa

Ahora si, una vez generadas las llaves ya podemos copiarlas hacia el servidor.

# Copiamos nuestra llave al servidor
cap setup_ssh_keys

Ahora tenemos que instalar todo lo necesario en el servidor, aunque esta parte devería de ir en el capítulo "1.0 PREPARANDO EL SERVIDOR", se ha decidido colocarla aqui, ya que seguimos el orden que llevan otros blogs en Internet que hablan sobre esto.

Para preparar el software necesario en el servidor es necesario ejecutar el comando 'install_rails_stack', el cual instalará apache, rubygems, mongrel, rails, etc.

# Instalamos el Software necesario en el servidor
cap install_rails_stack

También tenemos que hacer un paso en el que se crea el grupo 'deploy' en el servidor en caso no este, y se agrega al usuario 'deploy' a este nuevo grupo, con el objetivo que este usuario pueda tener acceso a algunas carpetas, como por ejemplo donde se encuentra la configuración de Apache y la de Mongrel, para que puedan ser colocados los archivos de configuración de estos servicios por cada aplicación que realizemos.

# Crea el grupo en el servidor y agrega al usuario a este grupo
cap setup_user_perms

Tenemos también que hacer que el servidor Apache apunte a la carpeta en la que se colocarán los archivos de configuración para este servicio de cada aplicación de la que hagamos el deployment, que en nuestro caso será la carpeta '/usr/local/apache2/conf/apps/' en el servidor.

# Configura apache
cap setup_apache

Hay que tener en cuenta también que para que Apache reconozca los Virtual Host, hay que agregar una línea en el archivo '/usr/local/apache2/conf/httpd.conf' en el servidor. La línea debe ser similar a la que se muestra a continuación.

NameVirtualHost *:80

También tenemos que hacer que Mongrel sepa donde ir a traer los archivos de configuración de cada aplicación.

# Configura Mongrel Cluster
cap setup_mongrel_cluster_path

Una vez hecho todo esto, ya podemos comenzar a configurar en el servidor aspectos especificos de nuestra aplicación, hasta el momento hemos configurado aspectos del servidor pero auxiliandonos del archivo 'config/deploy.rb' para realizar estas configuraciones remotas.

Luego tenemos que crear la estructura que utilizara Capistrano en el servidor para nuestra aplicación.

# Crea estructura de Capistrano en el servidor para nuestra aplicación
cap setup

Ahora debemos de crear el archivo de configuración para el Virtual host de nuestra aplicación en el servidor.

# Crea archivo de configuración de Virtual host
cap configure_apache

Luego también debemos de crear el archivo de configuración correspondiente al cluster de Mongrel para nuestra aplicación en el servidor.

# Crea archivo de configuración para el Mongrel cluster
cap configure_mongrel_cluster

Una vez hecho esto, crearemos el repositorio SVN en el servidor haciendo un import de nuestra aplicación hacia el servidor y creando en nuestra máquina una carpeta de trabajo SVN, en nuestro caso llamada '/home/luis/proyecto1_machine'. Lo explicado anteriormente lo realizaremos con la tarea 'setup_scm' la cual nos creará una carpeta de trabajo mencionada anteriormente a la que debemos de cambiarnos para realizar nuestro primer deployment.

# Crea el repositorio SVN y realiza el primer import hacia el servidor
cap setup_scm

Ahora si, a realizar nuestro primer deployment..., si deseamos realizar el deployment con migraciones sustituiremos la tarea 'deploy' por 'deploy_with_migrations'.

# Nos cambiamos a nuestro directorio de trabajo SVN
cd ../proyecto1_machine
# Realiza el deployment. Podemos poner 'deploy_with_migrations' en lugar de solo 'deploy'.
cap deploy

Luego debemos reiniciar Apache para que cargue la configuración del Virtual host de nuestra aplicación.

# Reiniciamos Apache
cap restart_apache

Solo nos queda abrir nuestro navegador web y verificar la URL de nuestro primer proyecto, es decir www.proyecto1.com.


2.3 Preparando nuestro segundo deployment
Primero que nada, debemos de tener nuestra segunda aplicacion queremos hacer el otro deployment, y para seguir un estandard la llamaremos 'proyecto2' la cual estará ubicada en '/home/luis/proyecto2'.

Asi mismo, tal y como lo realizamos en el primer deployment, debemos de configurar los DNS's necesarios para que nuestro dominio, ahora llamado 'www.proyecto2.com' apunte a la misma IP 192.168.0.10 del servidor.

Luego seguimos la misma secuencia del primer deployment.

#Nos ubicamos en la carpeta raiz de nuestro proyecto 2
cd /home/luis/proyecto2
# Aplicamos deprec a nuestro proyecto 2
deprec --apply-to . --name proyecto2 --domain www.proyecto2.com

Luego de esto solo debemos de configurar el archivo 'config/deploy.rb' de nuestro segundo proyecto similar al del primero, pero teniendo mucho cuidado en el puerto que le asignaremos para el Mongrel cluster. Si el anterior era el puerto 8010, entonces en esta segunda configuración podriamos asignarle a partir del puerto 8020, para que tomar el 8020, 8021, 8022 y 8023.

Posteriormente continuamos con la secuencia de comandos para la configuración correspondiente y el deployment.

# Crea estructura de Capistrano en el servidor
cap setup
# Crea archivo de configuración de Virtual host
cap configure_apache
# Crea archivo de configuración para el Mongrel cluster
cap configure_mongrel_cluster
# Crea el repositorio SVN y realiza el primer import hacia el servidor
cap setup_scm
# Nos cambiamos a nuestro directorio de trabajo SVN
cd ../proyecto2_machine
# Realiza el deployment
cap deploy
# Reiniciamos Apache
cap restart_apache
# De ser necesario, podemos reiniciar también el cluester de Mongrel
cap restart_mongrel_cluster


Y nuevamente abrimos nuestro navegador web y verificamos la URL de nuestro segundo proyecto, es decir www.proyecto2.com.

Asi podemos realizar, a partir del paso 2.3 en adelante, para nuestras n aplicaciones a las cuales queremos hacer un deployment. Cabe mencionar que este post es un extracto de muchos otros post que explican esto mismo, pero que no hacen enfasis en algunos puntos los cuales al moemnto de realizar las pruebas fueron surgiendo.

Agradecimientos también al Lic. Douglas Galindo por impulsar y apoyar la filosofía del Open Source.

Luis A. Molina

Este tuto ha sido probado en Ubuntu 7.0.4 con los siguientes gems:
  • deprec 1.7.1
  • rails 1.2.2
  • activerecord 1.15.2
  • net-ssh 1.1.1
  • net-sftp 1.1.0
  • rake 0.7.3
  • termios 0.9.4
  • capistrano 1.4.1