Blog 

Despliegue de aplicaciones Ruby On Rails

Publicado el 9 diciembre, 2019

Con Capistrano y Amazon EC2

En publicaciones anteriores, te hemos hablado en varias oportunidades de opciones cloud para el alojamiento de nuestras aplicaciones. En Garage Labs preferimos a Amazon Web Services (en adelante AWS), que es por lejos lo mejor que existe en el mercado.

Existen muchas razones por la que debes elegir a este proveedor para aprovisionar infraestructura para tus aplicaciones, que no las definiremos aquí, pero solo mencionaremos las siguientes:

  • Costo, pagas solo por lo que usas.
  • Escalabilidad, no necesitas aprovisionar grandes cantidades de infraestructura, ya que crece contigo.
  • Performance, una gran cantidad de herramientas que te ayudan a optimizar el uso de recursos.
  • Respaldo de un gigante de la informática.

Para esta publicación utilizaremos tan solo el servicio de Elastic Cloud Compute (EC2), para el levantamiento de instancias que hagan de servidor de aplicaciones. Por esta oportunidad también levantaremos el servicio de base de datos y almacenamiento de archivos en la misma instancia, pero dejemos en claro que AWS posee servicios auto-administrados de bases de datos (RDS) y de almacenamiento (S3), que podemos explicar su funcionamiento en siguientes publicaciones y además mostrar como “conversan” los distintos servicios.

Capistrano

Es una herramienta de código abierto para ejecutar scripts en múltiples servidores; su uso principal es la implementación de aplicaciones web. Automatiza el proceso de hacer que una nueva versión de una aplicación esté disponible en uno o más servidores web, incluidas las tareas de soporte como el cambio en la base de datos, ejecución de tareas, entre otros. Ademas permite el despliegue en distintos proveedores simultáneamente.

Gracias a esta herramienta podemos poder empezar a pensar en lo que es ofrecer el servicio de entrega continua o mas adelante integración continua.

A lo nuestro

Bueno no te mareo más con definiciones y explicaciones, si quieres más información búscala por ti mismo, no es por lo que estas leyendo esto, ¿no?

En esta publicación vamos a crear una aplicación con Ruby On Rails básica, con conexión a una base de datos PostgreSQL, le instalaremos la gema de Capistrano y configuraremos para el despliegue en nuestra instancia de AWS que le instalaremos todo lo necesario mediante SSH.

  • OJO: Puedes usar una cuenta gratuita de AWS, solo necesitas una tarjeta de crédito. No te hará cobros extras a no ser que te pongas a levantar servicios como loco.

Creando la aplicación

Partamos creando una nueva aplicación en rails, como cualquier otra, utilizando el comando:

rails new test-capistrano

Eso nos creará una nueva aplicación, vamos ahora a agregar las gemas necesarias en el archivo Gemfile. Las cuales, son las siguientes:

gem 'pg', '~> 0.18.4'

group :development, :test do
# Capistrano para despliegue de aplicación en servidor remoto
gem ‘capistrano’, ‘~> 3.0’
gem ‘capistrano-rvm’
gem ‘capistrano-bundler’, ‘1.1.1’
gem ‘capistrano-rails’, ‘1.1.3’
gem ‘capistrano-sidekiq’, github: ‘seuros/capistrano-sidekiq’ # No es necesaria
end

Obvio las versiones responden al momento en el que se desarrolló este post. La primera gema tiene que ver con PostgreSQL, que será nuestro motor de base de datos. Las primeras 4 gemas con las más importantes para poder utilizar Capistrano con Ruby On Rails y RVM (no recomiendo Rbenv). La última gema no la utilizaremos, pero la pongo, para mostrar que es posible ejecutar varios tipos de script con Capistrano, en este caso sidekiqNo se olviden de eliminar la gema de sqlite.

Luego necesitamos crear un archivo de configuración para la base de datos, puede usar el siguiente como ejemplo:

default: &default
adapter: postgresql
encoding: unicode
username: nombre_de_usuario
password: contraseña
pool: 5
timeout: 5000
development:
<<: *default
host: localhost
database: db/development_capistrano
test:
<<: *default
host: localhost
database: db/test_capistrano
production:
<<: *default
host: localhost
database: production_capistrano
view raw database.yml hosted with ❤ by GitHub

Luego correr los típicos comandos:

bundle install
rake db:create

Y estarán instaladas nuestras gemas y creadas nuestras bases de datos. Puedes ejecutar el comando rails s en la consola para verificar todo y luego entrar a localhost:3000 para comprobar el trabajo bien hecho.

Agregando contenido a la web

Vamos a crear una pequeña aplicación rápidamente y sin interfaz decente alguna. Solo queremos mostrar la ejecución de script con Capistrano.

Generemos un scaffold de Posts, nada complejo, típico blog.

rails g scaffold post name:string body:text

Luego generemos los cambios en la base de datos, ejecutando las migraciones, que el scaffold creo por nosotros.

rake db:migrate

Listo! Aplicación rails, que crea posts en base de datos Postgres.

Finalmente debemos crear el archivo secrets.yml en la carpeta config/, donde debemos crear las variables del secret_key_base para nuestra aplicación. Asumiré que tienes conocimiento de este archivo y que sabes como crearlo y darle las variables (rails secret RAILS_ENV=development o test o production).

Configurando Capistrano

Pueden encontrar la documentación de la gema aquí. Yo les daré algunos tips para empezar rápidamente.

En pasos anteriores ya dejamos instalada la gema de Capistrano, ahora solo queda correr los instaladores y empezar a configurar. Ejecutamos el siguiente comando:

bundle exec cap install

Y tendremos una salida como la siguiente:

Agreguemos un par de lineas a los archivos creados:

Luego debemos meternos en config/deploy.rb para configurar todas las tareas de despliegue. Importante: debes tener un repositorio en github (o el de tu preferencia), con una llave SSH registrada y con la capacidad de hacer push a través de SSH. Asumiré que esto último sabes como manejarlo, de no ser así, nos plantearemos la necesidad de hacer otro tutorial de como realizar la tarea, pero es tan corta y fácil, que no sé si amerita uno. De todos modos te dejo uno de Github.

Bueno, ahora el deploy.rb:

lock "~> 3.11.0"
# Es el stage de la aplicación, obviamente producción
set :stage, 'production'
# Nombre de la aplicación
set :application, "test_capistrano"
# URL de la aplicación en git (github, bitbucket, etc). DEBE SER SSH
set :repo_url, "git@github.com:danielVargas/test-capistrano.git"
# Default branch is :master
ask :branch, `git rev-parse –abbrev-ref HEAD`.chomp
# Indica el directorio donde se dejará la aplicación
set :deploy_to, "/var/www/html/test_capistrano"
# Variables de entorno necesarias para el despliegue con RVM
set :rvm_type, :user
set :rvm_ruby_version, '2.5.3'
set :rvm_binary, '~/.rvm/bin/rvm'
set :rvm_bin_path, "$HOME/bin"
set :default_env, { rvm_bin_path: '~/.rvm/bin' }
set :user, "ubuntu"
set :use_sudo, false
set :deploy_via, :copy
#############################################################
set :format, :pretty
set :rails_env, fetch(:stage)
# Default value for :pty is false
set :pty, false
# Número de releases de la aplicación que capistrano guardará como respaldo
set :keep_releases, 3
# Indica directorios de la aplicación que se deberán conservar con cada deploy nuevo
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system', 'public/uploads')
# Variables de entorno para passenger
set :passenger_environment_variables, { :path => '/usr/lib/ruby/vendor_ruby/phusion_passenger/bin:$PATH' }
set :passenger_restart_command, '/usr/lib/ruby/vendor_ruby/phusion_passenger/bin/passenger-config restart-app'
namespace :deploy do
## Tarea que pregunta por la rama desde la cúal se hará el despliegue
desc "Make sure local git is in sync with remote."
task :check_revision do
on roles(:app) do
unless `git rev-parse HEAD` == `git rev-parse origin/master`
puts "WARNING: HEAD is not the same as origin/master"
puts "Run `git push` to sync changes."
exit
end
end
end
# Tarea que se ejecuta luego de el despliegue, para:
# – Instalar gemas
# – Dar permisos de directorios
# – Crear bases de datos y correr migraciones
# – Compilar los assets en producción
# – Reiniciar el servidor
# Toma en cuenta que aqui puedes correr practicamente cuaquier comando que se te ocurra
desc 'Restart application'
task :restart do
on roles(:app), in: :sequence, wait: 5 do
within release_path do
execute :bundle, 'install'
execute :chmod, '777 '+release_path.join('tmp/cache/').to_s
execute :chmod, '777 '+release_path.join('log/').to_s
execute :rake, 'db:create RAILS_ENV=production'
execute :rake, 'db:migrate RAILS_ENV=production'
execute :rake, 'assets:precompile RAILS_ENV=production'
execute 'sudo service apache2 restart'
end
end
end
## Callbacks de las tareas definidas anteriormente
before :starting, :check_revision
after :finishing, :restart
end
view raw deploy.rb hosted with ❤ by GitHub

Configurado todo esto, es el momento de empezar a usar Amazon Web Services (AWS). Luego volveremos a la aplicación a configurar lo último que necesitamos antes de salir a producción.

Creando y configurando instancias en AWS

Para poder usar los servicios de AWS sin gastar dinero, puedes moverte por la capa gratuita que ellos proveen. Puedes ver la especificación de servicios gratuitos aquí. Para usarlos solo debes crear una cuenta en el proveedor, asociar una tarjeta de crédito (obvio, te van a cobrar si te pasas del free tieres una economía de escala multinacional, que más esperas). Si te mantienes dentro de los intervalos de lo que te regalan, puedes usar sus servicios gratis por un año.

El servicio de AWS que usaremos es EC2 (Elastic Compute Cloud), el servicio de instancias. Para ello seleccionamos el servicio en el menú.

Luego una vez dentro del servicio, lanzar una nueva instancia.

En el siguiente paso, se debe seleccionar un AMI, que son imágenes de instancias para no partir desde 0. Nosotros recomendamos la de Ubuntu, que es fácil de configurar y se encuentra dentro del free tier. a

Luego, es necesario elegir el tipo de instancia, ojo selecciona solo aquellas que se encuentre en el free tier, te recomendamos, t2.micro:

En el paso 3, deja todo como esta, de momento no nos importan esas configuraciones. Generalmente tienen que ver con las redes y monitoreo.

En el paso 4, Almacenamiento, deja todo tal cual, los 8 gb de almacenamiento de General Purpose SSD.

Luego en el paso 5, agrega algunos Tags si lo consideras necesario. Sirven para diferencias tus recursos. Muy útil si quieres diferencias las cuentas de tus clientes para hacer cobros.

En el paso 6, es muy importante abrir un puerto HTTP, para que la web pueda ser accedida. El SSH es necesario de igual forma, para que podamos hacer el deploy. Te recomiendo en el source, seleccionar tu propia IP, por temas de seguridad.

Y en el paso 7, puedes hacer una revisión de todos los pasos anteriores, y finalmente presionar “Launch”, para que la instancia se inicie.

Se pedirán generar un archivo .pem, descárgalo y guárdalo, es la única forma de acceder a la instancia. Si lo pierdes, quedará inaccesible.

Listo, ya tenemos nuestra instancia iniciada en AWS. En el menu “Instances” puedes verla ya en funcionamiento.

Instalación de entorno de producción

Con el archivo .pem, conseguido a partir del proceso anterior, posiciónate en la carpeta contenedora mediante el comando “cd”. Si es la primera vez que se conecta a la instancia, es necesario entregar los permisos respectivos al archivo .pem para realizar la conexión, a través del siguiente comando:

chmod 400 mi-llave.pem

Con este archivo, y los permisos respectivos, ingresar el siguiente comando en un terminal, para ingresar por SSH a la instancia:

ssh -i “cliente.pem” ubuntu@ec2-xx-xxx-xxx-xx.us-east-2.compute.amazonaws.com

Donde en las “x” se debe escribir el número IP.

Instalando RVM y Ruby

Primero debes obtener versión más reciente de RVM, mediante el siguiente comando:

curl -L https://get.rvm.io | bash -s stable — ruby

En este paso puede ocurrir algún problema en la instalación de RVM causado por GPG. Para solucionarlo, instale gpg2 con el comando sudo apt install gnupg2 y luego agregue la nueva llave pública que se indica en el output del error con el comando gpg2 — recv-keys [nueva llave pública].

Debe cerrar sesión con SSH y volver a ingresar para hacer uso de RVM.

RVM permite administrar diversas versiones de ruby y conjuntos de gemas para el lenguaje.

Para el caso de estas versión se utilizará la versión 2.5.3 del lenguaje, por lo que se deben ingresar los siguientes comandos en el terminal:

rvm install ruby-2.5.3

rvm — default use ruby-2.5.3

Finalmente como gestor de dependencias, se utiliza el más conocido para Ruby, el bundler, mediante el siguiente comando:

gem install bundler — no-rdoc — no-ri

Finalmente instala Node.js, con el siguiente comando:

sudo apt-get install -y nodejs &&
sudo ln -sf /usr/bin/nodejs /usr/local/bin/node

Instalando Ruby On Rails

Para efectos de estar versión del tutorial se utilizará la versión 5.2.3 del framework.

gem install rails -v 5.2.3

Instalando Apache

Primero debes actualizar los repositorios. Con el siguiente comando:

sudo apt-get update

Luego simplemente instalar Apache con el siguiente comando (van algunos paquetes complementarios):

sudo apt-get install apache2 curl git build-essential zlibc zlib1g-dev zlib1g libcurl4-openssl-dev libssl-dev libapr1-dev libaprutil1-dev libreadline6 libreadline6-dev

Instalando PostgresSQL

Solo necesitas ingresar el siguiente comando:

sudo apt-get install postgresql postgresql-contrib

Ingresar con el usuario postgres, que se crea como súper usuario, para generar otro, que será el encargado de la conexión de la aplicación.

sudo -i -u postgres

Lugo con la sesión iniciada, se debe crear el otro usuario, mediante el siguiente comando:

createuser — interactive

Esta funcionalidad, permite la creación del usuario dinámicamente, como muestra a continuación:

Output

Enter name of role to add: appuser
Shall the new role be a superuser? (y/n) y

Luego también es importante ingresar al cliente de postgres para crear la contraseña del nuevo usuario, de la siguiente forma (para aplicaciones serias, por favor no uses credenciales de este estilo):

psql

\password appuser

Enter pass: appuser

Repeat: appuser

Finalmente se deben instalar las librerías para comunicar la gema de rails con postgres, con el siguiente comando:

sudo apt-get install libpq-dev

Instalando Passenger y el módulo de Apache

Passenger será el encargado de desplegar la aplicación de RoR en el servidor apache, esta comunicación se logra gracias al modulo que instalaremos también. Primero se instalan algunos paquetes complementarios, para que Passenger funcione correctamente:

sudo apt-key adv — keyserver hkp://keyserver.ubuntu.com:80 — recv-keys 561F9B9CAC40B2F7
sudo apt-get install -y apt-transport-https ca-certificates

Posteriormente se agregan los enlaces a los repositorios donde se encuentran Passenger y su módulo:

sudo sh -c ‘echo deb https://oss-binaries.phusionpassenger.com/apt/passenger bionic main > /etc/apt/sources.list.d/passenger.list’

Y se instalan Passenger y el módulo para apache

sudo apt-get install -y libapache2-mod-passenger

Luego se comunica a Apache 2 con el módulo de Passenger y reinicia el servidor:

sudo a2enmod passenger

sudo apache2ctl restart

Finalmente se valida la instalación mediante el siguiente comando (selecciona Apache y Passenger):

sudo /usr/bin/passenger-config validate-install

Es importante considerar que es posible que la validación de Apache falle, de ser así el mismo programa de validación sugerirá la instalación de un componente, copiar el comando, instalar y eso solucionará el problema (sudo apt-get install apache2-dev).

Listo el entorno de producción, ahora para no volver a instalar todo en tus siguientes desarrollos, puedes generar un AMI en AWS (ojo se cobra). Botón derecho en la instancia -> Image -> Create Image.

Configurando los environments de Capistrano

En Capistrano tenemos la posibilidad de configurar varios ambientes de despliegue, por defecto stagingyproduction. Esto en la práctica se utiliza para hacerles despliegues en distintos ambiente para realizar pruebas. Para este tutorial usaremos uno de los que vienen definidos por defecto staging.

El cual debería quedar se la siguiente forma:

# Se definen los distintos roles para el despliegue, que pueden ser distintas instancias de amazon.
role :app, %w{tu_usuario@ec2-tu-ip.us-east-2.compute.amazonaws.com}
role :web, %w{tu_usuario@ec2-tu-ip.us-east-2.compute.amazonaws.com}
role :db, %w{tu_usuario@ec2-tu-ip.us-east-2.compute.amazonaws.com}
# Se definen los distintos servidores, asociados a un rol para hacer el despliegue.
set :user, "tu_usuario"
server "ec2-tu-ip.us-east-2.compute.amazonaws.com", roles: %w{web}
# Se indica la ubicación de la llave.pem descargada desde amazon. Esto para poder hacer la autenticación por SSH
set :ssh_options, {
forward_agent: true,
keys: ["~/.ssh/tu_llave.pem"],
user: 'tu_usuario'
}
view raw staging.rb hosted with ❤ by GitHub

Con todo esto configurado, queda solo el último paso, hacer el despliegue.

Desplegando la aplicación en AWS

Para poder realizar esta tarea, basta con ingresar el siguiente comando en la terminal, posicionada en la raíz del proyecto:

cap staging deploy

Donde “cap” es el comando que llama a Capistrano, “staging” es el entorno definido en la carpeta deploy, y el último es la tarea necesaria para realizar el despliegue.

Si realizaste todo bien. Deberías ver algo como esto:

Donde solo debes presionar “enter”, para iniciar el proceso.

A lo largo del despliegue veras un montón de comandos que el script correrá, solo debes esperar a que termine y que ninguno falle.

Si todo salió bien, deberías ver un mensaje como el siguiente en tu consola:

Quizás tengas algunos problemas haciendo el despliegue, al correr los script para crear y migrar la base de datos, hay algunos métodos para corregir eso en el despliegue, pero como solo es el primero, te conviene más entra a la maquina, en la ruta del proyecto y correrlos tu misme a mano. (rake db:create RAILS_ENV=production)

Pasos finales

Ahora ya tenemos la aplicación desplegada y lista en la maquina, ahora solo queda configurar apache y passenger para que identifiquen la página.

Primero debemos entrar a la carpeta de configuración de apache, agregar un nuevo archivo de configuración para el sitio (o editar alguno que ya exista). Te dejamos un ejemplo para que puedes usarlo como gustes.

<VirtualHost *:80>
ServerName test.com
ServerAlias test.com
ServerAdmin webmaster@localhost
#PassengerAppEnv staging
DocumentRoot /var/www/html/ruta_al_proyecto
#Redirect "/" "https://test.com/"
<Directory "/var/www/html/ruta_al_proyecto">
#DirectoryIndex index.php
Options +Indexes +FollowSymLinks +MultiViews
AllowOverride All
Allow from All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

Una vez configures el dominio y la ruta (no es necesario el dominio, si no tienes uno, puedes acceder por la ip). Debes agregar el sitio al registro de apache con el siguiente comando:

sudo a2ensite test-capistrano.conf

Obviamente reemplaza test-capistrano, por el nombre del archivo que tu hayas elegido.

Luego simplemente recarga los sitios del servidor con el siguiente comando:

sudo service apache2 reload

Luego si pones la ip o el dominio en un navegador, podrás ver tu sitio.

Es posible que tengas algunos problemas si usas la ip, y que cuando entres te siga mostrando el sitio por defecto de apache, y tendrás que deshabilitarlo con el comando sudo a2dissite 000-default.conf.

Es posible que cuando ingreses por primera vez, aparezca un error, de passenger

Eso es porque no se ha configurado correctamente el secret_key_base, eso se hace de la siguiente forma:

  • Ingresa a la instancia mediante SSH
  • Posiciónate mediante el comando cd en la carpeta del proyecto (Ej: /var/www/html/test_proyecto/current/, ojo debe ser current, ya que capistrano tiene esa estructura)
  • Ingresar el comando: bundle exec rake secret RAILS_ENV=production
  • Resulta algo como lo siguiente:
  • Copiar la cadena de texto y pegarla en el secrets.yml (o trabajarla como variable de entorno).
  • Debe ser el entorno de producción:
  • Reinicia el servidor nuevamente

Finalmente puedes entrar al domino o tu ip, en la ruta /posts para verificar tu trabajo. (Puede verificar el nuestra en la url: http://testcapistrano.garagelabs.cl/posts)

Finalmente

Si sigues los pasos que te entregamos tendrás un sitio web corriendo en AWS, y gran parte de la curva de aprendizaje ya la habrás superado. Hay muchos detalles que no abordamos aquí, pero que irás puliendo en medida que vayas avanzando en tus despliegues y conociendo un montón de servicios complementarios del proveedor que pueden potenciar en gran medida tus desarrollos; pero eso ya es materia de otro post. No olvides dejar tus comentarios si lo estimas o contactarnos en Garage Labs si necesitas ayuda.

Categorías

Etiquetas

Te puede interesar

0 comentarios

Enviar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Hablemos

¿Te sumas a la Transformación digital?

Te ayudamos a implementar nuevas tecnologías en tu empresa