Tuesday 6 March 2018

Automating Ruby App Deployment


The Problem: Application out of dev is ready but deployment to production server is nuisance.
1.     Current scenario had around 20+ dev whose continuously work on the application source code using Git.
2.     After update of code, deployment to production server of even a minor change in the code of the app is a huge hassle.

How did Capistrano Solve This Problem?
·        What is Capistrano?
Not an application or service, but a gem that works like rake; it bears similar functionality to the Ant and Nant scripts used for the purpose of deploying.
Capistrano is run on a user's local machine (typically the Rails app folder), and requires configuration on their end to run.
It directly connects from the user's machine to the remote server via SSH to perform deploys.
Commands are run through a console i.e. cap deploy (this is as close as it gets to 'one click' deploys.)
It produce logs, perform rollbacks etc.
It does not kick-off scheduled builds on its own.

Solution:
Deploy the latest commit from my 'master' branch in GitHub repository to staging server.
Configuring Capistrano's deploy.rb file will accomplish this.
One-click build on-demand at any time
All Capistrano deploys are done through 'force'
Have the capability to run custom commands on the staging server as a part of the deploy (i.e. 'bundle install', restart Apache, etc...)
Configuring Capistrano's deploy.rb file will accomplish this. Most of these commands are included out of the box.
Automatic deploy daily or after a commit on GitHub (optional)
A cron job is placed.
Applying changes to the production server become hassle free and updates are implied within seconds!

Implementation:
sudo apt-get update
sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev python-software-properties libffi-dev

cd
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
exec $SHELL

git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
exec $SHELL

git clone https://github.com/rbenv/rbenv-gem-rehash.git ~/.rbenv/plugins/rbenv-gem-rehash

rbenv install 2.2.4
rbenv global 2.2.4
ruby -v

echo "gem: --no-document" > ~/.gemrc
gem install bundler
rbenv rehash

sudo apt-get install libgmp-dev
gem install rails
rbenv rehash
rails -v

curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
sudo apt-get install -y nodejs

sudo apt-get install postgresql postgresql-contrib libpq-dev
sudo -u postgres createuser -s pguser
sudo -u postgres psql
\password pguser
\q

cd ~
rails new appname -d postgresql

cd appname

nano config/database.yml

  host: localhost
  username: nash
  password: qaz

rake db:create

rails server -b 0.0.0.0

OR we can do this as well!

nano config/boot.rb

require 'rubygems' 
require 'rails/commands/server'

module Rails 
  class Server
    alias :default_options_bk :default_options
    def default_options
      default_options_bk.merge!(Host: '0.0.0.0', Port: 8000)
    end
  end
end

Also,

Fork on Github

git config --global color.ui true
git config --global user.name "YOUR NAME"
git config --global user.email "YOUR@EMAIL.com"
ssh-keygen -t rsa -C "YOUR@EMAIL.com"

eval $(ssh-agent)
ssh-add

git config --global credential.helper 'cache --timeout=3600'

cat ~/.ssh/id_rsa.pub

ssh -T git@github.com

ssh user@productionserver

git clone https://github.com/jeremyolliver/hello_app.git

cd appname

bundle install --without test development

RAILS_ENV=production bundle exec rake db:create
RAILS_ENV=production bundle exec rake db:migrate

bundle exec rake secret

ddf4a6d37a956089984c8fe6160a6e3c18e48a448a07a50e4ab10a4edd6d3597f13ad9b6e0af4f5723f1ef52bfd2ffa78ab5b815d2bb8b15f14f48e7e307baad

export SECRET_KEY_BASE=ddf4a6d37a956089984c8fe6160a6e3c18e48a448a07a50e4ab10a4edd6d3597f13ad9b6e0af4f5723f1ef52bfd2ffa78ab5b815d2bb8b15f14f48e7e307baad

RAILS_ENV=production bundle exec rails server

Deployment for Capistrano
gem 'figaro'
gem 'puma'
group :development do
  gem 'capistrano'
  gem 'capistrano3-puma'
  gem 'capistrano-rails', require: false
  gem 'capistrano-bundler', require: false
  gem 'capistrano-rbenv'
end

Install the gems via bundler:
bundle install
It’s time to configure Capistrano, first by generating the config file, as follows:
cap install STAGES=production
This will create configuration files for Capistrano at config/deploy.rb and config/deploy/production.rb. deploy.rb is the main configuration file and production.rb contains environment specific settings, such as server IP, username, etc.
Add the following lines into the Capfile, found in the root of the application. The Capfile includes RBENV, Rails, and Puma integration tasks when finished:
require 'capistrano/bundler'
require 'capistrano/rbenv'
require 'capistrano/rails/assets' # for asset handling add
require 'capistrano/rails/migrations' # for running migrations
require 'capistrano/puma'

Now, edit deploy.rb as follows:
lock '3.4.0'
set :application, 'contactbook'
set :repo_url, 'git@github.com:user/contactbook.git' # Edit this to match your repository
set :branch, :master
set :deploy_to, '/home/deploy/contactbook'
set :pty, true
set :linked_files, %w{config/database.yml config/application.yml}
set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system public/uploads}
set :keep_releases, 5
set :rbenv_type, :user
set :rbenv_ruby_version, 'jruby-1.7.19' # Edit this if you are using MRI Ruby
set :puma_rackup, -> { File.join(current_path, 'config.ru') }
set :puma_state, "#{shared_path}/tmp/pids/puma.state"
set :puma_pid, "#{shared_path}/tmp/pids/puma.pid"
set :puma_bind, "unix://#{shared_path}/tmp/sockets/puma.sock"    #accept array for multi-bind
set :puma_conf, "#{shared_path}/puma.rb"
set :puma_access_log, "#{shared_path}/log/puma_error.log"
set :puma_error_log, "#{shared_path}/log/puma_access.log"
set :puma_role, :app
set :puma_env, fetch(:rack_env, fetch(:rails_env, 'production'))
set :puma_threads, [0, 8]
set :puma_workers, 0
set :puma_worker_timeout, nil
set :puma_init_active_record, true
set :puma_preload_app, false

After configuring NGINX we just hit
# cap deploy production
And that’s it! Problem solved!