Deploy Rail 5 app in DigitalOcean with Capistrano

Deploy Rail 5 app in DigitalOcean with Capistrano

enmanuelm19@gmail.com2018-06-13 19:19:45 UTC

Setting up Rails 5 app deployment with capistrano in DO


First of all you have to create a new account in DigitalOcean, do it here.

  1. Create a new droplet

    Before creating your new droplet you have to configure a SSH key, to do this go to Settings -> Security add-ssh

    In order to create a new SSH key in Linux or MacOs, open the console and run ssh-keygen, the console will ask for a passphrase, the ssh-key will be located in ~/.ssh/id_rsa.pub, if you already have this file just run cat ~/.ssh/id_rsa.pub, copy the content and paste it inside the SSH Keys field in DigitalOcean.

    Create the droplet with the resources that you need, if you don't know how estimate how many resources will you app need, I recommend create the most basic posible, DigitalOcean give you the option for increase the memory and CPU of your droplets. crear-droplet

  2. Configure your droplet

    Log in to your server with the following command:

    ssh root@droplet_ip

    Inside the server you need to add a new user, never user the root user:

    adduser name

    The following command add the recent created user superuser properties:

    usermod -aG sudo name

    Log in with the new user:

    su - name

    Create this folder and files in order to log in from your local machine:

    ~: mkdir .ssh

    ~: nano ~/.ssh/authorized_keys

    Inside the file ~/.ssh/authorized_keys you have to paste the ssh-key of your local machine, to exit nano editor press ctrl+x, log out from the droplet:

    exit

    From now on, you can access to your droplet putting in the console ssh name@ip_del_droplet

  3. Install Nginx

    Nginx is the server that will be act as a proxy for our app.

    sudo apt-get update

    sudo apt-get install curl git-core nginx -y

  4. Install RVM and Ruby

    In the section when says 2.5.0 you have to put the ruby version of your app.

    gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3

    curl -sSL https://get.rvm.io | bash -s stable

    source ~/.rvm/scripts/rvm

    rvm requirements

    rvm install 2.5.0

    rvm use 2.5.0 --default

  5. Install Rails and Bundler

    To handle the dependecies.

    gem install rails -V --no-ri --no-rdoc

    gem install bundler -V --no-ri --no-rdoc

  6. Install Postgresql

    In this case my app use Postgresql as DB, install it with the following commands:

    sudo apt-get install postgresql postgresql-contrib

    After the DB system is instaled, we proceed to create a new db and a user for it, I recommend to put the same name as the user we create for our droplet, and give it superuser permissions.

    createuser --interactive

    Create the db.

    createdb app-production

    To access to the psql console from our server, just do the following:

    psql -U name -d app-production

  7. Configure remote repository

    In order that our server could obtain the code of the app is necesary configure it to have permission. For accomplish that you need to generate an ssh-key from your server, log in to your server and run:

    ssh-keygen

    cat ~/.ssh/id_rsa.pub

    Copy the content and go to github under settings github-settings and paste the ssh key of your server with write and read permissions.

    Start the communication between your server and github with the following:

    ssh -T git@github.com

  8. Configure capistrano

    Add the gems on the Gemfile.

        group :development do   
          gem 'capistrano',         require: false
          gem 'capistrano-rvm',     require: false
          gem 'capistrano-rails',   require: false
          gem 'capistrano-bundler', require: false
          gem 'capistrano3-puma',   require: false  
        end
    

    Install and run the command to generate the files that we need to use capistrano.

    bundle install && cap install

    This command creates the following files Capfile, config/deploy.rb,config/deploy/production.rb. Inside Capfile paste this:

    require "capistrano/setup"
    require "capistrano/deploy"
    require "capistrano/rails"
    require "capistrano/bundler"
    require "capistrano/rvm"
    require 'capistrano/puma'
    require "capistrano/scm/git"
    install_plugin Capistrano::SCM::Git
    install_plugin Capistrano::Puma
    
    Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
    

    You have to put the following configuration inside config/deploy.rb

    server 'droplet_ip', roles: [:web, :app, :db], primary: true
    
    set :application, 'app_name'
    set :repo_url, 'git@github.com:user/repo_name.git'
    set :user, 'username'
    set :puma_threads, [4,16]
    set :puma_workers, 0
    
    set :pty,             true
    set :use_sudo,        false
    set :stage,           :production
    set :deploy_via,      :remote_cache
    set :deploy_to,       "/home/#{fetch(:user)}/apps/#{fetch(:application)}"
    set :puma_bind,       "unix://#{shared_path}/tmp/sockets/#{fetch(:application)}-puma.sock"
    set :puma_state,      "#{shared_path}/tmp/pids/puma.state"
    set :puma_pid,        "#{shared_path}/tmp/pids/puma.pid"
    set :puma_access_log, "#{release_path}/log/puma.error.log"
    set :puma_error_log,  "#{release_path}/log/puma.access.log"
    set :ssh_options,     { forward_agent: true, user: fetch(:user), keys: %w(~/.ssh/id_rsa) }
    set :puma_preload_app, true
    set :puma_worker_timeout, nil
    set :puma_init_active_record, true  # Change to false when not using ActiveRecord
    set :rvm_ruby_version, '2.5.0'
    
    set :branch,        :master
    
    set :linked_files, %w{config/database.yml config/master.key}
    set :linked_dirs,  %w{log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system public/uploads public/assets}
    
    namespace :puma do
        desc 'Create Directories for Puma Pids and Socket'
        task :make_dirs do
            on roles(:app) do
                execute "mkdir #{shared_path}/tmp/sockets -p"
                execute "mkdir #{shared_path}/tmp/pids -p"
            end
        end
        before :start, :make_dirs
    end
    
    namespace :deploy do
        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 github/master`
                    puts "WARNING: HEAD is not the same as origin/master"
                    puts "Run `git push` to sync changes."
                    exit
                end
            end
        end
    
        desc 'Initial Deploy'
        task :initial do
            on roles(:app) do
                before 'deploy:restart', 'puma:start'
                invoke 'deploy'
            end
        end
    
        desc 'Restart application'
        task :restart do
            on roles(:app), in: :sequence, wait: 5 do
                invoke 'puma:restart'
            end
        end
    
        before :starting,     :check_revision
        after  :finishing,    :compile_assets
        after  :finishing,    :cleanup
    end
    

    Remember to change app_name for the name of your app, the ruby version and the name of the branch you want to deploy. Now paste the following inside config/deploy/production:

    set :stage, :production
    set :rails_env, :production
    server 'ip_del_droplet', user: 'tu_usuario_en_el_servidor', roles: %w{web app db}, primary: true
    

    Create a file inside config folder called nginx.conf, and paste the following:

    upstream puma {
        server unix:///home/usuario/apps/app_name/shared/tmp/sockets/app_name-puma.sock;
    }
    
    server {
        listen 80 default_server deferred;
        # if you already have a domain, put it in  the following line, if not just comment it
        server_name domain.com; 
    
        root /home/username/apps/app_name/current/public;
        access_log /home/username/apps/app_name/current/log/nginx.access.log;
        error_log /home/username/apps/app_name/current/log/nginx.error.log info;
    
        location ^~ /assets/ {
            gzip_static on;
            expires max;
            add_header Cache-Control public;
        }
    
        try_files $uri/index.html $uri @puma;
        location @puma {
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header Host $http_host;
            proxy_redirect off;
    
            proxy_pass http://puma;
        }
    
        error_page 500 502 503 504 /500.html;
        client_max_body_size 10M;
        keepalive_timeout 10;
    }
    
  9. Installl ubuntu firewall (ufw)

    As an extra security layer we will install the ubuntu firewall, for that we have to log in in our server.

    ssh username@idroplet_ip

    sudo ufw app list

    We will see a list of apps, between we will find OpenSSH and Nginx, we have to allow to pass the firewall:

    sudo ufw allow OpenSSH

    sudo ufw allow 'Nginx Full'

    sudo ufw enable

    With this we are done configuring the firewall, that will allow conections in the port that OpenSSH and Nginx needs.

  10. Deploy your app

    Save and upload the code we give you in step 8 in the remote repository.

    git add .

    git commit -m "Adding capistrano configuration"

    git push remote branchname

    Run the following:

    cap production deploy:initial

    This step will fail, don't worry is because we have to create some files. Create the file home/username/apps/app_name/shared/config/database.yml and write the information needed to connect with the DB, create home/username/apps/app_name/shared/config/master.key and put the master key that you generate to handle rails credentials.

    It's possible that we need to install node in our server, For this do the following:

    curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -

    sudo apt-get install -y nodejs

    Create a symbolic link for Nginx

    sudo rm /etc/nginx/sites-enabled/default

    sudo ln -nfs /home/username/apps/app_name/current/config/nginx.conf /etc/nginx/sites-enabled/app_name"

    sudo service nginx restart

    Deploy your app (this run it in your local machine):

    cap production deploy

    Now you can acces to your application from web browser through the droplet ip or from the domain that you configure in the nginx.conf file.

  11. Configure SSL with Letsencrypt

    Now to make your website secure we will configure the SSL protocol, for that we need to install the certbot, log in to your server and type the following:

    sudo add-apt-repository ppa:certbot/certbot

    sudo apt-get update

    sudo apt-get install python-certbot-nginx

    In order to obtain the SSL certificate for our domains, we need to do the next:

    sudo certbot --nginx -d example.com -d www.example.com

    We have to put the domains we configure inside the nginx.conf file, if you have severals domains just add the flag -dfollowing by the domain.

    Make sure that everything is working and to automatic renew our certificate run:

    sudo certbot renew --dry-run


I hope this info has been useful to you, any doubts or error from my part just feel free to comment below.


Share