BKT en Producción: Scripts Durante el Despliegue

Continuando con la serie BKT en Producción, en este artículo vengo a compartir los scripts de despliegue que se usaban para mandar la aplicación a los entornos en la nube.

Estos son una versión ya mejorada pasada por varios proyectos pero que mantiene la esencia y la base de las primeras versiones.

Mencionaba en un artículo pasado la herramienta Code Deploy. Cuando se configura dicho servicio, hay que instalar un archivo llamado appspec.yml en la raíz del proyecto. En dicho archivo se indican los scripts que se ejecutaran durante el ciclo de vida del despliegue.

Aquí un ejemplo de un archivo appspec.yml :

version: 0.0
os: linux
files:
  - source: ./
    destination: /home/ubuntu/app/deployments/api-release
permissions:
  - object: /home/ubuntu/app/deployments/api-release
    owner: ubuntu
    group: ubuntu
hooks:
  BeforeInstall:
    - location: scripts/before_install.sh
      timeout: 120
      runas: ubuntu
  AfterInstall:
    - location: scripts/after_install.sh
      timeout: 300
      runas: ubuntu
  ApplicationStart:
    - location: scripts/application_start.sh
      timeout: 120
      runas: ubuntu

Donde podemos ver en la sección hooks los tres pasos del ciclo de vida de despliegue que nos interesa:

  • BeforeInstall: todo lo previo antes de descargar el código y reiniciar servicios
  • AfterInstall: justo después de descargar código, acá se reinician servicios y corren migraciones
  • ApplicationStart: comandos complementarios para levantar servicios principales o secundarios.

Idealmente, para tener un buen sistema de despliegue para una app Ruby on Rails, deberíamos tener una estructura de carpetas como la siguiente:

~/app
├── api
└── deployments
    ├── api-gems
    └── api-release

Donde en la carpeta api se guarda el código como tal y es donde se ejecuta la aplicación, en deployments/api-gems es donde se guardará el resultado de hacer bundle install y api-release es donde se hace el comando pull para traer los cambios del repositorio.

La clave de tener api y deployments/api-release es que usando el comando rsync podemos mover a api solamente los archivos modificados y nada más. Volviendo el despliegue más eficiente y veloz.

Ahora sí, los scripts.

deploy_api.sh

#!/bin/bash -xe

# app
# ├── api
# └── deployments
#     ├── api-gems
#     └── api-release

set -e

#
# CLEAN CURRENT APP
#
# No longer necessary as the scripts now use RSync.
#
# bash /home/ubuntu/app/deployments/api-release/scripts/before_deploy.sh

#
# PULL Repo from GitHub
#
bash /home/ubuntu/app/deployments/api-release/scripts/pull_repo.sh

#
# RUN bundle, symlink bundled gems to api/vendor, assets:precompile,
#  migrations, symlink nginx.$RAILS_ENV.conf
#
bash /home/ubuntu/app/deployments/api-release/scripts/after_deploy.sh

#
# SYNC api-release with api folder
#
bash /home/ubuntu/app/deployments/api-release/scripts/application_start.sh

Este script corre los siguientes.

pull_repo.sh

#!/bin/bash -xe

# app
# ├── api
# └── deployments
#     ├── api-gems
#     └── api-release

. /home/ubuntu/.profile

cd /home/ubuntu/app/deployments/api-release

#
# PULL from GitHub Repo
# There's an SSH key configured in the server's ~/.ssh folder
# that is allowed to pull changes from the private repo.
#
git pull origin master

after_deploy.sh

#!/bin/bash -xe

# app
# ├── api
# └── deployments
#     ├── api-gems
#     └── api-release

. /home/ubuntu/.profile

#
# DEPLOY in release folder
#
cd /home/ubuntu/app/deployments/api-release

#
# INSTALL gems
#
echo "$(date '+%F %T') Installing deployment gems" >> /home/ubuntu/deployment_logs/bundle.log 2>&1
/home/ubuntu/.rvm/bin/rvm default do bundle install --deployment \
  --without development test \
  --path /home/ubuntu/app/deployments/api-gems/bundle \
  >> /home/ubuntu/deployment_logs/bundle.log 2>&1

#
# SYMLINK api-gems to api/vendor/bundle
#
echo "$(date '+%F %T') Symlink api-gems to vendor/bundle" >> /home/ubuntu/deployment_logs/bundle.log 2>&1
ln -fsv /home/ubuntu/app/deployments/api-gems/bundle /home/ubuntu/app/api/vendor/ >> /home/ubuntu/deployment_logs/bundle.log 2>&1

#
# COMPILE assets
#
echo "$(date '+%F %T') Compiling assets" >> /home/ubuntu/deployment_logs/assets.log 2>&1
/home/ubuntu/.rvm/bin/rvm default do bundle exec rake assets:precompile >> /home/ubuntu/deployment_logs/assets.log 2>&1

#
# Run migrations
#
echo "$(date '+%F %T') Running migrations" >> /home/ubuntu/deployment_logs/migration.log 2>&1
/home/ubuntu/.rvm/bin/rvm default do bundle exec rake db:migrate RAILS_ENV=$RAILS_ENV >> /home/ubuntu/deployment_logs/migration.log 2>&1

#
# SYMLINK nginx configuration file to /etc/nginx/sites-enabled
#
echo "$(date '+%F %T') Symlinking nginx configuration file" >> /home/ubuntu/deployment_logs/symlinking.log 2>&1
sudo ln -fs /home/ubuntu/app/api/config/nginx.$RAILS_ENV.conf /etc/nginx/sites-enabled/

Para poder ejecutar comandos puntuales de Rails o Ruby, tuve que especificar la ruta exacta donde estaba instalado RVM (mi elección por defecto) para encontrar los binarios de Bundler y Rake.

De ahí la línea de código /home/ubuntu/.rvm/bin/rvm default do la cual tomé de Capistrano.

application_start.sh

#!/bin/bash -xe

# app
# ├── api
# └── deployments
#     ├── api-gems
#     └── api-release

cd /home/ubuntu/app/api/

. /home/ubuntu/.profile

#
# RSYNC: from api-release/ to api/
#
echo "$(date '+%F %T') rsyncing release folder with api folder" >> /home/ubuntu/deployment_logs/rsync.log 2>&1
rsync -arv --delete \
  --exclude "vendor/" \
  /home/ubuntu/app/deployments/api-release/ \
  /home/ubuntu/app/api/ \
  >> /home/ubuntu/deployment_logs/rsync.log 2>&1

#
# RESTART nginx
# nginx comes bundled with phussion passenger
#
sudo service nginx restart >> /home/ubuntu/deployment_logs/nginx.log 2>&1
	
truncate -s 0 log/$RAILS_ENV.log
chmod 664 log/$RAILS_ENV.log

echo "$(date '+%F %T') Changing ownership to ubuntu" >> /home/ubuntu/deployment_logs/chowning.log 2>&1
sudo chown -Rv ubuntu:ubuntu log tmp vendor >> /home/ubuntu/deployment_logs/chowning.log 2>&1

Con más orden y un poco más de contexto, puedes ver los scripts en mi repositorio de cosas de Backend, Backendstuff.

Actualmente, no uso estos scripts porque tengo un equipo de DevOps que me libera de esta carga pero la última vez que los usé fue en un proyecto en Azure desplegado con CircleCI y los scripts funcionaron de maravilla luego de que todo estuviera en orden.

Autor: cesc1989

Ingeniero de Sistemas que le gusta escribir y compartir sobre recursos que considera útiles, además que le gusta leer manga y ver anime.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios .