Cómo desplegar una aplicación Rails con Mina, Nginx y Puma

¿Qué es Mina?
Mina es una herramienta de despliegue parecida a Capistrano. Su gran ventaja es la velocidad. Mina es especialmente rápida porque es un generador de scripts Bash de despliegue: genera todo el procedimiento como un script Bash y lo ejecuta en remoto en el servidor. Además, ejecuta los comandos de forma inteligente: si no cambias los assets (CSS, JS, imágenes), no lanza la precompilación de assets. Y lo mismo con las migraciones.
Configuración de Mina
Veamos cómo configurar Mina con Puma. Primero tienes que añadir Mina y mina-puma al Gemfile, en el grupo de desarrollo. Mina cambió hace poco de mantenedor; el nuevo repositorio está aquí y lo mejor es instalarlo desde Git.
gem 'mina', require: falsegem 'mina-puma', require: false, github: 'untitledkingdom/mina-puma'
Si usas una versión de Rails anterior a la 5, añade la gema Puma a la sección global.
Después, instala las gemas y ejecuta el comando inicial de Mina para generar el archivo config/deploy.rb.
bundlemina init
Aquí tienes un ejemplo de la configuración por defecto de Mina 1.0.0. Haz clic aquí para ver la configuración completa.
Explicación detallada del archivo de despliegue de Mina
#Set the domain or ip address of the remote server.set :domain, 'yourdomain.ru'#Set the folder of the remote server where Mina will deploy your application.set :deploy_to, 'path/to/directory'#Set a link to the repository. Example: git@bitbucket.org/yourteam/myapp.gitset :repository, 'git@…'#Set the name of a branch you plan to deploy as default master.set :branch, 'master'#Fill in the names of the files and directories that will be symlinks to the shared directory.#All folders will be created automatically on Mina setup.#Don’t forget to add a path to the uploads folder if you are using Dragonfly or CarrierWave.#Otherwise, you will lose your uploads on each deploy.set :shared_dirs, fetch(:shared_dirs, []).push('log', 'tmp/pids', 'tmp/sockets', 'public/uploads')set :shared_files, fetch(:shared_files, []).push('config/database.yml', 'config/secrets.yml', 'config/puma.rb')#Username of ssh user for access to the remote server.set :user, 'username'#This is not a required field. You can use it to set an application name for easy recognition.set :application_name, 'MyApplication'#Set ruby version. If you have RVM installed globally, you’ll also need to set an RVM path,#like: set :rvm_use_path, '/usr/local/rvm/scripts/rvm'.#You can find the RVM location with the rvm info command.task :environment do invoke :'rvm:use', 'ruby-2.4.0@default'end
Por defecto, Mina crea todas las carpetas indicadas en shared_dirs y shared_files. Aun así, en la tarea setup puedes añadir la creación automática de archivos vacíos para rellenarlos más tarde.
task :setup do command %[touch "#{fetch(:shared_path)}/config/database.yml"] command %[touch "#{fetch(:shared_path)}/config/secrets.yml"] command %[touch "#{fetch(:shared_path)}/config/puma.rb"] comment "Be sure to edit '#{fetch(:shared_path)}/config/database.yml', 'secrets.yml' and puma.rb."end
Hecho esto, ya puedes ejecutar mina setup para preparar toda la estructura de archivos necesaria en el servidor remoto.
La sección deploy de tu deploy.rb debería quedar así:
task :deploy do deploy do comment "Deploying #{fetch(:application_name)} to #{fetch(:domain)}:#{fetch(:deploy_to)}" invoke :'git:clone' invoke :'deploy:link_shared_paths' invoke :'rvm:load_env_vars' invoke :'bundle:install' invoke :'rails:db_migrate' command %{#{fetch(:rails)} db:seed} invoke :'rails:assets_precompile' invoke :'deploy:cleanup' on :launch do invoke :'puma:phased_restart' end endend
Configuración de Puma
Crea o rellena el archivo puma.rb en la carpeta config.
environment "production"bind "unix:///{path_to_your_app}/shared/tmp/sockets/puma.sock"pidfile "/{path_to_your_app}/shared/tmp/pids/puma.pid"state_path "/{path_to_your_app}/shared/tmp/sockets/puma.state"directory "/{path_to_your_app}/current"workers 2threads 1,2daemonize trueactivate_control_app 'unix:///{path_to_your_app}/shared/tmp/sockets/pumactl.sock'prune_bundler
Tienes que indicar la ruta a pumactl.sock en activate_control_app. Si no, mina puma:stop y restart no funcionarán.
Rellena database.yml y secrets.yml
Ve a la carpeta shared y asegúrate de rellenar estos dos archivos.
Configuración de nginx
Crea el archivo myapp.conf en la carpeta /nginx/etc/conf.d con un contenido similar a este.
upstream mysite { server unix:///home/admin/mysite/shared/tmp/sockets/puma.sock fail_timeout=0;}server { listen 80; listen [::]:80; server_name mysite.com; root /home/admin/mysite/current/public; location ~ ^/assets/ { expires max; gzip_static on; gzip_vary on; add_header Cache-Control public; break; } location / { proxy_pass http://mysite; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location ~ ^/(500|404|422).html { root /home/admin/mysite/current/public; } error_page 500 502 503 504 /500.html; error_page 404 /404.html; error_page 422 /422.html; client_max_body_size 4G; keepalive_timeout 10;}
Genera las claves ssh
Genera claves ssh nuevas en tu servidor con el comando ssh-keygen. Después, añádelas a las deploy keys de tu proyecto en GitHub o Bitbucket.
Misión cumplida
Eso es todo. Ya puedes desplegar tu aplicación. Si ya has ejecutado mina setup, lanza mina deploy y espera.
Resolución de problemas
Si tienes problemas con el git clone, comprueba que has añadido la clave pública ssh a tu repositorio e intenta clonar el repositorio directamente desde el servidor.
En el primer arranque, usa mina puma:start en lugar de phased_restart.