Use Capistrano to deploy Rails app
Bài đăng này đã không được cập nhật trong 9 năm
Summary
- Tạo một VPS với DigitalOcean (Ubuntu 14.04)
- Cài đặt VPS cho Rails app để deploy (RVM, Git, Nginx, Passenger/Unicorn)
- Deploy với Capistrano gem
Tạo một VPS với DigitalOcean
Hãy bắt đầu tạo VPS với Digital Ocean - dịch vụ với mức giá và chất lượng rất tốt.
Chỉ với $10/month($0.015/hour) bạn đã có một droplet với 1GB RAM, 30GB SSD Disk, 2TB transfer...
Chọn plan cho VPS của bạn:
Chọn region của server:
Chọn hệ điều hành:
Cài đặt VPS cho Rails app để deploy
Sau khi tạo VPS thành công bạn có thể dùng ssh hoặc ftp (Filezilla) để truy cập vào VPS, ở đây mình dùng ssh:
Nếu chưa tạo SSH key ở local, hãy đọc bài viết này: https://help.github.com/articles/generating-ssh-keys/
Ở Local Terminal:
ssh root@your_droplet_ip
Nếu bạn gặp trường hợp tương tự như thế này (do identity của host bị thay đổi):
[XX@XX ~]$ ssh root@pong
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
...
Hãy ssh-keygen -R your_droplet_ip
để remove identity cũ của host khỏi known_hosts
Sau khi bạn đã đăng nhập vào VPS với root user, hãy tạo một user để bắt đầu làm việc với app:
groupadd -g 2000 dev
adduser --gid 2000 --uid 2100 deploy
sudo visudo
Cấp quyền cho group dev bằng cách thêm vào
%dev ALL=(ALL:ALL) ALL
Login vào deploy user: sudo su - deploy
Tạo SSH key cho user deploy, sau đó add SSH key vào github repo: https://help.github.com/articles/generating-ssh-keys/
Đăng nhập user deploy bằng ssh-key:
Sử dụng ssh-copy-id để làm việc này, nếu bạn đang dùng Mac, chạy brew install ssh-copy-id
để cài đặt ssh-copy-id
Chạy ở local: ssh-copy-id deploy@your_droplet_ip
Cài đặt Ruby
Cài đặt dependencies cho Ruby:
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
Cài đặt RVM
sudo apt-get install libgdbm-dev libncurses5-dev automake libtool bison libffi-dev
gpg --keyserver hkp://keys.gnupg.net --recv-keys D39DC0E3
curl -L https://get.rvm.io | bash -s stable
Update script và cài Ruby:
source ~/.rvm/scripts/rvm
echo "source ~/.rvm/scripts/rvm" >> ~/.bashrc
rvm install 2.1.0
rvm use 2.1.0 --default
gem install bundler
Cài đặt Nginx + Passenger/Unicorn
Nginx + Passenger
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7
sudo apt-get install -y apt-transport-https ca-certificates
Add Passenger APT repository
sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger trusty main > /etc/apt/sources.list.d/passenger.list'
sudo apt-get update
sudo apt-get install -y nginx-extras passenger
Sau khi cài đặt thành công passenger, chạy sudo service nginx start
hoặc sudo service nginx
để xem usage:
Usage: nginx {start|stop|restart|reload|force-reload|status|configtest|rotate|upgrade}
Bạn có thể kiểm tra process bằng cách: ps aux | grep nginx
Cấu hình Nginx đối với Passenger
sudo nano /etc/nginx/nginx.conf
Uncomment hai dòng như bên dưới:
##
# Phusion Passenger config
##
# Uncomment it if you installed passenger or passenger-enterprise
##
include /etc/nginx/passenger.conf;
Edit file sudo nano /etc/nginx/sites-enabled/default
như sau:
server {
listen 80 default_server;
passenger_enabled on;
root /var/www/your_app/current/public;
}
hoặc tạo file sau với nội dung như trên
sudo rm /etc/nginx/sites-enabled/default
sudo nano /etc/nginx/sites-available/your_app
sudo ln -s /etc/nginx/sites-available/your_app /etc/nginx/sites-enabled/default
Chạy sudo service nginx restart
để khởi động lại Nginx.
Nginx + Unicorn
Nginx: sudo apt-get -y install nginx
Cấu hình Nginx với Unicorn
Edit file sudo nano /etc/nginx/sites-enabled/default
như sau:
upstream backend-unicorn {
server unix:/var/www/your_app/current/tmp/sockets/unicorn.sock;
}
server {
listen 80;
root /var/www/your_app/current/public;
# Make site accessible from http://localhost/
server_name localhost;
location / {
try_files $uri @webapp;
}
location @webapp {
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 100;
proxy_pass http://backend-unicorn;
}
}
Cài đặt MySQL
sudo apt-get install mysql-server mysql-client libmysqlclient-dev
Cài đặt NodeJS (JavaScript runtime)
sudo apt-get install nodejs
Deploy với Capistrano gem
Nếu bạn sử dụng passenger làm Rails server:
Thêm vào Gemfile, sau đó bundle install
gem 'capistrano-rails'
gem 'capistrano-bundler'
gem 'capistrano-rvm'
gem 'capistrano-passenger'
Nếu bạn sử dụng unicorn làm Rails server:
gem 'capistrano-rails'
gem 'capistrano-bundler'
gem 'capistrano-rvm'
gem "unicorn"
gem "capistrano3-unicorn"
# in your_app local folder
bundle exec cap install
More: https://github.com/capistrano/capistrano
Capfile
# Load DSL and set up stages
require 'capistrano/setup'
# Include default deployment tasks
require 'capistrano/deploy'
# Include tasks from other gems included in your Gemfile
#
# For documentation on these, see for example:
#
# https://github.com/capistrano/rvm
# https://github.com/capistrano/rbenv
# https://github.com/capistrano/chruby
# https://github.com/capistrano/bundler
# https://github.com/capistrano/rails
# https://github.com/capistrano/passenger
#
require 'capistrano/rvm'
# require 'capistrano/rbenv'
# require 'capistrano/chruby'
require 'capistrano/bundler'
require 'capistrano/rails'
require 'capistrano/passenger'
# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
deploy.rb
# config valid only for Capistrano 3.1
lock '3.4.0'
set :application, "your_app"
set :repo_url, "git@github.com:your_account/your_app.git"
# Default branch is :master
ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp
# Default deploy_to directory is /var/www/my_app
set :deploy_to, "/var/www/your_app"
# Default value for :scm is :git
set :scm, :git
# Default value for :format is :pretty
set :format, :pretty
# Default value for :log_level is :debug
set :log_level, :debug
# Default value for :pty is false
set :pty, true
# Nếu sử dụng unicorn
set :pid_file, "#{shared_path}/tmp/pids/unicorn.pid"
set :unicorn_rack_env, ENV["RAILS_ENV"] || "production"
set :unicorn_config_path, "#{current_path}/config/unicorn.rb"
# Default value for :linked_files is []
# set :linked_files, fetch(:linked_files, []).push('.env')
# Default value for linked_dirs is []
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system', 'public/uploads', 'public/assets')
# Default value for default_env is {}
set :default_env, {
rails_env: ENV["RAILS_ENV"]
}
# Default value for keep_releases is 5
set :keep_releases, 3
namespace :deploy do
desc "seed database"
task :seed do
on roles(:db) do |host|
within "#{release_path}" do
execute :rake, "db:seed"
end
end
end
after :migrate, :seed
end
Rails.root/config/unicorn.rb
rails_env = ENV["RAILS_ENV"] || "production"
num_workers = ENV["NUM_UNICORN_WORKERS"]
worker_processes (num_workers ? num_workers.to_i : 3)
app_name = "matee"
app_directory = "/var/www/#{app_name}/current"
working_directory app_directory # available in 0.94.0+
listen "#{app_directory}/tmp/sockets/unicorn.sock", backlog: 128
timeout 10000
pid "#{app_directory}/tmp/pids/unicorn.pid"
stderr_path "#{app_directory}/log/unicorn_#{rails_env}.log"
stdout_path "#{app_directory}/log/unicorn_#{rails_env}.log"
preload_app true
GC.respond_to?(:copy_on_write_friendly=) and
GC.copy_on_write_friendly = true
before_exec do |server|
ENV["BUNDLE_GEMFILE"] = "#{app_directory}/Gemfile"
end
before_fork do |server, worker|
defined?(ActiveRecord::Base) and
ActiveRecord::Base.connection.disconnect!
old_pid = "#{server.config[:pid]}.oldbin"
if old_pid != server.pid
begin
sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
Process.kill(sig, File.read(old_pid).to_i)
rescue Errno::ENOENT, Errno::ESRCH
end
end
sleep 1
end
after_fork do |server, worker|
defined?(ActiveRecord::Base) and
ActiveRecord::Base.establish_connection
end
Bạn có thể cấu hình file config/deploy/production.rb
như sau
role :app, %w{deploy@your_droplet_ip}
role :web, %w{deploy@your_droplet_ip}
role :db, %w{deploy@your_droplet_ip}
server 'your_droplet_ip', user: 'deploy', roles: %w{web app db}
Let's deploy
Tạo và phân quyền các thư mục cho app của bạn:
# in vps, root user
deploy_to=/var/www/your_app
mkdir -p ${deploy_to}
mkdir ${deploy_to}/{releases,shared}
chown -R deploy:dev ${deploy_to}
chmod 2775 ${deploy_to}
Tao file database.yml
su - deploy
sudo nano /var/www/your_app/shared/config/database.yml
Cấu hình mysql cho app của bạn như sau:
default: &default
adapter: mysql2
encoding: utf8
pool: 5
username: root
password: your_root_password
production:
<<: *default
database: your_app_production
Tạo file secrets.yml
sudo nano /var/www/your_app/shared/config/secrets.yml
Điền vào file với nội dung như sau:
production:
secret_key_base: your_production_key_here
Bạn có thể tạo secret key bằng cách chạy rake secret
ở your_app local folder
Tạo database:
mysql -u root -p
CREATE USER 'your_user'@'localhost' IDENTIFIED BY 'mypass';
exit
mysql -u your_user -p
create database your_app_production;
In your_app local folder:
RAILS_ENV=production cap production deploy
Nhập vào branch muốn deploy: mặc định là branch hiện tại của bạn
Check your deploy:
# in vps
cd /var/www/your_app
ll
Bạn sẽ thấy thư mục current symlink đến bản releases 20141127094637 vừa deploy xong:
total 28
drwxr-sr-x 6 deploy deploy 4096 Nov 27 04:48 ./
drwxr-xr-x 3 root root 4096 Nov 27 03:30 ../
lrwxrwxrwx 1 deploy deploy 38 Nov 27 04:48 current -> /var/www/your_app/releases/20141127094637/
drwxrwsr-x 5 deploy deploy 4096 Nov 27 04:46 releases/
drwxrwsr-x 7 deploy deploy 4096 Nov 27 04:33 repo/
-rw-rw-r-- 1 deploy deploy 70 Nov 27 04:48 revisions.log
drwxrwsr-x 2 deploy deploy 4096 Nov 27 03:36 rvm1scripts/
drwxrwsr-x 6 deploy deploy 4096 Nov 27 04:33 shared/
Tiếp tục:
cd current
ll config
Symlink của database.yml và secrets.yml:
total 44
drwxrwsr-x 7 deploy deploy 4096 Nov 27 04:46 ./
drwxrwsr-x 14 deploy deploy 4096 Nov 27 04:48 ../
-rw-rw-r-- 1 deploy deploy 1436 Nov 13 09:09 application.rb
-rw-rw-r-- 1 deploy deploy 170 Nov 13 09:09 boot.rb
lrwxrwxrwx 1 deploy deploy 41 Nov 27 04:46 database.yml -> /var/www/your_app/shared/config/database.yml
-rw-rw-r-- 1 deploy deploy 150 Nov 13 09:09 environment.rb
drwxrwsr-x 2 deploy deploy 4096 Nov 13 09:09 environments/
drwxrwsr-x 2 deploy deploy 4096 Nov 13 09:09 initializers/
drwxrwsr-x 5 deploy deploy 4096 Nov 13 09:09 locales/
drwxrwsr-x 2 deploy deploy 4096 Nov 13 09:09 routes/
-rw-rw-r-- 1 deploy deploy 1653 Nov 13 09:09 routes.rb
lrwxrwxrwx 1 deploy deploy 40 Nov 27 04:46 secrets.yml -> /var/www/your_app/shared/config/secrets.yml
drwxrwsr-x 2 deploy deploy 4096 Nov 13 09:09 settings/
-rw-rw-r-- 1 deploy deploy 0 Nov 13 09:09 settings.yml
Sau khi deploy xong bạn có thể dùng trình duyệt truy cập vào app: http://your_droplet_ip
Update: Đối với các file chứa các thông tin bí mật như secrets.yml, database.yml và devise.rb (nếu có) các bạn có thể sử dụng biến môi trường thay vì symlink như cách ở trên. Hai gem được sử dụng phổ biến là: dotenv và figaro
Các bài viết tham khảo:
- https://www.digitalocean.com/community/tutorials/how-to-set-up-nginx-virtual-hosts-server-blocks-on-ubuntu-12-04-lts--3
- https://www.phusionpassenger.com/documentation/Users guide Nginx.html#rubygems_generic_install
- http://capistranorb.com/documentation/getting-started/authentication-and-authorisation/
- https://www.digitalocean.com/community/tutorials/how-to-set-up-automatic-deployment-with-git-with-a-vps
- http://capistranorb.com/documentation/getting-started/flow/
All rights reserved