1. Tạo 1 user để deploy
sudo adduser deploy
cấp quyền cho user
sudo nano /etc/sudoers
%deploy ALL=(ALL) ALL
chuyển sang làm việc với user deploy
sudo su - deploy
2. Add ssh key authentication
Để lấy code từ github thông qua ssh
ssh-keygen -t rsa -b 4096 -C "YOUR EMAIL"
và enter liên tục thôi. Dùng lệnh cat ~/.ssh/id_rsa.pub để hiển thị ssh-key, copy rồi paste vào setting SSH and GPG key của bạn rồi tạo mới SHH key
Tiếp theo lập ssh authentication để kết nối được với server ở local không phải cần password hay thông qua user ubuntu
mkdir .ssh sudo chmod 700 .ssh touch ~/.ssh/authorized_keys sudo chmod 600 ~/.ssh/authorized_keys
Ta lấy ssh-key dùng ở trên để rồi copy paste vào file authorized_keys là được Giờ các bạn có thể nhanh chóng vào server ở local thông qua lệnh
shh [email protected]<ip_server_aws>
<ip_server_aws> là IPv4 public ip, các bạn có thể thấy ở trang Instance
3. Cài đặt Rails
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 curl -sSL https://get.rvm.io | bash -s stable source ~/.rvm/scripts/rvm rvm requirements rvm install 2.5.1 rvm use ruby-2.5.1 --default
gem install rails 5.2.1 --no-ri --no-rdoc gem install bundler --no-ri --no-rdoc rails -v
sudo apt-get install mysql-server mysql-client libmysqlclient-dev
4. Cài đặt Nginx
sudo apt-get update sudo apt-get install curl git-core nginx -y
TIếp theo là cấu hình cho việc lưu log nginx. Thay đổi user default trong file sudo nano /etc/nginx/nginx.conf thành deploy
5. Cài đặt Capistrano
Thêm gem Capistrano vào Gemfile
gem 'capistrano', '~> 3.11.0', require: false gem 'capistrano-rvm', '~> 0.1.2', require: false gem 'capistrano-rails', '~> 1.4.0', require: false gem 'capistrano-bundler', '~> 1.3.0', require: false gem 'capistrano3-puma', '~> 3.1.1', require: false
cài đặt thư viện
bundle
Khởi tạo Capistrano
cap install
Capfile
require 'capistrano/setup' require 'capistrano/deploy' require 'capistrano/rails' require 'capistrano/bundler' require 'capistrano/rvm' require 'capistrano/puma' install_plugin Capistrano::Puma # Default puma tasks require "capistrano/scm/git" install_plugin Capistrano::SCM::Git Dir.glob('lib/capistrano/tasks/*.rake').eachmport r }
config/deploy/production.rb
# Change these # By default your port will be 22 server "YOUR_SERVER_IP", port: YOUR_PORT, roles: %w(web app db), primary: true set :repo_url, "YOUR_GIT_URL" set :application, "YOUR_APP_NAME_production" set :user, "ubuntu" set :puma_threads, [4, 16] set :puma_workers, 0 # Don"t change these unless you know what you"re doing set :pty, true set :use_sudo, false set :stage, :production set :deploy_via, :remote_cache set :deploy_to, "/home/#{fetch(:user)}/apps/#{fetch(:application)}" set :puma_bind, "unix://#{shared_path}/tmp/sockets/#{fetch(:application)}-puma.sock" set :puma_state, "#{shared_path}/tmp/pids/puma.state" set :puma_pid, "#{shared_path}/tmp/pids/puma.pid" set :puma_access_log, "#{release_path}/log/puma.access.log" set :puma_error_log, "#{release_path}/log/puma.error.log" set :ssh_options, { forward_agent: true, user: fetch(:user), keys: %w(~/.ssh/id_rsa.pub) } set :puma_preload_app, true set :puma_worker_timeout, nil set :puma_init_active_record, true # Change to false when not using ActiveRecord ## Defaults: # set :scm, :git # set :branch, :master # set :format, :pretty # set :log_level, :debug # set :keep_releases, 5 ## Linked Files & Directories (Default None): # set :linked_files, %w{config/database.yml} # set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system} namespace :puma do desc "Create Directories for Puma Pids and Socket" task :make_dirs do on roles(:app) do execute "mkdir #{shared_path}/tmp/sockets -p" execute "mkdir #{shared_path}/tmp/pids -p" end end before :start, :make_dirs end namespace :deploy do desc "Make sure local git is in sync with remote." task :check_revision do on roles(:app) do unless `git rev-parse HEAD` == `git rev-parse origin/master` puts "WARNING: HEAD is not the same as origin/master" puts "Run `git push` to sync changes." exit end end end desc "Initial Deploy" task :initial do on roles(:app) do invoke "deploy" end end before :starting, :check_revision after :finishing, :compile_assets after :finishing, :cleanup after :finishing, :restart end # ps aux | grep puma # Get puma pid # kill -s SIGUSR2 pid # Restart puma # kill -s SIGTERM pid # Stop puma
config/deploy/staging.rb
# By default your port will be 22 server 'YOUR_SERVER_IP', port: YOUR_PORT, roles: [:web, :app, :db], primary: true set :repo_url, 'YOUR_GIT_URL' set :application, 'YOUR_APP_NAME_staging' set :user, 'ubuntu' set :puma_threads, [4, 16] set :puma_workers, 0 set :branch, :staging set :port, 3000 # Don't change these unless you know what you're doing set :pty, true set :use_sudo, false set :stage, :production set :deploy_via, :remote_cache set :deploy_to, "/home/#{fetch(:user)}/apps/#{fetch(:application)}" set :puma_bind, "unix://#{shared_path}/tmp/sockets/#{fetch(:application)}-puma.sock" set :puma_state, "#{shared_path}/tmp/pids/puma.state" set :puma_pid, "#{shared_path}/tmp/pids/puma.pid" set :puma_access_log, "#{release_path}/log/puma.access.log" set :puma_error_log, "#{release_path}/log/puma.error.log" set :ssh_options, { forward_agent: true, user: fetch(:user), keys: %w(~/.ssh/id_rsa.pub) } set :puma_preload_app, true set :puma_worker_timeout, nil set :puma_init_active_record, true # Change to false when not using ActiveRecord ## Defaults: # set :scm, :git # set :format, :pretty # set :log_level, :debug # set :keep_releases, 5 ## Linked Files & Directories (Default None): # set :linked_files, %w{config/database.yml} # set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system} namespace :puma do desc 'Create Directories for Puma Pids and Socket' task :make_dirs do on roles(:app) do execute "mkdir #{shared_path}/tmp/sockets -p" execute "mkdir #{shared_path}/tmp/pids -p" end end before :start, :make_dirs end namespace :deploy do desc "Make sure local git is in sync with remote." task :check_revision do on roles(:app) do unless `git rev-parse HEAD` == `git rev-parse origin/master` puts "WARNING: HEAD is not the same as origin/master" puts "Run `git push` to sync changes." exit end end end desc 'Initial Deploy' task :initial do on roles(:app) do invoke 'deploy' end end before :starting, :check_revision after :finishing, :compile_assets after :finishing, :cleanup after :finishing, :restart end # ps aux | grep puma # Get puma pid # kill -s SIGUSR2 pid # Restart puma # kill -s SIGTERM pid # Stop puma
tạo file config/nginx
# config/nginx/nginx_production.conf upstream puma { server unix:///home/deploy/apps/YOUR_APP_NAME_production/shared/tmp/sockets/YOUR_APP_NAME_production-puma.sock; } server { listen 80 default_server deferred; # server_name example.com; root /home/deploy/apps/YOUR_APP_NAME_production/current/public; access_log /home/deploy/apps/YOUR_APP_NAME_production/current/log/nginx.access.log; error_log /home/deploy/apps/YOUR_APP_NAME_production/current/log/nginx.error.log info; location ^~ /assets/ { gzip_static on; expires max; add_header Cache-Control public; } try_files $uri/index.html $uri @puma; location @puma { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://puma; } error_page 500 502 503 504 /500.html; client_max_body_size 10M; keepalive_timeout 10; }
config/nginx/nginx_production.conf
# config/nginx/nginx_production.conf upstream puma { server unix:///home/deploy/apps/YOUR_APP_NAME_production/shared/tmp/sockets/YOUR_APP_NAME_production-puma.sock; } server { listen 80 default_server deferred; # server_name example.com; root /home/deploy/apps/YOUR_APP_NAME_production/current/public; access_log /home/deploy/apps/YOUR_APP_NAME_production/current/log/nginx.access.log; error_log /home/deploy/apps/YOUR_APP_NAME_production/current/log/nginx.error.log info; location ^~ /assets/ { gzip_static on; expires max; add_header Cache-Control public; } try_files $uri/index.html $uri @puma; location @puma { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://puma; } error_page 500 502 503 504 /500.html; client_max_body_size 10M; keepalive_timeout 10; }
config/nginx/nginx_staging.conf
# config/nginx/nginx_staging.conf upstream staging { server unix:///home/ubuntu/apps/YOUR_RAILS_APP_staging/shared/tmp/sockets/cap-rails-staging-puma.sock; } server { listen 3000 default_server deferred; # server_name example.com; root /home/ubuntu/apps/YOUR_RAILS_APP_staging/current/public; access_log /home/ubuntu/apps/YOUR_RAILS_APP_staging/current/log/nginx.access.log; error_log /home/ubuntu/apps/YOUR_RAILS_APP_staging/current/log/nginx.error.log info; location ^~ /assets/ { gzip_static on; expires max; add_header Cache-Control public; } try_files $uri/index.html $uri @staging; location @staging { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://staging; } error_page 500 502 503 504 /500.html; client_max_body_size 10M; keepalive_timeout 10; }
cap production deploy:initial cap staging deploy:initial
scp /config/master.key [email protected]_REMOTE_IP:/home/YOUR_REMOTE_USER/apps/YOUR_APP_NAME_production/shared/config/ scp /config/master.key [email protected]_REMOTE_IP:/home/YOUR_REMOTE_USER/apps/YOUR_APP_NAME_staging/shared/config/
/etc/nginx/sites-enabled
# the default remote config, you can backup it first sudo rm /etc/nginx/sites-enabled/default # symlink new configuration file sudo ln -nfs "/home/deploy/apps/YOUR_APP_NAME_production/current/config/nginx/nginx_production.conf" "/etc/nginx/sites-enabled/YOUR_APP_NAME_production" sudo ln -nfs "/home/deploy/apps/YOUR_APP_NAME_production/current/config/nginx/nginx_staging.conf" "/etc/nginx/sites-enabled/YOUR_APP_NAME_staging"
sudo service nginx restart
cap production deploy cap staging deploy
cap production / staging puma:status cap production / staging puma:start cap production / staging puma:stop cap production / staging puma:restart
tài liệu tham khảo: capistranorb nginx how-to-deploy-ruby-on-rails-apps-on-aws-ec2