Chạy server Ruby on Rails trên AWS sử dụng Unicorn và Nginx (phần 2)

Trong bài trước, chúng ta đã tìm hiểu về cách chạy khởi động instance EC2 và cách chạy Rails server trên instance đó. Lần này, hãy cùng xem cách chạy server đó bằng unicorn và Nginx như thế nào, và việc deploy tự động code bằng Capistrano được thực hiện ra sao nhé.

Cài đặt Unicorn

Ta đã có thể chạy server Rails trên instance, hãy chuyển sang dùng Unicorn cho server production này.

Hãy thêm dòng sau vào Gemfile

gem 'unicorn'

save file lại và thực hiện bundle install

Unicorn đã được cài đặt xong. Bây giờ ta cần thiết lập cho nó.

Bạn hãy tạo ra file config/unicorn.rb. Mở file và thay đổi nội dung của nó thành như sau:

# Thiết lập đường dẫn của app
app_dir = File.expand_path("../..", __FILE__)
shared_dir = "#{app_dir}/shared"
working_directory app_dir


# Một vài tùy chọn của unicorn
worker_processes 2
preload_app true
timeout 30

# Thiết lập socket
listen "#{shared_dir}/sockets/unicorn.sock", :backlog => 64

# Thiết lập đường dẫn tới file log
stderr_path "#{shared_dir}/log/unicorn.stderr.log"
stdout_path "#{shared_dir}/log/unicorn.stdout.log"

# Thiết lập master PID
pid "#{shared_dir}/pids/unicorn.pid"

Những thiết lập này liên quan tới đường dẫn của app, socket, log và PID. Ngoài ra bạn có thể chỉnh sửa và thêm các tùy chọn khác nếu cần thiết.

Bây giờ ta hãy cùng tạo các directory liên quan tới những thiết lập vừa rồi.

mkdir -p shared/pids shared/sockets shared/log

Tạo Script khởi động/tắt unicorn

Chúng ta hãy cùng tạo một script nhằm đơn giản hóa việc bật và tắt Unicorn và để chắc chắn rằng nó sẽ khởi động khi boot hệ điều hành.

Đầu tiên ta sử dụng câu lệnh sau để tạo ra một file mới chứa script, file này nằm trong thư mục /etc/init.d, do đó bạn phải tạo nó với quyền admin.

sudo vi /etc/init.d/unicorn_appname

Điền nội dung sau vào trong file, nhớ thay tên user và tên app của bạn và mục <user> và <app_name> trong đây nhé.

#!/bin/sh

### BEGIN INIT INFO
# Provides:          unicorn
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the unicorn app server
# Description:       starts unicorn using start-stop-daemon
### END INIT INFO

set -e

USAGE="Usage: $0 <start|stop|restart|upgrade|rotate|force-stop>"

# app settings
USER="<user>"
APP_NAME="<app_name>"
APP_ROOT="/home/$USER/$APP_NAME"
ENV="production"

# environment settings
PATH="/home/$USER/.rbenv/shims:/home/$USER/.rbenv/bin:$PATH"
CMD="cd $APP_ROOT && bundle exec unicorn -c config/unicorn.rb -E $ENV -D"
PID="$APP_ROOT/shared/pids/unicorn.pid"
OLD_PID="$PID.oldbin"

# make sure the app exists
cd $APP_ROOT || exit 1

sig () {
  test -s "$PID" && kill -$1 `cat $PID`
}

oldsig () {
  test -s $OLD_PID && kill -$1 `cat $OLD_PID`
}

case $1 in
  start)
    sig 0 && echo >&2 "Already running" && exit 0
    echo "Starting $APP_NAME"
    su - $USER -c "$CMD"
    ;;
  stop)
    echo "Stopping $APP_NAME"
    sig QUIT && exit 0
    echo >&2 "Not running"
    ;;
  force-stop)
    echo "Force stopping $APP_NAME"
    sig TERM && exit 0
    echo >&2 "Not running"
    ;;
  restart|reload|upgrade)
    sig USR2 && echo "reloaded $APP_NAME" && exit 0
    echo >&2 "Couldn't reload, starting '$CMD' instead"
    $CMD
    ;;
  rotate)
    sig USR1 && echo rotated logs OK && exit 0
    echo >&2 "Couldn't rotate logs" && exit 1
    ;;
  *)
    echo >&2 $USAGE
    exit 1
    ;;
esac

Hãy lưu file lại. Với script này, bạn có thể đơn giản hóa việc bật/tắt Unicorn cũng như Rails application của bạn với việc sử dụng lệnh

sudo service unicorn_<app_name> start/stop/restart

Tiếp theo ta cần thay đổi quyền truy nhập của script và cho phép Unicorn tự khởi động khi boot hệ điều hành.

sudo chmod 755 /etc/init.d/unicorn_appname
sudo update-rc.d unicorn_appname defaults

Hãy khởi động unicorn:

sudo service unicorn_<app_name> start

Bây giờ Rails application của chúng ta đã có thể được chạy bằng Unicorn trên môi trường production, và nó sẽ listen bằng socket được thiết lập trong shared/sockets/unicorn.sock. Trước khi application của chúng ta có thể được truy nhập từ bên ngoài vào, ta cần thiết lập Nginx, ở đây Nginx đóng vai trò là reverse proxy

Cài đặt và thiết lập Nginx

Ta sẽ cài đặt Nginx sử dụng apt-get.

sudo apt-get install nginx

Tiếp theo hãy mở file thiết lập của nginx

sudo vi /etc/nginx/sites-available/default

và thay đổi nội dung bên trong như sau:

upstream app {
    # Path to Unicorn SOCK file, as defined previously
    server unix:/home/<user>/<app_name>/shared/sockets/unicorn.sock fail_timeout=0;
}

server {
    listen 80;
    server_name localhost;

    root /home/<user>/<app_name>/public;

    try_files $uri/index.html $uri @app;

    location @app {
        proxy_pass http://app;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
    }

    error_page 500 502 503 504 /500.html;
    client_max_body_size 4G;
    keepalive_timeout 10;
}

Bạn nhớ thay đổi <user> và <app_name> theo user và tên Rails application của bạn nhé.

Hãy lưu file lại. Nội dung file này sẽ thiết lập Nginx như một reverse proxy nhờ đó, HTTP request có thể đi tới Unicorn thông qua socket Unix. Bạn hoàn toàn có thể thay đổi nội dung tùy theo hướng bạn muốn sử dụng.

Ta cần khởi động lại Nginx để thực hiện việc thay đổi thiết lập.

sudo service Nginx restart

Bây giờ Rails application của bạn đã có thể được truy cập vào được môi trường production thông qua public IP của EC2 instance.

Bạn chỉ cần gõ IP của EC2 instance vào browser là được.

http://ec2_instance_ip

Bạn sẽ nhìn thấy trang web của mình giống như nó được chạy từ Rails server, nhưng lần này là thông qua Nginx và Unicorn.

Cài đặt Capistrano và tự động hóa việc deploy

Capistrano là một framework cho việc xây dựng các script tự động hóa việc deploy. Mặc dù bản thân Capistrano được viết bằng ngôn ngữ Ruby nhưng nó có thể dễ dàng được sử dụng để deploy các project của những ngôn ngữ hay framwork khác như Rails, Java, PHP.

Đầu tiên, bạn hãy thêm dòng sau vào Gemfile.

gem 'capistrano'

Chạy bundle install

Tiếp theo, ta chạy lệnh sau

cap install

đề tự động tạo ra các file thiết lập cho deploy qua capistrano.

Chạy lệnh xong bạn sẽ thấy hiện ra các dòng chữ sau:

# mkdir -p config/deploy
# create config/deploy.rb
# create config/deploy/staging.rb
# create config/deploy/production.rb
# mkdir -p lib/capistrano/tasks
# Capified

File deploy.rb bao gồm một số tham số liên quan tới việc deploy tới server. Trong file này, ta sẽ cho capistrano biết ta cần kết nối tới server nào để deploy và deploy như thế nào.

Hãy vào file config/deploy.rb và sửa nội dung như sau (bạn có thể thay đổi tùy ý)

# Định nghĩa tên application
set :application, '<app_name>'

# Định nghĩa địa chỉ repository để capistrano access vào
set :repo_url, 'https://github.com/<git_user>/<app_name>.git'
set :scm, :git

# Định nghĩa nơi bạn sẽ đặt code của application
set :deploy_to, "/home/<deploy_user>/<app_name>"

set :pty, true

set :format, :pretty

# Thiết lập các chỉ dẫn sau khi deploy tại đây
# Sau khi kết thúc việc deploy, Capistrano sẽ
# thực hiện các lệnh ở dưới.

# namespace: deploy do

#   desc 'Restart application'
#   task :restart do
#     on roles(:app), in: :sequence, wait: 5 do
#       # Your restart mechanism here, for example:
#       execute :touch, release_path.join('tmp/restart.txt')
#     end
#   end

#   after :publishing, :restart

#   after :restart, :clear_cache do
#     on roles(:web), in: :groups, limit: 3, wait: 10 do
#       # Here we can do anything such as:
#       # within release_path do
#       #   execute :rake, 'cache:clear'
#       # end
#     end
#   end

# end

Nhớ thay đổi <user_name>, <git_user> và <app_name> theo thiết lập của bạn nhé.

Tiếp theo, ta cũng cần sửa cả file config/deploy/production.rb nữa.

# Định nghĩa roles, user và IP addres của server cần deploy
role :app, %{<user>@<ec2_instance_ip>}
role :web, %{<user>@<ec2_instance_ip>}
role :db, %{<user>@<ec2_instance_ip>}

# Định nghĩa server(s)
server '<ec2_instance_ip>', user: '<user>', roles: %w{web}

# SSH Options
set :ssh_options, {
    forward_agent: false,
    auth_methods: %w(password),
    password: '<user_deployers_password>',
    user: '<user>',
}

Sau khi lưu file lại, ta tiến hành deploy như sau, hãy chạy câu lệnh này ở môi trường development.

cap production deploy

Với thiết lập như ở trên, Capistrano sẽ thực hiện:

  • Két nối tới server cần deploy
  • Download source code của application
  • Thực hiện các deployment action.

Kết luận

Qua bài này tôi đã giới thiệu khái lược cho bạn cách sử dụng Unicorn, Nginx và Capistrano trong việc chạy server ở môi trường production. Đây chỉ là những thao tác đơn giản nhất, bạn hãy tìm hiểu thêm để có thể sử dụng được những thiết lập khác. Chúc bạn thành công.

Tài liệu tham khảo

https://www.digitalocean.com/community/tutorials/how-to-automate-ruby-on-rails-application-deployments-using-capistrano

https://www.digitalocean.com/community/tutorials/how-to-deploy-a-rails-app-with-unicorn-and-nginx-on-ubuntu-14-04