Blog 

Continuous Delivery en Ruby On Rails

Con GitFlow, Múltiples entornos y Capistrano

Continuous Delivery en Ruby On Rails

En nuestros artículos anteriores ya hemos visto varias herramientas para ya estar a este nivel: hacer entregas continuas de nuestra aplicación, de forma incremental, ágil y asegurando la calidad.

Ya tenemos la herramienta para desarrollar aplicaciones rápidamente (Ruby On Rails), la plataforma cloud que la soporte (AWS) y la herramienta para hacer las entregas de esta aplicación en AWS o cualquier cloud que elijas (Capistrano). Te recomiendo ver nuestro artículo que habla de esto previamente: Despliegue de aplicaciones Ruby On Rails

Con todos esos elementos ya podemos tener una aplicación disponible en Internet, pero surgen las siguientes problemáticas:

  • Si estoy continuamente subiendo versiones de la aplicación a la nube, ¿que pasará cuando suba un error?, ¿pierdo la operación?
  • Si estoy trabajando con más de una persona, en la misma aplicación, en el mismo repositorio remoto (Github o BitBucket, asumiremos que tienes conocimiento de esto, si no es así, comenta y pensaremos en sacar un artículo), que pasará cuando termine mi trabajo y quiera subirlo, ¿subiré parte de su trabajo incompleto que se encuentra en el repositorio?
  • Si estoy trabajando en una nueva funcionalidad, muy grande, y en producción surge un error crítico que debo resolver urgentemente, ¿debo corregirlo y subir mi trabajo que aún no está terminado?

Todos estos son graves problemas, más aún si no puedes perder la operación mucho tiempo (es esperable que nadie tenga que hacerlo, nunca). Bueno, aquí te voy a proponer el marco de trabajo que se usa en Garage Labs para mantener el caos controlado, a un nivel amigable, porque claro es imposible deshacerse de él y a todos nos hace falta de vez en cuando.

El Marco de trabajo

Manejar más de un entorno “productivo”

Las comillas dicen mucho, no vamos a tener 2 entornos productivos obvio, es quemar la plata. Pero lo que si podemos tener es otro entorno que simule ser uno, pero que consuma muchos menos recursos en AWS, a este lo llamaremos Staging, más que nada porque Capistrano usa este nombre, en realidad podría tener el que quieras.

Pero, ¿como se resuelve esto en la práctica?, Ruby On Rails claro. Te explico; para eso voy a utilizar la misma aplicación del post de Capistrano, mira:

Continuous Delivery en Ruby On Rails

Esta es la estructura básica de una aplicación en RoR. Luego en la carpeta “config”, tenemos lo que aparece en la siguiente figura.

Continuous Delivery en Ruby On Rails

Aquí dentro, entre muchas otras cosas, encontramos la carpeta environments, y dentro 3 de ellos:

  • developement
  • production
  • test (aquí se ejecutan tus pruebas automatizadas, pero no es material de este post)
Continuous Delivery en Ruby On Rails

Cómo te explique antes, vamos a simular productivo, por lo que crea una copia exacta de production.rb y llámala staging.rb. 

Con esto tenemos el archivo de configuración del entorno. 

Para la base de datos:

Continuous Delivery en Ruby On Rails

En la misma carpeta config, te encuentras el archivo database.yml, en donde hacemos todas las definiciones de base de datos que necesitemos en nuestros entornos.

Ahora hay que editar ese archivo, bastaría con copiar development y cambiar el nombre de la base de datos. Esto se hace porque productivo normalmente usa una configuración y un servicio de base de datos auto-administrada (RDS de AWS por ejemplo). Lo que queremos aquí es mantener la base de datos en la misma maquina, con la menor cantidad de configuraciones, además RDS es caro.

Continuous Delivery en Ruby On Rails

Ahora, el siguiente paso, en la misma carpeta se define un archivo (secrets.yml) para manejar la secret_key_base de Rails para la encriptación. Es bueno que definas una por entorno:

Continuous Delivery en Ruby On Rails

Con esto ya tendremos los entornos necesario para implementar el marco de trabajo.

Configurar los entornos de despliegue en Capistrano

Para realizar los despliegues de nuestras aplicaciones, Capistrano utiliza en especial 2 elementos:

  • La carpeta /config/deploy/ y los archivos que se encuentran ahí, que por defecto son staging.rb y production.rb, para los distintos entornos.
  • El archivo /config/deploy.rb, que ya vimos en la publicación anterior.

Los archivos de la carpeta deploy, deben verse de la siguiente forma c/u con su respectiva maquina:

Continuous Delivery en Ruby On Rails

Donde:

  • usuario, es el que se definió en la instancia de AWS
  • IP-SERVIDOR, es la ip o dns de la instancia de AWS. A veces se construyen asi: ec2–IP.ZONA.compute.amazonaws.com
  • llave_pem_del_sevidor.pem, es la llave que se descarga al crear una instancia en amazon y necesaria para hacer los despliegues.

Luego, el archivo deploy.rb, que por cierto vimos en el post de Capsitrano, te dejo el gist:

Aquí hay 2 cosas importante que notar:

set :rails_env, fetch(:stage), es la línea que se encarga indicar a capistrano cual es el entorno del despliegue. En este sentido, cuando pongamos, por ejemplo, en la términal cap production deploy, Capistrano ira a buscar el archivo que se encuentra en /config/deploy/production.rb, y desplegará en ese servidor, con el entorno indicado, por otro lado y tambíen como ejemplo, si ponemos en el terminal cap staging deploy, irá /config/deploy/staging.rb y a su respectivo servidor.

Antes de seguir es necesario hacerle algunos cambios al gist:

Las líneas que se encargan de la base de datos y los assest, cambiarlas por:

 execute :rake, “db:create RAILS_ENV=#{fetch(:stage)}”
 execute :rake, “db:migrate RAILS_ENV=#{fetch(:stage)}”
 execute :rake, “db:seed RAILS_ENV=#{fetch(:stage)}”
 execute :rake, “assets:precompile RAILS_ENV=#{fetch(:stage)}”

Con esto la base de datos y los assets se crearán y compilarán en los entornos respectivos al deploy.

Y en la parte de selección de ramas, hacer este cambio de momento, cuando entremos a GitFlow se entenderá (en la siguiente sección):

Continuous Delivery en Ruby On Rails
Selección de rama según entorno

GitFlow para organizar el trabajo individual o en equipo

En Garage Labs consideramos que la forma más optima de organizar el trabajo a la hora de desarrollar, es el propuesto en este articulo: A successful Git branching model. No abordaremos tantos detalles, ya que el artículo se explica prácticamente solo y tocaremos las partes que aquí nos interesan.

Para cualquier persona que haya trabajado con Git en algún momento, notara que la iniciar su desarrollo se crea por defecto la rama master. Aquí consideraremos a esa rama como la contenedora del código que ira a producción. Ademas necesitaremos otra rama extra, para desarrollar el código que ira a la maquina de staging, y esa llama la llamaremos develop.

git checkout -b develop

De esta forma ya tenemos la estructura base para el GitFlow.

Pero, ¿cómo sabemos que el código de develop ira a staging y el de máster a producción?

La línea (branch = fetch(:stage) == :staging ? ‘develop’ : ‘master’) de la imagen “Selección de rama según entorno”, es la encargada de hacer la magia. Hace fectch del :stage que proviene del comando “cap staging (o production) deploy” y a partir de ahí manda una rama u otra.

Entonces tenemos:

  • master: que contiene el código estable y probado, que va a producción
  • develop: que contiene el código que esta siendo desarrollado y que deber ser probado antes de pasar a producción.

A partir de aquí aparecen 2 casos que son necesarios de abordar:

  • ¿Qué pasa cuando aparece un bug en producción?
  • ¿Qué pasa cuando queremos desarrollar una nueva funcionalidad?

 Para esto tenemos la siguientes respuestas:

  • features: son ramas que salen a partir de develop (git checkout -b feature-1.1 develop) y contienen códigos pertenecientes a una nueva funcionalidad, que no puede tocar develop hasta que termine de ser desarrollada.
  • hotfix: son ramas que salen desde master (git checkout -b hotfix-1.1.1 master) que hacen referencia a errores nacidos en producción y deben ser corregidos con urgencia. Errores en develop son simplemente fix.

¿Pero cómo hacemos que el paso a producción develop sea ordenado y probado?, para eso existen los releases, que son un tipo de rama que contienen un conjunto de funcionalidades nuevas y probadas que se encuentran en develop y están en condiciones para pasar a master (producción). De esta forma aseguramos un correcto flujo sin afectar calidad.

Ejemplos y casos prácticos

Supongamos un software X que se encuentra en producción, es su versión 2.1.2.

De pronto un cliente te notifica que se ha presentado un error, en la url productiva, a la hora de iniciar sesión. Los pasos a seguir son:

  • git checkout master (verificar que estamos parados en producción)
  • git pull origin master (SIEMPRE hay que verificar si hay nuevos cambios en producción si trabajas en equipo).
  • git checkout -b hotfix-2.1.3 (aquí se aplicaran todas las acciones correctivas para resolver el bug).
  • git add ., git commit, git push (lo típico, no entraré en detalle, a no ser que sea necesario).
  • git checkout develop && git merge – -no-ff hotfix-2.1.3 (se pasa el código del bug corregido a develop, para probar)
  • cap staging deploy (se sube el bug resuelto a staging para verificar su corrección)
  • git checkout master && git merge — -no-ff hotfix-2.1.3 (se pasa finalmente el error corregido a master finalmente para subirlo a producción)
  • cap production deploy

Ahora supongamos el mismo software X, ahora el mismo cliente, en un ataque creativo, te propone una funcionalidad que le interesa desarrollar. Los pasos a seguir serían:

  • git checkout develop (irse a develop, las nuevas funcionalidades salen de ahí)
  • git pull origin develop (SIEMPRE hay que verificar si hay nuevos cambios en develop si trabajas en equipo).
  • git checkout -b feature-2.2.3 (aquí se aplicará todo el desarrollo de la nueva funcionalidad).
  • git add ., git commit, git push. Esto las veces que sea necesario
  • git checkout develop && git merge — -no-ff feature-2.2.3 (se pasa la nueva funcionalidad a develop, para probar)
  • cap staging deploy (se sube la nueva funcionalidad a staging y si esta todo correcto, se pasa al siguiente paso).
  • *Vamos a suponer un release solo con esta nueva funcionalidad
  • Desde develop: git checkout -b release-2.2.3
  • git checkout master && git merge — -no-ff release-2.2.3 (se pasan las nuevas funcionalidades a master, y se aumenta la versión del software, a través de tags).
  • cap production deploy
  • y LISTO! todo en producción y funcionando.

Conclusiones

Con esto ya les debería bastar para mantener un flujo estable en sus desarrollos y minimizar el riesgo ante posibles errores. Como les mencioné más arriba, es inevitable el caos, solo hay que aprender a convivir con él.

Luego se vendrán mas problemas a solucionar:

  • ¿Qué pasa si mi software empieza a ser usado por muchos usuarios y la capacidad del servidor ya no esta dando abasto? ¡Compremos servidores más grandes!… o no?
  • ¿Qué pasa si mi software tiene una carga de usuarios muy inestable con peaks de uso impredecibles? Aquí sería una perdida de recursos aprovisionar máquinas gigantes que no se están usando todo el tiempo.

Estas dudas las resolveremos en siguientes artículos. 

Ante la duda, solo pregunta.

Publicado el 26 julio, 2020

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 AYUDAMOS A IMPLEMENTAR SOFTWARE EN TU EMPRESA?

Estaríamos encantados de ayudarte

Garage Labs

contacto@garagelabs.cl

+569 8741 1528 | +569 3537 2885

Badajoz 100, Las Condes

Wework. Sánchez Fontecilla 310, Las Condes