Use Capistrano to deploy Rails app
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 ...
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($$.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/