Trang chủ Deploy Ruby on Rails app with Capistrano

Deploy Ruby on Rails app with Capistrano

  • 2024 Sep 02
  • 13 minutes read

Bài viết hướng dẫn từng bước để deploy một ứng dụng Ruby on Rails, với một số tech stack liên quan như Nginx, Passenger, Postgres, Elasticsearch, Redis, Sidekiq. Thiết lập và cấu hình deploy app bằng Capistrano.

 

1) Thiết lập cơ bản trên server

Set hostname:

sudo hostnamectl set-hostname [name-server]
# hoặc
sudo vi /etc/hostname
[name-server]

Set timezone:

sudo ln -sf /usr/share/zoneinfo/Asia/Ho_Chi_Minh /etc/localtime

Tạo user deploy app

# create new user deploy
sudo useradd -m -s /bin/bash [deploy]

# add user deploy vào group sudo (quyen quan tri cao nhat)
sudo usermod -aG sudo deploy  

# Cấu hình lệnh chạy bằng user deploy không cần password       
echo 'deploy ALL=(ALL) NOPASSWD:ALL' | sudo tee -a /etc/sudoers.d/01-permission-deploy-user

2) Cấu hình SSH

Ở máy client muốn ssh vào server để deploy, cần có cặp key ssh, với public key ta copy vào máy server

  • Ở máy server
mkdir .ssh
cd .ssh
touch authorized_keys
  • Tạo ssh key ở client
ssh-keygen -t rsa

Sau khi sinh ra cặp key public và private, ta copy public key vào authorized_keys trên server hoặc chạy lệnh sau để copy

ssh-copy-id deploy@server_ip

 Kiểm tra kết nối SSH thành công

ssh deploy@server_ip

3) Cài đặt những service, library cần thiết để deploy app

Install RVM

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install -y curl gnupg gnupg2 build-essential
sudo gpg2 --keyserver hkp://keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
curl -sSL https://get.rvm.io | sudo bash -s stable 
if sudo grep -q secure_path /etc/sudoers; then sudo sh -c "echo export rvmsudo_secure_path=1 >> /etc/profile.d/rvm_secure_path.sh" && echo Environment variable installed; fi
source ~/.rvm/scripts/rvm
sudo usermod -a -G rvm `whoami`
newgrp rvm
rvm -v

Install Ruby

# Install Ruby [3.1.2]
rvm install 3.1.2

Install Rails

# Install Rails [7.0.5.1]
gem install rails -v 7.0.5.1

Install dependency for process image by Vips instead of ImageMagick

sudo apt install -y libvips libvips-dev libvips-tools

Install NodeJS

sudo apt-get install -y nodejs && sudo ln -sf /usr/bin/nodejs /usr/local/bin/node
node -v

Install Yarn

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update
sudo apt-get install yarn -y

Install Postgres

# Install dependency for postgres
sudo apt-get install libpq-dev

# Install postgres version 12
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main 12" > /etc/apt/sources.list.d/pgdg.list'
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt update
sudo apt install postgresql-12
psql --version

sudo systemctl enable postgresql
sudo systemctl start postgresql
sudo service postgresql start 
  • Cấu hình psql:
sudo -su postgres psql
# 1. Create owner for rails app database
postgres=# CREATE ROLE [deploy] WITH LOGIN PASSWORD '[password]';
postgres=# \du

# 2. Create database my_db
postgres=# CREATE DATABASE "my_db";
postgres=# \list
postgres=# ALTER DATABASE my_db OWNER TO deploy;

# 3. Create hstore extension:
postgres=# ALTER ROLE deploy with SUPERUSER;
postgres=# \c my_db;
my_db=# CREATE EXTENSION IF NOT EXISTS hstore;

# 4. Change back permission of deploy
my_db=# ALTER ROLE deploy with NOSUPERUSER;

Install Redis

sudo apt update
sudo apt policy redis-server
sudo apt update
sudo apt -y install redis-server
sudo add-apt-repository ppa:redislabs/redis
sudo apt-get update
sudo apt-get install redis

redis-server -v

sudo systemctl enable redis-server
sudo systemctl start redis-server

sudo systemctl status redis
redis-cli ping
  • Nếu kết nối redis từ một server/node khác, ta cần cấu hình like redis config như sau:
sudo vi /etc/redis/redis.conf

bind 127.0.0.1 -::1 [10.0.0.15]

### set no for this setting
protected-mode no
# By default protected mode is enabled. You should disable it only if you are sure you want clients from other hosts to connect to Redis, even if no authentication is configured, nor a specific set of interfaces are explicitly listed using the "bind" directive.
  • Kiểm tra kết nối thành công
nc -v 10.0.0.15 6379
  • Kiểm tra log Redis
sudo tail -n 500 /var/log/syslog

Install Sidekiq

  • Tạo service sidekiq
sudo vi /lib/systemd/system/sidekiq.service
  • Cấu hình service
# /lib/systemd/system/sidekiq.service
[Unit]
Description=sidekiq
After=syslog.target network.target

[Service]
Type=simple
WorkingDirectory=/path/to/your/app
# WorkingDirectory=/home/my_app/deploy/current

ExecStart=/home/deploy/.rvm/bin/rvm in /path/to/your/app/current do bundle exec sidekiq -e production
# ExecStart=/home/deploy/.rvm/wrappers/ruby-3.1.2@myapp/bundle exec sidekiq --environment production --config /home/my_app/deploy/current/config/sidekig

# if we crash, restart
RestartSec=1
Restart=on-failure

# output goes to /var/log/syslog
StandardOutput=syslog
StandardError=syslog

# This will default to "bundler" if we don't specify it
SyslogIdentifier=sidekiq

[Install]
WantedBy=multi-user.target

Install Elasticsearch

curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-7.x.list
sudo apt update
sudo apt install elasticsearch=7.17.1
sudo vi /etc/elasticsearch/elasticsearch.yml
- network.host: localhost
- discovery.seed_hosts: ["127.0.0.1", "[::1]"]
# - discovery.seed_hosts: ["10.0.0.15", "127.0.0.1"]

sudo systemctl start elasticsearch
sudo systemctl enable elasticsearch

curl -X GET 'http://localhost:9200'

Install Nginx

sudo apt update
sudo apt-get install nginx
sudo systemctl enable nginx
sudo systemctl start nginx
  • Kiểm tra lỗi syntax cấu hình nginx
nginx -t
  • Mẫu cấu hình nginx
# etc/nginx/sites-enables/default
server {
        listen 80;
        listen [::]:80;

				root /home/my_app/deploy/current/public;
				server_name [server-ip];

        passenger_enabled on;
        passenger_app_env staging;
}

Install Passenger

sudo apt-get install -y dirmngr gnupg apt-transport-https ca-certificates curl

curl https://oss-binaries.phusionpassenger.com/auto-software-signing-gpg-key.txt | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/phusion.gpg >/dev/null

sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger $(lsb_release -cs) main > /etc/apt/sources.list.d/passenger.list'
  • Một số lệnh hữu ích
# Verify install successful or not
sudo /usr/bin/passenger-config validate-install

# show Process Running...
sudo /usr/sbin/passenger-memory-stats

# check passenger configuration is success
passenger-status

# check path ruby version that guide the passegener working in
passenger-config about ruby-command
ex: passenger_ruby /home/my_app/.rvm/gems/ruby-3.1.2/wrappers/ruby

# test passenger-restart
passenger-config restart-app /
passenger-config restart-app /path/to/app

4) Thiết lập môi trường, config deploy

Add gem capistrano

group :development do
  gem 'capistrano', require: false
  gem 'capistrano-passenger', '0.2.1', require: false
  gem 'capistrano-rails', '~> 1.4', require: false
  gem 'capistrano-rvm', '~> 0.1.2', require: false
end

# cap install STAGES=local,sandbox,qa,production

Cài đặt biến môi trường

Cách 1: Khai báo trực tiếp trong /etc/environment

sudo vi /etc/environment
TEST_KEY='TEST_VALUE'

Cách 2: Dùng gem 'dotenv-rails'

# Nên khai báo ở đầu file để load env đầu tiên
gem 'dotenv-rails', require: 'dotenv/rails-now'

Cấu hình file deploy.rb của Capistrano

# config/deploy.rb

append :linked_files, 'config/master.key', '.env'

# or symlink by rake task
before 'deploy:updated', 'deploy:copy_env_file'
namespace :deploy do
  task :copy_env_file do
    on roles(:all) do
      puts "Linking #{shared_path}/.env to #{release_path}/.env"
      execute "ln -s #{shared_path}/config/master.key #{release_path}/config/master.key"
			execute "ln -s #{shared_path}/.env #{release_path}/.env"
      puts "Linked #{shared_path}/.env to #{release_path}/.env"
    end
  end
end

Trong đó:

  • shared_path: is keyword, it means folder deploy/shared
  • release_path: is keyword, it means folder deploy/releases

Sau khi cấu hình file deploy xong, ta SSH tới server để set value secret trên folder share.

# in deploy/shared folder
$ cd deploy/shared
$ sudo vi .env

TEST_KEY='TEST_VALUE'
# or
TEST_KEY: 'TEST_VALUE'
# RAILS_MASTER_KEY: '[0bxxx]'

Cấu hình capistrano deploy

# config/deploy.rb
# config valid for current version and patch releases of Capistrano
lock "~> 3.18.0"

set :application, "my_app"
set :repo_url, "[email protected]:username/repository.git"

# Default branch is :master
set :branch, `git rev-parse --abbrev-ref HEAD`.chomp

# Default deploy_to directory is /var/www/my_app_name
set :deploy_to, "/home/my_app/deploy"

# Default value for :linked_files is []
# append :linked_files, "config/database.yml", 'config/master.key'

# Default value for linked_dirs is []
# symlink folder log, tmp, node_modules,.. from deploy/current folder to shared folder
append :linked_dirs, 'log', 'tmp', 'public/system', 'node_modules'

set :user, 'deploy'

set :rvm_ruby_string, 'ruby-3.1.2@my_app'

# Default value for keep_releases is 5
set :keep_releases, 5

# Uncomment the following to require manually verifying the host key before first deploy.
# set :ssh_options, verify_host_key: :secure
set :ssh_options, { forward_agent: true, user: fetch(:user), keys: %w(~/.ssh/id_rsa) }

before 'deploy:updated', 'deploy:copy_env_file'

namespace :deploy do
  task :copy_env_file do
    on roles(:all) do
	  execute "rm #{release_path}/.env && ln -s #{shared_path}/.env #{release_path}/.env"
      execute "ln -s #{shared_path}/master.key #{release_path}/config/master.key"
    end
  end
end

Cấu hình production environment

# config/environments/production.rb
config.require_master_key = true
config.asset_host = "[IP-server]"

config.active_storage.service = :local (:aws)

# Nếu domain đã có SSL thì cấu hình force_ssl = true, nếu chưa thì là false.
config.force_ssl = false
# config/deploy/production.rb

# Set môi trường sẽ được assign cho server sau khi deploy, nơi mà khi chạy lệnh trên server ta thêm biến môi trường:
set rails_env: production
# RAILS_ENV=production bundle exec rails console

server '[IP-server]', user: 'deploy', roles: %w[app db web]

Kiểm tra capistrano cấu hình đúng chưa

cap production deploy:check
cap production deploy:check --trace

Deploy Rails app với capistrano

cap production deploy

0 Comments